/*
 *
 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
 *
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.IO.Ports;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace RadioToolGUI
{
    internal partial class ConnectTab : UserControl
    {
        #region Members
        /*===================================================================*/
        private frmMain m_MainForm;
        private bool m_bDownlaodFwAbort;
        internal const Int32 DEVICE_SELECTION_CC3120SPI = 0;
        internal const Int32 DEVICE_SELECTION_CC3120UART = 1;
        internal const Int32 DEVICE_SELECTION_CC3220UART = 2;

        //Choose the default selection
        internal Int32 DEFAULT_DEVICE_SELECTION = DEVICE_SELECTION_CC3120SPI;
        /*===================================================================*/
        #endregion /* Members */

        #region Access
        /*===================================================================*/
        internal Button ConnectButton
        {
            get { return m_btnConnect; }
        }

        internal bool DownlaodFwAbort
        {
            get { return m_bDownlaodFwAbort; }
            set { m_bDownlaodFwAbort = value; }
        }
        /*===================================================================*/
        #endregion /* Access */

        #region LifeCycle
        /*===================================================================*/
        /// <summary>
        /// Mandatory constructor to initialize ConnectTab..
        /// </summary>
        internal ConnectTab(frmMain mf)
        {
            m_MainForm = mf;

            InitializeComponent();
            m_bDownlaodFwAbort = false;

            iFillDeviceList(DEFAULT_DEVICE_SELECTION);
            if (DEFAULT_DEVICE_SELECTION != DEVICE_SELECTION_CC3120SPI)
            {
                iFillComPortCombo(0);
            }

            iFillBaudRateList(1);
        }
        /*===================================================================*/
        #endregion /* LifeCycle */

        #region Operations
        /*===================================================================*/
        /// <summary>
        /// Enable/Disable controls in this tab.
        /// </summary>
        /// <param name="en"></param>
        internal void EnableControls(Boolean en)
        {
            m_btnConnect.Enabled = en;
        }

        private void iFillDeviceList(int default_selection)
        {
            m_cbo31xxDevices.Items.Add(new ComboboxItem(GlobalReference.TARGET_DEVICE_CC3120, Product.CC3120SPI));
            m_cbo31xxDevices.Items.Add(new ComboboxItem(GlobalReference.TARGET_DEVICE_CC3120UART, Product.CC3120UART));
            m_cbo31xxDevices.Items.Add(new ComboboxItem(GlobalReference.TARGET_DEVICE_CC3220, Product.CC3220));
            m_cbo31xxDevices.SelectedIndex = default_selection;
        }

        private void iFillBaudRateList(Int32 default_selection)
        {
            m_cboBaudRate.Items.Add(new ComboboxItem("57600", 57600));
            m_cboBaudRate.Items.Add(new ComboboxItem("115200", 115200));
            m_cboBaudRate.Items.Add(new ComboboxItem("460800", 460800));
            m_cboBaudRate.Items.Add(new ComboboxItem("921600", 921600));
            m_cboBaudRate.Items.Add(new ComboboxItem("1.5 Mbps", 1500000));
            m_cboBaudRate.Items.Add(new ComboboxItem("3 Mbps", 3000000));

            m_cboBaudRate.SelectedIndex = default_selection;
        }

        private void iFillComPortCombo(Int32 default_selection)
        {
            //Clear any previous items
            m_cboComPort.Items.Clear();

            // If default_selection == -1, do not list any port
            if (default_selection == -1)
            {
                return;
            }

            // List ports
            List<String> port_names = new List<string>();
            port_names.AddRange(SerialPort.GetPortNames());
            port_names.Sort(iComparePortsByLength);

            m_cboComPort.Items.AddRange(port_names.ToArray());

            if ((m_cboComPort.Items.Count > 0) && (m_cboComPort.Items.Count > default_selection))
            {
                m_cboComPort.SelectedItem = m_cboComPort.Items[default_selection];
            }
            else
            {
                m_cboComPort.SelectedItem = m_cboComPort.Items[0];
            }
        }

        private Int32 iGetIndexFromPortName()
        {
            string port_name;
            string port_idx_str = "";
            Int32 port_idx = -1;

            if (m_cboComPort.SelectedItem == null)
                return port_idx;

            port_name = m_cboComPort.SelectedItem.ToString();

            if ((port_name != null) && port_name.StartsWith("COM"))
            {
                port_idx_str = port_name.Replace("COM", "");
                if (!Int32.TryParse(port_idx_str, out port_idx))
                {
                    //Error
                }
            }

            return port_idx;
        }

        private int iComparePortsByLength(string x, string y)
        {
            if (x == null)
            {
                if (y == null)
                {
                    // If x is null and y is null, they're equal. 
                    return 0;
                }
                else
                {
                    // If x is null and y is not null, y is greater. 
                    return -1;
                }
            }
            else
            {
                // If x is not null...
                if (y == null)
                // ...and y is null, x is greater.
                {
                    return 1;
                }
                else
                {
                    // ...and y is not null, compare the lengths of the two strings.
                    int retval = x.Length.CompareTo(y.Length);

                    if (retval != 0)
                    {
                        // If the strings are not of equal length, the longer string is greater.
                        return retval;
                    }
                    else
                    {
                        // If the strings are of equal length, sort them with ordinary string comparison.
                        return x.CompareTo(y);
                    }
                }
            }
        }

        /// <summary>
        /// Performs connection. Always running in the background thread.
        /// </summary>
        private void t_Connect_DoWork(object sender, DoWorkEventArgs e)
        {
            Int32 ret = -1;
            ret = GlobalReference.ConnectDevice();
            e.Result = ret;
        }

        /// <summary>
        /// This will be called once the connection thread is complete.
        /// Now we enable all testing options for users.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void t_Connect_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Int32 connection_result = (Int32)e.Result;

            if (connection_result == 0) //Connection successful
            {
                m_btnConnect.Enabled = true;
                m_btnConnect.Text = "Disconnect";

                //Now enable other fields
                GlobalReference.WriteConsole(GlobalReference.CONNECTION_STATUS_CONNECTED);
                m_MainForm.updateConnectionStatus(GlobalReference.CONNECTION_STATUS_CONNECTED);

                //Getch infromation
                frmMain.RadioToolThread = new BackgroundWorker();

                //Starts Connection thread.
                frmMain.RadioToolThread.WorkerReportsProgress = true;
                frmMain.RadioToolThread.WorkerSupportsCancellation = true;
                frmMain.RadioToolThread.DoWork += new DoWorkEventHandler(t_RefreshDetails_DoWork);
                frmMain.RadioToolThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(t_RefreshDetails_RunWorkerCompleted);

                m_MainForm.updateTestingStatus(GlobalReference.TESTING_STATUS_FETCHING_INFO);
                GlobalReference.WriteConsole(GlobalReference.TESTING_STATUS_FETCHING_INFO);
                frmMain.RadioToolThread.RunWorkerAsync();

                return;
            }

            //Connection failure
            MessageBox.Show(Properties.Resources.msgbox_Connection_Error, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
            m_MainForm.updateConnectionStatus(GlobalReference.CONNECTION_STATUS_DISCONNECTED);
            m_btnConnect.Enabled = true;
        }

        /// <summary>
        /// A new worker thread to fecth device details. This includes Chip ID, FW version, and MAC address.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void t_RefreshDetails_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = GlobalReference.getDeviceVersion();
        }

        private void t_RefreshDetails_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            m_MainForm.UpdateDeviceInfo();

            GlobalReference.WriteConsole(String.Format("Chip ID={0}, ROM_ver={1}, FW_ver={2}.{3}.{4}, MAC={5}, Host_Driver={6}",
                GlobalReference.MyCurrentDevice.ChipId,
                GlobalReference.MyCurrentDevice.RomVer,
                GlobalReference.MyCurrentDevice.NwpVer,
                GlobalReference.MyCurrentDevice.FwVer,
                GlobalReference.MyCurrentDevice.PhyVer,
                GlobalReference.MyCurrentDevice.mac,
                GlobalReference.MyCurrentDevice.HostDriverVer));

            m_MainForm.updateTestingStatus(GlobalReference.TESTING_STATUS_IDLE);
            iEnableConnectionParameter(false);
            m_btnConnect.Enabled = true;

            if ((Int32) e.Result != 0)
            {
                MessageBox.Show(Properties.Resources.msgbox_fw_waring, "Device Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
            }
        }

        /// <summary>
        /// Performs disconnection. Always running in the background thread.
        /// </summary>
        private void t_Disconnect_DoWork(object sender, DoWorkEventArgs e)
        {
            Int32 ret = -1;
            ret = GlobalReference.DisconnectDevice();
        }

        /// <summary>
        /// This will be called once the disconnection thread is complete.
        /// Now we disable all testing options for users.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void t_Disconnect_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            m_btnConnect.Enabled = true;
            m_btnConnect.Text = "Connect";

            //Now enable other fields
            GlobalReference.WriteConsole(GlobalReference.CONNECTION_STATUS_DISCONNECTED);
            m_MainForm.updateConnectionStatus(GlobalReference.CONNECTION_STATUS_DISCONNECTED);
            iEnableConnectionParameter(true);

            m_MainForm.updateTargetDevice(GlobalReference.TARGET_DEVICE_NA);
        }

        private void iEnableConnectionParameter(bool en)
        {
            m_grpSettings.Enabled = en;
        }
        /*===================================================================*/
        #endregion /* Operations */

        #region Event Handling
        /*===================================================================*/
        /// <summary>
        /// "Connect" button click handler. This also creats a new thread to execute 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_btnConnect_Click(object sender, EventArgs e)
        {
            ((Button)sender).Enabled = false;
            if (!GlobalReference.isDeviceConnected()) //Not connected. Perform connection.
            {
                if (frmMain.RadioToolThread != null)
                {
                    if (frmMain.RadioToolThread.IsBusy != true)
                    {
                        frmMain.RadioToolThread.CancelAsync();
                    }
                }

                //Check product
                Product temp_product = (Product)(m_cbo31xxDevices.SelectedItem as ComboboxItem).Value;

                Int32 port_num = iGetIndexFromPortName();

                if (((ComboBox)m_cbo31xxDevices).SelectedIndex != DEVICE_SELECTION_CC3120SPI) //Selected CC3120 SPI, don't care about COM ports
                {
                    //Check COM port, only if using UART interface
                    if (port_num < 0)
                    {
                        //COM port error. Connection aboort.
                        MessageBox.Show("Incorrect COM port. Connection abort.", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                        ((Button)sender).Enabled = true;
                        return;
                    }
                }

                //All fields are correct. Now assign to GlobalReference & update GUI
                GlobalReference.MyCurrentDevice.product = temp_product;
                GlobalReference.MyCurrentDevice.port = port_num;
                GlobalReference.MyCurrentDevice.baud = (Int32)(m_cboBaudRate.SelectedItem as ComboboxItem).Value;
                m_MainForm.updateTargetDevice((m_cbo31xxDevices.SelectedItem as ComboboxItem).Text);

                //Creates a new worker thread
                frmMain.RadioToolThread = new BackgroundWorker();

                //Creates connection thread.
                frmMain.RadioToolThread.DoWork += new DoWorkEventHandler(t_Connect_DoWork);
                frmMain.RadioToolThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(t_Connect_RunWorkerCompleted);

                GlobalReference.WriteConsole(GlobalReference.CONNECTION_STATUS_CONNECTING);
                m_MainForm.updateConnectionStatus(GlobalReference.CONNECTION_STATUS_CONNECTING);
            }
            else //Connected. Perform disconnection.
            {
                //Creates a new worker thread
                frmMain.RadioToolThread = new BackgroundWorker();

                //Creates disconnection thread.
                frmMain.RadioToolThread.DoWork += new DoWorkEventHandler(t_Disconnect_DoWork);
                frmMain.RadioToolThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(t_Disconnect_RunWorkerCompleted);

                GlobalReference.WriteConsole(GlobalReference.CONNECTION_STATUS_DISCONNECTING);
                m_MainForm.updateConnectionStatus(GlobalReference.CONNECTION_STATUS_DISCONNECTING);
            }

            // Execute connection/disconnection
            frmMain.RadioToolThread.WorkerReportsProgress = false;
            frmMain.RadioToolThread.WorkerSupportsCancellation = true;
            frmMain.RadioToolThread.RunWorkerAsync();
        }

        private void m_btnRefreshPorts_Click(object sender, EventArgs e)
        {
            iFillComPortCombo(0);
        }

        private void m_cbo31xxDevices_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (((ComboBox)sender).SelectedIndex == DEVICE_SELECTION_CC3120SPI) //Selected CC3120 SPI, disable other fields
            {
                iFillComPortCombo(-1);
                m_cboComPort.Enabled = false;
                m_cboBaudRate.Enabled = false;
                m_btnRefreshPorts.Enabled = false;
            }
            else
            {
                if (((ComboBox)sender).SelectedIndex == DEVICE_SELECTION_CC3120UART) //Selected CC3120 SPI, disable other fields
                {
                    iFillComPortCombo(2);
                }
                else
                {
                    iFillComPortCombo(0);
                }
                m_cboComPort.Enabled = true;
                m_cboBaudRate.Enabled = true;
                m_btnRefreshPorts.Enabled = true;
            }
        }
        /*===================================================================*/
        #endregion /* Event Handling */
    }
}
