using System;
using Microsoft.SPOT;

namespace HSM
{
    public class Hsm
    {

        protected void Initialize(State topState)
        {
            // init top state
            this.topState = topState;
            // init deep history table
            StateHistory.Clear();
            // transition to initial top state
            InitTransitionState(topState);
        }

        protected Boolean IsParent(State parentState, State childState)
        {
            do
            {
                childState = childState(new Event(Signal.Null));
                if (childState == parentState)
                    return true;
            }
            while (childState != null);
            return false;
        }

        protected void TransitionState(State newState, bool toDeepHist)
        {
            // retrive historical state if appropriate
            if (toDeepHist)
            {
                newState = StateHistory.Retrieve(newState);
                if (newState == null)
                {
                    Debug.Print("TransitionState: ERROR - RetrieveDeephist failed!");
                    return;
                }
            }

            // exit signal to current state
            if (curState != null)
            {
                State parentState;
                // record the deep history state if appropriate
                parentState = curState(new Event(Signal.Null));
                while (parentState != null)
                {
                    // the top state cannot have a history sub state and returns stnone to all 
                    // unhandled signals anyhow
                    if (parentState == topState)
                        break;
                    else if ((parentState(new Event(Signal.DeepHistory)) == null) &&
                             (!IsParent(parentState, newState)))
                    {
                        StateHistory.Record(parentState, curState);
                        break;
                    }
                    parentState = parentState(new Event(Signal.Null));
                }
                // state exit chain 
                curState(new Event(Signal.Exit));
                parentState = curState(new Event(Signal.Null));
                while (!IsParent(parentState, newState))
                {
                    parentState(new Event(Signal.Exit));
                    parentState = parentState(new Event(Signal.Null));
                }
                // set current state to parent state
                curState = parentState;
            }
            else
                Debug.Print("TransitionState: ERROR - current state is invalid!");

            // entry signal to new state
            while (curState != newState)
            {
                State parentState = newState;
                State lastChild = newState;
                while (true)
                {
                    parentState = parentState(new Event(Signal.Null));
                    if (parentState == curState)
                    {
                        lastChild(new Event(Signal.Entry));
                        // set current state to last child state
                        curState = lastChild;
                        break;
                    }
                    lastChild = parentState;
                }
            }

            // init signal to new state
            newState(new Event(Signal.Init));
        }

        protected void TransitionState(State newState)
        {
            TransitionState(newState, false);
        }

        protected void InitTransitionState(State newState)
        {
            // set new state
            curState = newState;
            if (curState != null)
            {
                // entry signal to current state
                curState(new Event(Signal.Entry));
                // init signal to current state
                curState(new Event(Signal.Init));
            }
            else
                Debug.Print("InitTransitionState: ERROR - current state is invalid!\n");
        }

        public void SignalCurrentState(Event e)
        {
            if (curState != null)
            {
                State parentState = curState;
                do
                    parentState = parentState(e);
                while (parentState != null);
            }
            else
                Debug.Print("SignalCurrentState: ERROR - current state is invalid!\n");
        }

        protected Boolean IsInState(State state)
        {
            State parentState = curState;
            do
            {
                if (state == parentState)
                    return true;
                parentState = parentState(new Event(Signal.Null));
            }
            while (parentState != null);
            return false;
        }

        private History StateHistory = new History();
        private State curState = null;
        private State topState = null;
    }
}
