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

namespace ChamChat
{
    public class HTS7057_Task : Task
    {
        // Each step is separately programmed to the TSE11.
        // Steps are programmed at 110% of required step duration.
        // End of program is always of shutdown type to avoid stressing to much. (Excluding last step)
        // If next step is of different type (tA or tB), then the next temperature is prepared in other chamber.
        // Default temperatures are 0 °C for tA and 85 °C for tB.

        public HTS7057_Task(Client Client, Profile Profile)
            : base(Client, Profile)
        {
            _Profile = Profile;
            _Client = Client;
            _SerSteps = _Profile.SerializedStepList;

        }


        // Needs to be overriden, because chamber is never stand alone programmable
        public override Boolean IsStandAloneProgrammable(Profile profile)
        {
            return false;
        }


        private const Int32 PollingIntervalSeconds = 10;
        private List<Step> _SerSteps;


        public override Boolean IsControlledProgrammable(Profile profile)
        {
            foreach (Step step in profile.SerializedStepList)
            {
                List<Step> steps = new List<Step>();
                steps.Add(step);
                Profile p = new Profile(_Client.Type.ToStr(), steps, new List<Loop>(), profile.Options);
                if (!_Client.IsProgrammable(p))
                    return false;
            }
            return true;
        }


        protected override void ThreadStandAlone()
        {
            _Status = TaskStatus.Aborted;
            _TaskProgress = "Client cannot operate in standaline mode";
            _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 += 1;

                    // Last step has been performed?
                    if (iCurrentSerialStep > _SerSteps.Count - 1)
                    {
                        // Do not stop client, but leave running if option given
                        if (_Profile.Options.Contains(ProfileOption.HTS7057_EndModeHold))
                        {
                            _Status = TaskStatus.Finished;
                            AddEventLog(ProfileOption.HTS7057_EndModeHold.ToStr());
                            _TaskProgress = ProfileOption.HTS7057_EndModeOff.ToStr();
                            _TaskProgress += string.Format(" (setting is {0:0.0} °C)", _SerSteps[_SerSteps.Count - 1].T);
                            continue;
                        }
                        else
                        {
                            _Client.Stop();
                            _Status = TaskStatus.Finished;
                            AddEventLog(ProfileOption.HTS7057_EndModeOff.ToStr());
                            _TaskProgress = ProfileOption.HTS7057_EndModeOff.ToStr();
                            continue;
                        }
                    }

                    Profile newProfile = GetProfile(iCurrentSerialStep);

                    currentStep = _SerSteps[iCurrentSerialStep];
                    AddEventLog(string.Format("{0} minutes @ {1:0.0} °C", _SerSteps[iCurrentSerialStep].DurationMin, _SerSteps[iCurrentSerialStep].T));

                    RestartClient(newProfile);

                    if (_Status != TaskStatus.IsRunning) // Might be altered in RestartClient()
                        continue;

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


                // Poll only every 'PollingIntervalSeconds' seconds
                if (DateTime.Now > lastPolled.AddSeconds(PollingIntervalSeconds))
                {
                    // Is finished?
                    try
                    {
                        if (_Client.IsFinished())
                        {
                            _Status = TaskStatus.Interrupted; // Profile did not finish, client did
                            continue;
                        }
                    }
                    catch { }

                    try
                    {
                        double taskRunTimeMin = (DateTime.Now - _startThread).TotalMinutes;
                        double pTest = Math.Max(0,Math.Min(100, Math.Floor(taskRunTimeMin / _Profile.DurationMin * 100)));
                        double stepRuntime = (DateTime.Now - startOfCurrentProgram).TotalMinutes;
                        double pStep = Math.Max(0,Math.Min(100, Math.Floor(stepRuntime / _SerSteps[iCurrentSerialStep].DurationMin * 100)));
                        double taskTimeRemain = Math.Max(0, _Profile.DurationMin - taskRunTimeMin);
                        double taskTimeRemainHrs = Math.Floor(taskTimeRemain / 60);
                        double taskTimeRemainMin = Math.Floor(taskTimeRemain - 60 * taskTimeRemainHrs);
                        _TaskProgress = String.Format("Step #{0:00}/{1:00} progress {2:0}%, test progress {3:0}%, remaining test time {4:0}h{5:00}m", iCurrentSerialStep + 1, _Profile.SerializedStepList.Count, pStep, pTest, taskTimeRemainHrs, taskTimeRemainMin);
                    }
                    catch
                    {
                        _TaskProgress = "Progress could not be calculated.";
                    }
                    lastPolled = DateTime.Now;
                }


                System.Threading.Thread.Sleep(200);

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

                _isKicking = true;
            }
            _isKicking = false;
        }


        private void RestartClient(Profile newProfile)
        {
            // Write profile
            try
            {
                if (!_Client.WriteProfile(newProfile))
                {
                    _Status = TaskStatus.Interrupted;
                    return;
                }
            }
            catch (Exception ex)
            {
                _Status = TaskStatus.ExceptionOccured;
                _TaskProgress = string.Format("Client WriteProfile() exception: {0}", ex.Message);
                AddEventLog(_TaskProgress);
                return;
            }


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


        private Profile GetProfile(int iStep)
        {
            // Stepslist
            List<Step> steps = new List<Step>();
            steps.Add(_SerSteps[iStep].DeepClone());

            // Loops
            List<Loop> loops = new List<Loop>();

            // Options
            List<ProfileOption> options = new List<ProfileOption>();

            // Create profile
            return new Profile(_Profile.Title, steps, loops, options);
        }
    }
}
