using System;
using System.Net;
using System.Windows.Forms;
using System.Timers;

namespace ClimaChamber
{
    /// <summary>
    /// Global controller for climatic chamber
    /// </summary>
    public class ClimaController
    {
        /// <summary>
        /// GUI for climatic chamber controller. Provides setup and connection to device
        /// </summary>
        private ClimaChamberWnd _View = new ClimaChamberWnd();
        /// <summary>
        /// Exact type of climatic chamber based on User choice
        /// </summary>
        private IClimaticChamber _Model;
        /// <summary>
        /// Internal timer for update value from chamber
        /// </summary>
        private System.Timers.Timer ConfigTimer;
        /// <summary>
        /// Actual state of connected climatic chamber
        /// </summary>
        private DeviceStatus ChamberStatus;
        /// <summary>
        /// TRUE - climatic chamber is ON
        /// </summary>
        private bool isRunning;

        /// <summary>
        /// Posible device state
        /// </summary>
        public enum DeviceStatus
        {
            NotConnected,
            Connecting,
            Connected
        }

        /// <summary>
        /// Contructor for climatic chamber controller.
        /// </summary>
        public ClimaController() 
        {
            this._View = new ClimaChamberWnd();
            this._View.setController(this);
            ChamberStatus = DeviceStatus.NotConnected;
        }

        /// <summary>
        /// Show GUI
        /// </summary>
        /// <returns></returns>
        public Boolean Configure()
        {
            if (ConfigTimer != null)
                ConfigTimer.Start();
            this._View.ShowDialog();
            if(ConfigTimer != null)
                ConfigTimer.Stop();
       
            return (ChamberStatus == DeviceStatus.Connected);
        }

        /// <summary>
        /// Try connect with selected device by Ethernet or serial port
        /// </summary>
        public void Connect()
        {
            bool ChamberConnected = false;

            if (ChamberStatus == DeviceStatus.NotConnected)
            {
                switch (this._View.SelectedChamber)
                {
                    case 0: // Weiss
                        _Model = new Weiss();
                        break;
                    case 1: // Angelantoni
                        _Model = new Modbus();
                        break;
                    case 2: // Watlow F4
                        _Model = new ModbusRateTable();
                        break;
                }
                ChamberStatus = DeviceStatus.Connecting;
                _View.UpdateConnectionControls();
                if (_View.IsEthernetSelected) // network
                {
                    IPAddress address;
                    if (IPAddress.TryParse(_View.IpAddress, out address))
                    {
                        if (_View.TcpPort >= 0)
                            ChamberConnected = _Model.Connect(address, _View.TcpPort);
                        else
                        {
                            MessageBox.Show("Incorrect TCP Port", "Climatic Chamber connection", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            ChamberConnected = false;
                        }
                    }
                    else
                    {
                        MessageBox.Show("Incorrect IP address format", "Climatic Chamber connection", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        ChamberConnected = false;
                    }
                }
                else // Serial line
                {
                    ChamberConnected = _Model.Connect(_View.ComPort,
                                   _View.ComBaudrate,
                                   _View.ComDatabits,
                                   _View.ComStopbits,
                                   _View.ComParity);
                }
            }
            else // Disconnect
            {
                _Model.Stop();                               // stop chamber
                _Model.Disconnect();                         // disconnect chamber
                ChamberStatus = DeviceStatus.NotConnected;
                if (ConfigTimer != null)
                {
                    ConfigTimer.Stop();
                    ConfigTimer.Dispose();
                    ConfigTimer = null;
                }
            }

            if (ChamberConnected)
            {
                bool result = false;
                int i = 0;
                do
                    result = _Model.Run();                                  // start chamber
                while (!result && ++i < 3);
                if (result)
                {
                    ChamberStatus = DeviceStatus.Connected;
                    ConfigTimer = new System.Timers.Timer(1000);
                    UpdateClimaRates();
                    ConfigTimer.Elapsed += new ElapsedEventHandler(ClimaWeatherUpdate);
                    ConfigTimer.Enabled = true;
                }
                else
                {
                    _Model.Disconnect();
                    ChamberStatus = DeviceStatus.NotConnected;
                }
            }
            else
            {
                ChamberStatus = DeviceStatus.NotConnected;
            }
            _View.UpdateConnectionControls();
        }

        /// <summary>
        /// Reads actual value from chamber. This method is called by internal timer.
        /// </summary>
        /// <param name="source">Object whos call this method</param>
        /// <param name="e">Arguments from caller</param>
        private void ClimaWeatherUpdate(object source, ElapsedEventArgs e)
        {
            _Model.ManualUpdate();
            _View.UpdateWeather(_Model.GetActTemp(), _Model.GetActHumi());
            
        }

        /// <summary>
        /// Set temperature and humidity on connected climatic chamber
        /// </summary>
        /// <param name="temp">Temperature set-point</param>
        /// <param name="humidity">Humidity set-point</param>
        /// <returns>TRUE - success</returns>
        public bool SetTempHumi(float temp, float humidity)
        {
            return (_Model.SetTempHumi(temp, humidity));
        }

        /// <summary>
        /// Set all posible parameters on connected climatic chamber
        /// </summary>
        /// <param name="heatRate">Heating gradient</param>
        /// <param name="coolRate">Cooling gradient</param>
        /// <param name="humRate">Humidity gradient</param>
        /// <param name="deHumRate">Dehumidity gradient</param>
        /// <param name="temp">Temperature set-point</param>
        /// <param name="humidity">Humidity set-point</param>
        /// <returns>TRUE - success</returns>
        public bool SetAll( float heatRate, float coolRate, float humRate, float deHumRate, float temp, float humidity)
        {
            return _Model.SetAll(heatRate, coolRate, humRate, deHumRate, temp, humidity);
        }

        /// <summary>
        /// Dispose all components
        /// </summary>
        public void Dispose()
        {
            if (_Model != null)
            {
                //_Model.UpdaterStop();
                _Model.Disconnect();
                _Model = null;
            }
            if (ConfigTimer != null)
                ConfigTimer.Dispose();
        }

        /// <summary>
        /// Set gradients from GUI
        /// </summary>
        public void ConfigureClimaRates()
        {
            _Model.SetChangeRates(_View.HeatRate, _View.CoolRate, _View.HumidRate, _View.DryRate);
        }

        /// <summary>
        /// Set gradients in GUI to 0
        /// </summary>
        public void UpdateClimaRates()
        {
            _View.HeatRate = 0;// _Model.
            _View.CoolRate = 0;//_Model.
            _View.HumidRate = 0;//_Model.
            _View.DryRate = 0;//_Model.
        }

        /// <summary>
        /// Get array of all opened COM ports in this PC
        /// </summary>
        /// <returns>Array of opened COM ports</returns>
        public String[] getAvailableCOMs()
        {
            return System.IO.Ports.SerialPort.GetPortNames();
        }

        /// <summary>
        /// Get actual chamber status
        /// </summary>
        public DeviceStatus Status
        { get { return ChamberStatus; } }

        /// <summary>
        /// Turn on climatic chamber
        /// </summary>
        /// <returns>TRUE - success</returns>
        public bool Run()
        {
            isRunning = true;
            return _Model.Run();
        }

        /// <summary>
        /// Turn off climatic chamber
        /// </summary>
        /// <returns>TRUE - success</returns>
        public bool Stop()
        {
            isRunning = false;
            return _Model.Stop();
        }

        /// <summary>
        /// Set gradients Heating=Cooling Humidity=Dehumidity
        /// </summary>
        /// <param name="tempRate">Heating(Cooling) gradient</param>
        /// <param name="humRate">Humidity(Dehumidity) gradient</param>
        /// <returns></returns>
        public bool SetChangeRates(float tempRate, float humRate)
        {
            if (tempRate < 0.0f)
                tempRate = -tempRate;
            if (humRate < 0.0f)
                humRate = -humRate;
            return _Model.SetChangeRates(tempRate, tempRate, humRate, humRate);
        }

        /// <summary>
        /// Reads values from climatic chamber
        /// </summary>
        public void ManualUpdate()
        {
            _Model.ManualUpdate();
        }

        /// <summary>
        /// Gets last know temperature from climatic chamber
        /// </summary>
        public float ActTemp { get { return (_Model.GetActTemp()); } }

        /// <summary>
        /// Gets last know humidity from climatic chamber
        /// </summary>
        public float ActHum { get { return (_Model.GetActHumi()); } }

        /// <summary>
        /// Gets name of connected climatic chamber
        /// </summary>
        public string Name { get { return (_Model.ChamberName); } }

        /// <summary>
        /// Gets current state of climatic chamber
        /// </summary>
        public bool GetCurrentState { get { return isRunning; } }
    }
}
