/*******************************************************************************
* 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.Data;
using System.Drawing;
using System.Linq;
using System.Text;

using System.Windows.Forms;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.Globalization;
using System.Threading;
using System.Management;
using System.Diagnostics;
using System.IO;
using System.Collections.Specialized;

using HealthSensorPlatform.DeviceDescriptions;
using HealthSensorPlatform.CustomControls;
using HealthSensorPlatform.View;
using HealthSensorPlatform.Presenter;
using HealthSensorPlatform.Model;

using MAX30205EVKit.Presenter;

using Maxim.MAX30101GUI;
using Maxim.Utility;

using MaximStyle;

namespace HealthSensorPlatform
{

    public partial class HspForm : Form, IFormView, IHspSetting
    {
        /* Constants */
        //Software Version
        internal const int MAJORVERSION = 3;
        internal const int MINORVERSION = 0;
        internal const int PATCHVERSION = 1;
        const string VersionSuffix = "";

        const string PartNumber = "MAXREFDES100";

        const string readVersionCommand = "/System/ReadVer\r\n";
        const string readFlashIDCommand = "/S25FS512/ReadId\r\n";
        const string firmwareKey = "HSP FW Version"; 

        /* Fields */
        SplashScreen splashScreen = new SplashScreen();
        FormConnection connectionDialog = new FormConnection();
        string port;
        bool connected = false;
        bool internalUpdate = false; // Used to prevent events from triggering
        bool streaming = false; // Disable GUI features while streaming is enabled

        static ManagementEventWatcher watcher; 

        /* // TODO: Do not remember what this is for, can remove
        MaximStyle.MaximNumericUpDown[] ud_cmd = new MaximStyle.MaximNumericUpDown[3];
        MaximStyle.MaximNumericUpDown[] ud_data = new MaximStyle.MaximNumericUpDown[8];
        Panel[] p_cmd = new Panel[3];
        Panel[] p_data = new Panel[8];
        */

        IDeviceView[] deviceViews;
        Maxim.CustomControls.RegisterView[] registerViews;
        TabPage[] max30001TabPages;

        RPCSupport.RPCClient rpcClient;
        RPCSupport.Devices.I2cDevice i2cDevice;
        RPCSupport.Devices.SpiDevice spiDevice;
        RPCSupport.Devices.MAX30101 max30101;
        RPCSupport.Devices.MAX30205 max30205;
        RPCSupport.Devices.LIS2HD lis2hd;
        RPCSupport.Devices.MAX30001 max30001;
        RPCSupport.DataLogging.DataLogging logging;

        ListDictionary registerViewDict;

        //FileLogPresenter fileLogPresenter;
        RawFileLogPresenter rawFileLogPresenter;
        OpticalFileLogPresenter opticalFileLogPresenter;
        TemperaturePresenter temperaturePresenter;

        Model.MAX30001Model max30001Model;

        public event EventHandler<EnableEventArgs> FileLogEnable;
        public event EventHandler<EnableEventArgs> OpticalFileLogEnable;
        public event EventHandler<EventArgs> RegisterExport;
        public event EventHandler<EventArgs> RegisterImport;

        public event EventHandler<EventArgs> Connected;

        /* Constructor */
        /// <summary>
        /// 
        /// </summary>
        public HspForm()
        {
            InitializeComponent();

            InitSplashScreen();

            //initialize text in status bar
            ss_Status.Items[0].Text = "";
            ss_Status.Items[1].Text = "";

            deviceViews = new IDeviceView[] { 
                opticalView1, 
                temperatureView1,
                ecgView1,
                dataLoggingView1,
                rvLIS2HD,
                rvMAX14720,
                rvMAX30001,
                rvMAX30101,
                rvMAX30205,
                rvMAX30205_U18
            };

            registerViews = new Maxim.CustomControls.RegisterView[] {
                rvMAX30101,
                rvMAX30205,
                rvMAX30205_U18,
                rvLIS2HD,
                rvMAX30001,
                rvMAX14720
            };

            max30001TabPages = new TabPage[] { tpMAX30001BioZInputMux, tpBioZChannel, tpMAX30001BioZLoad, tpMAX30001Pace };

            registerViewDict = new ListDictionary();

            InitRpcClient();

            InitRpcDevices();
            PrimitivesViewInit();
            RegistersTabInit();
            LoggingViewInit();
            TemperatureViewInit();
            OpticalViewInit();

            BlockDiagramsInit();
            ECGPlotsViewInit();

            InitalizeUSBDetection();

            FileLoggingInit();

            RegisterExportInit();

            SetupSwitchPictureBoxes();
        }

        private void SetupSwitchPictureBoxes()
        {
            //ECGMux Tab
            picECGLeadConfig.Controls.Add(picSw1);
            picSw1.Controls.Add(picSw2);
            picSw2.Controls.Add(picSw3);
            picSw3.Controls.Add(picSw4);
            picSw4.Controls.Add(picSw5);
            picSw5.Controls.Add(picSw6);
            picSw6.Controls.Add(picSw7);
            picSw7.Controls.Add(picSw8);
            picSw8.Controls.Add(picSw9);
            picSw9.Controls.Add(picSw10);
            picSw10.Controls.Add(picSw11);
            picSw11.Controls.Add(picSw12);
            picSw12.Controls.Add(picSw13);
            picSw13.Controls.Add(picSw14);

            //BioZMux Tab
            pictureBox2.Controls.Add(picLoff1);
            picLoff1.Controls.Add(picLoff2);
            picLoff2.Controls.Add(picLeadOn);
            picLeadOn.Controls.Add(picBioZsw1);
            picBioZsw1.Controls.Add(picBioZsw2);
            picBioZsw2.Controls.Add(picLeadBias1);
            picLeadBias1.Controls.Add(picLeadBias2);
            picLeadBias2.Controls.Add(picBioZCal1);
            picBioZCal1.Controls.Add(picBioZCal2);

            //BioZLoad Tab
            picBioZLoad.Controls.Add(picBioZLresload); 
            picBioZLresload.Controls.Add(picBioZLloff1);
            picBioZLloff1.Controls.Add(picBioZLloff2);
            picBioZLloff2.Controls.Add(picBioZLlon);
            picBioZLlon.Controls.Add(picBioZLsw1);
            picBioZLsw1.Controls.Add(picBioZLsw2);
            picBioZLsw2.Controls.Add(picBioZLlb1);
            picBioZLlb1.Controls.Add(picBioZLlb2);
            picBioZLlb2.Controls.Add(picBioZLcal1);
            picBioZLcal1.Controls.Add(picBioZLcal2);


        }

        /* Delegates */
        delegate void guiDisconnectedCallBack();

        public void MessageInfo(string message)
        {
            ss_Status.MessagesLabel.Text = message;
        }

        public void MessageBoxShow(string message)
        {
            MessageBox.Show(message);
        }

        public void MessageBoxShow(string message, string title)
        {
            MessageBox.Show(message, title);
        }


        /// <summary>
        /// http://stackoverflow.com/questions/286184/how-to-capture-a-serial-port-that-disappears-because-the-usb-cable-gets-unplugge
        /// </summary>
        private void InitalizeUSBDetection()
        {
            WqlEventQuery query;
            ManagementScope scope;

            scope = new ManagementScope("root\\CIMV2");
            scope.Options.EnablePrivileges = true;

            query = new WqlEventQuery();
            query.EventClassName = "__InstanceDeletionEvent"; // "__InstanceCreationEvent" can be used for insertion
            query.WithinInterval = new TimeSpan(0, 0, 3);
            query.Condition = @"TargetInstance ISA 'Win32_USBControllerdevice'";
            watcher = new ManagementEventWatcher(scope, query);
            watcher.EventArrived += new EventArrivedEventHandler(USBRemovedHandler);
            watcher.Start();
        }
        
        /// <summary>
        /// http://stackoverflow.com/questions/286184/how-to-capture-a-serial-port-that-disappears-because-the-usb-cable-gets-unplugge
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void USBRemovedHandler(object sender, EventArrivedEventArgs e)
        {
            PropertyData targetInstanceData = e.NewEvent.Properties["TargetInstance"];

            if (targetInstanceData != null)
            {
                ManagementBaseObject targetInstanceObject = (ManagementBaseObject)targetInstanceData.Value;
                if (targetInstanceData != null)
                {
                    string dependent = targetInstanceObject.Properties["Dependent"].Value.ToString();
                    string deviceId = dependent.Substring(dependent.IndexOf("DeviceID=") + 10);

                    if (deviceId.StartsWith("USB\\\\VID_0B6A&PID_003C")  // FW 1.5
                        || deviceId.StartsWith("USB\\\\VID_1F00&PID_2012\\\\0123456789")
                        || deviceId.StartsWith("USB\\\\VID_0B6A&PID_0100")) // FW 2.0 mbed
                    {
                        // device is disconnected
                        guiDisconnected();
                        try
                        {
                            rpcClient.Disconnect();
                        }
                        catch (System.IO.IOException)
                        {

                        }
                        catch (NullReferenceException)
                        {
                            // Disconnecting when it doesn't exist
                            // Occurs when you pull the USB while on the com port selection window
                        }
                    }
                }

            }
            
        }

        private void InitSplashScreen()
        {
            splashScreen.maximSplashScreen1.CopyrightString = "©2016 Maxim Integrated Products, Inc.";
            splashScreen.maximSplashScreen1.VersionString = "Version " + MAJORVERSION.ToString() + "." + MINORVERSION.ToString() + "." + PATCHVERSION.ToString() + VersionSuffix;
            splashScreen.maximSplashScreen1.ApplicationName = this.Text;
            //splashScreen.maximSplashScreen1.Checked = Properties.Settings.Default.DisableSplash;
        }

        void comPortConnect()
        {
            bool tryPreviousComPort = true;

            connectionDialog.Reset();
            connectionDialog.ScanPorts();

            while (true)
            {
                // Try to connect to the previous port, if we have not tried before 
                // and it the port string is not an empty string ("")
                if (tryPreviousComPort == true
                    && String.Compare(Properties.Settings.Default.ComPort, "") != 0)
                {
                    tryPreviousComPort = false;
                }
                else
                {
                    connectionDialog.ShowDialog();
                }

                // Break out of loop if user canceled
                if (connectionDialog.Canceled == true)
                {
                    guiDisconnected();
                    break;
                }

                // Get the port from the dialog form, returns default property
                // for first use
                port = connectionDialog.Port;
                if (String.Compare(port, "") == 0)
                    continue;


                // Connect to port
                try
                {
                    rpcClient.Connect(port);
                    if (rpcClient.IsConnected() == true)
                    {
                        guiConnected();
                        break;
                    }
                    else // Connected to COM port, but cannot query STM32_HV
                    {
                        MessageBox.Show("Device not found on selected port", "Connection Issue");
                        guiDisconnected();
                    }
                }
                catch (TimeoutException)
                {
                    guiDisconnected();
                    rpcClient.Disconnect();
                    MessageBox.Show("Unable to communicate with device, communication timed out.\nVerified selected port is correct.",
                        "Connection Issue");
                }
                catch (System.IO.IOException)
                {
                    guiDisconnected();
                    MessageBox.Show("Select a valid COM port. Unable to connect with device.\n\nVerify the " + PartNumber + " board is plugged in and turned on. Turn on by pressing the button on the board for 3 seconds until the red LED starts blinking.",
                        "Connection Issue");
                }
                catch (UnauthorizedAccessException)
                {
                    // Port does not exist
                }
            }
        }

        bool checkFirmware()
        {
            string reply;

            reply = rpcClient.RawRpcCall(readVersionCommand, true);

            return reply.StartsWith(firmwareKey);
        }

        void guiConnected()
        {
            ss_Status.Items[1].Text = rpcClient.Version();//rpcClient.RawRpcCall(readVersionCommand, true); // TODO this should be moved into the RPCSupport as a virtual system device
            ss_Status.Items[2].Text = "Hardware Connected (" + port + ")";

            connected = true;

            //connectionToolStripMenuItem.Enabled = false;
            //disconnectToolStripMenuItem.Enabled = true;

            connectionToolStripMenuItem.Checked = true;
            disconnectToolStripMenuItem.Checked = false;

            // Enable Communication for each Device View
            foreach (IDeviceView device in deviceViews)
            {
                device.Connected = true;
            }

            // TODO Ask F/W team if this is needed
            rpcClient.MAX30001.WriteReg(0x02, 0x3);
            rpcClient.MAX30001.WriteReg(0x03, 0x3);

            max30001Model = new Model.MAX30001Model(max30001);
            partIDInit();

            dataLoggingView1.Enabled = checkFlashID();

            // Read device registers
            UpdateRegisters();

            if (Connected != null)
                Connected(this, new EventArgs());
        }


        void guiDisconnected()
        {
            if (this.ss_Status.InvokeRequired)
            {
                guiDisconnectedCallBack d = new guiDisconnectedCallBack(guiDisconnected);
                this.Invoke(d);
            }
            else
            {
                ss_Status.Items[1].Text = "";
                ss_Status.Items[2].Text = "Hardware Not Connected";
                //UpdateConnection(null, false);;
                connected = false;

                //connectionToolStripMenuItem.Enabled = true;
                //disconnectToolStripMenuItem.Enabled = false;

                connectionToolStripMenuItem.Checked = false;
                disconnectToolStripMenuItem.Checked = true;

                foreach (IDeviceView device in deviceViews)
                {
                    device.Connected = false;
                }
            }
        }

        void InitRpcClient()
        {
            rpcClient = new RPCSupport.RPCClient(RPCSupport.RPCClient.ePipeline.eSerialWrap);
            rpcClient.InitPipeline();
        }

        void InitRpcDevices()
        {
            i2cDevice = rpcClient.Init_I2cDevice();
            spiDevice = rpcClient.Init_SpiDevice();
            max30101 = rpcClient.Init_MAX30101(0xAE);
            max30205 = rpcClient.Init_MAX30205(0x32);
            lis2hd = rpcClient.Init_LIS2HD(0x50);
            max30001 = rpcClient.Init_MAX30001();
            logging = rpcClient.Init_Logging();
        }

        private void Form_Load(object sender, EventArgs e)
        {
          if (splashScreen.SplashDisabled() == false)
                splashScreen.ShowDialog();
            
            while (splashScreen.Visible == true)
                Thread.Sleep(100);

            comPortConnect();
        }

        void PrimitivesViewInit()
        {
            primitivesView1.rpcClient = rpcClient;
            primitivesView1.i2cDevice = i2cDevice;
            primitivesView1.spiDevice = spiDevice;
            primitivesView1.max30101 = max30101;
            primitivesView1.max30205 = max30205;
            primitivesView1.max30001 = max30001;
            primitivesView1.lis2hd = lis2hd;

            maximTabControl1.HideTab("tpPrimitives");
        }

        void OpticalViewInit()
        {
            opticalView1.RPCClient = rpcClient;
            //opticalView1.StreamingStartStop += new OpticalView.StreamingStartStopEventHandler(opticalView_StreamingStartStop);
            opticalView1.StreamingStartStop += new EventHandler<StreamingStartStopEventArgs>(opticalView_StreamingStartStop);
            dataLoggingView1.LoggingParametersChanged += new DataLoggingView.LoggingParametersHandler(dataLoggingFlashReadEventHandler);

            MaximSKAlgorithmClass algorithm = new MaximSKAlgorithmClass();
            OpticalAlgorithmPresenter presenter = new OpticalAlgorithmPresenter(rpcClient, algorithm, opticalView1);
        }

        void ECGPlotsViewInit()
        {
            ecgView1.RPCClient = rpcClient;

            //cboEnRToR.SelectedIndexChanged += new EventHandler(cboStreaming_SelectedIndexChanged);
            //cboECGChannelEnable.SelectedIndexChanged += new EventHandler(cboStreaming_SelectedIndexChanged);

            ecgView1.GetEcgInitArgsPointer = new CustomControls.EcgView.GetEcgInitArgs(getEcgArgs); 
            ecgView1.GetRToRInitArgsPointer = new CustomControls.EcgView.GetRToRInitArgs(getRToRArgs);
            ecgView1.GetBioZInitArgsPointer = new CustomControls.EcgView.GetBioZInitArgs(getBioZArgs);
            ecgView1.GetPaceInitArgsPointer = new CustomControls.EcgView.GetPaceInitArgs(getPaceArgs);

            ecgView1.StreamingStartStop += new EventHandler<StreamingStartStopEventArgs>(ecgView_StreamingStartStop);

            ecgView1.HspSetting = this;

            StreamPresenter streamPresenter = new StreamPresenter(rpcClient, ecgView1, this);
        }

        void LoggingViewInit()
        {
            dataLoggingView1.RPCClient = rpcClient;

            dataLoggingView1.GetEcgInitArgsPointer = new CustomControls.DataLoggingView.GetEcgInitArgs(getEcgArgs);
            dataLoggingView1.GetRToRInitArgsPointer = new CustomControls.DataLoggingView.GetRToRInitArgs(getRToRArgs);
            dataLoggingView1.GetBioZInitArgsPointer = new CustomControls.DataLoggingView.GetBioZInitArgs(getBioZArgs);
            dataLoggingView1.GetPaceInitArgsPointer = new CustomControls.DataLoggingView.GetPaceInitArgs(getPaceArgs);
            dataLoggingView1.GetFMSTRInitArgsPointer = new CustomControls.DataLoggingView.GetFMSTRInitArgs(getFMSTRArgs);
            dataLoggingView1.GetHRModeInitArgsPointer = new CustomControls.DataLoggingView.GetHRModeInitArgs(opticalView1.GetHRModeArgs);
            dataLoggingView1.GetSpO2HRModeInitArgsPointer = new CustomControls.DataLoggingView.GetSpO2HRModeInitArgs(opticalView1.GetSpO2HRModeArgs);
            dataLoggingView1.GetMultiModeInitArgsPointer = new CustomControls.DataLoggingView.GetMultiModeInitArgs(opticalView1.GetMultiModeArgs);

            Model.DataLogModel dataLogModel = new Model.DataLogModel(rpcClient);
            DataLogPresenter dataLogPresenter = new DataLogPresenter(this, ecgView1, opticalView1, dataLogModel, dataLoggingView1, new RawFileLogView(), new RawFileLogView(), new RawFileLogView(), 
                new RawFileLogView(), new RawFileLogView(), new RawFileLogView(), new RawFileLogView(), new RawFileLogView(), new RawFileLogView());
        }

        void FileLoggingInit()
        {
            IRawFileLogView ecgFileLog = new RawFileLogView();
            IRawFileLogView bioZFileLog = new RawFileLogView();

            //fileLogPresenter = new FileLogPresenter(ecgFileLog, bioZFileLog, this, rpcClient, ecgView1);
            //ecgView1.EcgLog = ecgFileLog;
            //ecgView1.BioZLog = bioZFileLog;

            IRawFileLogView ecgRawFileLog = new RawFileLogView();
            IRawFileLogView paceRawFileLog = new RawFileLogView();
            IRawFileLogView rToRFileLog = new RawFileLogView();
            IRawFileLogView bioZRawFileLog = new RawFileLogView();
            rawFileLogPresenter = new RawFileLogPresenter(ecgRawFileLog, bioZRawFileLog, rToRFileLog, paceRawFileLog, this, rpcClient, ecgView1, dataLoggingView1);

            IRawFileLogView opticalFileLog = new RawFileLogView();
            IRawFileLogView accelFileLog = new RawFileLogView();

            opticalFileLogPresenter = new OpticalFileLogPresenter(rpcClient, this, opticalView1, opticalFileLog, accelFileLog);

        }

        void RegisterExportInit()
        {
            RegisterExportPresenter registerExportPresenter;
            RegisterExportForm exportForm = new RegisterExportForm();
            List<Maxim.CustomControls.RegisterView> registerViewList = new List<Maxim.CustomControls.RegisterView>(); 

            registerViewList.AddRange(registerViews);

            registerExportPresenter = new RegisterExportPresenter(this, exportForm, registerViewList);
        }
        
        private void txt_Status_TextChanged(object sender, EventArgs e)
        {
            //Scroll to the bottom of the textbox when text is added
            txt_Status.SelectionStart = txt_Status.Text.Length;
            txt_Status.ScrollToCaret();
            txt_Status.Refresh();
        }

        private void btn_Clear_Click(object sender, EventArgs e)
        {
            //clear the status box
            txt_Status.Text = "";
        }

        private void rvLIS2HDInit()
        {
            Maxim.CustomControls.LIS2DHInfo lis2DHInfo = new Maxim.CustomControls.LIS2DHInfo();
            Maxim.CustomControls.DeviceController controller = new Maxim.CustomControls.DeviceController();
            controller.SetRegisterInfo(lis2DHInfo);
            controller.SetInterface(rpcClient);
            rvLIS2HD.SetController(controller);
            //rvLIS2HD.ReadAll();
            rvLIS2HD.DisplayRegisters();
            rvLIS2HD.DeviceName = "LIS2DH";


            //registerInfoData[0] = lis2DHInfo.Info;
        }

        private void rvMAX30101Init()
        {
            Maxim.CustomControls.MAX30101Info max30101Info = new Maxim.CustomControls.MAX30101Info();
            Maxim.CustomControls.DeviceController controller = new Maxim.CustomControls.DeviceController();
            controller.SetRegisterInfo(max30101Info);
            controller.SetInterface(rpcClient);
            rvMAX30101.SetController(controller);
            //rvMAX30101.ReadAll();
            rvMAX30101.DisplayRegisters();
            rvMAX30101.DeviceName = "MAX30101";


            //registerInfoData[1] = max30101Info.Info;
        }

        private void rvMAX30205Init()
        {
            Maxim.CustomControls.MAX30205Info max30205Info = new Maxim.CustomControls.MAX30205Info(0x92);
            Maxim.CustomControls.DeviceController controller = new Maxim.CustomControls.DeviceController();
            controller.SetRegisterInfo(max30205Info);
            controller.SetInterface(rpcClient);
            rvMAX30205.SetController(controller);
            //rvMAX30205.ReadAll();
            rvMAX30205.DisplayRegisters();
            rvMAX30205.DeviceName = "MAX30205 (Top)";


            //registerInfoData[2] = max30205Info.Info;
        }

        private void rvMAX30205_U18Init()
        {
            Maxim.CustomControls.MAX30205Info max30205Info = new Maxim.CustomControls.MAX30205Info();
            Maxim.CustomControls.DeviceController controller = new Maxim.CustomControls.DeviceController();
            controller.SetRegisterInfo(max30205Info);
            controller.SetInterface(rpcClient);
            rvMAX30205_U18.SetController(controller);
            //rvMAX30205_U18.ReadAll();
            rvMAX30205_U18.DisplayRegisters();
            rvMAX30205_U18.DeviceName = "MAX30205 (Bottom)";


            //registerInfoData[3] = max30205Info.Info;
        }

        private void rvMAX30001Init()
        {
            Maxim.CustomControls.MAX30001Info max30001Info = new Maxim.CustomControls.MAX30001Info();
            Maxim.CustomControls.DeviceController controller = new Maxim.CustomControls.DeviceController();
            controller.SetRegisterInfo(max30001Info);
            controller.SetInterface(rpcClient);
            rvMAX30001.SetController(controller);
            //rvMAX30001.ReadAll();
            rvMAX30001.DisplayRegisters();
            rvMAX30001.DeviceName = "MAX30001";


            //registerInfoData[4] = max30001Info.Info;
        }

        private void rvMAX30003Init()
        {
            Maxim.CustomControls.MAX30003Info max30003Info = new Maxim.CustomControls.MAX30003Info();
            Maxim.CustomControls.DeviceController controller = new Maxim.CustomControls.DeviceController();
            controller.SetRegisterInfo(max30003Info);
            controller.SetInterface(rpcClient);
            rvMAX30001.SetController(controller);
            //rvMAX30001.ReadAll();
            rvMAX30001.DisplayRegisters();
            rvMAX30001.DeviceName = "MAX30003";
        }

        private void rvMAX14720Init()
        {
            Maxim.CustomControls.MAX14720Info max14720Info = new Maxim.CustomControls.MAX14720Info();
            Maxim.CustomControls.DeviceController controller = new Maxim.CustomControls.DeviceController();
            controller.SetRegisterInfo(max14720Info);
            controller.SetInterface(rpcClient);
            rvMAX14720.SetController(controller);
            //rvMAX14720.ReadAll();
            rvMAX14720.DisplayRegisters();
            rvMAX14720.DeviceName = "MAX14720";

            //registerInfoData[5] = max14720Info.Info;
        }

        private void partIDInit()
        {
            //rvMAX30001.Clear();
            tabPageEnable(null, false);
            switch (max30001Model.PartID)
            {
                case Model.MAX30001Model.Part.MAX30003:
                    // General GUI
                    bioZMilliohmScaleToolStripMenuItem.Enabled = false;
                    registerNamesToolStripMenuItem.Text = "Show MAX30003 Register Names";
                    paceFileLogToolStripMenuItem.Enabled = false;
                    bioZFileLogToolStripMenuItem.Enabled = false;

                    // Block Diagram
                    picBlockDiagram.Image = global::HealthSensorPlatform.Properties.Resources.system_block_diagram_max30003;
                    linkBioZChannel.Visible = false;
                    linkBioZMux.Visible = false;
                    linkBioZLoad.Visible = false;
                    linkPaceChannel.Visible = false;
                    linkLabelQuickBioimpedance.Visible = false;
                    linkLabelQuickRespiration.Visible = false;

                    // Global Panel
                    chkEnBioZ.Enabled = false;
                    chkEnPace.Enabled = false;
                    grpGlobalControls.Text = "MAX30003 Global Controls";

                    // Tabs - not working 
                    tabPageEnable(null, true);

                    // Plots
                    ecgView1.BioZFunction(false);

                    // Data Logging Tab
                    dataLoggingView1.PaceFunction(false);
                    dataLoggingView1.BioZFunction(false);

                    //Register Tabs
                    for(int i = 0; i < cboRegisterTabDevice.Items.Count; i++)
                    {
                        if ((string)cboRegisterTabDevice.Items[i] == "MAX30001")
                        {
                            cboRegisterTabDevice.Items[i] = "MAX30003";
                        }

                    }
                    rvMAX30003Init();

                    break;
                case Model.MAX30001Model.Part.MAX30001:
                    // Defaults
                    // General GUI
                    bioZMilliohmScaleToolStripMenuItem.Enabled = true;
                    registerNamesToolStripMenuItem.Text = "Show MAX30001 Register Names";
                    paceFileLogToolStripMenuItem.Enabled = true;
                    bioZFileLogToolStripMenuItem.Enabled = true;

                    // Block Diagram
                    picBlockDiagram.Image = global::HealthSensorPlatform.Properties.Resources.system_block_diagram;
                    linkBioZChannel.Visible = true;
                    linkBioZMux.Visible = true;
                    linkBioZLoad.Visible = true;
                    linkPaceChannel.Visible = true;
                    linkLabelQuickBioimpedance.Visible = true;
                    linkLabelQuickRespiration.Visible = true;

                    // Global Panel
                    grpGlobalControls.Text = "MAX30001 Global Controls";
                    chkEnBioZ.Enabled = true;
                    chkEnECG.Enabled = true;
                    chkEnPace.Enabled = true;
                    chkEnRtor.Enabled = true;

                    // Tabs - not working
                    tabPageEnable(null, true);

                    // Plots
                    ecgView1.BioZFunction(true);

                    // Data Logging Tab
                    dataLoggingView1.PaceFunction(true);
                    dataLoggingView1.BioZFunction(true);

                    //Register Tabs
                    for (int i = 0; i < cboRegisterTabDevice.Items.Count; i++)
                    {
                        if ((string)cboRegisterTabDevice.Items[i] == "MAX30003")
                        {
                            cboRegisterTabDevice.Items[i] = "MAX30001";
                        }

                    }
                    rvMAX30001Init();
                    break;
            }
        }

        private bool checkFlashID()
        {
            string reply;

            reply = rpcClient.RawRpcCall(readFlashIDCommand, true);

            return (reply.StartsWith("00010219"));
        }

        private void tabPageEnable(TabPage exception, bool enable)
        {
            if (enable)
            {
                if (max30001Model.PartID == MAX30001Model.Part.MAX30003)
                {
                    foreach (TabPage tp in maximTabControl1.TabPages)
                    {
                        if (!max30001TabPages.Contains(tp))
                            tp.Enabled = true;
                    }
                }
                else
                {
                    foreach (TabPage tp in maximTabControl1.TabPages)
                    {
                        tp.Enabled = true;
                    }
                }
            }
            else
            {
                foreach (TabPage tp in maximTabControl1.TabPages)
                {
                    if (tp != exception)
                        tp.Enabled = false;
                }
            }

            maximTabControl1.Invalidate();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            splashScreen = new SplashScreen(120);
            InitSplashScreen();
            splashScreen.Show();
            splashScreen.ShowOKButton();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void maximTabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {

            temperatureView1.TimerState(false);
            //ecgView1.Stream = false; // Disconnect plotting event for flash log data loading

            if (maximTabControl1.SelectedTab == tpBlockDiagram)
            {
                updateBlockDiagramLayout();
            }
            else if (maximTabControl1.SelectedTab == tpTemperature)
            {
                if (connected)
                {
                    // TODO
                    /*
                    temperatureView1.TimerState(true);
                    temperatureView1.UpdateRegisters();
                    temperatureView1.ReadTemperature();
                    */
                    temperatureView1.TimerState(true);
                    temperaturePresenter.OnReadAll(this, new EventArgs());
                }
            }
            else if (maximTabControl1.SelectedTab == tpMAX30001EcgChannel)
            {
                if (connected)
                { 
                    ECGChannelUpdateRegisters(); // and R to R
                    ECGGlobalUpdateRegisters();
                }

                pnlMAX30001Global.Parent = tpMAX30001EcgChannel;
            }
            else if (maximTabControl1.SelectedTab == tpMAX30001InputMux)
            {
                if (connected)
                {
                    ECGInputMuxUpdateRegisters();
                    ECGGlobalUpdateRegisters();
                }

                pnlMAX30001Global.Parent = tpMAX30001InputMux;
            }
            else if (maximTabControl1.SelectedTab == tpMAX30001Plots)
            {
                pnlMAX30001Global.Parent = tpMAX30001Plots;
                //ecgView1.Stream = true;
                //pnlMAX30001Global.BringToFront();
                if (connected)
                {
                    ECGGlobalUpdateRegisters();
                }
            }
            else if (maximTabControl1.SelectedTab == tpBioZChannel)
            {
                if (connected)
                {
                    BioZChannelUpdateRegisters();
                    ECGGlobalUpdateRegisters();
                }
                pnlMAX30001Global.Parent = tpBioZChannel;
            }
            else if (maximTabControl1.SelectedTab == tpMAX30001BioZInputMux)
            {
                if (connected)
                {
                    BioZInputMuxUpdateRegisters();
                    ECGGlobalUpdateRegisters();
                }
                pnlMAX30001Global.Parent = tpMAX30001BioZInputMux;
            }
            else if (maximTabControl1.SelectedTab == tpMAX30001BioZLoad)
            {
                if (connected)
                {
                    BioZLoadUpdateRegisters();
                    ECGGlobalUpdateRegisters();
                }
                UpdateBioZLoadSwitches(); 
                pnlMAX30001Global.Parent = tpMAX30001BioZLoad;
            }
            else if (maximTabControl1.SelectedTab == tpMAX30001Pace)
            {
                if (connected)
                {
                    PaceChannelUpdateRegisters();
                    ECGGlobalUpdateRegisters();
                }
                pnlMAX30001Global.Parent = tpMAX30001Pace;
            }
            else if (maximTabControl1.SelectedTab == tpFlashLog)
            {
                if (connected)
                {
                    dataLoggingView1.UpdateArgs();
                }
            }
            else if (maximTabControl1.SelectedTab == tpRegisters)
            {
                if (connected)
                {
                    ((Maxim.CustomControls.RegisterView)registerViewDict[(string)(cboRegisterTabDevice.SelectedItem)]).ReadAll(); 
                }
            }

            if (maximTabControl1.SelectedTab != null) // Support dynamic adding/removing of tabs
                pnlMAX30001Global.Location = new Point(0, maximTabControl1.SelectedTab.Size.Height - pnlMAX30001Global.Size.Height);

            ss_Status.MessagesLabel.Text = ""; // Clear any message from previous tabs
        }

        /// <summary>
        /// Read all registers, used for first connect
        /// </summary>
        public void UpdateRegisters()
        {
            ECGChannelUpdateRegisters();
            ECGInputMuxUpdateRegisters();
            ECGGlobalUpdateRegisters();
            BioZChannelUpdateRegisters();
            BioZInputMuxUpdateRegisters();
            BioZLoadUpdateRegisters();
            PaceChannelUpdateRegisters();

            //rvLIS2HD.ReadAll();
            //rvMAX14720.ReadAll();
            //rvMAX30001.ReadAll();
            //rvMAX30101.ReadAll();
            //rvMAX30205.ReadAll();
            //rvMAX30205_U18.ReadAll();
        }

        #region Temperature Tab
        private void TemperatureViewInit()
        {
            MAX30205Model max30205_1 = new MAX30205Model(rpcClient.I2cDevice, 0x92);
            MAX30205Model max30205_2 = new MAX30205Model(rpcClient.I2cDevice, 0x90);

            temperaturePresenter = new MAX30205EVKit.Presenter.TemperaturePresenter(new MAX30205Model[] { max30205_1, max30205_2 }, temperatureView1, temperatureView1);
            //temperatureView1.max30205_1 = max30205;
            //temperatureView1.i2cDevice = i2cDevice;
        }
        #endregion

        #region Register Tab
        private void RegistersTabInit()
        {
            pnlRegMAX30101.Visible = true;
            pnlRegMAX30101.Visible = false;
            pnlRegMAX30205.Visible = false;
            pnlRegMAX14720.Visible = false;

            rvLIS2HDInit();
            rvMAX30001Init();
            rvMAX30101Init();
            rvMAX30205Init();
            rvMAX30205_U18Init();
            rvMAX14720Init();
            registerViewDict.Add(rvLIS2HD.DeviceName, rvLIS2HD);
            registerViewDict.Add(rvMAX30001.DeviceName, rvMAX30001);
            registerViewDict.Add("MAX30003", rvMAX30001);
            registerViewDict.Add(rvMAX30101.DeviceName, rvMAX30101);
            registerViewDict.Add(rvMAX30205.DeviceName, rvMAX30205);
            registerViewDict.Add(rvMAX30205_U18.DeviceName, rvMAX30205_U18);
            registerViewDict.Add(rvMAX14720.DeviceName, rvMAX14720);

            cboRegisterTabDevice.Items.Clear();
            foreach (Maxim.CustomControls.RegisterView rv in registerViews)
                cboRegisterTabDevice.Items.Add(rv.DeviceName);

            cboRegisterTabDevice.SelectedIndex = 0;

            // Update status message when write is complete. Clear status message when entering the register data cell
            foreach (Maxim.CustomControls.RegisterView rv in registerViews)
            {
                rv.CellBeginEdit += new Maxim.CustomControls.RegisterView.CellBeginEditHandler(registerView_CellBeginEdit);
                rv.RegisterWriteComplete += new Maxim.CustomControls.RegisterView.RegisterWriteCompleteHandler(registerView_WriteComplete);
            }

        }

        private void cboRegisterTabDevice_SelectedIndexChanged(object sender, EventArgs e)
        {
            MaximStyle.MaximComboBox cbo = (MaximStyle.MaximComboBox)sender;

            if (cbo.Text.CompareTo(rvMAX30001.DeviceName) == 0)
            {
                pnlRegMAX30001.Visible = true;
                pnlRegMAX30101.Visible = false;
                pnlRegMAX30205.Visible = false;
                pnlRegMAX30205_U18.Visible = false;
                pnlRegLIS2HD.Visible = false;
                pnlRegMAX14720.Visible = false;
                rvMAX30001.ReadAll();
            }
            else if (cbo.Text.CompareTo(rvMAX30101.DeviceName) == 0)
            {
                pnlRegMAX30001.Visible = false;
                pnlRegMAX30101.Visible = true;
                pnlRegMAX30205.Visible = false;
                pnlRegMAX30205_U18.Visible = false;
                pnlRegLIS2HD.Visible = false;
                pnlRegMAX14720.Visible = false;
                rvMAX30101.ReadAll();
            }
            else if (cbo.Text.CompareTo(rvMAX30205.DeviceName) == 0)
            {
                pnlRegMAX30001.Visible = false;
                pnlRegMAX30101.Visible = false;
                pnlRegMAX30205.Visible = true;
                pnlRegMAX30205_U18.Visible = false;
                pnlRegLIS2HD.Visible = false;
                pnlRegMAX14720.Visible = false;
                rvMAX30205.ReadAll();
            }
            else if (cbo.Text.CompareTo(rvMAX30205_U18.DeviceName) == 0)
            {
                pnlRegMAX30001.Visible = false;
                pnlRegMAX30101.Visible = false;
                pnlRegMAX30205.Visible = true;
                pnlRegMAX30205_U18.Visible = true;
                pnlRegLIS2HD.Visible = false;
                pnlRegMAX14720.Visible = false;
                rvMAX30205_U18.ReadAll();
            }
            else if (cbo.Text.CompareTo(rvLIS2HD.DeviceName) == 0)
            {
                pnlRegMAX30001.Visible = false;
                pnlRegMAX30101.Visible = false;
                pnlRegMAX30205.Visible = false;
                pnlRegMAX30205_U18.Visible = false;
                pnlRegLIS2HD.Visible = true;
                pnlRegMAX14720.Visible = false;
                rvLIS2HD.ReadAll();
            }
            else if (cbo.Text.CompareTo(rvMAX14720.DeviceName) == 0)
            {
                pnlRegMAX30001.Visible = false;
                pnlRegMAX30101.Visible = false;
                pnlRegMAX30205.Visible = false;
                pnlRegMAX30205_U18.Visible = false;
                pnlRegLIS2HD.Visible = false;
                pnlRegMAX14720.Visible = true;
                rvMAX14720.ReadAll();
            }
        }
        #endregion

        #region Optical Tab
        private void opticalView_StreamingStartStop(object sender, StreamingStartStopEventArgs e)
        {
            /* Disable user selection to Tabs when Streaming is enabled */
            streaming = e.state;
            if (e.state == true)
            {
                tabPageEnable(tpOpticalSensor, false);
            }
            else
            {
                tabPageEnable(tpOpticalSensor, true);
            }
            maximTabControl1.Invalidate(); // Redraw the tabs
        }
        #endregion

        #region Block Diagrams
        private void BlockDiagramsInit()
        {
            initalizeECGConfigFields(); 
            initalizeECGInputMuxFields();
            initalizeRToRFields();
            initalizeECGGlobalControls();
            initalizeBioZConfigFields();
            initalizeBioZInputMuxFields();
            initalizeBioZLoadFields();
            initalizePaceChannelFields();

            // Set ECG Gain setting for chart Y-scale purposes
            cboChannelGain.SelectedIndexChanged += new EventHandler(cboChannelGain_SelectedIndexChanged);

            //cboECGChannelEnable.SelectedIndex = 0; // Default is disabled

            initalizeToolTipEvents(ecgChannelField);
            initalizeToolTipEvents(rToRField);
            initalizeToolTipEvents(ecgInputMuxField);
            initalizeToolTipEvents(ecgGlobalField);
            initalizeToolTipEvents(bioZChannelField);
            initalizeToolTipEvents(bioZInputMuxField);
            initalizeToolTipEvents(bioZLoadField);
            initalizeToolTipEvents(paceChannelField);

            // Force panel load for location placement in case of window resize
            // on a tab where the panel is not shown otherwise it will show in a strange location
            pnlMAX30001Global.Parent = tpMAX30001EcgChannel;
        }

        #endregion

        #region Streaming
        private void ecgView_StreamingStartStop(object sender, StreamingStartStopEventArgs e)
        {
            streaming = e.state;
            /* Disable user selection to Tabs when Streaming is enabled */
            if (e.state == true)
            {
                tabPageEnable(tpMAX30001Plots, false);

                grpChannelPlotEnable.Enabled = false;
                grpGlobalControls.Enabled = false;
                menuStrip1.Enabled = false;
            }
            else
            {
                if (connected)
                {
                    // Rewrite enable bits for global controls to allow GUI to maintain consistent state
                    foreach (RegisterField field in channelEnableField)
                    {
                        MaximCheckBox chk = (MaximStyle.MaximCheckBox)field.Control;

                        if (chk.Checked)
                            writeCheckBox(chk);
                    }

                    if (cboEnRbias.SelectedIndex != 0)
                        writeComboBox(cboEnRbias);
                    else if (cboBioZEnRbias.SelectedIndex != 0)
                        writeComboBox(cboBioZEnRbias);

                    if (cboEnDcloff.SelectedIndex != 0)
                        writeComboBox(cboEnDcloff);
                    else if (cboBioZEnDcloff.SelectedIndex != 0)
                        writeComboBox(cboBioZEnDcloff);

                    if (cboBioZDigitalLeadOffDetectionEnable.SelectedIndex != 0)
                        writeComboBox(cboBioZDigitalLeadOffDetectionEnable);

                    if (cboEmuxEnUlpLon.SelectedIndex != 0)
                        writeComboBox(cboEmuxEnUlpLon);
                    else if (cboBmuxEnUlpLon.SelectedIndex != 0)
                        writeComboBox(cboBmuxEnUlpLon);

                }
                
                tabPageEnable(tpMAX30001Plots, true);

                grpChannelPlotEnable.Enabled = true;
                grpGlobalControls.Enabled = true;
                menuStrip1.Enabled = true;
            }

            maximTabControl1.Invalidate(); // Redraw the tabs
        }
        #endregion

        #region Data Logging
        // When Data Logging reads the flash, update the values on the other tabs to match what is in the flash
        // The data logging tab will generate an event and run this method
        private void dataLoggingFlashReadEventHandler(object sender, EventArgs e)
        {
            DataLoggingView dlView = (DataLoggingView)sender;

            if (dlView.EnableOpticalHR)
            {
                opticalView1.SetHRModeArgs(dlView.HRModeArgs);
            }
            else if (dlView.EnableOpticalSpO2)
            {
                opticalView1.SetSpO2HRModeArgs(dlView.SpO2HRModeArgs);
            }
            else if (dlView.EnableOpticalMulti)
            {
                opticalView1.SetMultiModeArgs(dlView.MultiModeArgs);
            }

            if (dlView.EnableEcg)
            {
                setEcgArgs(dlView.EcgArgs);
            }

            if (dlView.EnableBioz)
            {
                setBioZArgs(dlView.BioZArgs);
            }

            if (dlView.EnableRToR)
            {
                setRToRArgs(dlView.RToRArgs);
            }

            if (dlView.EnablePace)
            {
                setPaceArgs(dlView.PaceArgs);
            }
        }
        #endregion

        private void connectionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (connectionToolStripMenuItem.Checked)
            {
                connectionToolStripMenuItem.Checked = true;
                return;
            }

            if (!connected)
            {
                comPortConnect();
            }
        }

        private void disconnectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (disconnectToolStripMenuItem.Checked)
            {
                disconnectToolStripMenuItem.Checked = true;
                return;
            }

            if (connected)
            {
                guiDisconnected();
                rpcClient.Disconnect();
            }
        }

        /// <summary>
        /// Stop streaming before exiting HSP
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void HspForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (connected)
                {
                    opticalView1.Close(); // Stop streaming
                    ecgView1.Close();
                }
            }
            catch (Exception)
            {
                // We are exiting the GUI, we can ignore all exceptions
                // Adding this for the case where the user unplugs HSP and quickly tries to close
                // before the operating system reports USB removed
                // Occurs while streaming ECG because we re-write the interrupt registers on stream stop
                return;
            }
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001InputMux;
        }

        private void linkLabel2_Click(object sender, EventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001BioZInputMux;
        }

        private void linkLabelBioZLoad_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001BioZLoad;
        }

        private void linkLabelQuickOpticalSpO2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (connected)
            {
                opticalView1.OpticalSensorDefaults();
                maximTabControl1.SelectedTab = tpOpticalSensor;
            }
        }

        private void linkLabelQuickTemperature_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (connected)
            {
                temperaturePresenter.WriteRegister(0, 1, 0);
                temperaturePresenter.WriteRegister(1, 1, 0);
                maximTabControl1.SelectedTab = tpTemperature;
            }
        }

        private void linkLabelQuickEcg_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (connected)
            {
                // SW_RST
                max30001Model.Write(0x08, 1);
                max30001Model.Write(0x08, 0);
                // CNFG_GEN
                max30001Model.Write(0x10, 0x080017);
                // CNFG_EMUX
                max30001Model.Write(0x14, 0x000000);
                // CNFG_RTOR1
                max30001Model.Write(0x1d, 0x3FA300);

                ECGChannelUpdateRegisters();
                ECGInputMuxUpdateRegisters();

                maximTabControl1.SelectedTab = tpMAX30001Plots;
            }
        }

        private void linkLabelQuickBioimpedance_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (connected)
            {
                // SW_RST
                max30001Model.Write(0x08, 1);
                max30001Model.Write(0x08, 0);
                // CNFG_GEN
                max30001Model.Write(0x10, 0x040027);
                // CNFG_BMUX
                max30001Model.Write(0x17, 0x000040);
                // CNFG_BIOZ
                max30001Model.Write(0x18, 0x601430);

                ECGChannelUpdateRegisters();
                ECGInputMuxUpdateRegisters();
                BioZChannelUpdateRegisters();
                BioZInputMuxUpdateRegisters();

                bioZMilliohmScaleToolStripMenuItem.Checked = false;
                ecgView1.BioZMilliOhmRange = false;

                maximTabControl1.SelectedTab = tpMAX30001Plots;
            }
        }

        private void linkLabelQuickRespiration_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (connected)
            {
                // SW_RST
                max30001Model.Write(0x08, 1);
                max30001Model.Write(0x08, 0);
                // CNFG_GEN
                max30001Model.Write(0x10, 0x040027);
                // CNFG_BMUX
                max30001Model.Write(0x17, 0x000040);
                // CNFG_BIOZ
                max30001Model.Write(0x18, 0x201050);

                ECGChannelUpdateRegisters();
                ECGInputMuxUpdateRegisters();
                BioZChannelUpdateRegisters();
                BioZInputMuxUpdateRegisters();

                bioZMilliohmScaleToolStripMenuItem.Checked = true;
                ecgView1.BioZMilliOhmRange = true;

                maximTabControl1.SelectedTab = tpMAX30001Plots;
            }
        }

        private void btnSwRst_Click(object sender, EventArgs e)
        {
            if (!connected)
                return;

            max30001.WriteReg(0x08, 0x01);
            max30001.WriteReg(0x08, 0x00);

            UpdateRegisters();
        }

        private void btnSynch_Click(object sender, EventArgs e)
        {
            if (!connected)
                return;

            max30001.WriteReg(0x09, 0x01);
            max30001.WriteReg(0x09, 0x00);
        }

        private void btnFifoRst_Click(object sender, EventArgs e)
        {
            if (!connected)
                return;

            max30001.WriteReg(0x0A, 0x01);
            max30001.WriteReg(0x0A, 0x00);
        }

        private void registerNamesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            List<RegisterField>[] fields = new List<RegisterField>[]{ ecgChannelField, ecgInputMuxField, ecgGlobalField, rToRField,  bioZChannelField, bioZInputMuxField, bioZLoadField, paceChannelField };
            ToolStripMenuItem tsi = (ToolStripMenuItem)sender;

            if (tsi.Checked == true)
            {
                // Combo Boxes
                foreach (List<RegisterField> listRegisterField in fields)
                {
                    foreach(RegisterField rf in listRegisterField)
                    {
                        if (rf.Label != null)
                            rf.Label.Text = rf.Name;
                    }
                }

                // Check Boxes
                foreach (RegisterField rf in channelEnableField)
                {
                    rf.LabelDescription = rf.Control.Text;
                    rf.Control.Text = rf.Name;
                }

                string calHighRegName = "CAL_THIGH";
                cboCalFifty.Items[0] = calHighRegName;
                cboBioZCalFifty.Items[0] = calHighRegName;
                if (cboCalFifty.SelectedIndex == 0)
                {
                    cboCalFifty.SelectedIndex = ecgCalFiftyField.ReadField();
                    cboBioZCalFifty.SelectedIndex = biozCalFiftyField.ReadField();
                }

            }
            else
            {
                // Combo Boxes
                foreach (List<RegisterField> listRegisterField in fields)
                {
                    foreach (RegisterField rf in listRegisterField)
                    {
                        if (rf.Label != null)
                            rf.Label.Text = rf.LabelDescription;
                    }
                }
                
                // Check Boxes
                foreach (RegisterField rf in channelEnableField)
                {
                    rf.Control.Text = rf.LabelDescription;
                }

                string calHighRegName = "Time High";
                cboCalFifty.Items[0] = calHighRegName;
                cboBioZCalFifty.Items[0] = calHighRegName;
                if (cboCalFifty.SelectedIndex == 0)
                {
                    cboCalFifty.SelectedIndex = ecgCalFiftyField.ReadField();
                    cboBioZCalFifty.SelectedIndex = biozCalFiftyField.ReadField();
                }
            }
        }

        /// <summary>
        /// Prevent tab changes while streaming
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void maximTabControl1_Selecting(object sender, TabControlCancelEventArgs e)
        {
            if (streaming && connected)
                e.Cancel = true;
        }

        private void showAdvancedTabToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (showAdvancedTabToolStripMenuItem.Checked)
            {
                maximTabControl1.UnhideTab("tpPrimitives");
            }
            else
            {
                maximTabControl1.HideTab("tpPrimitives");
            }
        }

        private void registerView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
        {
            ss_Status.MessagesLabel.Text = "";
        }

        private void registerView_WriteComplete(object sender, Maxim.CustomControls.RegisterView.RegisterArgs e)
        {
            ss_Status.MessagesLabel.Text = "Write Reg " + e.Register.ToString("X2") + "h Complete";
        }

        private void bioZMilliohmScaleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Set the BioZ chart to be in units of milliohms or ohms
            ecgView1.BioZMilliOhmRange = bioZMilliohmScaleToolStripMenuItem.Checked;
        }

        public void LogFileItem(StreamType stream, bool state)
        {
            switch(stream)
            {
                case StreamType.Ecg:
                    eCGFileLogToolStripMenuItem.Checked = state;
                    break;
                case StreamType.RToR:
                    rToRFileLogToolStripMenuItem.Checked = state;
                    break;
                case StreamType.Pace:
                    paceFileLogToolStripMenuItem.Checked = state;
                    break;
                case StreamType.BioZ:
                    bioZFileLogToolStripMenuItem.Checked = state;
                    break;
                case StreamType.Optical:
                    opticalAccelerometerFileLogToolStripMenuItem.Checked = state;
                    break;
                
                // TODO other stream types
            }
        }

        public void OpticalLogFileItem(bool state)
        {
            opticalAccelerometerFileLogToolStripMenuItem.Checked = state;
        }

        public void EcgLogFileItem(bool state)
        {
            eCGFileLogToolStripMenuItem.Checked = state;
        }

        public void BioZLogFileItem(bool state)
        {
            bioZFileLogToolStripMenuItem.Checked = state;
        }

        public void RtoRLogFileItem(bool state)
        {
            rToRFileLogToolStripMenuItem.Checked = state;
        }

        public void PaceLogFileItem(bool state)
        {
            paceFileLogToolStripMenuItem.Checked = state;
        }

        private void eCGBioZFileLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (FileLogEnable != null)
                FileLogEnable(this, new EnableEventArgs(StreamType.Ecg, !eCGBioZFileLogToolStripMenuItem.Checked));
        }

        private void eCGFileLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (FileLogEnable != null)
                FileLogEnable(this, new EnableEventArgs(StreamType.Ecg, !eCGFileLogToolStripMenuItem.Checked));
        }

        private void rToRFileLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (FileLogEnable != null)
                FileLogEnable(this, new EnableEventArgs(StreamType.RToR, !rToRFileLogToolStripMenuItem.Checked));
        }

        private void paceFileLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (FileLogEnable != null)
                FileLogEnable(this, new EnableEventArgs(StreamType.Pace, !paceFileLogToolStripMenuItem.Checked));
        }

        private void bioZFileLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (FileLogEnable != null)
                FileLogEnable(this, new EnableEventArgs(StreamType.BioZ, !bioZFileLogToolStripMenuItem.Checked));
        }

        private void opticalAccelerometerFileLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (OpticalFileLogEnable != null)
                OpticalFileLogEnable(this, new EnableEventArgs(StreamType.Optical,!opticalAccelerometerFileLogToolStripMenuItem.Checked));
        }

        private void registerExportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (RegisterExport != null)
                RegisterExport(this, e);
        }

        private void loadAllRegisterSettingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (RegisterImport != null)
                RegisterImport(this, e);
        }


        #region Home Tab
        private void linkTempSensor_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpTemperature;
        }

        private void linkOpticalSensor_Click(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpOpticalSensor;
        }

        private void linkEcgChannel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001EcgChannel;
        }

        private void linkEcgMux_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001InputMux;
        }

        private void linkPaceChannel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001Pace;
        }

        private void linkBioZChannel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpBioZChannel;
        }

        private void linkBioZMux_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001BioZInputMux;
        }

        private void linkBioZLoad_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            maximTabControl1.SelectedTab = tpMAX30001BioZLoad;
        } 
        #endregion

        private void updateBlockDiagramLayout()
        {
            //Stopwatch stopWatch = new Stopwatch();
            double scale;
            int scaledWidth, scaledHeight;
            int widthOffset;
            int blockWidth = pnlBlockDiagram.Width;
            int blockHeight = pnlBlockDiagram.Height;

            int height = maximTabControl1.TabPages[0].Height;
            int width = maximTabControl1.TabPages[0].Width;

            //stopWatch.Start();

            if (height < width)
            {
                scale = (double)height / blockHeight;
            }
            else
            {
                scale = (double)width / blockWidth;
            }

            scaledWidth = (int)(scale * blockWidth + 0.5);
            scaledHeight = (int)(scale * blockHeight + 0.5);
            widthOffset = (int)((width - scaledWidth + 0.5) / 2.0);

            pnlBlockDiagram.Width = scaledWidth;
            pnlBlockDiagram.Height = scaledHeight;
            pnlBlockDiagram.Location = new Point(widthOffset, 0);

            //System.Diagnostics.Debug.Print(" X: " + linkTempSensor.Location.X + " Y: " + linkTempSensor.Location.Y);
            scaleControlCenter(linkTempSensor, scale);
            scaleControlCenter(linkOpticalSensor, scale);
            scaleControlCenter(linkEcgChannel, scale);
            scaleControlCenter(linkEcgMux, scale);
            scaleControlCenter(linkBioZChannel, scale);
            scaleControlCenter(linkBioZMux, scale);
            scaleControlCenter(linkBioZLoad, scale);
            scaleControlCenter(linkPaceChannel, scale);

            // Quick Start Location
            tlbQuickStart.Location = new Point(pnlBlockDiagram.Location.X + pnlBlockDiagram.Size.Width + 5, pnlBlockDiagram.Location.Y + 50);
            //System.Diagnostics.Debug.Print("Scale: " + scale + " X: " + linkTempSensor.Location.X + " Y: " + linkTempSensor.Location.Y);

            if (maximTabControl1.SelectedTab.Name != "tpBlockDiagram")
            {
                maximTabControl1.Refresh();
            }

            //stopWatch.Stop();

            //TimeSpan ts = stopWatch.Elapsed;

            //string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
            //System.Diagnostics.Debug.Print("Run time " + elapsedTime);
        }

        private void updateOT90DiagramLayout(Panel panel)
        {
            updateOT90DiagramLayout(panel, 0);
        }

        private void updateOT90DiagramLayout(Panel panel, int heightOffset)
        {
            Stopwatch stopWatch = new Stopwatch();
            double scale, scaleWidth, scaleHeight;
            int scaledWidth, scaledHeight;
            int widthOffset;
            int blockWidth = panel.Width;
            int blockHeight = panel.Height;

            int height = maximTabControl1.SelectedTab.Height - pnlMAX30001Global.Height - heightOffset;
            int width = maximTabControl1.SelectedTab.Width;

            stopWatch.Start();

            // Scale to the smaller dimension, the smaller side dominates the scaling factor
            scaleWidth = (double)(width - 40) / blockWidth;
            scaleHeight = (double)height / blockHeight;
            if (scaleWidth < scaleHeight)
                scale = scaleWidth;
            else
                scale = scaleHeight;

            scaledWidth = (int)(scale * blockWidth + 0.5);
            scaledHeight = (int)(scale * blockHeight + 0.5);
            widthOffset = (int)((width - scaledWidth + 0.5) / 2.0);

            panel.Width = scaledWidth;
            panel.Height = scaledHeight;
            panel.Location = new Point(widthOffset, 10);

            foreach (Control cntl in panel.Controls)
            {
                scaleControl(cntl, scale);
            }

            //maximTabControl1.Refresh();

            stopWatch.Stop();

            TimeSpan ts = stopWatch.Elapsed;

            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
            System.Diagnostics.Debug.Print("Run time " + elapsedTime);
        }

        void scaleControl(Control c, double scale)
        {
            int x = c.Location.X;
            int y = c.Location.Y;

            double newX, newY;

            newX = ((x) * scale + 0.5);
            newY = ((y + c.Size.Height / 2) * scale + 0.5) - c.Size.Height / 2;

            c.Location = new Point((int)(newX), (int)(newY));
        }

        void scaleControlCenter(Control c, double scale)
        {
            int x = c.Location.X;
            int y = c.Location.Y;

            double newX, newY;

            newX = ((x + c.Size.Width / 2) * scale + 0.5) - c.Size.Width / 2;
            newY = ((y + c.Size.Height / 2) * scale + 0.5) - c.Size.Height / 2;

            c.Location = new Point((int)newX, (int)newY);
        }

        void scaleControlRight(Control c, double scale)
        {
            double AlignX = 590.0/636;
            int x = c.Location.X;
            int y = c.Location.Y;

            double newX, newY;

            newX = ((x + c.Size.Width) * scale + 0.5) - c.Size.Width;
            newY = ((y + c.Size.Height / 2) * scale + 0.5) - c.Size.Height / 2;

            c.Location = new Point((int)(AlignX * picBlockDiagram.Width - c.Size.Width), (int)(newY));

        }

        private void updateAllBlockDiagrams()
        {
            //this.SuspendLayout();
            updateBlockDiagramLayout();
            updateOT90DiagramLayout(pnlEcgChannel, grpRToR.Height + 20);
            updateOT90DiagramLayout(pnlEcgMux);
            updateOT90DiagramLayout(pnlBioZChannel);
            updateOT90DiagramLayout(pnlBioZMux);
            updateOT90DiagramLayout(pnlBioZLoad);
            updateOT90DiagramLayout(pnlPace);
            //this.ResumeLayout();
        }

        private void HspForm_ResizeEnd(object sender, EventArgs e)
        {
            updateAllBlockDiagrams();
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == /*WM_SIZE*/ 0x0005)
            {
                if (this.WindowState == FormWindowState.Maximized || this.WindowState == FormWindowState.Normal)
                {
                    updateAllBlockDiagrams();
                }
            }

            base.WndProc(ref m);
        }

        public class DebugTextWriter : StreamWriter
        {
            public DebugTextWriter()
                : base(new DebugOutStream(), Encoding.Unicode, 1024)
            {
                this.AutoFlush = true;
            }

            class DebugOutStream : Stream
            {
                public override void Write(byte[] buffer, int offset, int count)
                {
                    Debug.Write(Encoding.Unicode.GetString(buffer, offset, count));
                }

                public override bool CanRead { get { return false; } }
                public override bool CanSeek { get { return false; } }
                public override bool CanWrite { get { return true; } }
                public override void Flush() { Debug.Flush(); }
                public override long Length { get { throw new InvalidOperationException(); } }
                public override int Read(byte[] buffer, int offset, int count) { throw new InvalidOperationException(); }
                public override long Seek(long offset, SeekOrigin origin) { throw new InvalidOperationException(); }
                public override void SetLength(long value) { throw new InvalidOperationException(); }
                public override long Position
                {
                    get { throw new InvalidOperationException(); }
                    set { throw new InvalidOperationException(); }
                }
            };
        }

        private void onlineDocumentationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Process.Start("http://www.maximintegrated.com/MAXREFDES100");
        }
    }
}
