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

namespace ChamChat
{
    public class PLxKPH_Task : Task
    {
        public PLxKPH_Task(Client Client, Profile Profile)
            : base(Client, Profile)
        {
            _Profile = Profile;
            _Client = Client;
            _SerSteps = _Profile.SerializedStepList;
        }

        private const Int32 PollingIntervalSeconds = 10;

        private List<Step> _SerSteps;


        public override Boolean IsControlledProgrammable(Profile profile)
        {
            ClientMessage msg = new ClientMessage(String.Format("The {0} can only operate in stand alone mode.", _Client.TypeDescription));
            _Client.ClientMessages = new List<ClientMessage>();
            _Client.ClientMessages.Add(msg);
            return false;
        }


        protected override void ThreadStandAlone()
        {
            // Write profile
            try
            {
                if (!_Client.WriteProfile(_Profile))
                {
                    _Status = TaskStatus.Stopped;
                    return;
                }
            }
            catch (Exception ex)
            {
                _Status = TaskStatus.ExceptionOccured;
                _TaskProgress = string.Format("Client WriteProfile() exception: {0}", ex.Message);
                return;
            }


            // Start client
            try
            {
                if (!_Client.Start())
                {
                    _Status = TaskStatus.Stopped;
                    return;
                }
            }
            catch (Exception ex)
            {
                _Status = TaskStatus.ExceptionOccured;
                _TaskProgress = string.Format("Client Start() exception: {0}", ex.Message);
                return;
            }

            DateTime lastPolled = DateTime.Now;

            while (_Status == TaskStatus.IsRunning)
            {
                // Poll only every 'PollingIntervalSeconds' seconds
                if (DateTime.Now > lastPolled.AddSeconds(PollingIntervalSeconds))
                {
                    // Is finished?
                    try
                    {
                        if (_Client.IsFinished())
                        {
                            _Status = TaskStatus.Finished;
                            continue;
                        }
                    }
                    catch { }

                    try
                    {
                        // Client return "Current conditions are x °C and x %rh"
                        _TaskProgress = _Client.Progress();
                        double runTimeMin = (DateTime.Now - StartOfTask).TotalMinutes;
                        double p = runTimeMin / _Profile.DurationMin * 100;
                        double remainTimeMin = Math.Max(0,_Profile.DurationMin - runTimeMin);
                        double hrs = Math.Floor(remainTimeMin /60);
                        double min = Math.Floor(remainTimeMin - 60 * hrs);
                        _TaskProgress += String.Format(", {0:0.0}% completed, {1:0}h{2:00} remaining",p, hrs,min );

                    }
                    catch { }
                    lastPolled = DateTime.Now;
                }

                System.Threading.Thread.Sleep(100);

                if (_Status == TaskStatus.AbortRequested)
                {
                    try
                    {
                        if (_Client.Stop())
                            _Status = TaskStatus.Aborted;
                    }
                    catch { }
                }

                _isKicking = true;
            }
            _isKicking = false;
        }
        

        

        protected override void ThreadControlled()
        {
            Int32 iCurrentSerialStep = -1;
            DateTime startOfCurrentProgram = new DateTime(1900, 1, 1);
            DateTime endOfCurrentProgram = new DateTime(1900, 1, 1);
            Step currentStep = _SerSteps[0];
            DateTime lastPolled = DateTime.Now;

            while (_Status == TaskStatus.IsRunning)
            {
                if (DateTime.Now > endOfCurrentProgram)
                {
                    iCurrentSerialStep++; 

                    // Last step has been performed?
                    if (iCurrentSerialStep > _SerSteps.Count - 1)
                    {
                        _Client.Stop();
                        _Status = TaskStatus.Finished;
                        AddEventLog("Last step performed");
                        continue;
                    }

                    // Get step details
                    currentStep = _SerSteps[iCurrentSerialStep];
                    double TargetT = currentStep.T;
                    double TargetRH = currentStep.RH;
                    double DurationMin = currentStep.DurationMin;

                    double StartT = currentStep.T; // Set start to be target, but change to value of previous step if ramped.
                    if (currentStep.RampCtrlT)
                    {
                        if (iCurrentSerialStep == 0)
                            StartT = 23;
                        else
                            StartT = _SerSteps[iCurrentSerialStep - 1].T;
                    }

                    double StartRH = currentStep.RH; // Set start to be target, but change to value of previous step if ramped.
                    if (currentStep.RampCtrlRH)
                    {
                        if (iCurrentSerialStep == 0)
                            StartRH = 60;
                        else
                            StartRH = _SerSteps[iCurrentSerialStep - 1].RH;
                    }

                    RestartClient(DurationMin, StartT, TargetT, StartRH, TargetRH);

                     if (_Status != TaskStatus.IsRunning)
                        continue;

                    
                    // Update variables
                    startOfCurrentProgram = DateTime.Now;
                    endOfCurrentProgram = DateTime.Now.AddMinutes(DurationMin);
                    lastPolled = DateTime.Now;


                    #region Update events
                    try
                    {
                        string s = "Set conditions: ";
                        if (currentStep.RampCtrlT)
                            s += String.Format("to {0:0.0} °C ({1:0.00} ˜°C/min), ", TargetT, (TargetT - StartT) / DurationMin);
                        else
                            s += String.Format("{0:0.0} °C, ", TargetT);
                        if (currentStep.RampCtrlRH)
                            s += String.Format("to {0:0.0} %rh ({1:0.00} ˜%rh/min), ", TargetRH, (TargetRH - StartRH) / DurationMin);
                        else
                            s += String.Format("{0:0.0} %rh, ", TargetRH);
                       
                        s += string.Format("{0:0}h{1:00}", currentStep.Hours, currentStep.Minutes);

                        AddEventLog(s);
                         s = string.Format("Partial profile end is {0:00}-{1:00} @ {2:00}:{3:00}:{4:00}", endOfCurrentProgram.Day, endOfCurrentProgram.Month, endOfCurrentProgram.Hour, endOfCurrentProgram.Minute, endOfCurrentProgram.Second);
                        AddEventLog(s);
                    }
                    catch (Exception ex) { AddEventLog("Update events: " + ex.Message); }
                    #endregion Update events
                }



                // Poll only every 'PollingIntervalSeconds' seconds
                if (DateTime.Now > lastPolled.AddSeconds(PollingIntervalSeconds))
                {
                    // Is finished?
                    try
                    {
                        if (_Client.IsFinished())
                        {
                            // Last step is held (page 61) when program ends. Client will never finish by itself.
                            AddEventLog("Client finished, but task did not!");
                            _Status = TaskStatus.Interrupted; // Profile did not finish, client did
                            continue;
                        }
                    }
                    catch { } // Always continue and keep trying


                    #region Taskprogress
                    try
                    {
                        double taskRunTime = (DateTime.Now - _startThread).TotalMinutes;
                        double pTest = Math.Min(100, Math.Floor(taskRunTime / _Profile.DurationMin * 100));
                        double taskTimeRemain = _Profile.DurationMin - taskRunTime;
                        double taskTimeRemainHrs = Math.Floor(taskTimeRemain / 60);
                        double taskTimeRemainMin = Math.Floor(taskTimeRemain - 60 * taskTimeRemainHrs);
                        double currentStepRuntime = (DateTime.Now - startOfCurrentProgram).TotalMinutes;
                        double pStep = Math.Min(100, Math.Floor(currentStepRuntime / currentStep.DurationMin * 100));
                        _TaskProgress = String.Format("Step #{0:0}/{1:0} progress {2:0}%, test progress {3:0}%, remaining test time {4:0}h{5:00}m", iCurrentSerialStep + 1,_SerSteps.Count, pStep, pTest, taskTimeRemainHrs, taskTimeRemainMin);
                        if (iCurrentSerialStep + 1 == _SerSteps.Count && pTest > 99.99)
                            _TaskProgress = "Profile executed";
                    }
                    catch { _TaskProgress = "n/a"; }
                    #endregion Taskprogress


                    lastPolled = DateTime.Now;
                }

                System.Threading.Thread.Sleep(200);

                _isKicking = true;
            }


            if (_Status == TaskStatus.AbortRequested)
            {
                try
                {
                    if (_Client.Stop())
                        _Status = TaskStatus.Aborted;
                }
                catch { }
            }

            _isKicking = false;
        }


        private void RestartClient(Double DurationMin, Double StartT, Double TargetT, Double StartRH, Double TargetRH)
        {
            // Stop client
            try
            {
                AddEventLog("Info: Stopping client");
                if (!_Client.Stop())
                {
                    _Status = TaskStatus.Interrupted;
                    AddEventLog("Task interrupted while trying to stop client!");
                    return;
                }
            }
            catch (Exception ex)
            {
                _Status = TaskStatus.ExceptionOccured;
                _TaskProgress = string.Format("Client Stop() exception: {0}", ex.Message);
                AddEventLog(_TaskProgress);
                return;
            }

            // Wait to finish
            try
            {
                Boolean timedOut = true;

                DateTime startWait = DateTime.Now;
                while (DateTime.Now < startWait.AddSeconds(60))
                {
                    System.Threading.Thread.Sleep(1000);
                    if (_Client.IsFinished())
                    {
                        timedOut = false;
                        break;
                    }
                }
                if (timedOut)
                {
                    _Status = TaskStatus.Interrupted;
                    AddEventLog("Time out (60 s) in RestartClient.WaitForFinish");
                    return;
                }
            }
            catch (Exception ex)
            {
                _Status = TaskStatus.ExceptionOccured;
                _TaskProgress = string.Format("Client WaitForFinish exception: {0}", ex.Message);
                AddEventLog(_TaskProgress);
                return;
            }

            AddEventLog("Info: Client stopped");


            // Start client
            try
            {
                AddEventLog("Info: Starting client");

                if (!((PLxKPH_Client)_Client).Start(DurationMin, StartT, TargetT, StartRH, TargetRH))
                {
                    // Try to start again after 10 sec
                    AddEventLog("First attempt to start client failed!");
                    System.Threading.Thread.Sleep(10000);

                    if (!((PLxKPH_Client)_Client).Start(DurationMin, StartT, TargetT, StartRH, TargetRH))
                    {
                        _Status = TaskStatus.Interrupted;
                        AddEventLog("Task interrupted while trying to start client!");
                        return;
                    }
                }
            }
            catch (Exception ex)
            {
                _Status = TaskStatus.ExceptionOccured;
                _TaskProgress = string.Format("Client Start() exception: {0}", ex.Message);
                AddEventLog(_TaskProgress);
                return;
            }
            AddEventLog("Info: Client started");
        }
    }
}
