using System;
using System.Collections.Generic;
using System.Linq;

namespace Wayne.Lib.StateEngine
{
    public static class StateTransitionLookupAnalyze
    {
        #region Method: GetNextStateName

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stateMachine"></param>
        /// <param name="sourceStateFactoryName"></param>
        /// <param name="transitionType"></param>
        /// <param name="historyType"></param>
        /// <param name="fromAnyState"></param>
        /// <returns></returns>
        public static string GetNextStateName(this StateMachine stateMachine, string sourceStateFactoryName, object transitionType, out HistoryType historyType, out bool fromAnyState)
        {
            if (sourceStateFactoryName != null && transitionType != null)
            {
                // Note: This routine should also work when stateMachine is null
                //       (in this case: only look in the local lookupDictionary).
                foreach (StateMachine tempStateMachine in stateMachine.GetStateMachineHierarchy())
                {
                    StateLookupEntry entry;
                    if (tempStateMachine.StateTransitionLookup.TryLookup(sourceStateFactoryName, transitionType, out entry))
                    {
                        historyType = entry.HistoryType;
                        fromAnyState = false;
                        return entry.NextStateFactoryName;
                    }

                    if (tempStateMachine.StateTransitionLookup.TryLookup(AnyState.FactoryName, transitionType, out entry))
                    {
                        historyType = entry.HistoryType;
                        fromAnyState = true;
                        return entry.NextStateFactoryName;
                    }
                }
            }

            historyType = HistoryType.None;
            fromAnyState = false;
            return "";
        }

        #endregion

        /// <summary>
        /// Returns a list of the source State names that is in the lookup table.
        /// </summary>
        /// <param name="stateTransitionLookup"></param>
        /// <param name="includeAnyStates">Should the AnyStates be included?</param>
        public static string[] GetSourceStateNameList(this StateTransitionLookup stateTransitionLookup, bool includeAnyStates)
        {
            IEnumerable<string> result = stateTransitionLookup.LookupTable.Keys;

            if (!includeAnyStates)
                result = result.Where(x => x != AnyState.FactoryName); // If AnyState is included, remove it.

            return result.ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stateMachine"></param>
        /// <returns></returns>
        public static IEnumerable<StateMachine> GetStateMachineHierarchy(this StateMachine stateMachine)
        {
            StateMachine tempMachine = stateMachine;
            do
            {
                yield return tempMachine;
                tempMachine = tempMachine.ParentStateMachine;
            } while (tempMachine != null);
        }

        /// <summary>
        /// Returns a list of the AnyState-transition names (no duplicates).
        /// </summary>
        /// <returns></returns>
        public static string[] GetAnyStateTransitionNameList(this StateMachine stateMachine)
        {
            return stateMachine.GetStateMachineHierarchy()
                .SelectMany(sm => sm.StateTransitionLookup
                            .GetTransitionsForState(AnyState.FactoryName)
                            .Keys
                            .Select(t => Transition.GetTransitionName(t)))
                .Distinct()
                .ToArray();
        }

        /// <summary>
        /// Returns a dictionary of the transitions from a state (dictionary key) to a state (dictionary value).
        /// </summary>
        /// <param name="stateMachine"></param>
        /// <param name="sourceStateFactoryName"></param>
        /// <param name="includeAnyStates"></param>
        /// <returns></returns>
        public static Dictionary<string, string> GetStateTransitionsDict(this StateMachine stateMachine, string sourceStateFactoryName, bool includeAnyStates)
        {
            return stateMachine.StateTransitionLookup.GetTransitionsForState(sourceStateFactoryName)
                .ToDictionary(x => Transition.GetTransitionName(x.Key), x => x.Value.NextStateFactoryName);
        }

        /// <summary>
        /// Gets a list of all transitions.
        /// </summary>
        /// <param name="stateMachine"></param>
        /// <param name="includeAnyStates">Should the AnyState-transitions be included?</param>
        /// <returns></returns>
        public static TransitionInfo[] GetTransitionInfoArray(this StateMachine stateMachine, bool includeAnyStates)
        {
            return stateMachine.StateTransitionLookup.LookupTable
                .SelectMany(fromStatePair => fromStatePair.Value.Select(statePair =>
                    new TransitionInfo(fromStatePair.Key,
                        Transition.GetTransitionName(statePair.Key),
                        statePair.Value.NextStateFactoryName,
                        statePair.Value.HistoryType)))
                .Where(x => includeAnyStates || x.FromStateFactoryName != AnyState.FactoryName)
                .ToArray();

        }


    }
}