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

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

        #endregion

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

        /// <summary>
        ///     Occurs when the COM_AnalyzerSendMessageReq button was clicked.
        /// </summary>
        public event EventHandler<SendMessageEventArgs> ButtonSendMessageClicked;

        /// <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 ScheduleTableEntries = new List<ScheduleTableEntry>(this.listViewScheduleTable.Items.Count);
            ScheduleTableEntries.AddRange(
                from ListViewItem item in this.listViewScheduleTable.Items
                select
                    new ScheduleTableEntry
                    {
                        FrameID = Convert.ToByte(item.SubItems[0].Text),
                        TimingOffset = Convert.ToUInt16(item.SubItems[1].Text),
                        TimingPeriod = Convert.ToUInt16(item.SubItems[2].Text)
                    });

            return ChannelConfigurationLIN.CreatePackets(ScheduleTableEntries,
                                                         Convert.ToUInt32((LINBaudrate)this.comboBoxBaudrate.SelectedItem),
                                                         this.checkBoxMasterMode.Checked,
                                                         this.checkBoxRxOwn.Checked,
                                                         (uint)this.numericUpDownTimingBase.Value);

            throw new NotSupportedException();
        }

        #endregion

        #region Methods

        // ReSharper disable once InconsistentNaming
        private void OnAddScheduleTableEntry(object sender, EventArgs e)
        {
            var listViewItem =
                new ListViewItem(
                    new[]
                    {
                        this.numericUpDownFrameID.Value.ToString(CultureInfo.InvariantCulture), this.numericUpDownTimingOffset.Value.ToString(CultureInfo.InvariantCulture),
                        this.numericUpDownTimingPeriod.Value.ToString(CultureInfo.InvariantCulture)
                    });

            this.listViewScheduleTable.Items.Add(listViewItem);
            this.label14.Text = this.listViewScheduleTable.Items.Count.ToString(CultureInfo.InvariantCulture);
        }

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

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

        // ReSharper disable once InconsistentNaming
        private void OnButtonAddFilterClicked(object sender, EventArgs e)
        {
            var newBusFilter = new BusSpecificFilterLIN
            {
                FrameID = Convert.ToByte(this.numericUpDownFilterConfigurationFrameID.Value),
                Extended = this.checkBoxFilterConfigurationExt.Checked,
                Checksum = this.checkBoxFilterConfigurationCRC.Checked
                    ? LINChecksumType.Enhanced : LINChecksumType.Classic,
                RX = this.checkBoxFilterConfigurationRX.Checked,
                Length = Convert.ToByte(this.numericUpDownFilterConfigurationPayloadLength.Value),
            };

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

            this.busSpecificFilters.Add(newBusFilter);
            this.lvFilters.Items.Add(new ListViewItem(
                new[]
                {
                    newBusFilter.FrameID.ToString(CultureInfo.InvariantCulture),    // [0]
                    newBusFilter.Length.ToString(CultureInfo.InvariantCulture),     // [1]
                    newBusFilter.Extended.ToString(CultureInfo.InvariantCulture),   // [2]
                    newBusFilter.RX.ToString(CultureInfo.InvariantCulture),         // [3]
                    Enum.GetName(typeof(LINChecksumType), newBusFilter.Checksum)    // [4]
                })
            );
        }

        private void OnButtonRemoveFilterClicked(object sender, EventArgs e)
        {
            var removedItems = new List<IBusSpecificFilter>();
            foreach (ListViewItem item in lvFilters.SelectedItems)
            {
                if (item.SubItems.Count >= 5)
                {
                    var itemEnum = LINChecksumType.Classic;
                    foreach (LINChecksumType enumValue in Enum.GetValues(typeof(LINChecksumType)))
                    {
                        if (Enum.GetName(typeof(LINChecksumType), enumValue) == item.SubItems[4].Text)
                        {
                            itemEnum = enumValue;
                            break;
                        }
                    }

                    if (byte.TryParse(item.SubItems[0].Text, out var tempId)
                        && byte.TryParse(item.SubItems[1].Text, out var tempLength)
                        && bool.TryParse(item.SubItems[2].Text, out var isExtended)
                        && bool.TryParse(item.SubItems[3].Text, out var isRx)
                        )
                    {
                        removedItems.AddRange(
                    busSpecificFilters.Where(p =>
                                p is BusSpecificFilterLIN filterLin
                                && filterLin.FrameID == tempId
                                && filterLin.Length == tempLength
                                && filterLin.Extended == isExtended
                                && filterLin.RX == isRx
                                && filterLin.Checksum == itemEnum)
                            );
                    }
                }

                lvFilters.Items.Remove(item);
            }

            if (removedItems.Any())
            {
                // Deactivate removed filters in device
                IFilterConfiguration filterConfiguration = new FilterConfigurationLIN
                {
                    BusType = BusTypes.LIN,
                    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 BusSpecificFilterLIN);
            lvFilters.Items.Clear();
        }

        private void OnButtonResetAllFiltersClicked(object sender, EventArgs e)
        {
            ButtonResetAllFiltersClicked?.Invoke(this, new ResetAllFiltersEventArgs(BusTypes.LIN, 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 FilterConfigurationLIN
            {
                BusType = BusTypes.LIN,
                ChannelId = 0,
                SF = this.checkBoxFilterConfigurationSF.Checked,
                AA = this.checkBoxFilterConfigurationAA.Checked
            };

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

        private void OnButtonSendMessageClicked(object sender, EventArgs e)
        {
            var handler = this.ButtonSendMessageClicked;
            if (handler == null)
            {
                return;
            }
            
            var data = this.textBoxSendMessageData.Text.GetByteArray();
            if (data == null)
            {
                // ReSharper disable LocalizableElement
                MessageBox.Show(
                    "The data stream value is invalid. Please ensure, that the data stream is a valid hex stream. Examples: '00 0A 4B', '00-0A-4B' or '00 0a-4B'",
                    "Invalid data stream value",
                    MessageBoxButtons.OK);
                // ReSharper restore LocalizableElement
                return;
            }

            var length = data.Length > 8 ? 8 : data.Length;
            var config = new SendLINMessageConfiguration()
            {
                BusType = BusTypes.LIN,
                ChannelId = 0,
                FrameID =
                    Convert.ToByte(
                        this.numericUpDownSendMessage.Value),
                Length = (byte)length,
                Sync = this.checkBoxSendMessageSync.Checked,
                Checksum = this.checkBoxSendMessageCrc.Checked?LINChecksumType.Enhanced : LINChecksumType.Classic,
                Data = data.Take(length).ToArray()
            };

            handler(this, new SendMessageEventArgs(config));
        }

        #endregion

        #region Constants and Fields

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

        #endregion
    }
}