/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All rights Reserved.
* 
* This software is protected by copyright laws of the United States and
* of foreign countries. This material may also be protected by patent laws
* and technology transfer regulations of the United States and of foreign
* countries. This software is furnished under a license agreement and/or a
* nondisclosure agreement and may only be used or reproduced in accordance
* with the terms of those agreements. Dissemination of this information to
* any party or parties not specified in the license agreement and/or
* nondisclosure agreement is expressly prohibited.
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;

using HealthSensorPlatform.DeviceDescriptions;
using HealthSensorPlatform.CustomControls;
using RPCSupport.DeviceSupport;
//using Maxim.MAX30101GUI.DeviceDescriptions;
//using Maxim.MAX30101;

namespace Maxim.CustomControls
{
    public partial class RegisterView : UserControl, IDeviceView
    {
        private bool connected = false;
        public bool Connected
        {
            get
            {
                return connected;
            }
            set 
            {
                connected = value;
            }
        }

        public string DeviceName;

        public DeviceController Controller
        {
            get { return controller; }
        }
        private DeviceController controller;


        /// <summary>
        /// Value of current selected register 
        /// </summary>
        int RegisterValue;

        Label[] bitLabels = new Label[24];
        
        Font RegularFont;
        Font BoldFont;

        public event RegisterWriteCompleteHandler RegisterWriteComplete;
        public delegate void RegisterWriteCompleteHandler(object sender, RegisterArgs e);

        public event CellBeginEditHandler CellBeginEdit;
        public delegate void CellBeginEditHandler(object sender, DataGridViewCellCancelEventArgs e);

        public RegisterView()
        {
            //accelDevice = new AccelDevice();
            InitializeComponent();

            // Initialize font styles for label
            RegularFont = new Font(lblBit00.Font, FontStyle.Regular); 
            BoldFont = new Font(lblBit00.Font, FontStyle.Bold);

            // References to all bit labels
            for(int i = 0; i < 24; i++)
            {
                string labelName = "lblBit" + i.ToString("D2");
                bitLabels[i] = (Label)panelRegisterBits.Controls[labelName];
            }

            dgv_Registers.CellBeginEdit += OnCellBeginEdit; // Pass thru event
            dgv_Registers.SelectionChanged += dgv_Registers_SelectionChanged;
            dgv_Registers.CellEndEdit += dgv_Registers_CellEndEdit;
        }

        public void SetController(DeviceController controller)
        {
            Clear();
            this.controller = controller;
        }

        /// <summary>
        /// RegisterInfo register value to string
        /// </summary>
        /// <param name="reg"></param>
        /// <returns></returns>
        private string FormatData(RegisterInfo reg)
        {
            return reg.dataField.ToString("X" + (reg.numBytes * 2).ToString());
        }

        /// <summary>
        /// Populate register tables
        /// </summary>
        public void DisplayRegisters()
        {
            if (dgv_Registers.RowCount == 0)
            {
                //add rows to table
                foreach (RegisterInfo reg in controller.InfoArray)
                {
                    //get the number of bytes of the register
                    int numBytes = reg.numBytes;

                    String dataStr = FormatData(reg);
                    //add a row to the table with the register address, name, and value
                    dgv_Registers.Rows.Add(reg.address.ToString("X2") + "h", reg.detailedName, dataStr);
                }
                
                //resize length of table to the number of rows
                if (dgv_Registers.RowCount > 8)
                {
                    dgv_Registers.Height = (dgv_Registers.RowCount - 1) * 23 + 42;
                    dgv_RegDes.Height = dgv_Registers.Height;
                }
                else
                {
                    dgv_Registers.Height = 7 * 23 + 42;
                    dgv_RegDes.Height = dgv_Registers.Height;
                }
            }
            else //the table has already been populated with rows
            {
                //update the value column with the new value in the ADC registers
                for (int i = 0; i < dgv_Registers.Rows.Count; i++)
                {
                    RegisterInfo reg = controller.InfoArray[i];
                    int numBytes = reg.numBytes;
                    String dataStr = FormatData(reg);
                    //add a row to the table with the register address, name, and value
                    dgv_Registers[col_Value.Index, i].Value = dataStr;
                }
            }
        }

        public void Clear()
        {
            dgv_Registers.Rows.Clear();
            dgv_Registers.Refresh();
        }

        public string RegistersToString()
        {
            //RegisterProcess registerProcess;
            StringBuilder sb = new StringBuilder();
            string dataStr;
            string[][] bitDescriptions;
            int longestRow;

            //registerProcess = new RegisterProcess(controller.InfoArray);
            
            bitDescriptions = new string[controller.InfoArray.Length + 4][];

            UpdateRegisterIndexWidth();

            // Generate register data to string
            for (int i = 0; i < controller.InfoArray.Length; i++)
            {
                RegisterInfo reg = controller.InfoArray[i];

                bitDescriptions[i] = new string[reg.description.list.Count];

                for (int k = 0; k < reg.description.list.Count; k++)
                {
                    RegisterBitDescriptions.RegisterBitDescription b = reg.description.list[k];
                    int bitValue = (reg.dataField >> b.Index) & ((1 << b.Width) - 1); // MAX30001 only
                    bitDescriptions[i][k] = b.name + " = " + bitValue.ToString("X") + "h";
                }
            }

            // Find longest row for how deep to loop to print out all registers bits
            longestRow = controller.InfoArray.Length + 1;
            for (int j = 0; j < controller.InfoArray.Length; j++ )
            {
                if (controller.InfoArray[j].description.list.Count > longestRow)
                {
                    longestRow = controller.InfoArray[j].description.list.Count + 1;
                }
            }

            for (int i = 0; i < longestRow; i++)
            {
                if (i < controller.InfoArray.Length) // register map
                {
                    RegisterInfo reg = controller.InfoArray[i];

                    dataStr = FormatData(reg);

                    sb.Append(this.DeviceName);
                    sb.Append(", ");
                    sb.Append(reg.address.ToString("X2"));
                    sb.Append("h, ");
                    sb.Append(reg.detailedName);
                    sb.Append(", ");
                    sb.Append(dataStr);
                    sb.Append("h");
                    sb.Append(", ");
                    sb.Append(", ");
                }
                else // finished with register map, just have descriptions
                {
                    sb.Append(", ");
                    sb.Append(", ");
                    sb.Append(", ");
                    sb.Append(", ");
                    sb.Append(", ");
                }

                if (i == 0) // Register name and values
                {
                    for (int k = 0; k < controller.InfoArray.Length; k++)
                    {
                        if (controller.InfoArray[k].type != RegisterInfo.RegisterType.WriteOnly)
                        {
                            sb.Append(controller.InfoArray[k].name);
                            sb.Append(" = ");
                            sb.Append(FormatData(controller.InfoArray[k]));
                            sb.Append("h");
                            sb.Append(", ");
                        }
                    }
                }
                else // descriptions
                {
                    for (int k = 0; k < controller.InfoArray.Length; k++)
                    {
                        if (controller.InfoArray[k].type != RegisterInfo.RegisterType.WriteOnly)
                        {
                            if (i - 1 < bitDescriptions[k].Length)
                            {
                                sb.Append(bitDescriptions[k][i - 1]);
                            }
                            sb.Append(',');
                        }
                    }
                }
                sb.Append(Environment.NewLine);
            }

            /*
            foreach(RegisterInfo reg in controller.InfoArray)
            {
                if (reg.address == 1 || reg.address == 8 || reg.address == 9 || reg.address == 10)
                    continue;

                sb.Append("% ");
                sb.Append(reg.name);
                sb.Append(" (");
                sb.Append(reg.address.ToString("X2"));
                sb.Append("h)");
                sb.Append(" = ");
                sb.Append(FormatData(reg));
                sb.Append("h\n");

                foreach(RegisterBitDescriptions.RegisterBitDescription b in reg.description.list)
                {
                    int bitValue = (RegisterData(reg) >> b.Index) & ((1 << b.Width) - 1); // MAX30001 only
                    sb.Append("% ");
                    sb.Append(b.name);
                    sb.Append(" = ");
                    sb.Append(bitValue.ToString("X"));
                    sb.Append("h\n");
                }
            }
            */
            return sb.ToString().TrimEnd(Environment.NewLine.ToCharArray());
        }

        /// <summary>
        /// Register selection change display register description and update display bits
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgv_Registers_SelectionChanged(object sender, EventArgs e)
        {
            UpdateRegisters();
        }

        private void UpdateRegisters()
        {
            //get the index of the row selected
            int row = dgv_Registers.CurrentRow.Index;

            //make sure there is bit description info and then display it
            if (controller.InfoArray[row].description != null)
            {
                lbl_bitDes.Text = "Bit Description: " + controller.InfoArray[row].name + " Register";
                UpdateRegisterDesTable(controller.InfoArray[row].description);
                RegisterValue = Int32.Parse((string)(dgv_Registers[2, row].Value), System.Globalization.NumberStyles.HexNumber);
                UpdateRegisterBits(controller.InfoArray[row].description, RegisterValue);
            }
        }

        /// <summary>
        /// Fill register description table
        /// </summary>
        /// <param name="bitsDes"></param>
        public void UpdateRegisterDesTable(RegisterBitDescriptions bitsDes)
        {
            //int numBits = bitsDes.Count;

            dgv_RegDes.Rows.Clear();

            foreach (RegisterBitDescriptions.RegisterBitDescription b in bitsDes.list)
            {
                dgv_RegDes.Rows.Add(b.bit, b.name, b.description);
            }

        }

        public void UpdateRegisterBits()
        {
            int row;

            if (dgv_Registers.CurrentRow != null)
            {
                row = dgv_Registers.CurrentRow.Index;

                if (controller.InfoArray[row].description != null)
                {
                    RegisterValue = Int32.Parse((string)(dgv_Registers[2, row].Value), System.Globalization.NumberStyles.HexNumber);
                    UpdateRegisterBits(controller.InfoArray[row].description, RegisterValue);
                }
            }
        }

        public void UpdateRegisterIndexWidth()
        {
            RegisterInfo[] info = controller.InfoArray;

            foreach(RegisterInfo regInfo in info) // Registers
            {
                foreach (RegisterBitDescriptions.RegisterBitDescription b in regInfo.description.list) // Bits
                {
                    string[] numbers = Regex.Split(b.bit, @"\D+"); // Bits [20:17]
                    string registerBitName;
                    int number1 = -1;
                    int number2 = -1;

                    // Find Bit Range
                    foreach (string value in numbers)
                    {
                        if (value != String.Empty)
                        {
                            if (number1 == -1) // First number found
                                number1 = Int32.Parse(value);
                            else // Second number found
                                number2 = Int32.Parse(value);
                        }
                    }

                    if (number2 == -1) // Single Number: single bit field (example: Bit 20)
                    {
                        b.Width = 1;
                        b.Index = number1;
                    }
                    else // Multiple Bits: multiple bit field (example: Bit [20:17])
                    {
                        registerBitName = Regex.Replace(b.name, @"\[\d+:\d+\]", String.Empty); // Remove [#.#] from bit field name

                        for (int i = number1; i >= number2; i--)
                        {
                            bitLabels[i].Text = registerBitName + "[" + (i - number2) + "]"; // Calculate bit field number
                        }

                        b.Index = number2;
                        b.Width = number1 - number2 + 1; 

                    }
                }
            }
        }

        /// <summary>
        /// Fill bit table
        /// </summary>
        /// <param name="bitDes"></param>
        /// <param name="regValue"></param>
        public void UpdateRegisterBits(RegisterBitDescriptions bitDes, int regValue)
        {
            // Reset Bit State
            for (int i = 0; i < 24; i++ )
            {
                bitLabels[i].Text = "-";

                // Set label based on bit state
                if (((regValue >> i) & 0x01) == 0x01)
                    bitLabels[i].Font = BoldFont;
                else
                    bitLabels[i].Font = RegularFont;
            }

            foreach (RegisterBitDescriptions.RegisterBitDescription b in bitDes.list)
            {
                string[] numbers = Regex.Split(b.bit, @"\D+"); // Bits [20:17]
                string registerBitName;
                int number1 = -1;
                int number2 = -1;

                // Find Bit Range
                foreach (string value in numbers)
                {
                    if (value != String.Empty)
                    {
                        if (number1 == -1) // First number found
                            number1 = Int32.Parse(value);
                        else // Second number found
                            number2 = Int32.Parse(value);
                    }
                }

                if (number2 == -1) // Single Number: single bit field (example: Bit 20)
                {
                    bitLabels[number1].Text = b.name;
                }
                else // Multiple Bits: multiple bit field (example: Bit [20:17])
                {
                    registerBitName = Regex.Replace(b.name, @"\[\d+:\d+\]", String.Empty); // Remove [#.#] from bit field name

                    for (int i = number1; i >= number2; i--)
                    {
                        bitLabels[i].Text = registerBitName + "[" + (i - number2) + "]"; // Calculate bit field number
                    }
                    
                }
            }
        }

        /// <summary>
        /// Parse user input and write register
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgv_Registers_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            //check if hardware is connected
            //if (online && !COMMConnected())
            //    return;

            RegisterInfo reg = controller.InfoArray[e.RowIndex];
            byte address = (byte)reg.address;
            byte numBytes = (byte)reg.numBytes;

            int[] data = reg.data;

            string value = (string)dgv_Registers[col_Value.Index, e.RowIndex].Value;
            string error = ValidateHexValue(value, numBytes);

            if (error == "Valid")
            {
                //erase error text if one exist
                if (dgv_Registers[col_Value.Index, e.RowIndex].ErrorText != String.Empty)
                    dgv_Registers[col_Value.Index, e.RowIndex].ErrorText = String.Empty;

                //convert string hex to number hex
                int hex = Int32.Parse(value, System.Globalization.NumberStyles.HexNumber);

                //write value back to GUI with correct number of characters
                dgv_Registers[col_Value.Index, e.RowIndex].Value = hex.ToString("X" + numBytes * 2);

                WriteRegister(reg, hex);

                if (RegisterWriteComplete != null)
                {
                    RegisterWriteComplete(this, new RegisterArgs { Register = address });
                }

            }
            else//value is not valid
            {
                byte zero = 0;
                dgv_Registers[col_Value.Index, e.RowIndex].Value = zero.ToString("X" + numBytes * 2); //enter zeros with correct number of characters
                dgv_Registers[col_Value.Index, e.RowIndex].ErrorText = error;    //add error text
            }

            UpdateRegisters();
        }

        /// <summary>
        /// Write data to register
        /// </summary>
        /// <param name="reg"></param>
        /// <param name="hex"></param>
        public void WriteRegister(RegisterInfo reg, int hex)
        {
            byte numBytes = (byte)reg.numBytes;
            int[] data = reg.data;

            switch (numBytes)
            {
                case 4:
                    data[0] = (byte)(hex >> 24);
                    data[1] = (byte)(hex >> 16);
                    data[2] = (byte)(hex >> 8);
                    data[3] = (byte)hex;
                    break;
                case 3:
                    hex &= 0x00FFFFFF;//clear bits not used
                    data[0] = (byte)(hex >> 16);
                    data[1] = (byte)(hex >> 8);
                    data[2] = (byte)hex;
                    break;
                case 2:
                    hex &= 0x0000FFFF;//clear bits not used
                    data[0] = (byte)(hex >> 8);
                    data[1] = (byte)hex;
                    break;
                case 1:
                    hex &= 0x000000FF;//clear bits not used
                    data[0] = (byte)hex;
                    break;
            }
            controller.WriteRegister(reg);
        }

        /// <summary>
        /// Verify hex
        /// </summary>
        /// <param name="value"></param>
        /// <param name="numBytes"></param>
        /// <returns></returns>
        public String ValidateHexValue(String value, byte numBytes)
        {
            char[] character = new char[numBytes * 2];
            String error = "Valid";

            if (value == null)
            {
                error = "Empty Value";
                return error;
            }

            //check to see if too many characters were entered
            if (value.Length > numBytes * 2)
                error = "Too many characters";
            else
            {
                character = value.ToCharArray();    //split string into character array

                for (int i = 0; i < value.Length; i++)
                {
                    if (Char.IsLetterOrDigit(character[i])) //check for letter and digits
                    {
                        if (Char.IsLower(character[i]))
                            character[i] = Char.ToUpper(character[i]); //changed letter to upper case

                        //check A-F
                        if (character[i] > 'F')
                            error = "Invalid Letter";
                    }
                    else
                        error = "Invalid Character";
                }
            }
            return error;
        }

        public void WriteAll()
        {
            if (Connected)
                controller.WriteAll();
            DisplayRegisters();
        }

        /// <summary>
        /// Read all button handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_ReadAll_Click(object sender, EventArgs e)
        {
            ReadAll();
        }

        /// <summary>
        /// Read all registers and display results
        /// </summary>
        public void ReadAll()
        {
            if (Connected)
                controller.ReadAll();
            DisplayRegisters();
            UpdateRegisterBits();
        }

        public RegisterInfo RegisterAddress(int address)
        {
            for(int i = 0; i < controller.InfoArray.Length; i++)
            {
                if (controller.InfoArray[i].address == address)
                    return controller.InfoArray[i];
            }

            return null;
        }

        /// <summary>
        /// Read register handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRead_Click(object sender, EventArgs e)
        {
            if (connected)
            {
                controller.ReadRegister(controller.InfoArray[dgv_Registers.SelectedRows[0].Index]);
                DisplayRegisters();
                UpdateRegisterBits();
            }
        }

        /// <summary>
        /// Toggle register bit handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lblBit_Click(object sender, EventArgs e)
        {
            Label lbl = (Label)sender;

            string[] number = Regex.Split(lbl.Name, @"\D+");

            if (lbl.Font.Bold == true)
                lbl.Font = RegularFont;
            else
                lbl.Font = BoldFont;

            foreach(string value in number)
            {
                if (value != String.Empty)
                {
                    int bitNumber = Int32.Parse(value);

                    RegisterValue &= ~(1 << bitNumber); // clear bit
                    RegisterValue |= ((lbl.Font.Bold == true ? 1 : 0) << bitNumber); // set bit
                }
            }
        }

        /// <summary>
        /// Write register button handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnWrite_Click(object sender, EventArgs e)
        {
            if (connected)
            {
                WriteRegister(controller.InfoArray[dgv_Registers.SelectedRows[0].Index], RegisterValue);
                controller.ReadRegister(controller.InfoArray[dgv_Registers.SelectedRows[0].Index]);
                DisplayRegisters();
            }
        }

        /// <summary>
        /// On cell begin edit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnCellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
        {
            if (CellBeginEdit != null)
            {
                CellBeginEdit(sender, e);
            }
        } 

        private void DataGridViewColumns(DataGridView listview, SizeF factor)
        {
            foreach (DataGridViewColumn column in listview.Columns)
            {
                column.Width = (int)Math.Round(column.Width * factor.Width);
            }
        }

        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
        {
            base.ScaleControl(factor, specified);
            DataGridViewColumns(dgv_RegDes, factor);
            DataGridViewColumns(dgv_Registers, factor);
        }
        /*
        public class RegisterProcess
        {
            RegisterBit[][] Data;

            public RegisterProcess(RegisterInfo[] info)
            {
                Data = new RegisterBit[info.Length][];
				
				for (int i = 0; i < info.Length; i++)
				{
					Data[i] = new RegisterBit[info[i].description.list.Count];
					
					for (int j = 0; j < info[i].description.list.Count; j++)
					{
						int fieldValue;
                        fieldValue = (info[i].dataField >> info[i].description.list[j].Index) & info[i].description.list[j].Width;
						Data[i][j] = new RegisterBit(info[i].description.list[j].name, fieldValue);
					}
				}
            }

            class RegisterBit
            {
                string Name;
                int Data;
				
				public RegisterBit(string name, int data)
				{
					Name = name;
					Data = data;
				}
            }
        }
        */

        public class RegisterArgs : EventArgs
        {
            public int Register;
        }


    }
}
