#region  --------------- Copyright Dresser Wayne Pignone -------------
/*
 * $Log: /Wrk/WayneLibraries/Wrk/StateEngine/CompositeState.cs $
 * 
 * 7     08-03-25 14:15 Mattias.larsson
 * Clean-up.
 * 
 * 6     08-02-26 14:06 Mattias.larsson
 * Renamed stateName to factoryName.
 * 
 * 5     07-08-15 15:27 roger.m�nsson
 * Added SetDebugLogger method.
 * 
 * 4     07-08-07 8:26 Mattias.larsson
 * 
 * 3     07-03-02 13:19 roger.m�nsson
 */
#endregion
#region Old file header
/*===============================================================================
 * CompositeState
 * 
 * Change history
 * When         Who     Comment
 * ----------   ------  ------------------------------------
 * 2006-07-17   RMa     Major updates, Changed state machine and event handling.
 * 2006-05-09   RMa     Implemented the dispose handling.
 * 2006-03-01   RMa     FXCop updates: CreateStateMachine is not virtual anymore.
 * 2006-02-03   RMa     FXCop updates: Made constuctor protected + misc.
 * 2006-01-27   RMa     Removed interface Event. Use Event class instead.
 * 2006-01-05   RMa     Added the internal function LookupState
 * 2005-12-14   RMa     Changed so Internal_Enter is called by the state machine, so we are sure that
 *                      the internal stuff gets called ,if the user forgets to call base.Enter()....
 * 2005-12-09   RMa     Fixed so the state entry always is sent into a composite state machine.
 * 2005-12-05   RMa     This header added.
 * 
---------------------------------------------------------------------------------*/
#endregion
using System;
using System.Linq;
using Wayne.Lib.Log;

namespace Wayne.Lib.StateEngine
{
    /// <summary>
    /// Composite state is an abstract class that should be overriden to 
    /// create a state that within itself have a state machine with sub states.
    /// When creating the composite state, a message handling thread should be sent
    /// in to the composite state constructor. It can either be a new thread if the 
    /// composite state machine should run in a separate thread , or the thread of the 
    /// parent state machine if the machine should run in the same thread.
    /// </summary>
    abstract public class CompositeState : State
    {
        #region Fields

        private StateMachine stateMachine;
        private bool disposed;

        #endregion

        #region Construction

        /// <summary>
        /// Initializes a new instance of a composite state.
        /// </summary>
        protected CompositeState()
        {
            stateMachine = new CompositeStateMachine(this);
        }

        /// <summary>
        /// Destructor
        /// </summary>
        ~CompositeState()
        {
            Dispose(false);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Handles an incoming event.
        /// </summary>
        /// <param name="stateEngineEvent">Event that is received.</param>
        /// <param name="transition">Perform a transaction by assigning a transition object to this ref parameter.</param>
        protected sealed override void HandleEvent(StateEngineEvent stateEngineEvent, ref Transition transition)
        {
            BeforeHandleEvent(stateEngineEvent, ref transition);

            if (!stateEngineEvent.Handled)
                stateMachine.HandleEvent(stateEngineEvent, ref transition);

            if (!stateEngineEvent.Handled)
                UnhandledEvent(stateEngineEvent, ref transition);
        }

        /// <summary>
        /// Method that can be overriden in descendant classes, so events can be handed before they are sent to the 
        /// state machine.
        /// </summary>
        /// <param name="stateEngineEvent"></param>        
        /// <param name="transition">Perform a transaction by assigning a transition object to this ref parameter.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")]
        public virtual void BeforeHandleEvent(StateEngineEvent stateEngineEvent, ref Transition transition)
        {
        }



        /// <summary>
        /// Method that can be overriden if wanting to handle events that not has been handled in the composite state machine.
        /// </summary>
        /// <param name="stateEngineEvent"></param>
        /// <param name="transition">Perform a transaction by assigning a transition object to this ref parameter.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#")]
        public virtual void UnhandledEvent(StateEngineEvent stateEngineEvent, ref Transition transition)
        {
        }

        #endregion

        #region Internal Methods
        internal override void PerformEnter(StateEntry entry, ref Transition transition)
        {
            base.PerformEnter(entry, ref transition);

            if (!stateMachine.Initialized)
                this.Initialize();

            //If base did not result in any transition, 
            if (transition == null)
            {
                stateMachine.EnterState(entry, ref transition);
                if (transition != null)
                    stateMachine.HandleTransition(ref transition);
            }

        }

        internal override void PerformExit()
        {
            base.PerformExit();

            stateMachine.ExitState();
        }

        /// <summary>
        /// Initializes and creates the states of the composite state engine.
        /// </summary>
        internal void Initialize()
        {
            if (!stateMachine.Initialized)
                stateMachine.Initialize();
        }


        /// <summary>
        /// This function is used to locate a specific state in the state machine.
        /// 
        /// The composite state checks first if its own FactoryName matches the name, 
        /// if not it calls each created state subsequently.
        /// </summary>
        /// <param name="factoryName">The name of the state that should be looked up.</param>
        /// <returns>True if the state is or contains a state with this name.</returns>
        internal override bool LookupState(string factoryName)
        {
            if (base.LookupState(factoryName))
                return true;

            foreach (State state in StateMachine.CreatedStates)
            {
                if (state.LookupState(factoryName))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Internal dispose method.
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            try
            {
                if (!disposed)
                {
                    disposed = true;
                    if (disposing)
                        stateMachine.Dispose();
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Returns the state machine that is used with the Composite state.
        /// </summary>
        public StateMachine StateMachine
        {
            get { return stateMachine; }
        }

        /// <summary>
        /// Set the parent state machine for the state.
        /// </summary>
        internal override void SetParentStateMachine(StateMachine parentStateMachine)
        {
            base.SetParentStateMachine(parentStateMachine);
            this.stateMachine.ParentStateMachine = parentStateMachine;
        }

        /// <summary>
        /// Set the debug logger for the composite state, and assign it on to the state machine.
        /// </summary>
        /// <param name="debugLogger"></param>
        /// <param name="logCategory"></param>
        internal override void SetDebugLogger(IDebugLogger debugLogger, object logCategory)
        {
            base.SetDebugLogger(debugLogger, logCategory);
            this.stateMachine.SetDebugLogger(debugLogger, logCategory);
        }

        public override string Name
        {
            get { return GetType().Namespace.Split('.').LastOrDefault() ?? base.Name; }
        }

        #endregion
    }
}