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

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MaximStyle;
using MAX30205EVKit.View;

namespace HealthSensorPlatform.CustomControls
{
    public partial class TemperatureView : UserControl, ITemperatureView, IDeviceView
    {
        List<MaximCheckBox> temperatureConfigRegister1;
        List<MaximCheckBox> temperatureConfigRegister2;

        bool unitsCelsius = true;
        int refreshRate = 10;
        double temperature1C = 25;
        //int temperature1Hex;
        double temperature2C = 25;

        static string degreeCUnit = "°C";
        static string degreeFUnit = "°F";
        static string blankTemperature = "--.---";

        public static bool I2CErrorShown;

        bool internalUpdate = false;
        

        public event EventHandler<EventArgs> ReadAll;
        public event EventHandler<EventArgs> UnitsChanged;
        public event EventHandler<EventArgs> RequestReadTemperature;
        public event EventHandler<WriteRegisterEventArgs> WriteRegister;
        public event EventHandler<TemperatureEventArgs> OverTemperatureChanged;
        public event EventHandler<TemperatureEventArgs> HystersisChanged;
        public event EventHandler<DataFormatEventArgs> DataFormatChanged;

        public TemperatureView()
        {
            InitializeComponent();

            temperatureInitialize();
            temperatureEventsInitialize();
        }

        public bool UnitsCelsius
        {
            get
            {
                return unitsCelsius;
            }
        }

        public int RefreshRate
        {
            get
            {
                return refreshRate;
            }
            set
            {
                refreshRate = value;
                cboRefreshRate.Text = refreshRate.ToString();
                timer1.Interval = value * 1000;
            }
        }

        public bool Connected { get; set; }

        public void DisplayTemperature(int index, TemperatureValue tempValue)
        {
            double temperature;
            int hex;

            if (unitsCelsius)
                temperature = tempValue.TemperatureC;
            else
                temperature = tempValue.TemperatureF;

            hex = tempValue.TemperatureHex;

            if (index == 0)
            {
                temperature1C = tempValue.TemperatureC;
                lblTemperature1.Text = temperature.ToString("F3");
                lblTemperatureHex1.Text = "(" + hex.ToString("X4") + "h)";
            }
            else if (index == 1)
            {
                temperature2C = tempValue.TemperatureC;
                lblTemperature2.Text = temperature.ToString("F3");
                lblTemperatureHex2.Text = "(" + hex.ToString("X4") + "h)";
            }
        }

        public void DisplayHysteresis(int index, TemperatureValue tempValue)
        {
            double temperature;
            int hex;

            if (unitsCelsius)
                temperature = tempValue.TemperatureC;
            else
                temperature = tempValue.TemperatureF;

            hex = tempValue.TemperatureHex;

            if (index == 0)
            {
                txtHysteresis1.Text = temperature.ToString("F2");
                lblHysteresis1.Text = "(" + hex.ToString("X4") + "h)";
            }
            else if (index == 1)
            {
                txtHysteresis2.Text = temperature.ToString("F2");
                lblHysteresis2.Text = "(" + hex.ToString("X4") + "h)";
            }
        }

        public void DisplayOverTemperature(int index, TemperatureValue tempValue)
        {
            double temperature;
            int hex;

            if (unitsCelsius)
                temperature = tempValue.TemperatureC;
            else
                temperature = tempValue.TemperatureF;

            hex = tempValue.TemperatureHex;

            if (index == 0)
            {
                txtOvertemperature1.Text = temperature.ToString("F2");
                lblOvertemperature1.Text = "(" + hex.ToString("X4") + "h)";
            }
            else if (index == 1)
            {
                txtOvertemperature2.Text = temperature.ToString("F2");
                lblOvertemperature2.Text = "(" + hex.ToString("X4") + "h)";
            }
        }

        public void DisplayConfigurationRegister(int index, int hex)
        {
            if (index == 0)
            {
                byteToConfigurationCheckBoxes((byte)hex, temperatureConfigRegister1);
            }
            else if (index == 1)
            {
                byteToConfigurationCheckBoxes((byte)hex, temperatureConfigRegister2);

            }
        }

        public void DisplayError(Control control, string message)
        {
            if (control!= null)
                errorProvider1.SetError(control, message);
        }

        public void TimerState(bool state)
        {
            timer1.Enabled = state;
        }


        #region Temperature Tab UI
        void temperatureInitialize()
        {
            temperatureConfigRegister1 = new List<MaximCheckBox>();
            temperatureConfigRegister2 = new List<MaximCheckBox>();

            temperatureConfigRegister1.Add(chkOneShot1);
            temperatureConfigRegister1.Add(chkTimeout1);
            temperatureConfigRegister1.Add(chkDataFormat1);
            temperatureConfigRegister1.Add(chkFaultQueue11);
            temperatureConfigRegister1.Add(chkFaultQueue01);
            temperatureConfigRegister1.Add(chkOSPolarity1);
            temperatureConfigRegister1.Add(chkComparatorInterrupt1);
            temperatureConfigRegister1.Add(chkShutdown1);

            temperatureConfigRegister2.Add(chkOneShot2);
            temperatureConfigRegister2.Add(chkTimeout2);
            temperatureConfigRegister2.Add(chkDataFormat2);
            temperatureConfigRegister2.Add(chkFaultQueue12);
            temperatureConfigRegister2.Add(chkFaultQueue02);
            temperatureConfigRegister2.Add(chkOSPolarity2);
            temperatureConfigRegister2.Add(chkComparatorInterrupt2);
            temperatureConfigRegister2.Add(chkShutdown2);

            cboRefreshRate.SelectedIndex = 0;
        }

        void temperatureEventsInitialize()
        {
            tglCelsiusFahrenheit.CheckedChanged += new System.EventHandler(tglCelsiusFahrenheit_CheckedChanged);
            txtOvertemperature1.Leave += new System.EventHandler(temperatureRegisters_Leave);
            txtOvertemperature2.Leave += new System.EventHandler(temperatureRegisters_Leave);
            txtHysteresis1.Leave += new System.EventHandler(temperatureRegisters_Leave);
            txtHysteresis2.Leave += new System.EventHandler(temperatureRegisters_Leave);
            
            foreach (MaximCheckBox cb in temperatureConfigRegister1)
            {
                cb.CheckedChanged += new System.EventHandler(configurationRegister_CheckedChanged);
            }

            foreach (MaximCheckBox cb in temperatureConfigRegister2)
            {
                cb.CheckedChanged += new System.EventHandler(configurationRegister_CheckedChanged);
            }
        }

        void convertUnits(bool celsius)
        {
            if (celsius == true)
            {
                lblUnits1.Text = degreeCUnit;
                lblUnits2.Text = degreeCUnit;
                lblUnits3.Text = degreeCUnit;
                lblUnits4.Text = degreeCUnit;
                lblUnits5.Text = degreeCUnit;
                lblUnits6.Text = degreeCUnit;

                //lblTemperature1.Text = fahrenheitToCelsius(Convert.ToDouble(lblTemperature1.Text)).ToString("F2");
                //lblTemperature2.Text = fahrenheitToCelsius(Convert.ToDouble(lblTemperature2.Text)).ToString("F2");
                //lblTemperature1.Text = blankTemperature;
                //lblTemperature2.Text = blankTemperature;

                txtOvertemperature1.Text = fahrenheitToCelsius(Convert.ToDouble(txtOvertemperature1.Text)).ToString("F2");
                txtOvertemperature2.Text = fahrenheitToCelsius(Convert.ToDouble(txtOvertemperature2.Text)).ToString("F2");

                txtHysteresis1.Text = fahrenheitToCelsius(Convert.ToDouble(txtHysteresis1.Text)).ToString("F2");
                txtHysteresis2.Text = fahrenheitToCelsius(Convert.ToDouble(txtHysteresis2.Text)).ToString("F2");
            }
            else
            {
                lblUnits1.Text = degreeFUnit;
                lblUnits2.Text = degreeFUnit;
                lblUnits3.Text = degreeFUnit;
                lblUnits4.Text = degreeFUnit;
                lblUnits5.Text = degreeFUnit;
                lblUnits6.Text = degreeFUnit;

                //lblTemperature1.Text = celsiusToFahrenheit(Convert.ToDouble(lblTemperature1.Text)).ToString("F2");
                //lblTemperature2.Text = celsiusToFahrenheit(Convert.ToDouble(lblTemperature2.Text)).ToString("F2");
                //lblTemperature1.Text = blankTemperature;
                //lblTemperature2.Text = blankTemperature;

                txtOvertemperature1.Text = celsiusToFahrenheit(Convert.ToDouble(txtOvertemperature1.Text)).ToString("F2");
                txtOvertemperature2.Text = celsiusToFahrenheit(Convert.ToDouble(txtOvertemperature2.Text)).ToString("F2");

                txtHysteresis1.Text = celsiusToFahrenheit(Convert.ToDouble(txtHysteresis1.Text)).ToString("F2");
                txtHysteresis2.Text = celsiusToFahrenheit(Convert.ToDouble(txtHysteresis2.Text)).ToString("F2");
            }
        }

        void configurationRegisterToByte()
        {
            int config1, config2;

            config1 =
                ((chkOneShot1.Checked == true ? 1 : 0) << 7) |
                ((chkTimeout1.Checked == true ? 1 : 0) << 6) |
                ((chkDataFormat1.Checked == true ? 1 : 0) << 5) |
                ((chkFaultQueue11.Checked == true ? 1 : 0) << 4) |
                ((chkFaultQueue01.Checked == true ? 1 : 0) << 3) |
                ((chkOSPolarity1.Checked == true ? 1 : 0) << 2) |
                ((chkComparatorInterrupt1.Checked == true ? 1 : 0) << 1) |
                ((chkShutdown1.Checked == true ? 1 : 0) << 0);

            config2 =
                ((chkOneShot2.Checked == true ? 1 : 0) << 7) |
                ((chkTimeout2.Checked == true ? 1 : 0) << 6) |
                ((chkDataFormat2.Checked == true ? 1 : 0) << 5) |
                ((chkFaultQueue12.Checked == true ? 1 : 0) << 4) |
                ((chkFaultQueue02.Checked == true ? 1 : 0) << 3) |
                ((chkOSPolarity2.Checked == true ? 1 : 0) << 2) |
                ((chkComparatorInterrupt2.Checked == true ? 1 : 0) << 1) |
                ((chkShutdown2.Checked == true ? 1 : 0) << 0);

            lblConfigRegister1.Text = configurationByteToString((byte)config1);
            lblConfigRegister2.Text = configurationByteToString((byte)config2);

            if (WriteRegister != null && !internalUpdate)
            {
                WriteRegister(this, new WriteRegisterEventArgs() { Index = 0, Register = 0x01, Data = config1 });
                WriteRegister(this, new WriteRegisterEventArgs() { Index = 1, Register = 0x01, Data = config2 });
            }
        }

        string configurationByteToString(byte hex)
        {
            return "(" + hex.ToString("X2") + "h)";
        }

        void byteToConfigurationCheckBoxes(byte hex, List<MaximCheckBox> list)
        {
            internalUpdate = true;
            for (int i = 0; i < 8; i++ )
            {
                list[i].Checked = (hex & (1 << (7 - i))) == (1 << (7 - i));
            }
            internalUpdate = false;
        }

        #endregion

        #region Helper Functions
        double celsiusToFahrenheit(double temperature)
        {
            return temperature * 9 / 5 + 32;
        }

        double fahrenheitToCelsius(double temperature)
        {
            return (temperature - 32) * 5 / 9;
        }

        short temperatureToHex(double temperature)
        {
            return (short)(temperature * Math.Pow(2, 8));
        }

        public double HexToTemperature(int hex, bool dataFormat)
        {
            return hexToTemperature(new byte[] { (byte)(hex >> 8), (byte)(hex & 0xFF) }, dataFormat);
        }

        double hexToTemperature(byte[] hex, bool dataFormat)
        {
            int rawCode;
            int normalCode;

            if (hex.Length != 2)
                return 0;

            rawCode = (hex[0] << 8) + hex[1];

            if (rawCode > 0x7fff)
                normalCode = rawCode - 0x10000;
            else
                normalCode = rawCode;

            if (dataFormat == false)
                return normalCode / Math.Pow(2, 8);
            else
                return normalCode / Math.Pow(2, 8) + 64;

        }

        #endregion


        #region Temperature Tab Events
        private void tglCelsiusFahrenheit_CheckedChanged(object sender, EventArgs e)
        {
            MaximStyle.MaximToggleButton tgl = (MaximStyle.MaximToggleButton)sender;

            if (internalUpdate == true)
                return;

            unitsCelsius = !tgl.Checked;

            convertUnits(unitsCelsius);

            if (lblTemperature1.Text != blankTemperature)
            {
                if (unitsCelsius)
                {
                    lblTemperature1.Text = temperature1C.ToString("F3");
                    lblTemperature2.Text = temperature2C.ToString("F3");
                }
                else
                {
                    lblTemperature1.Text = celsiusToFahrenheit(temperature1C).ToString("F3");
                    lblTemperature2.Text = celsiusToFahrenheit(temperature2C).ToString("F3");
                }
            }
        }

        void temperatureRegisters_Leave(object sender, EventArgs e)
        {
            TextBox tb = (TextBox)sender;
            byte[] tempData = new byte[2];
            double temperature; // Temperature in C

            if (Double.TryParse(tb.Text, out temperature))
            {

                //To clear just the textBox that raised this event, send SetError with empty string
                errorProvider1.SetError(tb, "");

                // Save Temperature value in C
                if (unitsCelsius == true)
                    temperature = Convert.ToDouble(tb.Text);
                else
                    temperature = fahrenheitToCelsius(Convert.ToDouble(tb.Text));

                if (OverTemperatureChanged != null && HystersisChanged != null)
                {
                    if (ReferenceEquals(tb, txtOvertemperature1))
                    {
                        OverTemperatureChanged(this, new TemperatureEventArgs() { Index = 0, Temperature = temperature, Control = tb }); 
                    }
                    else if (ReferenceEquals(tb, txtOvertemperature2))
                    {
                        OverTemperatureChanged(this, new TemperatureEventArgs() { Index = 1, Temperature = temperature, Control = tb }); 
                    }
                    else if (ReferenceEquals(tb, txtHysteresis1))
                    {
                        HystersisChanged(this, new TemperatureEventArgs() { Index = 0, Temperature = temperature, Control = tb }); 
                    }
                    else if (ReferenceEquals(tb, txtHysteresis2))
                    {
                        HystersisChanged(this, new TemperatureEventArgs() { Index = 1, Temperature = temperature, Control = tb }); 
                    }
                }
            }
            else
            {
                errorProvider1.SetError(tb, "Invalid Temperature Format");
            }
        }

        private void configurationRegister_CheckedChanged(object sender, EventArgs e)
        {
            try
            {
                configurationRegisterToByte();
            }
            catch (Exception)
            {
                ShowI2CErrorMessage();
            }
        }


        #endregion

        private void maximButton1_Click(object sender, EventArgs e)
        {
            if (ReadAll != null)
            {
                try
                {
                    ReadAll(this, e);
                }
                catch (Exception)
                {
                    ShowI2CErrorMessage();
                }
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (RequestReadTemperature != null)
            {
                try
                {
                    RequestReadTemperature(this, e);
                }
                catch (Exception)
                {
                    ShowI2CErrorMessage();
                }
            }
        }

        private static void ShowI2CErrorMessage()
        {
            string message = "MAX30205 was not found. Please reconnect and try again or choose 'Cancel' to exit.";
            string caption = "I2C Error";
            MessageBoxButtons buttons = MessageBoxButtons.RetryCancel;
            MessageBoxIcon icon = MessageBoxIcon.Warning;
            DialogResult result;

            // Displays the MessageBox if there isn't one displayed already.

            if (!I2CErrorShown)
            {
                I2CErrorShown = true;
                result = MessageBox.Show(message, caption, buttons, icon);

                if (result == System.Windows.Forms.DialogResult.Cancel)
                {
                    Application.Exit();
                }
                else
                {
                    //Application.Restart();// Don't do this. Rerun address scan
                    //InitBoard();
                }

                I2CErrorShown = false;

            }
        }

        private void cboRefreshRate_SelectedIndexChanged(object sender, EventArgs e)
        {
            RefreshRate = Convert.ToInt32(cboRefreshRate.SelectedItem);
        }

        private void tglCelsiusFahrenheit_CheckedChanged_1(object sender, EventArgs e)
        {

        }

        private void lblTemperature1_Click(object sender, EventArgs e)
        {

        }

        private void chkDataFormat1_Click(object sender, EventArgs e)
        {
            MaximCheckBox chk = (MaximCheckBox)sender;

            if (DataFormatChanged != null)
            {
                if (chk == chkDataFormat1)
                    DataFormatChanged(this, new DataFormatEventArgs() { Index = 0, DataFormat = chkDataFormat1.Checked });
                else if (chk == chkDataFormat2)
                    DataFormatChanged(this, new DataFormatEventArgs() { Index = 1, DataFormat = chkDataFormat2.Checked });
                else
                    throw new NotImplementedException();
            }
        }
    }
}
