﻿using AsmodeeDigital.Common.Plugin.Manager.Coroutine;
using AsmodeeDigital.Common.Plugin.Utils;
using AsmodeeDigital.PlayReal.Samples.Logic.Gameplay;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace AsmodeeDigital.PlayReal.Samples.Logic.ArtificialIntelligence
{
    /// <summary>
    /// Artificial intelligence playing in local and distant game modes
    /// </summary>
    public class AI
    {
        /// <summary>
        /// Reference to the gameplay logic, to perform action on UI or create events in the game state
        /// </summary>
        private GameplayLogicBase _gameplayLogic;

        /// <summary>
        /// State machine of the AI
        /// </summary>
        private StateMachine _fsmAI = new StateMachine("AI");

        /// <summary>
        /// State of the AI
        /// </summary>
        public AIStates AIState = AIStates.NotPlaying;

        public AI(GameplayLogicBase gameplayLogic)
        {
            _gameplayLogic = gameplayLogic;

            InitFSMAI();
        }

        /// <summary>
        /// Initialize the AI state machine
        /// </summary>
        private void InitFSMAI()
        {
            //--- Create the state with action when entering a state
            ActionState aiNotPlaying = _fsmAI.AddActionState("AINotPlaying");
            ActionState aiStartPlaying = _fsmAI.AddActionState("AIStartPlaying", AIStartPlaying_OnEnter);
            ActionState aiRollDices = _fsmAI.AddActionState("AIRollDices", AIRollDices_OnEnter);
            ActionState aiAnalyzeDices = _fsmAI.AddActionState("AIAnalyzeDices", AIAnalyzeDices_OnEnter);
            //---

            //--- Create the transitions between actions with conditions
            _fsmAI.AddTransition(aiNotPlaying, aiStartPlaying, () => AIState == AIStates.StartPlaying);

            _fsmAI.AddTransition(aiStartPlaying, aiRollDices, () => AIState == AIStates.RollDices);
            _fsmAI.AddTransition(aiStartPlaying, aiAnalyzeDices, () => AIState == AIStates.AnalyzingDice);

            _fsmAI.AddTransition(aiRollDices, aiAnalyzeDices, () => AIState == AIStates.AnalyzingDice);
            _fsmAI.AddTransition(aiRollDices, aiNotPlaying, () => AIState == AIStates.NotPlaying);

            _fsmAI.AddTransition(aiAnalyzeDices, aiRollDices, () => AIState == AIStates.RollDices);
            _fsmAI.AddTransition(aiAnalyzeDices, aiNotPlaying, () => AIState == AIStates.NotPlaying);
            //---

            //--- Set the current state of the FSM to *AI not playing*
            _fsmAI.CurrentState = aiNotPlaying;
            //---

            _fsmAI.IsDebug = false;
        }

        public void SetState(AIStates aiState)
        {
            AIState = aiState;
            _fsmAI.Update();
        }

        #region Entry points of FSM's actions
        /// <summary>
        /// When AI start playing :
        /// - if dice are not rolled on this turn => Roll dices
        /// - if dices are rolled and can be rolled a second time => Analyze result
        /// - else AI stops playing
        /// </summary>
        private void AIStartPlaying_OnEnter()
        {
            if (_gameplayLogic.IsFirtDiceRoll())
            {
                SetState(AIStates.RollDices);
            }
            else if (_gameplayLogic.CanRollDices())
            {
                SetState(AIStates.AnalyzingDice);
            }
            else
            {
                SetState(AIStates.NotPlaying);
            }
        }

        /// <summary>
        /// Launch dices roll animation in UI
        /// </summary>
        private void AIRollDices_OnEnter()
        {
            CoroutineManager.StartCoroutine(AIRollDicesAsync());
        }

        /// <summary>
        /// Analyze dices values
        /// </summary>
        private void AIAnalyzeDices_OnEnter()
        {
            CoroutineManager.StartCoroutine(AIAnalyzeDicesAsync());
        }
        #endregion

        #region Coroutine of FSMs actions
        /// <summary>
        /// Coroutine that creates *dice rolled* event in the game state, then asks the UI to launch the dice roll animation
        /// After 1.75 seconds, analyze the result
        /// </summary>
        /// <returns></returns>
        private IEnumerator AIRollDicesAsync()
        {
            //--- Roll dices and replace log name of the gameplay logic with the bot name
            string logName = _gameplayLogic.LogName;

            _gameplayLogic.LogName = String.Format("Bot ({0} for {1})", _gameplayLogic.LogName, _gameplayLogic.CurrentPlayer.Name);

            _gameplayLogic.RollSelectedDices();
            _gameplayLogic.RollDicesAnimation();

            _gameplayLogic.LogName = logName;
            //---

            yield return new WaitForSeconds(1.75f);

            SetState(AIStates.AnalyzingDice);

            yield return null;
        }

        /// <summary>
        /// Coroutine that analyzes the results of the dices. If dices can be rerolled and value is below 4 then the dice is rerolled
        /// </summary>
        /// <returns></returns>
        private IEnumerator AIAnalyzeDicesAsync()
        {
            yield return new WaitForSeconds(1f);

            if (_gameplayLogic.CanRollDices())
            {
                //--- Select dice of value <= 3 in hope to obtain better values
                List<int> dicesValues = _gameplayLogic.GetLastDicesValues();

                bool dicesSelected = false;

                for (int i = 0; i < 3; i++)
                {
                    if (dicesValues[i] <= 3)
                    {
                        dicesSelected = true;

                        if (!_gameplayLogic.Dices[i].Selected)
                        {
                            _gameplayLogic.TryChangeDiceSelection(i, true);
                            yield return new WaitForSeconds(0.5f);
                        }
                    }
                    else if (dicesValues[i] > 3 && _gameplayLogic.Dices[i].Selected)
                    {
                        _gameplayLogic.TryChangeDiceSelection(i, false);
                        yield return new WaitForSeconds(0.5f);
                    }
                }

                //---

                yield return new WaitForSeconds(1f);

                if (dicesSelected)
                {
                    SetState(AIStates.RollDices);
                }
                else
                {
                    SetState(AIStates.NotPlaying);
                    _gameplayLogic.EndOfTurn();
                }
            }
            else
            {
                yield return new WaitForSeconds(1f);

                SetState(AIStates.NotPlaying);
                _gameplayLogic.EndOfTurn();
            }

            yield return null;
        }
        #endregion
    }

    public enum AIStates
    {
        NotPlaying,
        StartPlaying,
        RollDices,
        WaitingRollingDices,
        AnalyzingDice
    }
}