using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ArdupilotMega.Controls;
using ArdupilotMega.Controls.BackstageView;
using ArdupilotMega.Utilities;
using log4net;

namespace ArdupilotMega.GCSViews.ConfigurationView
{
    public partial class ConfigFriendlyParams : UserControl, IActivate
    {
        #region Class Fields

        private static readonly ILog log =
          LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private readonly ParameterMetaDataRepository _parameterMetaDataRepository;
        private Dictionary<string, string> _params = new Dictionary<string, string>();

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the parameter mode.
        /// </summary>
        /// <value>
        /// The parameter mode.
        /// </value>
        public string ParameterMode { get; set; }

        #endregion

        #region Constructor

        public ConfigFriendlyParams()
        {
            InitializeComponent();
            tableLayoutPanel1.Height = this.Height;
            _parameterMetaDataRepository = new ParameterMetaDataRepository();

            MainV2.comPort.ParamListChanged += comPort_ParamListChanged;
            Resize += this_Resize;

            BUT_rerequestparams.Click += BUT_rerequestparams_Click;
            BUT_writePIDS.Click += BUT_writePIDS_Click;
        }

        #endregion

        #region Events

        /// <summary>
        /// Handles the Click event of the BUT_writePIDS control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void BUT_writePIDS_Click(object sender, EventArgs e)
        {
            bool errorThrown = false;
            _params.ForEach(x =>
            {
                var matchingControls = tableLayoutPanel1.Controls.Find(x.Key, true);
                if (matchingControls.Length > 0)
                {
                    var ctl = (IDynamicParameterControl)matchingControls[0];
                    try
                    {
                        MainV2.comPort.setParam(x.Key, float.Parse(ctl.Value));
                    }
                    catch
                    {
                        errorThrown = true;
                        CustomMessageBox.Show("Set " + x.Key + " Failed");
                    }
                }
            });
            if (!errorThrown)
            {
                CustomMessageBox.Show("Parameters successfully saved.");
            }
        }

        /// <summary>
        /// Handles the Click event of the BUT_rerequestparams control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void BUT_rerequestparams_Click(object sender, EventArgs e)
        {
            if (!MainV2.comPort.BaseStream.IsOpen)
                return;

            ((Control)sender).Enabled = false;

            try
            {
                MainV2.comPort.getParamList();
            }
            catch (Exception ex)
            {
                log.Error("Exception getting param list", ex);
                CustomMessageBox.Show("Error: getting param list");
            }


            ((Control)sender).Enabled = true;

            BindParamList();
        }

        /// <summary>
        /// Handles the Resize event of the this control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void this_Resize(object sender, EventArgs e)
        {
            tableLayoutPanel1.Height = this.Height - 50;
        }

        /// <summary>
        /// Handles the Load event of the ConfigRawParamsV2 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public void Activate()
        {
            BindParamList();
        }

        /// <summary>
        /// Handles the ParamListChanged event of the comPort control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void comPort_ParamListChanged(object sender, EventArgs e)
        {
            SortParamList();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Sorts the param list.
        /// </summary>
        private void SortParamList()
        {
            // Clear list
            _params.Clear();

            // When the parameter list is changed, re sort the list for our View's purposes
            MainV2.comPort.MAV.param.Keys.ForEach(x =>
            {
                string displayName = _parameterMetaDataRepository.GetParameterMetaData(x.ToString(), ParameterMetaDataConstants.DisplayName);
                string parameterMode = _parameterMetaDataRepository.GetParameterMetaData(x.ToString(), ParameterMetaDataConstants.User);

                // If we have a friendly display name AND
                if (!String.IsNullOrEmpty(displayName) &&
                    // The user type is equal to the ParameterMode specified at class instantiation OR
                      ((!String.IsNullOrEmpty(parameterMode) && parameterMode == ParameterMode) ||
                    // The user type is empty and this is in Advanced mode
                      String.IsNullOrEmpty(parameterMode) && ParameterMode == ParameterMetaDataConstants.Advanced))
                {
                    _params.Add(x.ToString(), displayName);
                }
            });
            _params = _params.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
        }

        /// <summary>
        /// Binds the param list.
        /// </summary>
        private void BindParamList()
        {
            tableLayoutPanel1.Controls.Clear();
            if (_params == null || _params.Count == 0) SortParamList();

            // get the params if nothing exists already
            if (_params != null && _params.Count == 0)
            {
                try
                {
                    Utilities.ParameterMetaDataParser.GetParameterInformation();

                    _parameterMetaDataRepository.Reload();

                    SortParamList();
                }
                catch (Exception exp) { log.Error(exp); } // just to cleanup any errors
            }

            this.SuspendLayout();

            _params.OrderBy(x => x.Key).ForEach(x =>
         {
             AddControl(x);
         });

            this.ResumeLayout();
        }

        void AddControl(KeyValuePair<string,string> x)
        {
            if (!String.IsNullOrEmpty(x.Key))
            {
                try
                {
                    bool controlAdded = false;

                    string value = ((float)MainV2.comPort.MAV.param[x.Key]).ToString("0.###", CultureInfo.InvariantCulture);
                    string description = _parameterMetaDataRepository.GetParameterMetaData(x.Key, ParameterMetaDataConstants.Description);
                    string displayName = x.Value + " (" + x.Key + ")";
                    string units = _parameterMetaDataRepository.GetParameterMetaData(x.Key, ParameterMetaDataConstants.Units);

                    // If this is a range
                    string rangeRaw = _parameterMetaDataRepository.GetParameterMetaData(x.Key, ParameterMetaDataConstants.Range);
                    string incrementRaw = _parameterMetaDataRepository.GetParameterMetaData(x.Key, ParameterMetaDataConstants.Increment);
                    
                    if (!String.IsNullOrEmpty(rangeRaw) && !String.IsNullOrEmpty(incrementRaw))
                    {
                        float increment, intValue;
                        float.TryParse(incrementRaw, out increment);
                        float.TryParse(value, out intValue);

                        string[] rangeParts = rangeRaw.Split(new[] { ' ' });
                        if (rangeParts.Count() == 2 && increment > 0)
                        {
                            float lowerRange;
                            float.TryParse(rangeParts[0], out lowerRange);
                            float upperRange;
                            float.TryParse(rangeParts[1], out upperRange);

                            float displayscale = 1;

                        //    var rangeControl = new RangeControl();

                            if (units.ToLower() == "centi-degrees")
                            {
                                Console.WriteLine(x.Key + " scale");
                                displayscale = 100;
                                units = "Degrees (Scaled)";
                                increment /= 100;
                            } else if (units.ToLower() == "centimeters")
                            {
                                Console.WriteLine(x.Key + " scale");
                                displayscale = 100;
                                units = "Meters (Scaled)";
                                increment /= 100;
                            }

                            var rangeControl = new RangeControl(x.Key, FitDescriptionText(units, description), displayName, increment, displayscale, lowerRange, upperRange, value);

                            /*
                            rangeControl.Name = x.Key;
                            rangeControl.Increment = increment;
                            rangeControl.DescriptionText = FitDescriptionText(units, description);
                            rangeControl.LabelText = displayName;
                            rangeControl.MinRange = lowerRange;
                            rangeControl.MaxRange = upperRange;
                            rangeControl.Value = value;
                            */
                            Console.WriteLine("{0} {1} {2} {3} {4}", x.Key, increment, lowerRange, upperRange, value);

                            ThemeManager.ApplyThemeTo(rangeControl);

                            if (intValue < lowerRange)
                                rangeControl.NumericUpDownControl.BackColor = Color.Orange;

                            if (intValue > upperRange)
                                rangeControl.NumericUpDownControl.BackColor = Color.Orange;
/*
                            rangeControl.TrackBarControl.Minimum = Math.Min(scaledLowerRange, (int)intValue);
                            rangeControl.TrackBarControl.Maximum = Math.Max(scaledUpperRange, (int)intValue);
                            rangeControl.TrackBarControl.TickFrequency = scaledIncrement;
                            rangeControl.TrackBarControl.Value = (int)intValue;

                            rangeControl.NumericUpDownControl.Increment = (decimal)increment;
                            rangeControl.NumericUpDownControl.DecimalPlaces = scaler.ToString(CultureInfo.InvariantCulture).Length - 1;
                            rangeControl.NumericUpDownControl.Minimum = (decimal)Math.Min(lowerRange, intValue);
                            rangeControl.NumericUpDownControl.Maximum = (decimal)Math.Max(upperRange, intValue);
                            rangeControl.NumericUpDownControl.Value = (decimal)(intValue);
                            */
                            rangeControl.AttachEvents();

                            tableLayoutPanel1.Controls.Add(rangeControl);

                            controlAdded = true;
                        }
                    }

                    if (!controlAdded)
                    {
                        // If this is a subset of values
                        string availableValuesRaw = _parameterMetaDataRepository.GetParameterMetaData(x.Key, ParameterMetaDataConstants.Values);
                        if (!String.IsNullOrEmpty(availableValuesRaw))
                        {
                            string[] availableValues = availableValuesRaw.Split(new[] { ',' });
                            if (availableValues.Any())
                            {
                                var valueControl = new ValuesControl();
                                valueControl.Name = x.Key;
                                valueControl.DescriptionText = FitDescriptionText(units, description);
                                valueControl.LabelText = displayName;

                                ThemeManager.ApplyThemeTo(valueControl);

                                var splitValues = new List<KeyValuePair<string, string>>();
                                // Add the values to the ddl
                                foreach (string val in availableValues)
                                {
                                    string[] valParts = val.Split(new[] { ':' });
                                    splitValues.Add(new KeyValuePair<string, string>(valParts[0].Trim(), (valParts.Length > 1) ? valParts[1].Trim() : valParts[0].Trim()));
                                };
                                valueControl.ComboBoxControl.DisplayMember = "Value";
                                valueControl.ComboBoxControl.ValueMember = "Key";
                                valueControl.ComboBoxControl.DataSource = splitValues;
                                valueControl.ComboBoxControl.SelectedValue = value;

                                tableLayoutPanel1.Controls.Add(valueControl);
                            }
                        }
                    }
                } // if there is an error simply dont show it, ie bad pde file, bad scale etc
                catch (Exception ex) { log.Error(ex); }
            }
        }

        /// <summary>
        /// Fits the description text.
        /// </summary>
        /// <param name="description">The description.</param>
        /// <returns></returns>
        private string FitDescriptionText(string description)
        {
            return FitDescriptionText(string.Empty, description);
        }

        /// <summary>
        /// Fits the description text.
        /// </summary>
        /// <param name="units">The units.</param>
        /// <param name="description">The description.</param>
        /// <returns></returns>
        private string FitDescriptionText(string units, string description)
        {
            var returnDescription = new StringBuilder();

            if (!String.IsNullOrEmpty(units))
            {
                returnDescription.Append(String.Format("Units: {0}{1}", units, Environment.NewLine));
            }

            if (!String.IsNullOrEmpty(description))
            {
                returnDescription.Append("Description: ");
                var descriptionParts = description.Split(new char[] { ' ' });
                for (int i = 0; i < descriptionParts.Length; i++)
                {
                    returnDescription.Append(String.Format("{0} ", descriptionParts[i]));
                    if (i != 0 && i % 12 == 0) returnDescription.Append(Environment.NewLine);
                }
            }

            return returnDescription.ToString();
        }

        #endregion
    }
}
