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

namespace HwComWrapperNETGui.UserControls
{
    public partial class ChannelConfigurationControlEthernet : UserControl
    {
        public ChannelConfigurationControlEthernet(BusTypes busType)
        {
            BusType = busType;
            InitializeComponent();
            if (!DesignMode)
            {
                if(BusType == BusTypes.ETH)
                {
                    cbMode.DataSource = new List<ETHModes>
                    {
                        ETHModes.AUTO,
                        ETHModes.BASE_1000_TX_FIXED,
                        ETHModes.BASE_100_TX_FIXED,
                        ETHModes.BASE_10_TX_FIXED
                    };
                    cbMode.SelectedItem = ETHModes.AUTO;
                }
                else if(BusType == BusTypes.BROADR_REACH)
                {
                    cbMode.DataSource = new List<ETHModes>
                    {
                        ETHModes.BASE_100_T1_MASTER,
                        ETHModes.BASE_100_T1_SLAVE,
                        ETHModes.BASE_1000_T1_MASTER,
                        ETHModes.BASE_1000_T1_SLAVE
                    };
                    cbMode.SelectedItem = ETHModes.BASE_100_T1_MASTER;
                }
            }
        }

        /// <summary>
        ///     Occurs when the COM_AnalyzerConfigureChannelReq button was clicked.
        /// </summary>
        public BusTypes BusType;

        /// <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;

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

        /// <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 macBytes = GetMacAddressBytes(tbDestMacAddress.Text);
            var vlanIds = GetVlanIds();
            return new List<ChannelConfigurationETH>
            {
                new ChannelConfigurationETH
                {
                    BusType = BusType,
                    ChannelId = 0,
                    Mode = (ETHModes)cbMode.SelectedItem,
                    MAC_Address_01 = macBytes[0],
                    MAC_Address_02 = macBytes[1],
                    MAC_Address_03 = macBytes[2],
                    MAC_Address_04 = macBytes[3],
                    MAC_Address_05 = macBytes[4],
                    MAC_Address_06 = macBytes[5],
                    PM = cbPM.Checked,
                    VLANs = vlanIds,
                    VLAN_Count = (byte)vlanIds.Length
                }
            };
        }

        private byte[] GetMacAddressBytes(String macText)
        {
            var errorMessageBuilder = new StringBuilder();
            var macBytes = new byte[6];
            PhysicalAddress MacAddress = null;
            try
            {
                MacAddress = PhysicalAddress.Parse(macText.Replace(":", "-"));
                macBytes = MacAddress.GetAddressBytes();
            }
            catch (FormatException ex)
            {
                errorMessageBuilder.AppendLine($"Wrong MAC syntax: {ex.Message} [{macText}]");
            }

            if (MacAddress != null && macBytes.Length != 6)
            {
                errorMessageBuilder.AppendLine("MAC address byte less than 6 bytes: " + MacAddress);
                macBytes = new byte[6];
            }

            if (errorMessageBuilder.Length > 0)
            {
                MessageBox.Show(errorMessageBuilder.ToString(), "MAC Address wrong!");
            }

            return macBytes;
        }

        private PhysicalAddress GetMacAddress(String macText)
        {
            var errorMessageBuilder = new StringBuilder();
            PhysicalAddress MacAddress = null;
            try
            {
                MacAddress = PhysicalAddress.Parse(macText.Replace(":", "-"));
            }
            catch (FormatException ex)
            {
                errorMessageBuilder.AppendLine($"Wrong MAC syntax: {ex.Message} [{macText}]");
            }

            if (errorMessageBuilder.Length > 0)
            {
                MessageBox.Show(errorMessageBuilder.ToString(), "MAC Address wrong!");
            }

            return MacAddress;
        }

        private IPAddress GetIPAddress(String ipText)
        {
            var errorMessageBuilder = new StringBuilder();
            IPAddress ipAddress = null;

            try
            {
                ipAddress = IPAddress.Parse(ipText);
            }
            catch(FormatException e)
            {
                errorMessageBuilder.AppendLine($"Wrong IP syntax: {e.Message} [{ipText}]");
            }

            if (errorMessageBuilder.Length > 0)
            {
                MessageBox.Show(errorMessageBuilder.ToString(), "IP Address wrong!");
            }

            return ipAddress;
        }

        private ushort[] GetVlanIds()
        {
            var enteredVlans = tbVlanIds.Text.Split(new []{',', ' ', '-', ':'}, StringSplitOptions.RemoveEmptyEntries);
            var errorMessageBuilder = new StringBuilder();
            var parsedVlanList = new List<ushort>();
            foreach (var enteredVlan in enteredVlans)
            {
                // Only 256 vlans possible. Bytes max value is 255.
                if (parsedVlanList.Count >= byte.MaxValue)
                {
                    errorMessageBuilder.AppendLine(
                        $"More than {byte.MaxValue} VLAN Ids configured! Only the first {byte.MaxValue} are taken.");
                    break;
                }

                if (!ushort.TryParse(enteredVlan, out var parsedVlan))
                {
                    errorMessageBuilder.AppendLine("Invalid Vlan ID: " + enteredVlan);
                    continue;
                }

                parsedVlanList.Add(parsedVlan);
            }

            if (errorMessageBuilder.Length > 0)
            {
                MessageBox.Show(errorMessageBuilder.ToString(), "VLAN Id warnings");
            }

            return parsedVlanList.ToArray();
        }

        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 ChannelConfigurationControlEthernet_Load(object sender, EventArgs e)
        {
            var toolTipPromiscuousMode = new ToolTip
            {
                // Set up the delays for the ToolTip.
                AutoPopDelay = 5000, InitialDelay = 500, ReshowDelay = 250, 
                
                // Force the ToolTip text to be displayed whether or not the form is active.
                ShowAlways = true
            };

            // Set a Tooltip with the configured behaviour on the objects.
            toolTipPromiscuousMode.SetToolTip(cbPM, "Promiscuous-Mode is currently not supported!");
            toolTipPromiscuousMode.SetToolTip(tbVlanIds, $"Valid separator: \',\' \'-\' \':\'\nValid Range: 0 - {ushort.MaxValue}");
            toolTipPromiscuousMode.SetToolTip(tbDestMacAddress, $"IPv4 MAC address\nValid separator: \'-\' \':\'\nValid Range: 0x00 - 0xFF (hex)");
        }

        private void lbDestMac_Click(object sender, EventArgs e)
        {

        }

        private void checkBoxFilterConfigDstMAC_CheckedChanged(object sender, EventArgs e)
        {
            textBoxDstMacFilter.Enabled = checkBoxFilterConfigDstMAC.Checked;
        }

        private void checkBoxFilterConfigSrcMAC_CheckedChanged(object sender, EventArgs e)
        {
            textBoxSrcMACFilter.Enabled = checkBoxFilterConfigSrcMAC.Checked;
        }

        private void checkBoxFilterConfigVLAN_CheckedChanged(object sender, EventArgs e)
        {
            numericUpDownVLAN.Enabled = checkBoxFilterConfigVLAN.Checked;
        }

        private void checkBoxFilterConfigEtherType_CheckedChanged(object sender, EventArgs e)
        {
            numericUpDownEtherType.Enabled = checkBoxFilterConfigEtherType.Checked;
        }

        private void checkBoxFilterConfigDstIPv4_CheckedChanged(object sender, EventArgs e)
        {
            textBoxDstIPv4Filter.Enabled = checkBoxFilterConfigDstIPv4.Checked;
        }

        private void checkBoxFilterConfigSrcIPv4_CheckedChanged(object sender, EventArgs e)
        {
            textBoxSrcIPv4Filter.Enabled = checkBoxFilterConfigSrcIPv4.Checked;
        }

        private void checkBoxFilterConfigDstPort_CheckedChanged(object sender, EventArgs e)
        {
            numericUpDownDstPort.Enabled = checkBoxFilterConfigDstPort.Checked;
        }

        private void checkBoxFilterConfigSrcPort_CheckedChanged(object sender, EventArgs e)
        {
            numericUpDownSrcPort.Enabled = checkBoxFilterConfigSrcPort.Checked;
        }

        private void buttonAddFilterLIN_Click(object sender, EventArgs e)
        {
            var toolTip = new ToolTip
            {
                // Set up the delays for the ToolTip.
                AutoPopDelay = 5000,
                InitialDelay = 500,
                ReshowDelay = 250,

                // Force the ToolTip text to be displayed whether or not the form is active.
                ShowAlways = true
            };

            var newBusFilter = new BusSpecificFilterETH
            {
                FlagDestinationMAC = checkBoxFilterConfigDstMAC.Checked,
                FlagSourceMAC = checkBoxFilterConfigSrcMAC.Checked,
                FlagVLAN = checkBoxFilterConfigVLAN.Checked,
                FlagEtherType = checkBoxFilterConfigEtherType.Checked,
                FlagDestinationIPv4 = checkBoxFilterConfigDstIPv4.Checked,
                FlagSourceIPv4 = checkBoxFilterConfigSrcIPv4.Checked,
                FlagDestinationPort = checkBoxFilterConfigDstPort.Checked,
                FlagSourcePort = checkBoxFilterConfigSrcPort.Checked,
            };

            if(newBusFilter.FlagDestinationMAC)
            {
                newBusFilter.DestinationMAC = GetMacAddress(textBoxDstMacFilter.Text);
                if (newBusFilter.DestinationMAC == null) return;
            }

            if (newBusFilter.FlagSourceMAC)
            {
                newBusFilter.SourceMAC = GetMacAddress(textBoxSrcMACFilter.Text);
                if (newBusFilter.SourceMAC == null) return;
            }

            if(newBusFilter.FlagVLAN)
            {
                newBusFilter.VLAN = Convert.ToInt16(numericUpDownVLAN.Value);
            }

            if(newBusFilter.FlagEtherType)
            {
                newBusFilter.EtherType = Convert.ToInt16(numericUpDownEtherType.Value);
            }

            if(newBusFilter.FlagDestinationIPv4)
            {
                newBusFilter.DestinationIPv4 = GetIPAddress(textBoxDstIPv4Filter.Text);
            }

            if (newBusFilter.FlagSourceIPv4)
            {
                newBusFilter.SourceIPv4 = GetIPAddress(textBoxSrcIPv4Filter.Text);
            }

            if(newBusFilter.FlagDestinationPort)
            {
                newBusFilter.DestinationPort = Convert.ToUInt16(numericUpDownDstPort.Value);
            }

            if (newBusFilter.FlagSourcePort)
            {
                newBusFilter.SourcePort = Convert.ToUInt16(numericUpDownSrcPort.Value);
            }

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

            this.busSpecificFilters.Add(newBusFilter);
            this.lvFilters.Items.Add(new ListViewItem(
                new[]
                {
                    newBusFilter.DestinationMAC.ToString(),
                    newBusFilter.SourceMAC.ToString(),
                    newBusFilter.VLAN.ToString(CultureInfo.InvariantCulture),
                    newBusFilter.EtherType.ToString(CultureInfo.InvariantCulture),
                    newBusFilter.DestinationIPv4.ToString(),
                    newBusFilter.SourceIPv4.ToString(),
                    newBusFilter.DestinationPort.ToString(CultureInfo.InvariantCulture),
                    newBusFilter.SourcePort.ToString(CultureInfo.InvariantCulture)
                })
            );
        }

        private void btRemoveFilter_Click(object sender, EventArgs e)
        {
            var removedItems = new List<IBusSpecificFilter>();
            foreach (ListViewItem item in lvFilters.SelectedItems)
            {
                if (item.SubItems.Count >= 2)
                {
                    if (UInt16.TryParse(item.SubItems[2].Text, out var itemVLAN)
                        && UInt16.TryParse(item.SubItems[3].Text, out var itemEtherType)
                        && UInt16.TryParse(item.SubItems[6].Text, out var itemDstPort)
                        && UInt16.TryParse(item.SubItems[7].Text, out var itemSrcPort))
                    {
                        removedItems.AddRange(
                    busSpecificFilters.Where(p =>
                                p is BusSpecificFilterETH filterETH
                                && filterETH.DestinationMAC.ToString() == item.SubItems[0].Text
                                && filterETH.SourceMAC.ToString() == item.SubItems[1].Text
                                && filterETH.VLAN == itemVLAN
                                && filterETH.EtherType == itemEtherType
                                && filterETH.DestinationIPv4.ToString() == item.SubItems[4].Text
                                && filterETH.SourceIPv4.ToString() == item.SubItems[5].Text
                                && filterETH.DestinationPort == itemDstPort
                                && filterETH.SourcePort == itemSrcPort));
                    }
                }

                lvFilters.Items.Remove(item);
            }

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

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

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

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

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

        private void buttonClearFilterLIN_Click(object sender, EventArgs e)
        {
            this.busSpecificFilters.RemoveAll(p => p is BusSpecificFilterETH);
            lvFilters.Items.Clear();
        }

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

        #region Constants and Fields

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

        #endregion

    }
}
