using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ChamChat.Models;

namespace ChamChat
{
    public partial class HTS7057_Client : Client
    {
        private const Int32 _OptionRange = 4000;
        private const Int32 _DataTransferTimeOut = 1000;
        private DateTime _ReadyToSend = DateTime.Now; // Timestamp when client is ready to communicate
        private DateTime _dtTestStart = DateTime.Now;
        

        public HTS7057_Client(Int32 MIDS, ClientType Type)
            : base(MIDS,  Type)
        {
            // Set options available in client
            _Parameters = new List<Parameter>();
            _Parameters.Add(Parameter.T);
            //_Parameters.Add(Parameter.RampT);

            // Add option range 
            _ClientOptions = new List<ProfileOption>();
            foreach (ProfileOption o in Enum.GetValues(typeof(ProfileOption)))
                if ((int)o >= _OptionRange && (int)o < _OptionRange+999)
                    _ClientOptions.Add(o);

            _InterfaceType = InterfaceType.FDTI_USB_COM422;

        }







        public override Profile ReadProfile(int RAM, ref System.ComponentModel.BackgroundWorker bgw)
        {
            String title = string.Format("{0:00000} {1}", _MIDS, _Type.ToStr());
            List<Step> steps = new List<Step>();
            Step step = new Step(365 * 24 * 60);

            string cmd;
            string read;

            cmd = string.Format("${0:00}I", _Address);
            DataTransfer(cmd, out read, _DataTransferTimeOut);

            if (read.Length >= 6)
            {
                double sw = 0;
                if (double.TryParse(read.Substring(0, 6), out sw))
                {
                    step.T = sw;
                }
            }
            steps.Add(step);
            return new Profile(title, steps, new List<Loop>(), new List<ProfileOption>());
        }



        public override Boolean WriteProfile(Profile profile)
        {
            return WriteProfile(profile, -1);
        }

        public override Boolean WriteProfile(Profile profile, int RAM)
        {
            // Only stand alone profiles allowed!
            if (!IsProgrammable(profile))
                return false;
            _Profile = profile;

            return true;
        }




        public override Boolean IsProgrammable(Profile profile)
        {
            // Profile is programmable if all below applies

            // Profile contains just 1 step
            if (profile.SerializedStepList.Count != 1)
            {
                _ClientMessages.Add(new ClientMessage("Temperature range is -70 to 200 °C"));
                return false;
            }

            Step step = profile.SerializedStepList[0];

            // Temperature is limited between -70 & 200 °C
            if (step.T < -70 || step.T > 200)
            {
                _ClientMessages.Add(new ClientMessage("Temperature range is -70 to 200 °C"));
                return false;
            }
            
            return true;
        }



        // To do
        public override Boolean HasAlarm(out List<Alarm> alarms)
        {
            alarms = new List<Alarm>();
            return true;
        }



        public override Boolean IsIdle()
        {
            // Chamber is idle if return value's last 16 chars are 0
            string cmd;
            string read;

            cmd = string.Format("${0:00}I", _Address);
            DataTransfer(cmd, out read, _DataTransferTimeOut);
            try
            {
                if (read.Substring(15,1) == "0")
                    return true;
            }
            catch { }
            return false;
        }


        public override Boolean Start()
        {
            // Command consists of $01E xXxx.x bbbbbbbb
            // The minus sign for temperature must be on location of X
            // Bit 3 is on/off bit of chamber (return as bit 1 in 16-bit output)
            // Bit 5 is on/off bit of heater/cooler (return as bit 3 in 16-bit output)

            if (_Profile == null)
                return false;

            string cmd;
            string read;

            double t = _Profile.Steps[0].T;
            if( t< 0)
                cmd = string.Format("${0:00}E 0-{1:00.0} 00010100", _Address, Math.Abs(t));
            else
                cmd = string.Format("${0:00}E {1:0000.0} 00010100", _Address, t);


            DataTransfer(cmd, out read, _DataTransferTimeOut);

            _dtTestStart = DateTime.Now;

            return TxRxSucces(cmd, read);
        }


        public override Boolean IsFinished()
        {
            // Chambers does not finish on its own
            return IsIdle();
        }


        public override Boolean Stop()
        {
            string cmd;
            string read;
            cmd = string.Format("${0:00}E 0023.0 00000000", _Address);
            DataTransfer(cmd, out read, _DataTransferTimeOut);
            return TxRxSucces(cmd, read);
        }



        public override Boolean IsOnline()
        {
            // Chamber is online if Sollwert in °C is returned
            string cmd;
            string read;

            cmd = string.Format("${0:00}I", _Address);
            DataTransfer(cmd, out read, _DataTransferTimeOut);

            if (read.Length < 6)
                return false;
            
            double sw = 0;
            if (double.TryParse(read.Substring(0, 6), out sw))
                return true;

            return false;
        }


        public override String Progress()
        {
            string cmd;
            string read;

            cmd = string.Format("${0:00}I", _Address);
            DataTransfer(cmd, out read, _DataTransferTimeOut);

            if (read.Length < 13)
                return "n/a";

            string progress = "";

            double sw = 0;
            if (double.TryParse(read.Substring(0, 6), out sw))
                progress += string.Format("Setting is {0:0.0} °C", sw);

            double iw = 0;
            if (double.TryParse(read.Substring(7, 6), out iw))
                progress += string.Format(", current temperature is {0:0.0} °C",iw);

            if (progress.Length == 0)
                progress += "n/a";

            return progress;
        }


        private bool TxRxSucces(string cmd, string read)
        {
            if (cmd.Contains("I"))
                if (read.Length == 30)
                    if (read.Substring(4, 1) == "." && read.Substring(11, 1) == ".")
                        return true;

            if (cmd.Contains("E"))
                return read == "0";

            return false;
        }






        // Performs transfer and adds interface logs to list in Client parent class
        private Boolean DataTransfer(string cmd, out string read, int ReturnDataWaitms)
        {
            // If no maximum number of attempts is given, it defaults to 3.
            return DataTransfer(cmd, out read, ReturnDataWaitms, 3);
        }





        // Performs transfer and adds interface logs to list in Client parent class
        private Boolean DataTransfer(string cmd, out string read, int ReturnDataWaitms, int MaxNumOfAttempts)
        {
            // Construct command to send
            cmd += (char)13; // CR
            cmd = cmd.Replace(" ", "");


            // Wait for _ReadyToSend or max 1 seconds
            DateTime go = DateTime.Now.AddSeconds(1);
            while (DateTime.Now < _ReadyToSend && DateTime.Now < go)
                System.Threading.Thread.Sleep(50);

            read = "";
            bool success = false;
            int attempts = 0;
            while (!success && attempts <= MaxNumOfAttempts)
            {
                DataTransferLog log = _Interface.DataTransfer(cmd, out read, ReturnDataWaitms);
                success = TxRxSucces(cmd, read);
                attempts++;

                // Mask for showing in GUI
                if (cmd.Contains("I"))
                    log.ShowInGUI = false;

                // Notify user via GUI of failed attempts
                if (attempts > 1)
                    log.Command += string.Format(" [Attempt {0:0}]", attempts);

                // Add to logs
                AppendDataTransferLog(log);


                // Set _ReadyToSend acc. manual specs
                _ReadyToSend = DateTime.Now.AddMilliseconds(2000);


                // Sleeps
                if (!success)
                    System.Threading.Thread.Sleep(3000); // Sleep 3 seconds before resending if failed
            }
            return success;
        }



    }
}
