using System;
using System.Collections.Generic;
using Unity.VisualScripting;

namespace NBC
{
    public class Fsm<TOwnerClass>
    {
        private FsmBaseState<TOwnerClass> _currentState;

        private Dictionary<uint, FsmBaseState<TOwnerClass>> _states = new Dictionary<uint, FsmBaseState<TOwnerClass>>();

        private Dictionary<Type, uint> _stateType2Id = new Dictionary<Type, uint>();

        private bool _doDebugOutput;

        private TOwnerClass _owner;

        private string _description;


        public uint CurrentStateId => _currentState.StateId;

        public FsmBaseState<TOwnerClass> CurrentState => _currentState;

        public Fsm(string description, TOwnerClass owner, bool debugOutput = false)
        {
            _description = description;
            _owner = owner;
            _doDebugOutput = debugOutput;
        }

        public void RegisterState<T>() where T : FsmBaseState<TOwnerClass>, new()
        {
            var type = typeof(T);
            if (_stateType2Id.ContainsKey(type))
            {
                LogError("State {0} already registered", type);
                return;
            }

            T result = new T();
            _stateType2Id[type] = result.StateId;
            _states[result.StateId] = result;
            result.Init(_owner, this);
        }

        public void Start<T>() where T : FsmBaseState<TOwnerClass>, new()
        {
            Log("start");
            EnterState(typeof(T));
        }

        public void EnterState(Type type, FsmBaseState<TOwnerClass> sourceState = null)
        {
            if (_stateType2Id.TryGetValue(type, out var id))
            {
                EnterState(id, sourceState);
            }
        }

        public void EnterState(uint stateId, FsmBaseState<TOwnerClass> sourceState = null)
        {
            uint prevState = 0;
            FsmTransmit param = null;
            if (_currentState != null)
            {
                prevState = _currentState.StateId;
                param = _currentState.Params;
                _currentState.Exit();
            }

            _currentState = _states[stateId];
            _currentState.PrevState = prevState;
            if (param != null)
            {
                _currentState.Params.AddRange(param);
                param.Clear();
            }

            if (sourceState != null)
            {
                _currentState.Transition(sourceState);
            }
            else
            {
                _currentState.Enter();
            }
        }

        public void Update()
        {
            if (_currentState != null)
            {
                _currentState.PreUpdate();
                var type = _currentState.Update();
                if (type != 0)
                {
                    _currentState.NextState = type;
                    EnterState(type);
                }
            }
        }

        public void FixedUpdate()
        {
            if (_currentState != null)
            {
                var type = _currentState.FixedUpdate();
                if (type != 0)
                {
                    _currentState.NextState = type;
                    EnterState(type);
                }
            }
        }

        public void LateUpdate()
        {
            if (_currentState != null)
            {
                var type = _currentState.LateUpdate();
                if (type != 0)
                {
                    _currentState.NextState = type;
                    EnterState(type);
                }
            }
        }


        public void LogError(string textPattern, params object[] args)
        {
        }

        public void Log(string text)
        {
            if (!_doDebugOutput)
            {
            }
        }
    }
}