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

namespace HwComWrapperNETGui.UserControls
{
    public partial class MsgCat86 : UserControl
    {
        #region Constructors and Destructiors

        /// <summary>
        ///     Initializes a new instance of the <see cref="MsgCat86"/> class.
        /// </summary>
        public MsgCat86()
        {
            InitializeComponent();

            if (!this.DesignMode)
            {
                this.nudBufferEntriesLimit.Value = 1000; 
                this.nudBufferSizeLimit.Value = 10000; 
                this.nudChannelID.Value = 1; 
                this.nudControlChannelID.Value = 0; 
                this.nudPort.Value = 0; 
                this.nudStatisticsInterval.Value = 0; 
                this.nudPreStartWaitRange.Value = 0; 
                this.cbControlCommand.DataSource = Enum.GetValues(typeof(ReplayCommand));  
                this.cbControlCommand.SelectedItem = ReplayCommand.Start;                  
                this.tbChannelCount.Text = Convert.ToString(this.lvChannelID.Items.Count); 
            }
        }

        #endregion

        #region Events
        /// <summary>
        ///     Occurs when the COM_ReplayConfigrureReq button was clicked.
        /// </summary>
        public event EventHandler <ReplayConfigureEventArgs> ButtonReplayConfigClicked;

        /// <summary>
        ///     Occurs when the COM_ReplayControlReq button was clicked.
        /// </summary>
        public event EventHandler <ReplayControlEventArgs> ButtonReplayControlClicked;

        /// <summary>
        ///     Occurs when the button for COM_ReplayStreamReq was clicked. 
        /// </summary>
        public event EventHandler <ReplayStreamEventArgs> ButtonReplayStreamClicked;


        /// <summary>
        ///     Occurs when the get button for COM_ReplayStatisticsReq was clicked. 
        /// </summary>
        public event EventHandler ButtonGetReplayStatisticsClicked;
        #endregion

        #region Public Methods
        /// <summary>
        ///     Gets the <see cref="ReplayConfiguration"/> depending on the bus type.
        /// </summary>
        /// <returns> the <see cref="ReplayConfiguration"/> depending on the bus type. </returns>
        public ReplayConfiguration GetReplayConfiguration()
        {
            return new ReplayConfiguration
            {
                BufferEntriesLimit = Convert.ToUInt32(this.nudBufferEntriesLimit.Value),
                BufferSizeLimit = Convert.ToUInt32(this.nudBufferSizeLimit.Value),
                Port = Convert.ToUInt16(this.nudPort.Value),
                StatisticsInterval = Convert.ToUInt16(this.nudStatisticsInterval.Value),
                ChannelCount = Convert.ToByte(this.lvChannelID.Items.Count),
                Flags = Convert.ToByte((this.checkUseSeq.Checked) ? ReplayConfiguration.FlagValue.USE_SEQUENCES : ReplayConfiguration.FlagValue.NONE),
                ChannelID = channelIDsList.ToArray()
            };            
        }

        /// <summary>
        ///    Updates GUI Statistics list with Response values.
        /// </summary>
        public void UpdateStatisticsList(ReplayStatistics replayStatistics)
        {
            // Get list
            var newReplayStatistics = replayStatistics;

            // Check if already in list
            if(ReplayStatisticsList.Exists(p => newReplayStatistics.Equals(p)))
            {
                // This info is already in list
                return;
            }

            // Clear List
            ReplayStatisticsList.Clear();

            // Add the new statistics
            ReplayStatisticsList.Add(newReplayStatistics);

            // Clear GUI List
            lvReplayStatistics.Items.Clear();

            // Update GUI List
            for (int i = 0; i < newReplayStatistics.ChannelCount; i++)
            {
                lvReplayStatistics.Items.Add(new ListViewItem(
                    new[]
                    {
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].ChannelID),
                        Enum.GetName(typeof(ReplayStatus),newReplayStatistics.ChannelStatistics[i].ReplayStatus),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].GoodRxCount),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].BadRxCount),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].BufferEntriesLimit),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].BufferSizeLimit),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].CurrentBufferEntriesCount),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].CurrentBufferSize),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].MaxFillLevelEntries),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].MaxFillLevelBytes),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].MinFillLevelEntries),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].MinFillLevelBytes),
                        Convert.ToString(newReplayStatistics.ChannelStatistics[i].DropCount),
                        Enum.GetName(typeof(ReplayBufferInfo), newReplayStatistics.ChannelStatistics[i].BufferInformation)
                    })
                );
            }
        }
        #endregion

        #region Methods
        private void OnButtonAddChannelIDClicked(object sender, EventArgs e)
        {
            // Get Channel ID value
            var newChannelIDs = new byte[1]{
                Convert.ToByte(this.nudChannelID.Value),
            };

            // Check if its in list
            if (channelIDsList.Exists(p => newChannelIDs[0].Equals(p)))
            {
                // Already in list
                return;
            }

            // Add to channel id list
            this.channelIDsList.Add(Convert.ToByte(newChannelIDs[0]));

            // Add to GUI List
            this.lvChannelID.Items.Add(new ListViewItem(
                new[]
                {
                    Convert.ToString(Convert.ToUInt16(newChannelIDs[0]))
                })
            );

            // Update Channel count
            this.tbChannelCount.Text = Convert.ToString(this.lvChannelID.Items.Count);

            // Check that item has been added
            if(0 < this.lvChannelID.Items.Count)
            { 
                // Clear error
                this.errorChannelCount.Clear(); 
            }            
        }

        private void OnButtonRemoveChannelIDClicked(object sender, EventArgs e)
        {
            int byteIndex = 0; // Byte index in Channel IDs list
            var removedItems = new List<byte>();

            // Get selected item
            foreach (ListViewItem item in lvChannelID.SelectedItems)
            {
                // Check subitem count = 1 Channel ID
                if(item.SubItems.Count >= 1)
                {
                    // Get Text as Byte
                    if(byte.TryParse(item.SubItems[0].Text, out var tempChannelID))
                    {
                        // Remove Item From List
                        removedItems.AddRange(
                            channelIDsList.Where(p => p is byte channelID
                            && channelID == tempChannelID)
                            );
                    }
                }

                // Remove Selected Items
                lvChannelID.Items.Remove(item);
                channelIDsList.Remove(removedItems.ToArray()[byteIndex]);
                byteIndex++;

                // Update Channel Count
                this.tbChannelCount.Text = Convert.ToString(this.lvChannelID.Items.Count);
            }
        }

        private void OnButtonClearChannelIDClicked(object sender, EventArgs e)
        {
            // Clear Lists
            this.channelIDsList.Clear();
            lvChannelID.Items.Clear();

            // Update Channel Count
            this.tbChannelCount.Text = Convert.ToString(this.lvChannelID.Items.Count);
        }

        private void OnButtonReplayConfigureClicked(object sender, EventArgs e)
        {

            if (Convert.ToUInt32(this.tbChannelCount.Text) == 0)
            {
                this.errorChannelCount.SetError(this.tbChannelCount, "There are no channels to configure!");
                return;
            }
            else
            {
                this.errorChannelCount.Clear();
            }
            

            var handler = this.ButtonReplayConfigClicked;

            if (handler == null)
            {
                return;
            }

            // Get Configurations
            var replayConfigurations = this.GetReplayConfiguration();
            handler(this, new ReplayConfigureEventArgs(replayConfigurations));
        }

        private void OnButtonReplayControlClicked(object sender, EventArgs e)
        {
            var handler = this.ButtonReplayControlClicked;

            if (handler == null)
            {
                return;
            }

            // get channel id
            byte channel_id = 0; 

            if((ReplayCommand)this.cbControlCommand.SelectedItem == ReplayCommand.Reset)
            {
                channel_id = 0;
            }
            else
            {
                channel_id = Convert.ToByte(this.nudControlChannelID.Value);
            }

            var replayControl = new ReplayControl
            {
                ChannelID = channel_id,
                Command = Convert.ToByte((ReplayCommand)this.cbControlCommand.SelectedItem),
                PreStartWaitRange = Convert.ToUInt16(this.nudPreStartWaitRange.Value)
            };

            handler(this, new ReplayControlEventArgs(replayControl));
        }

        private void OnButtonReplayStreamClicked(object sender, EventArgs e)
        {
            // NOTE: This Entire Function is a dummy function. needs rework !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -> Disabled button
            var handler = this.ButtonReplayStreamClicked;
            if (handler == null)
            {
                return;
            }

            // Dummy Stream
            uint fragment_paload_length = 8;
            uint first_fragment_header_length = 20;

            // Create stream
            var replayStream = new ReplayStream();
            replayStream.ChannelID = 10;
            replayStream.Counter = 0;
            replayStream.Flags = 1;

            // First Fragment
            var firstFragment = new ReplayStreamFirstFragment
            {
                TotalFrameLength = first_fragment_header_length + fragment_paload_length,
                SetElementID = 500,
                TxEventID = 800,
                TimeStamp = 500
            };
            replayStream.FirstFragment = firstFragment;
            replayStream.FragmentLength = Convert.ToUInt16(fragment_paload_length + first_fragment_header_length);
            var payload = new byte[8]
            {
                        Convert.ToByte(0x01),
                        Convert.ToByte(0x02),
                        Convert.ToByte(0x03),
                        Convert.ToByte(0x04),
                        Convert.ToByte(0x05),
                        Convert.ToByte(0x06),
                        Convert.ToByte(0x07),
                        Convert.ToByte(0x08)
            };
            payload.CopyTo(replayStream.Payload, 0);
    
            // Call Handler
            handler(this, new ReplayStreamEventArgs(replayStream));
        }

        private void OnButtonGetReplayStatisticsClicked(object sender, EventArgs e)
        {
            ButtonGetReplayStatisticsClicked?.Invoke(this, EventArgs.Empty);
        }
        #endregion

        #region Constants and Fields
        private readonly List<byte> channelIDsList = new List<byte>();
        private readonly List<ReplayStatistics> ReplayStatisticsList = new List<ReplayStatistics>();
        #endregion

    }
}
