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

namespace ChamChat
{
    public partial class PLxKPH_Client : Client
    {
        private const Int32 _OptionRange = 2000;
        private const int _RAM = 1;
        private const Int32 _ReturnDataWaitms = 300; // Time out for client to prepare and return data
        private DateTime _ReadyToSend = DateTime.Now; // Timestamp when client is ready to communicate
        

        public PLxKPH_Client(Int32 MIDS,  ClientType Type)
            : base(MIDS,  Type)
        {

            // Set options available in client
            _Parameters = new List<Parameter>();
            _Parameters.Add(Parameter.T);
            _Parameters.Add(Parameter.RH);
            _Parameters.Add(Parameter.RampT);
            _Parameters.Add(Parameter.RampRH);


            // 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);

            // Set availabel addresses for programming
            _AvailableRAMs = new List<int>();
            for (int i = 1; i < 21; i++)
                _AvailableRAMs.Add(i);

            _InterfaceType = InterfaceType.FDTI_USB_COM422;
        }


        

       // To do
        public override Profile ReadProfile(int RAM, ref System.ComponentModel.BackgroundWorker bgw)
        {
            Profile profile = new Profile();
            return profile;
        }


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

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

            string title = profile.Title.Substring(0, (int)Math.Min(14, profile.Title.Length)).Trim();

            string sRAM = string.Format("{0:0}", RAM);

            string sAddr = _Address.ToString();
            string cmd;
            string read;
            string cmd_pre = sAddr + ", PRGM DATA WRITE, PGM" + sRAM + ", ";



            // Cancel RAM program editing
            cmd = cmd_pre + "EDIT CANCEL";
            DataTransfer(cmd, out read, _ReturnDataWaitms,1);
            if( !read.Contains("ERR-3"))
                DataTransfer(cmd, out read, _ReturnDataWaitms, 3);



            // Open RAM program
            cmd = cmd_pre + "EDIT START";
            DataTransfer(cmd,out read, _ReturnDataWaitms);


            // Steps
            try
            {
                for (int i = 0; i < profile.Steps.Count; i++)
                {
                    Step step = profile.Steps[i];
                    cmd = cmd_pre;
                    cmd += String.Format("STEP{0:0}, ", i + 1);
                    cmd += String.Format("TEMP{0:0.0}, ", step.T);
                    cmd += step.RampCtrlT ? "TRAMPON, " : "TRAMPOFF, ";
                    cmd += step.HasRH ? String.Format("HUMI{0:0}, ", step.RH) : "HUMI OFF, ";
                    cmd += step.RampCtrlRH ? "HRAMPON, " : "HRAMPOFF, ";
                    cmd += String.Format("TIME{0:00}:{1:00}, ", step.Hours, step.Minutes);
                    cmd += "GRANTY OFF, ";
                    cmd += "PAUSE OFF";
                    DataTransfer(cmd, out read, _ReturnDataWaitms);
                }
            }
            catch (Exception ex) { throw new Exception("writing steps - " + ex.Message); }


            try
            {

                // Loops
                if (profile.Loops.Count > 0)
                {
                    Loop loop = profile.Loops[0];
                    cmd = cmd_pre;
                    cmd += String.Format("COUNT, A({0}.{1}.{2})", loop.N - 1, loop.Last + 1, loop.First + 1);

                    if (profile.Loops.Count > 1)
                    {
                        loop = profile.Loops[1];
                        cmd += String.Format(", B({0}.{1}.{2})", loop.N - 1, loop.Last + 1, loop.First + 1);
                    }
                    DataTransfer(cmd, out read, _ReturnDataWaitms);
                }
            }
            catch (Exception ex) { throw new Exception("writing loops - " + ex.Message); }



            // End mode
            try
            {
                cmd = profile.Options.Contains(ProfileOption.PLxKPH_EndModeHold) ? cmd_pre + "END, HOLD" : cmd_pre + "END, OFF";
                DataTransfer(cmd, out read, _ReturnDataWaitms);
            }
            catch (Exception ex) { throw new Exception("writing end mode - " + ex.Message); }



            // Name of program
            cmd = cmd_pre + "NAME, " + title;
            DataTransfer(cmd, out read, _ReturnDataWaitms);


            // Close and save RAM program
            cmd = cmd_pre + "EDIT END";
            DataTransfer(cmd, out read, _ReturnDataWaitms);

            // Wait 3 seconds to close program
            System.Threading.Thread.Sleep(3000);
         
            Boolean result = false;
            if (_Interface.InterfaceStatus == null)
                result = true;

            _Profile = profile;
            
            return result;
        }


        public override Boolean IsProgrammable(Profile profile)
        {
            // Profile is programmable if all below applies
            //  number of profile steps 99
            //  number of loops < 3
            //  -40 < t < 150

            if(profile.Steps.Count > 99)
                return false;

            if (profile.Loops.Count > 2)
                return false;

            foreach (Loop loop in profile.Loops)
                if (loop.N > 99 || loop.N < 2) 
                    return false;
            
            foreach (Step step in profile.Steps)
            {
                if (step.T < -40 || step.T > 150)
                    return false;
            }
            return true;
        }


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

        
        public override Boolean IsIdle()
        {
            // IsIdle() returns true if status of chamber is 'Standby'. See page 36 MODE?.

            string sAddr = _Address.ToString();
            string cmd;
            string read;

            cmd = sAddr + ", MODE?";
            DataTransfer(cmd,out read, _ReturnDataWaitms);
            
            if (read.Contains("STANDBY"))
                return true;

            return false;
        }


        public override Boolean Start() 
        {
            if (_Profile == null)
                return false;

           string sAddr = _Address.ToString();
            string cmd;
            string read;

            cmd = sAddr + ", PRGM, RUN, RAM:" + _RAM+", STEP1";  // See page 56

            return DataTransfer(cmd,out read, _ReturnDataWaitms);
        }
       

        public override Boolean IsFinished()
        {
            // Finished() returns true if chamber is not running. See page 36 MODE?.
            string sAddr = _Address.ToString();
            string cmd;
            string read;

            cmd = sAddr + ", MON?";
            DataTransfer(cmd, out read, _ReturnDataWaitms);

            try
            {
                double t = Double.Parse(read.Split(',')[0]);
                double RH = Double.Parse(read.Split(',')[1]);
                _Progress = String.Format("Current conditions are {0:0.0} °C and {1:0} %rh", t, RH);
                _LastProgressUpdate = DateTime.Now;
            }
            catch { _Progress = DateTime.Now.ToString("yyyy-MM-dd @ hh:mm"); }


            if (read.Contains("STANDBY"))
                return true;

            if (read.Contains("OFF"))
                return true;
            
            return false;
        }

        
        public override Boolean Stop() 
        {
            string sAddr = _Address.ToString();
            string cmd;
            string read;
            cmd = sAddr + ", MODE, STANDBY";
            return DataTransfer(cmd, out read, _ReturnDataWaitms);
        }


        public override Boolean IsOnline()
        {
            try
            {
                string sAddr = _Address.ToString();
                string cmd;
                string read;
                cmd = sAddr + ", TYPE?";
                DataTransfer(cmd, out read, _ReturnDataWaitms);
                return (read.ToLower().Contains("scp220") );
            }
            catch
            {
                return false;
            }

        }



        private string _Progress = "";
        private DateTime _LastProgressUpdate = DateTime.Now;
        public override String Progress()
        {
            if(_LastProgressUpdate.AddMinutes(3) < DateTime.Now)
                IsFinished();
            return _Progress;
        }



        private bool TxRxSucces(string cmd, string read)
        {
            // Command not correctly processed
            if (read.StartsWith("NA:"))
                return false;

            // No response received
            if (read.Trim().Length < 1)
                return false;

            // Handle monitoring commands
            if (cmd.Trim().Contains("?"))
            {
                return true;
            }
            else
            {
                if (read.Trim().StartsWith("OK:"))
                    return true;
            }
            return false;
        } 



        private Boolean DataTransfer(string cmd, out string read, int ReturnDataWaitms)
        {
            // If no maximum number of attempts is given, it defaults to 4.
            return DataTransfer(cmd, out read, ReturnDataWaitms,4);
        }
       


        // 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 += (char)10; // LF
            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(10);

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

                // Mask for showing in GUI
                if (cmd.Contains("MON?") || cmd.Contains("EDIT CANCEL") || cmd.Contains("EDITCANCEL"))
                    log.ShowInGUI = false;
                

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

                // Add to logs
                AppendDataTransferLog(log);


                // Set _ReadyToSend acc. manual specs
                if (cmd.Contains("?"))
                    _ReadyToSend = DateTime.Now.AddMilliseconds(500);
                else
                    _ReadyToSend = DateTime.Now.AddMilliseconds(1200);


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