/*
 *
 *   Copyright (C) 2016 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission of
 *   Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been supplied,
 *   and under no circumstances can it be used with non-TI connectivity device.
 *
 */
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System.Threading;

namespace RadioToolGUI
{
    #region Enums & Structs
    /*===================================================================*/
    internal enum Band
    {
        BAND_2_4GHZ = 0, /* BG Band */
        BAND_5GHZ   = 1, /* A Band */
        BAND_4_9GHZ = 2  /* A Band */
    }

    internal enum Product
    {
        CC3120SPI,
        CC3120UART,
        CC3220
    }

    internal enum RadioTxMode_e
    {
        RADIO_TX_CONTINUOUS = 1,
        RADIO_TX_PACKETIZED = 2,
        RADIO_TX_CW = 3,
        MAX_RADIO_TX_MODE = 0xff
    }

    internal enum RadioPreamble_e
    {
        LONG_PREAMBLE_MODE = 0,
        SHORT_PREAMBLE_MODE = 1,
        MAX_NUM_PREAMBLE = 0xff
    }

    internal enum RadioDataPattern_e
    {
        ALL_0_PATTERN = 0,
        ALL_1_PATTERN = 1,
        INCREMENTAL_PATTERN = 2,
        DECREMENTAL_PATTERN = 3,
        PN9_PATTERN = 4,
        PN15_PATTERN = 5,
        PN23_PATTERN = 6,
        MAX_NUM_PATTERN = 0xff
    }

    internal enum RadioCCAThreshold_e
    {
        SL_TX_INHIBIT_THRESHOLD_MIN     = 1,
        SL_TX_INHIBIT_THRESHOLD_LOW     = 2,
        SL_TX_INHIBIT_THRESHOLD_DEFAULT = 3,
        SL_TX_INHIBIT_THRESHOLD_MED     = 4,
        SL_TX_INHIBIT_THRESHOLD_HIGH    = 5,
        SL_TX_INHIBIT_THRESHOLD_MAX     = 6
    }

    internal enum Channel_e
    {
        CHANNEL_1 = 1,
        CHANNEL_2 = 2,
        CHANNEL_3 = 3,
        CHANNEL_4 = 4,
        CHANNEL_5 = 5,
        CHANNEL_6 = 6,
        CHANNEL_7 = 7,
        CHANNEL_8 = 8,
        CHANNEL_9 = 9,
        CHANNEL_10 = 10,
        CHANNEL_11 = 11,
        CHANNEL_12 = 12,
        CHANNEL_13 = 13,
        MAX_CHANNELS = 0xff
    }

    internal enum RateIndex_e
    {
        RATE_1M    = 1,
        RATE_2M    = 2,
        RATE_5_5M  = 3,
        RATE_11M   = 4,
        RATE_6M    = 6,
        RATE_9M    = 7,
        RATE_12M   = 8,
        RATE_18M   = 9,
        RATE_24M   = 10,
        RATE_36M   = 11,
        RATE_48M   = 12,
        RATE_54M   = 13,
        RATE_MCS_0 = 14,
        RATE_MCS_1 = 15,
        RATE_MCS_2 = 16,
        RATE_MCS_3 = 17,
        RATE_MCS_4 = 18,
        RATE_MCS_5 = 19,
        RATE_MCS_6 = 20,
        RATE_MCS_7 = 21,
        MAX_NUM_RATES = 0xFF
    }

    internal struct CurrentDevice
    {
        internal Product product;
        internal Int32 port;
        internal Int32 baud;
        internal String ChipId;
        internal String RomVer;
        internal String NwpVer;
        internal String FwVer;
        internal String HostDriverVer;
        internal String PhyVer;
        internal String CC3220AppVer;
        internal String mac;
    }

    internal class TxArgPack
    {
        internal RadioTxMode_e          eTxMode;
        internal SByte                  ePowerLevel_Tone;
        internal Channel_e              eChannel;
        internal RateIndex_e            eRate;
        internal RadioPreamble_e        ePreamble;
        internal RadioDataPattern_e     eDataPattern;
        internal UInt16                 eSize;
        internal UInt32                 eDelay;
        internal UInt32                 eAmount;
        internal Byte                   eOverrideCCA;
        internal RadioCCAThreshold_e    eCCAThreshold;
        internal Byte                   eEnableACKs;
        internal Byte[]                 epDstMac;

        internal TxArgPack(
            RadioTxMode_e       eTxMode,
            SByte               ePowerLevel_Tone,
            Channel_e           eChannel,
            RateIndex_e         eRate,
            RadioPreamble_e     ePreamble,
            RadioDataPattern_e  eDataPattern,
            UInt16              eSize,
            UInt32              eDelay,
            UInt32              eAmount,
            Byte                eOverrideCCA,
            RadioCCAThreshold_e eCCAThreshold,
            Byte                eEnableACKs,
            [In, Out] Byte[]    epDstMac)
        {
            this.eTxMode = eTxMode;
            this.ePowerLevel_Tone = ePowerLevel_Tone;
            this.eChannel = eChannel;
            this.eRate = eRate;
            this.ePreamble = ePreamble;
            this.eDataPattern = eDataPattern;
            this.eSize = eSize;
            this.eDelay = eDelay;
            this.eAmount = eAmount;
            this.eOverrideCCA = eOverrideCCA;
            this.eCCAThreshold = eCCAThreshold;
            this.eEnableACKs = eEnableACKs;
            this.epDstMac = epDstMac;
        }
    }

    internal class GetStatsArgPack
    {
        UInt32 fcsPackets;
        UInt32 addrMMPackets;
        Int16 avgRssiMgmt;
        Int16 avgRssiOther;
        UInt16 pRssiHistogram;
        UInt16 pRateHistogram;
        UInt32 StartTimeStamp;
        UInt32 GetTimeStamp;

        internal GetStatsArgPack(UInt32 fcsPackets,
        UInt32 addrMMPackets,
        Int16 avgRssiMgmt,
        Int16 avgRssiOther,
        UInt16 pRssiHistogram,
        UInt16 pRateHistogram,
        UInt32 StartTimeStamp,
        UInt32 GetTimeStamp)
        {
            this.fcsPackets = fcsPackets;
            this.addrMMPackets = addrMMPackets;
            this.avgRssiMgmt = avgRssiMgmt;
            this.avgRssiOther = avgRssiOther;
            this.pRssiHistogram = pRssiHistogram;
            this.pRateHistogram = pRateHistogram;
            this.StartTimeStamp = StartTimeStamp;
            this.GetTimeStamp = GetTimeStamp;
        }
    }

    internal class ComboboxItem
    {
        internal string Text { get; set; }
        internal object Value { get; set; }

        internal ComboboxItem(string text, object value)
        {
            this.Text = text;
            this.Value = value;
        }

        public override string ToString()
        {
            return Text;
        }
    }

    internal class SlGetRxStatResponse_t
    {
        internal UInt32     ReceivedValidPacketsNumber;
        internal UInt32     ReceivedFcsErrorPacketsNumber;
        internal UInt32     ReceivedAddressMismatchPacketsNumber;
        internal Int16      AvarageDataCtrlRssi;
        internal Int16      AvarageMgMntRssi;
        internal UInt16[]   RateHistogram;    
        internal UInt16[]   RssiHistogram;   
        internal UInt32     StartTimeStamp;
        internal UInt32     GetTimeStamp;

        internal static readonly UInt32 SL_WLAN_NUM_OF_RATE_INDEXES = 20;
        internal static readonly UInt32 SL_WLAN_SIZE_OF_RSSI_HISTOGRAM = 6;

        internal SlGetRxStatResponse_t()
        {
            RateHistogram = new UInt16[SL_WLAN_NUM_OF_RATE_INDEXES];
            RssiHistogram = new UInt16[SL_WLAN_SIZE_OF_RSSI_HISTOGRAM];
        }

        internal SlGetRxStatResponse_t(UInt32 ReceivedValidPacketsNumber,
            UInt32 ReceivedFcsErrorPacketsNumber,
            UInt32 ReceivedAddressMismatchPacketsNumber,
            Int16 AvarageDataCtrlRssi,
            Int16 AvarageMgMntRssi,
            UInt16[] RateHistogram,  
            UInt16[] RssiHistogram,  
            UInt32 StartTimeStamp,
            UInt32 GetTimeStamp)
        {
            this.ReceivedValidPacketsNumber = ReceivedValidPacketsNumber;
            this.ReceivedFcsErrorPacketsNumber = ReceivedFcsErrorPacketsNumber;
            this.ReceivedAddressMismatchPacketsNumber = ReceivedAddressMismatchPacketsNumber;
            this.AvarageDataCtrlRssi = AvarageDataCtrlRssi;
            this.AvarageMgMntRssi = AvarageMgMntRssi;
            this.RateHistogram = RateHistogram;
            this.RssiHistogram = RssiHistogram;
            this.StartTimeStamp = StartTimeStamp;
            this.GetTimeStamp = GetTimeStamp;
        }
    };
    /*===================================================================*/
    #endregion /* Enums & Structs */

    internal class GlobalReference
    {
        #region Flags
        /*===================================================================*/
        private static String m_ConnectionState;
        private static String m_TestingState;
        internal static CurrentDevice MyCurrentDevice;
        internal static RadioTxMode_e TxMode;

        private static Form m_MainForm;
        private static RichTextBox m_console;

        internal static String FW_VER_PREFIX = "31.";

        internal const string CC3120SPI_LIB_FILE_NAME = "CC3120LibSPI.dll";
        internal const string CC3120UART_LIB_FILE_NAME = "CC3120LibUART.dll";
        internal const string CC3220UART_LIB_FILE_NAME = "CC3220Lib.dll";

        internal const string CHIP_ID_0X00 = "CC3120R";
        internal const string CHIP_ID_0X01 = "CC3120F";
        internal const string CHIP_ID_0X03 = "CC3120Z";
        internal const string CHIP_ID_0X10 = "CC3220R";
        internal const string CHIP_ID_0X11 = "CC3220F";
        internal const string CHIP_ID_0X13 = "CC3220Z";
        internal const string CHIP_ID_0X18 = "CC3220RS";
        internal const string CHIP_ID_0X1B = "CC3220ZS";

        private static Mutex m_getStatsMutex;
        /*===================================================================*/
        #endregion /* Flags */

        #region Constants
        /*===================================================================*/
        internal const String CONNECTION_STATUS_NA = "-";
        internal const String CONNECTION_STATUS_CONNECTED = "Connected";
        internal const String CONNECTION_STATUS_DISCONNECTED = "Disconnected";
        internal const String CONNECTION_STATUS_CONNECTING = "Connecting...";
        internal const String CONNECTION_STATUS_DISCONNECTING = "Disconnecting...";

        internal const String TESTING_STATUS_NA = "-";
        internal const String TESTING_STATUS_IDLE = "Idle";
        internal const String TESTING_STATUS_FETCHING_INFO = "Fetching Information...";
        internal const String TESTING_STATUS_TX_RUNNING = "Tx Running";
        internal const String TESTING_STATUS_TX_STOPPING = "Tx Stopping";
        internal const String TESTING_STATUS_RX_RUNNING = "Rx Running";
        internal const String TESTING_STATUS_RX_STOPPING = "Rx Stopping";

        internal const String TARGET_DEVICE_NA = "-";
        internal const String TARGET_DEVICE_CC3120 = "CC3120 SPI";
        internal const String TARGET_DEVICE_CC3120UART = "CC3120 UART";
        internal const String TARGET_DEVICE_CC3220 = "CC3220 UART";

        private static Mutex mut = new Mutex();
        /*===================================================================*/
        #endregion /* Constants */

        #region LifeCycle
        /*===================================================================*/
        internal GlobalReference()
        {
            TxMode = RadioTxMode_e.RADIO_TX_CONTINUOUS;

            m_ConnectionState = CONNECTION_STATUS_DISCONNECTED;
            m_TestingState = CONNECTION_STATUS_NA;

            m_console = null;
        }
        /*===================================================================*/
        #endregion /* LifeCycle */

        #region Operation
        /*===================================================================*/
        internal static void ConsoleInitialization(Form mf, RichTextBox c){
            m_MainForm = mf;
            m_console = c;
        }

        internal static Boolean isDeviceConnected()
        {
            return m_ConnectionState.Equals(CONNECTION_STATUS_CONNECTED);
        }

        internal static String getTestingState()
        {
            return m_TestingState;
        }

        internal static Int32 ConnectDevice()
        {
            // Set Method name delegates
            if (MyCurrentDevice.product == Product.CC3120SPI)
            {
                LibraryImport.RadioToolOpen     = CC3120SPILibrary.RadioToolOpen;
                LibraryImport.RadioToolClose    = CC3120SPILibrary.RadioToolClose;
                LibraryImport.RadioStartTX      = CC3120SPILibrary.RadioStartTX;
                LibraryImport.RadioStopTX       = CC3120SPILibrary.RadioStopTX;
                LibraryImport.RadioStartRX      = CC3120SPILibrary.RadioStartRX;
                LibraryImport.RadioStopRX       = CC3120SPILibrary.RadioStopRX;
                LibraryImport.RadioGetStats     = CC3120SPILibrary.RadioGetStats;
                LibraryImport.RadioGetMacAddr   = CC3120SPILibrary.RadioGetMacAddr;
                LibraryImport.RadioGetDeviceVersion = CC3120SPILibrary.RadioGetDeviceVersion;
            }
            else if (MyCurrentDevice.product == Product.CC3120UART)
            {
                LibraryImport.RadioToolOpen     = CC3120UARTLibrary.RadioToolOpen;
                LibraryImport.RadioToolClose    = CC3120UARTLibrary.RadioToolClose;
                LibraryImport.RadioStartTX      = CC3120UARTLibrary.RadioStartTX;
                LibraryImport.RadioStopTX       = CC3120UARTLibrary.RadioStopTX;
                LibraryImport.RadioStartRX      = CC3120UARTLibrary.RadioStartRX;
                LibraryImport.RadioStopRX       = CC3120UARTLibrary.RadioStopRX;
                LibraryImport.RadioGetStats     = CC3120UARTLibrary.RadioGetStats;
                LibraryImport.RadioGetMacAddr   = CC3120UARTLibrary.RadioGetMacAddr;
                LibraryImport.RadioGetDeviceVersion = CC3120UARTLibrary.RadioGetDeviceVersion;
            }
            else if (MyCurrentDevice.product == Product.CC3220)
            {
                LibraryImport.RadioToolOpen     = CC3220Library.RadioToolOpen;
                LibraryImport.RadioToolClose    = CC3220Library.RadioToolClose;
                LibraryImport.RadioStartTX      = CC3220Library.RadioStartTX;
                LibraryImport.RadioStopTX       = CC3220Library.RadioStopTX;
                LibraryImport.RadioStartRX      = CC3220Library.RadioStartRX;
                LibraryImport.RadioStopRX       = CC3220Library.RadioStopRX;
                LibraryImport.RadioGetStats     = CC3220Library.RadioGetStats;
                LibraryImport.RadioGetMacAddr   = CC3220Library.RadioGetMacAddr;
                LibraryImport.RadioGetDeviceVersion = CC3220Library.RadioGetDeviceVersion;
            }
            else
            {
                // TODO - report error
            }

            // Connect to the device
            Int32 ret = -1;
            ret = LibraryImport.RadioToolOpen((Byte)MyCurrentDevice.port, (UInt64)MyCurrentDevice.baud);
            
            if (ret >= 0)
            {
                m_ConnectionState = CONNECTION_STATUS_CONNECTED;
                m_TestingState = TESTING_STATUS_IDLE;
            }
            else
            {
                m_ConnectionState = CONNECTION_STATUS_DISCONNECTED;
                m_TestingState = TESTING_STATUS_NA;
            }

            return ret;
        }

        /// <summary>
        /// Disconnect device. Returns the API value.
        /// </summary>
        /// <returns></returns>
        internal static Int32 DisconnectDevice()
        {
            Int32 ret = -1;
            ret = LibraryImport.RadioToolClose();
            m_ConnectionState = CONNECTION_STATUS_DISCONNECTED;
            return ret;
        }

        /// <summary>
        /// Starts RX testing.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        internal static Int32 StartTX(TxArgPack args)
        {
            m_TestingState = TESTING_STATUS_TX_RUNNING;

            Int32 ret = -1;
            ret = LibraryImport.RadioStartTX(
                args.eTxMode,
                args.ePowerLevel_Tone,
                (Byte) args.eChannel,
                args.eRate,
                (Byte) args.ePreamble,
                args.eDataPattern,
                args.eSize,
                args.eDelay,
                args.eAmount,
                args.eOverrideCCA,
                args.eCCAThreshold,
                args.eEnableACKs,
                args.epDstMac);

            //Check the return value.
            //0 means TX successfully started. negative values otherwise.
            if (ret != 0)
            {
                m_TestingState = TESTING_STATUS_IDLE;
            }

            return ret;
        }

        /// <summary>
        /// Stops TX Testing.
        /// </summary>
        /// <param name="eTxMode"></param>
        /// <returns></returns>
        internal static Int32 StopTX(RadioTxMode_e eTxMode)
        {
            Int32 ret = -1;
            ret = LibraryImport.RadioStopTX(eTxMode);

            m_TestingState = TESTING_STATUS_IDLE;
            return ret;
        }

        /// <summary>
        /// Starts RX testing. Connection status will always be TESTING_STATUS_RX_RUNNING until function exits.
        /// This is non-blocking.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        internal static Int32 StartRX(Channel_e eChannel)
        {
            m_TestingState = TESTING_STATUS_RX_RUNNING;
            Int32 ret =  LibraryImport.RadioStartRX((Byte) eChannel);

            if (ret == 0) //Creates a Mutex for GetStats later
            {
                m_getStatsMutex = new Mutex(false, "m_getStatsMutex");
            }
            else
            {
                m_TestingState = TESTING_STATUS_IDLE;
            }

            return ret;
        }

        /// <summary>
        /// Stops RX Testing.
        /// </summary>
        /// <param name="eTxMode"></param>
        /// <returns></returns>
        internal static Int32 StopRX()
        {
            Int32 ret = -1;
            ret = LibraryImport.RadioStopRX();

            m_TestingState = TESTING_STATUS_IDLE;
            return ret;
        }

        /// <summary>
        /// Gets RX statistics.
        /// </summary>
        /// <param name="resp"></param>
        /// <returns></returns>
        internal static Int32 GetStats(SlGetRxStatResponse_t resp)
        {
            m_getStatsMutex.WaitOne();
            //MUTEX BLOCK
            Int32 ret = -1;

            Byte[] stats = new Byte[256];
            ret = LibraryImport.RadioGetStats(stats);

            resp.ReceivedValidPacketsNumber = BitConverter.ToUInt32(stats, 0);
            resp.ReceivedFcsErrorPacketsNumber = BitConverter.ToUInt32(stats, 4);
            resp.ReceivedAddressMismatchPacketsNumber = BitConverter.ToUInt32(stats, 8);
            resp.AvarageDataCtrlRssi = BitConverter.ToInt16(stats, 12);
            resp.AvarageMgMntRssi = BitConverter.ToInt16(stats, 14);

            Int32 index = 0;
            for(index = 0; index < SlGetRxStatResponse_t.SL_WLAN_NUM_OF_RATE_INDEXES; index++)
            {
                resp.RateHistogram[index] = BitConverter.ToUInt16(stats, 16 + (2 * index));
            }
            for (index = 0; index < SlGetRxStatResponse_t.SL_WLAN_SIZE_OF_RSSI_HISTOGRAM; index++)
            {
                resp.RssiHistogram[index] = BitConverter.ToUInt16(stats, 16 + (2 * ((Int32) SlGetRxStatResponse_t.SL_WLAN_NUM_OF_RATE_INDEXES + index)));
            }

            index = 16 + (2 * ((Int32)SlGetRxStatResponse_t.SL_WLAN_NUM_OF_RATE_INDEXES + (Int32)SlGetRxStatResponse_t.SL_WLAN_SIZE_OF_RSSI_HISTOGRAM));
            resp.StartTimeStamp = BitConverter.ToUInt32(stats, index);
            resp.GetTimeStamp = BitConverter.ToUInt32(stats, index + 4);

            //MUTEX BLOCK END
            m_getStatsMutex.ReleaseMutex();
            return ret;
        }

        /// <summary>
        /// Gets device version from libraries. 
        /// </summary>
        /// <returns>
        /// A complete structure if success. Empty fields if there's an error.
        /// </returns>
        internal static Int32 getDeviceVersion()
        {
            Int32 retError = -1;

            Byte[] info = new Byte[64];
            Byte[] infoHostDriver = new byte[64];
            UInt16 infoHostDriverLength = 0;

            retError = LibraryImport.RadioGetDeviceVersion(info, ref infoHostDriverLength, infoHostDriver);

            MyCurrentDevice.CC3220AppVer = "N/A";

            // If the function itself returns an error code, we exit immediately.
            if (retError != 0)
            {
                MyCurrentDevice.ChipId = "Error (" + retError.ToString() + ")";
                MyCurrentDevice.FwVer  = "Error (" + retError.ToString() + ")";
                MyCurrentDevice.PhyVer = "Error (" + retError.ToString() + ")";
                MyCurrentDevice.NwpVer = "Error (" + retError.ToString() + ")";
                MyCurrentDevice.RomVer = "Error (" + retError.ToString() + ")";
                MyCurrentDevice.HostDriverVer = "Error (" + retError.ToString() + ")";
                return retError;
            }

            // Assign values
            UInt32[] fwVer_intArr = new UInt32[4];
            UInt32[] phyVer_intArr = new UInt32[4];
            UInt32[] nwpVer_intArr = new UInt32[4];

            for (int i = 0; i < 4; i++)
            {
                fwVer_intArr[i]  = (UInt32)info[4 + i];
                phyVer_intArr[i] = (UInt32)info[8 + i];
                nwpVer_intArr[i] = (UInt32)info[12 + i];
            }

            // Classify chip IDs
            StringBuilder sb_chipId = new StringBuilder();
            sb_chipId.Append("0x" + BitConverter.ToString(info, 0, 1));
            switch (info[0])
            {
                case 0x00:
                    sb_chipId.Append(" (" + CHIP_ID_0X00  + ")");
                    break;
                case 0x01:
                    sb_chipId.Append(" (" + CHIP_ID_0X01 + ")");
                    break;
                case 0x03:
                    sb_chipId.Append(" (" + CHIP_ID_0X03 + ")");
                    break;
                case 0x10:
                    sb_chipId.Append(" (" + CHIP_ID_0X10 + ")");
                    break;
                case 0x11:
                    sb_chipId.Append(" (" + CHIP_ID_0X11 + ")");
                    break;
                case 0x13:
                    sb_chipId.Append(" (" + CHIP_ID_0X13 + ")");
                    break;
                case 0x18:
                    sb_chipId.Append(" (" + CHIP_ID_0X18 + ")");
                    break;
                case 0x1B:
                    sb_chipId.Append(" (" + CHIP_ID_0X1B + ")");
                    break;
                default:
                    sb_chipId.Append(" (Unknown)");
                    break;
            }

            MyCurrentDevice.ChipId = sb_chipId.ToString();
            MyCurrentDevice.FwVer  = String.Format("{0}{1:d}.{2:d}.{3:d}.{4:d}", FW_VER_PREFIX, fwVer_intArr[0], fwVer_intArr[1], fwVer_intArr[2], fwVer_intArr[3]);
            MyCurrentDevice.PhyVer = String.Format("{0:d}.{1:d}.{2:d}.{3:d}", phyVer_intArr[0], phyVer_intArr[1], phyVer_intArr[2], phyVer_intArr[3]);
            MyCurrentDevice.NwpVer = String.Format("{0:d}.{1:d}.{2:d}.{3:d}", nwpVer_intArr[0], nwpVer_intArr[1], nwpVer_intArr[2], nwpVer_intArr[3]);
            MyCurrentDevice.RomVer = "0x" + info[16].ToString("X") + info[17].ToString("X");

            if (MyCurrentDevice.product == Product.CC3220)
            {
                MyCurrentDevice.CC3220AppVer = info[20].ToString("D") + "." + info[21].ToString("D");
            }

            MyCurrentDevice.HostDriverVer = System.Text.Encoding.ASCII.GetString(infoHostDriver, 0, infoHostDriverLength);

            //Acuire MAC Address
            MyCurrentDevice.mac = getMacAddress();

            return 0;
        }

        /// <summary>
        /// Acquires Mac Address from libraries.
        /// </summary>
        /// <returns>
        /// The MAC address. If reading has error, will return an error messsage related to the error.
        /// </returns>
        internal static String getMacAddress()
        {
            Int32 ret = -1;            
            Byte[] mac = new Byte[8];
            ret = LibraryImport.RadioGetMacAddr(mac);

            if (ret != 0)
            {
                return "Error(" + ret + ")";
            }   

            return String.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
        }

        public static byte[] StringToByteArray(String hex)
        {
            int NumberChars = hex.Length;
            byte[] bytes = new byte[NumberChars / 2];
            for (int i = 0; i < NumberChars; i += 2)
                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
            return bytes;
        }

        /// <summary>
        /// Writes messages to console. Date and time will automatically be appended.
        /// </summary>
        /// <param name="msg"></param>
        public static void WriteConsole(String msg)
        {
            mut.WaitOne();

            if (m_console.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(WriteConsole);
                m_MainForm.Invoke(d, new object[] { msg });
            }
            else
            {
                if (m_console.TextLength != 0)
                {
                    m_console.AppendText("\n");
                }

                m_console.AppendText(String.Format("[{0}] {1}", DateTime.Now.ToString(), msg));

                //Scroll to bottom
                m_console.SelectionStart = m_console.Text.Length;
                m_console.ScrollToCaret();
            }

            mut.ReleaseMutex();
        }

        delegate void SetTextCallback(String text);

        public static String GetAssemblyVersion()
        {
            return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
        }

        /// <summary>
        /// Checks if all three dll files exists, and retrieves version information if they are.
        /// </summary>
        /// <returns></returns>
        public static DLLInfo CheckDLLFile()
        {
            DLLInfo ret = new DLLInfo();

            if (File.Exists(CC3120SPI_LIB_FILE_NAME))
            {
                ret.Lib3120SPIExist = true;
                ret.Lib3120Version = FileVersionInfo.GetVersionInfo(CC3120SPI_LIB_FILE_NAME).FileVersion;
            }

            if (File.Exists(CC3120UART_LIB_FILE_NAME))
            {
                ret.Lib3120UARTExist = true;
                ret.Lib3120UARTVersion = FileVersionInfo.GetVersionInfo(CC3120UART_LIB_FILE_NAME).FileVersion;
            }

            if (File.Exists(CC3220UART_LIB_FILE_NAME))
            {
                ret.Lib3220Exist = true;
                ret.Lib3220Version = FileVersionInfo.GetVersionInfo(CC3220UART_LIB_FILE_NAME).FileVersion;
            }

            return ret;
        }
        /*===================================================================*/
        #endregion /* Operation */
    }

    internal class ChannelData
    {
        #region Members
        /*===================================================================*/
        private String m_Name;
        private Int32 m_Freq;
        private Int32 m_PrimIdx;
        private Band m_Band;
        /*===================================================================*/
        #endregion /* Members */

        #region Access
        /*===================================================================*/
        internal System.String Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        internal System.Int32 Freq
        {
            get { return m_Freq; }
            set { m_Freq = value; }
        }

        internal System.Int32 PrimIdx
        {
            get { return m_PrimIdx; }
            set { m_PrimIdx = value; }
        }

        internal Band Band
        {
            get { return m_Band; }
            set { m_Band = value; }
        }
        /*===================================================================*/
        #endregion /* Access */

        #region LifeCycle
        /*===================================================================*/
        internal ChannelData(String name, int freq, int prim_idx, Band band)
        {
            m_Name = name;
            m_Freq = freq;
            m_PrimIdx = prim_idx;
            m_Band = band;
        }
        /*===================================================================*/
        #endregion /* LifeCycle */

        #region Operations
        /*===================================================================*/

        /*===================================================================*/
        #endregion /* Operations */
    }

    internal class ChannelTable : List<ChannelData>
    {
        #region Members
        /*===================================================================*/

        /*===================================================================*/
        #endregion /* Members */

        #region Access
        /*===================================================================*/

        /*===================================================================*/
        #endregion /* Access */

        #region LifeCycle
        /*===================================================================*/
        internal ChannelTable() : base() { }
        /*===================================================================*/
        #endregion /* LifeCycle */

        #region Operations
        /*===================================================================*/
        internal void AddChannel(String chan_name, int freq, int prim_chan_idx, Band band)
        {
            this.Add(new ChannelData(chan_name, freq, prim_chan_idx, band));
        }

        internal int GetIndexByName(String chan_name)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if (this[i].Name == chan_name)
                {
                    return i;
                }
            }

            return -1;
        }

        internal int GetIndexByBandAndPrime(Band band, int primary_chan_idx)
        {
            for (int i = 0; i < this.Count; i++)
            {
                if ((this[i].Band == band) && (this[i].PrimIdx == primary_chan_idx))
                {
                    return i;
                }
            }

            return -1;
        }
        /*===================================================================*/
        #endregion /* Operations */
    }

    internal class DLLInfo
    {
        internal Boolean Lib3120SPIExist;
        internal Boolean Lib3120UARTExist;
        internal Boolean Lib3220Exist;

        internal String Lib3120Version;
        internal String Lib3120UARTVersion;
        internal String Lib3220Version;

        internal DLLInfo()
        {
            Lib3120SPIExist = false;
            Lib3120UARTExist = false;
            Lib3220Exist = false;
            Lib3120Version = "N/A";
            Lib3120UARTVersion = "N/A";
            Lib3220Version = "N/A";
        }
    }
}
