using System;
using System.Threading;
using HWModules;
using SensorModules;
using SWModules;

namespace CPF.Profile_States
{
    public class Park : Profile
    {
        private readonly TimeSpan ecDelay = new TimeSpan(0, 5, 0);
        public Park(string name)
        {
            stateName.Clear();
            stateName.Append(name);

            ECTimeout = 30 * 60 * 1000;

            instrumentTimer.Change(-1, -1);
            lastProfile = -1;
        }

        private enum Action : int
        {
            park = 1,
            wait,
            lastAction,
            size = lastAction
        }
        private static Action currentAction;

        public override int doStateEntryActions()
        {
            basicStateEntry();

            //TODO P2 need to make sure this gives enough time for instruments to stabilize
            msc1.powerUp();
            flbb1.powerUp();
            optode1.powerUp();
            radiometer1.powerUp();

            sbTemp.Clear();
            sbTemp.Append(DateTime.Now.ToString("yyyy-MMM-ddTHH:mm:ssZ"));
            sbTemp.Append(Literals.tabChar);
            sbTemp.Append("Start Park, bellows position = ");
            sbTemp.Append(SV.BellowsPosition.ToString("f2"));
            SciLogger.writeToLog(sbTemp);

            sbTemp.Clear();
            sbTemp.Append("Start Park");
            sbTemp.Append(Literals.tabChar);
            sbTemp.Append(DateTime.Now.ToString("yyyy-MMM-ddTHH:mm:ssZ"));
            SciLogger.appendToMsgFooter(sbTemp);

            SV.RunPVPID = true;

            currentAction = (Action)1;

            //set park time and start timer
            if (lastProfile != SV.ProfileNum) //ok we've moved onto a new profile
            {
                EngrLogger.Comment("Anchor period reset on new profile.");
                lastProfile = SV.ProfileNum;
                lastParkStartTime = DateTime.Now;
                parkTime = getParkTime();
            }
            else //we've been here before, shorten it up. 
            {
                EngrLogger.Comment("Anchor period shortened for current profile.");
                parkTime = getParkTime();
                TimeSpan elapsed = DateTime.Now - lastParkStartTime;
                if ((parkTime - elapsed).Ticks > 0)
                    parkTime -= elapsed;
            }


            parkTimer = new Timer(new TimerCallback(parkTimerCallback), null, parkTime, parkTime);

            sbTemp.Clear();
            sbTemp.Append("Starting park period = ");
            sbTemp.Append(parkTime.ToString());
            EngrLogger.writeToColumns(sbTemp);

            //start instrument timer and take sample in 1 s, then sample based on pre-defined period
            instrumentTimer.Change(instrumentTimerDelay, Mission.parkSamplePeriod);

            //ensure park exit flag is false
            parkPeriodExpired = false;

            //set park Timed Out flag false
            parkTimedout = false;

            return (1);
        }

        public override int doStateActions()
        {

            switch (currentAction)
            {
                #region Sequential state actions

                case (Action.park):
                    nextAction = setPV();
                    SV.RunPVPID = true;
                    msc1.SequenceT1T2Samples();
                    break;
                    
                case (Action.wait):
                    nextAction = wait();
                    break;

                default:
                    //shouldn't get here
                    sbTemp.Clear();
                    sbTemp.Append("[ERROR] got to default state in Park Action Sequence");
                    EngrLogger.writeToColumns(sbTemp);
                    sbTemp.Clear();

                    break;
                #endregion
            }

            currentAction = (Action)checkActionReturn(nextAction, (int)currentAction);

            return (1);
        }

        private static CPFStates nextState = new CPFStates();

        public override CPFStates checkEvents()
        {
            //findNextState looks at parkPeriodExpired flag, and determines if SM should transition to ascend, descend, or stay in park.
            //because instruments and park period are on timers, there isn't really anything else to check here
            nextState = findNextState();

            if(nextState != CPFStates.park)
            {
                if (SciLogger.isPHandNO3Done() == 0)     //TODO P1 this needs to look like what's in the anchor state
                    return (CPFStates.park);
                else
                    return (nextState);
            }
            else
                return(CPFStates.park);
        }

        public override int doExitActions(bool timedOut)
        {
            if (timedOut)
            {
                //TODO P0 shouldn't all this happen timedOut or not?
                basicStateExit(true);

                //set park exit flag false
                parkPeriodExpired = false;

            }
            else
            {
                basicStateExit(false);
            }

            SciLogger.endSampleCycle();

            //TODO P2 check to see if this is necessary since PressureTableNum also get incremented in find next state
            //increment PressureTableNum
            SV.PressureTableNum++;

            return (1);
        }

        public override CPFStates doTimeoutAction()
        {

            //set park exit flag false
            parkPeriodExpired = false;

            //set park exit flag true
            parkTimedout = true;

            return (findNextState());
        }

        //instantiate park time and timer 
        private static TimeSpan parkTime;
        private static Timer parkTimer;

        //gets called when park timer finishes (float should exit park state)
        private static void parkTimerCallback(object o)
        {
            //since SV.CurrentState is protected var, state transition still needs to be handled through checkEvents() method in Program class, so we set a flag here that is examined by checkEvents() which transitions state machine to next concrete state
            
            //turn off instrument sample timer
            instrumentTimer.Change(-1, -1);

            //turn off park timer
            parkTimer.Change(-1, -1);
            
            parkPeriodExpired = true;
        }

        //shell routine to be used when neutral buoyancy routine is fully implemented
        private static int callCount = 0;
        private static int findNeutralBuoyancy()
        {
            if (callCount > 10)
            {
                callCount = 0;
                return ((int)Action.wait);
            }
            else
            {
                callCount++;
                return (0);
            }
        }

        private static int holdParkPosition()
        {
            return (1);
        }

        //go do sleep and do nothing
        private static int wait()
        {
            return ((int)Action.wait);
        }

        //decides what the next appropriate state is
        private static CPFStates findNextState()
        {
            //if parkTime expired, or state has timed out, increment pressureTableNum, and move to next state
            if ((parkPeriodExpired) || (parkTimedout))
            {
                //Done with this pressure table entry
                SV.PressureTableNum++;

                if (SV.PastState == CPFStates.descend)
                {
                    //if there are still depth stops in the descend table
                    if (SV.PressureTableNum < Mission.descendTable.Length)
                        return (CPFStates.descend);
                    else
                        return (CPFStates.startCP);
                }
                else
                {
                    //if there are still depth stops in the ascend table
                    if (SV.PressureTableNum < Mission.ascendTable.Length)
                        return (CPFStates.ascend);
                    else
                        return (CPFStates.startCP);
                }
            }
            else
            //If park hasn't expired, stay in park
            {
                return (CPFStates.park);
            }
        }

        //flag to indicate time to exit park time.
        private static bool parkPeriodExpired;

        //flag to indicate timeout
        private static bool parkTimedout;

        private static int lastProfile;
        private static DateTime lastParkStartTime;

    }
 
}
