using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Ebel.Hardware.Communications;

namespace HwComWrapperNETGui.UserControls
{
    /// <summary>
    ///     The channel configuration control.
    /// </summary>
    public partial class ChannelConfigurationControlFlexRay : UserControl
    {
        #region Constructors and Destructors
        
        /// <summary>
        ///     Initializes a new instance of the <see cref="ChannelConfigurationControlFlexRay"/> class.
        /// </summary>
        public ChannelConfigurationControlFlexRay()
        {
            this.InitializeComponent();
            if (!this.DesignMode)
            {
                this.comboBoxCHIOpcode.DataSource = Enum.GetValues((typeof(CHIOpcode)));
                this.comboBoxCHIOpcode.SelectedItem = CHIOpcode.SetConfigmode;
            }

            OnAddCHIDirective(null, null); // Add dummy entry to allow instant configuration
        }

        #endregion

        /// <summary>
        ///     Occurs when the COM_AnalyzerResetAllFiltersReq button was clicked.
        /// </summary>
        public event EventHandler<ResetAllFiltersEventArgs> ButtonResetAllFiltersClicked;

        /// <summary>
        ///     Occurs when the COM_AnalyzerConfigureChannelReq button was clicked.
        /// </summary>
        public event EventHandler<ChannelConfigurationsEventArgs> ButtonSetChannelConfigurationClicked;

        /// <summary>
        ///     Occurs when the COM_AnalyzerConfigureFilterReq button was clicked.
        /// </summary>
        public event EventHandler<FilterConfigurationEventArgs> ButtonSetFilterConfigurationClicked;

        #region Public Methods

        /// <summary>
        ///     Gets the <see cref="ChannelConfiguration"/> depending on the bus type.
        /// </summary>
        /// <returns> the <see cref="ChannelConfiguration"/> depending on the bus type.</returns>
        public IEnumerable<ChannelConfiguration> GetChannelConfiguration()
        {
            var chiDirectives = new List<CHIDirective>(this.listViewCHIDirectives.Items.Count);
            chiDirectives.AddRange(
                from ListViewItem item in this.listViewCHIDirectives.Items
                select
                    new CHIDirective
                    {
                        Opcode = (CHIOpcode)Enum.Parse(typeof(CHIOpcode), item.SubItems[0].Text),
                        Address = Convert.ToUInt32(item.SubItems[1].Text),
                        Value = Convert.ToUInt32(item.SubItems[2].Text)
                    });

            return ChannelConfigurationFlexRay.CreatePackets(chiDirectives, 0, this.checkBoxColdStart.Checked);
        }

        #endregion
        
        #region Methods

        // ReSharper disable once InconsistentNaming
        private void OnAddCHIDirective(object sender, EventArgs e)
        {
            var listViewItem =
                new ListViewItem(
                    new[]
                    {
                        this.comboBoxCHIOpcode.SelectedValue.ToString(), this.numericUpDownAddress.Value.ToString(CultureInfo.InvariantCulture),
                        this.numericUpDownValue.Value.ToString(CultureInfo.InvariantCulture)
                    });

            this.listViewCHIDirectives.Items.Add(listViewItem);
            this.labelNumberOfCHIDirectives.Text = this.listViewCHIDirectives.Items.Count.ToString(CultureInfo.InvariantCulture);
        }

        // ReSharper disable once InconsistentNaming
        private void OnRemoveCHIDirectives(object sender, EventArgs e)
        {
            foreach (ListViewItem listViewItem in this.listViewCHIDirectives.SelectedItems)
            {
                this.listViewCHIDirectives.Items.Remove(listViewItem);
            }

            this.labelNumberOfCHIDirectives.Text = this.listViewCHIDirectives.Items.Count.ToString(CultureInfo.InvariantCulture);
        }

        // ReSharper disable once InconsistentNaming
        private void OnButtonAddFilterClicked(object sender, EventArgs e)
        {
            var newBusFilter = new BusSpecificFilterFlexRay
            {
                CycleOffset = Convert.ToByte(this.numericUpDownFilterConfigurationCycleOffset.Value),
                CycleRepetition = Convert.ToByte(this.numericUpDownFilterConfigurationCycleRepetition.Value),
                FID = Convert.ToUInt16(this.numericUpDownFilterConfigurationFID.Value),
                NF = this.checkBoxFilterConfigurationNF.Checked
            };

            if (busSpecificFilters.Exists(p => newBusFilter.IsEqual(p)))
            {
                // Already in list
                return;
            }

            this.busSpecificFilters.Add(newBusFilter);
            this.lvFilters.Items.Add(new ListViewItem(
                new[]
                {
                    newBusFilter.FID.ToString(CultureInfo.InvariantCulture),             // [0]
                    newBusFilter.CycleOffset.ToString(CultureInfo.InvariantCulture),     // [1]
                    newBusFilter.CycleRepetition.ToString(CultureInfo.InvariantCulture), // [2]
                    newBusFilter.NF.ToString(CultureInfo.InvariantCulture),              // [3]
                })
            );
        }

        private void OnButtonRemoveFilterClicked(object sender, EventArgs e)
        {
            var removedItems = new List<IBusSpecificFilter>();
            foreach (ListViewItem item in lvFilters.SelectedItems)
            {
                if (item.SubItems.Count >= 4)
                {
                    if (ushort.TryParse(item.SubItems[0].Text, out var tempId)
                        && byte.TryParse(item.SubItems[1].Text, out var tempOffset)
                        && byte.TryParse(item.SubItems[2].Text, out var tempRepetition)
                        && bool.TryParse(item.SubItems[3].Text, out var isNF)
                        )
                    {
                        removedItems.AddRange(
                    busSpecificFilters.Where(p =>
                                p is BusSpecificFilterFlexRay filterFlexRay
                                && filterFlexRay.FID == tempId
                                && filterFlexRay.CycleOffset == tempOffset
                                && filterFlexRay.CycleRepetition == tempRepetition
                                && filterFlexRay.NF == isNF)
                            );
                    }
                }

                lvFilters.Items.Remove(item);
            }

            if (removedItems.Any())
            {
                // Deactivate removed filters in device
                IFilterConfiguration filterConfiguration = new FilterConfigurationFlexRay
                {
                    BusType = BusTypes.FlexRay,
                    ChannelId = 0,
                    SF = false
                };

                filterConfiguration.AddFilters(removedItems);
                ButtonSetFilterConfigurationClicked?.Invoke(this, new FilterConfigurationEventArgs(filterConfiguration));
                removedItems.ForEach(x => busSpecificFilters.Remove(x));
            }
        }

        // ReSharper disable once InconsistentNaming
        private void OnButtonClearFilterClicked(object sender, EventArgs e)
        {
            this.busSpecificFilters.RemoveAll(p => p is BusSpecificFilterFlexRay);
            lvFilters.Items.Clear();
        }

        private void OnButtonResetAllFiltersClicked(object sender, EventArgs e)
        {
            ButtonResetAllFiltersClicked?.Invoke(this, new ResetAllFiltersEventArgs(BusTypes.FlexRay, 0));
        }

        private void OnButtonSetChannelConfigurationClicked(object sender, EventArgs e)
        {
            var handler = this.ButtonSetChannelConfigurationClicked;
            if (handler == null)
            {
                return;
            }

            var channelConfigurations = this.GetChannelConfiguration();
            handler(this, new ChannelConfigurationsEventArgs(channelConfigurations));
        }

        private void OnButtonSetFilterConfigurationClicked(object sender, EventArgs e)
        {
            var handler = this.ButtonSetFilterConfigurationClicked;
            if (handler == null)
            {
                return;
            }

            IFilterConfiguration filterConfiguration = new FilterConfigurationFlexRay
            {
                BusType = BusTypes.FlexRay,
                ChannelId = 0,
                SF = this.checkBoxFilterConfigurationSF.Checked,
                AA = this.checkBoxFilterConfigurationAA.Checked
            };

            filterConfiguration.AddFilters(busSpecificFilters.Where(p => p is BusSpecificFilterFlexRay));
            handler(this, new FilterConfigurationEventArgs(filterConfiguration));
        }

        #endregion

        #region Constants and Fields

        private readonly List<IBusSpecificFilter> busSpecificFilters = new List<IBusSpecificFilter>();

        #endregion

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void btnImportFromChi_Click(object sender, EventArgs e)
        {
            try
            {
                var dialog = new OpenFileDialog
                {
                    Filter = "chi files(*.chi)|*.chi",
                    RestoreDirectory = true,
                    CheckFileExists = true
                };
            
                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    var chiDirectives = new ChiParser().Parse(File.ReadAllLines(dialog.FileName));
                    var listViewItemsToAdd = chiDirectives.Select(x =>
                    {
                        return new ListViewItem(
                            new[]
                            {
                                x.Opcode.ToString(), x.Address.ToString(CultureInfo.InvariantCulture),
                                x.Value.ToString(CultureInfo.InvariantCulture)
                            });
                    });
                    this.listViewCHIDirectives.Items.Clear();
                    this.listViewCHIDirectives.Items.AddRange(listViewItemsToAdd.ToArray());
                    this.labelNumberOfCHIDirectives.Text = this.listViewCHIDirectives.Items.Count.ToString(CultureInfo.InvariantCulture);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Failure during parsing chi file: {ex.Message}", "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }

        class ChiParser
        {
            private readonly Regex command = new Regex(".*;");
            private readonly Regex hexValue = new Regex("0x[a-fA-F0-9]*");

            public IEnumerable<CHIDirective> Parse(string[] chiLines)
            {
                var list = new List<CHIDirective>();
                foreach (var line in chiLines)
                {
                    var cmd = command.Match(line).Value;
                    var chi = new CHIDirective();
                    switch (cmd)
                    {
                        case string s when s.StartsWith("SET_CONFIGMODE"):
                            chi.Opcode = CHIOpcode.SetConfigmode;
                            break;

                        case string s when s.StartsWith("CHECK_CTRL_TYPE"):
                            chi.Opcode = CHIOpcode.CheckCtrlType;
                            chi.Value = GetValue(cmd);
                            break;

                        case string s when s.StartsWith("WRITE32"):
                            chi.Opcode = CHIOpcode.Write32;
                            chi.Value = GetValue(cmd);
                            chi.Address = GetValue(cmd, 1);
                            break;

                        case string s when s.StartsWith("WAIT_TILL_CLEARED32"):
                            chi.Opcode = CHIOpcode.WaitTillCleared;
                            chi.Value = GetValue(cmd);
                            chi.Address = GetValue(cmd, 1);
                            break;

                        case string s when s.StartsWith("CONFIG_COMPLETE_ASYNC"):
                            chi.Opcode = CHIOpcode.ConfigCompleteAsync;
                            break;

                        case string s when s.StartsWith("CONFIG_COMPLETE"):
                            chi.Opcode = CHIOpcode.ConfigComplete;
                            break;

                    }

                    if (chi.Opcode != CHIOpcode.None)
                    {
                        list.Add(chi);
                    }
                }

                return list;
            }

            uint GetValue(string l, int index = 0)
            {
                var m = hexValue.Matches(l);
                return uint.Parse(m[index].Value.Replace("0x", ""), NumberStyles.HexNumber);
            }
        }
    }
}