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 ChannelConfigurationControlCANFD : UserControl
    {
        #region Constructors and Destructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="ChannelConfigurationControlCANFD"/> class.
        /// </summary>
        public ChannelConfigurationControlCANFD()
        {
            this.InitializeComponent();
            if (!this.DesignMode)
            {
                this.comboBoxCanFdDataBaudrate.DataSource = Enum.GetValues(typeof(CANFDBaudrate));
                this.comboBoxCanFdDataBaudrate.SelectedItem = CANFDBaudrate.KBit2000;

                this.comboBoxCanFdArbitrationBaudrate.DataSource = Enum.GetValues(typeof(CANFDArbitrationBaudrate));
                this.comboBoxCanFdArbitrationBaudrate.SelectedItem = CANFDArbitrationBaudrate.KBit500;
            }
        }

        #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()
        {
            return new List<ChannelConfigurationCANFD>
            {
                new ChannelConfigurationCANFD
                {
                    BusType = BusTypes.CAN_FD, 
                    ArbitrationBaudrate = (CANFDArbitrationBaudrate)this.comboBoxCanFdArbitrationBaudrate.SelectedItem,
                    DataBaudrate = (CANFDBaudrate)this.comboBoxCanFdDataBaudrate.SelectedItem,
                    Protocol = this.radioButtonIso.Checked ? CANFDProtocolType.Iso : CANFDProtocolType.NonIso,
                    FD = this.checkBoxFdMode.Checked,
                    BRS = this.checkBoxBRS.Checked,
                    LO = this.checkBoxCanFdLo.Checked,
                    ChannelId = 0
                }
            };
        }

        #endregion

        #region Methods
        // ReSharper disable once InconsistentNaming
        private void OnButtonAddFilterClicked(object sender, EventArgs e)
        {
            var newBusFilter = new BusSpecificFilterCANFD
            {
                Ex = this.checkBoxFilterConfigurationCanFdEx.Checked,
                FD = this.checkBoxFilterConfigurationCanFdFd.Checked,
                ID = Convert.ToUInt32(this.numericUpDownFilterConfigurationCanFdID.Value)
            };

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

            this.busSpecificFilters.Add(newBusFilter);
            this.lvFilters.Items.Add(new ListViewItem(
                new[]
                {
                    newBusFilter.ID.ToString(CultureInfo.InvariantCulture),
                    newBusFilter.Ex.ToString(CultureInfo.InvariantCulture),
                    newBusFilter.FD.ToString(CultureInfo.InvariantCulture)
                })
            );
        }

        private void OnButtonRemoveFilterClicked(object sender, EventArgs e)
        {
            var removedItems = new List<IBusSpecificFilter>();
            foreach (ListViewItem item in lvFilters.SelectedItems)
            {
                if (item.SubItems.Count >= 3)
                {
                    if (uint.TryParse(item.SubItems[0].Text, out var tempId)
                        && bool.TryParse(item.SubItems[1].Text, out var isExtended)
                        && bool.TryParse(item.SubItems[2].Text, out var isFdFrame))
                    {
                        removedItems.AddRange(
                    busSpecificFilters.Where(p =>
                                p is BusSpecificFilterCANFD filterCanFd
                                && filterCanFd.ID == tempId
                                && filterCanFd.Ex == isExtended
                                && filterCanFd.FD == isFdFrame));
                    }
                }

                lvFilters.Items.Remove(item);
            }

            if (removedItems.Any())
            {
                // Deactivate removed filters in device
                IFilterConfiguration filterConfiguration = new FilterConfigurationCANFD
                {
                    BusType = BusTypes.CAN_FD,
                    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 BusSpecificFilterCANFD);
            lvFilters.Items.Clear();
            //OnButtonResetAllFiltersClicked(this, null);
        }

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

        private void OnButtonSendMessageClicked(object sender, EventArgs e)
        {
            var handler = this.ButtonSendMessageClicked;
            if (handler == null)
            {
                return;
            }
            
            var data = this.textBoxSendMessageDataCANFD.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 > 64 ? 64 : data.Length;
            var config = new SendCANFDMessageConfiguration
            {
                BusType = BusTypes.CAN_FD,
                ChannelId = 0,
                ID =
                    Convert.ToUInt32(
                        this.numericUpDownSendMessageCANFDID.Value),
                DLC = (byte)length,
                Ex = this.checkBoxSendMessageCANFDEx.Checked,
                BRS = this.checkBoxSendBrs.Checked,
                FD = this.checkBoxSendFd.Checked,
                Data = data.Take(length).ToArray()
            };

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

        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 FilterConfigurationCANFD
            {
                BusType = BusTypes.CAN_FD,
                ChannelId = 0,
                SF = this.checkBoxFilterConfigurationSF.Checked,
                AA = this.checkBoxFilterConfigurationAA.Checked
            };

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

        #endregion

        #region Constants and Fields

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

        #endregion
    }
}