﻿using AsmodeeDigital.Common.Plugin.Manager.Random;
using AsmodeeDigital.Common.Plugin.Utils;
using AsmodeeDigital.Common.Plugin.Utils.Extensions;
using AsmodeeDigital.PlayReal.Plugin.Domain.GameState;
using AsmodeeDigital.PlayReal.Plugin.Logic.Engine;
using AsmodeeDigital.PlayReal.Samples.Domain.GameState.Events;
using System;
using System.Collections.Generic;

namespace AsmodeeDigital.PlayReal.Samples.Logic.Engine
{
    /// <summary>
    /// Game engine. Interprets the events of the game state to retrieve results
    /// </summary>
    public class GameEngine : IGameEngine
    {
        public int MaxScore = 3;

        /// <summary>
        /// Interprets the events to retrieve results according to the type of evaluation requested
        /// </summary>
        /// <typeparam name="T">Type of the result</typeparam>
        /// <param name="gameState">Game state which contains all events</param>
        /// <param name="playerLocalId">Player local ID whose result we wish</param>
        /// <param name="evaluation">Type of evaluation requested</param>
        /// <returns></returns>
        public T ComputeEvents<T>(GameStateBase gameState, int playerLocalId, Evaluation evaluation)
        {
            if (gameState == null || gameState.Events == null || gameState.Events.Count == 0)
                return default(T);

            int countPlayers = 0;
            int[] Scores = null;
            int[] DiceSum = null;
            int[][] DiceResults = new int[3][] { new int[3], new int[3], new int[3] };

            foreach (IEvent eventGame in gameState.Events)
            {
                GameInitialized gameInitialized = eventGame as GameInitialized;
                DiceRolled diceRolled = eventGame as DiceRolled;
                RoundEnded roundEnded = eventGame as RoundEnded;

                //---> Initialized seed and count players
                if (gameInitialized != null)
                {
                    countPlayers = gameInitialized.CountPlayers;
                    Scores = new int[countPlayers];
                    DiceSum = new int[countPlayers];

                    RandomManager.SetSeed(gameInitialized.Seed);
                }
                //---> Roll selected dice
                else if (diceRolled != null)
                {
                    foreach (int index in diceRolled.DiceIndicesRolled)
                    {
                        DiceResults[eventGame.PlayerLocalID - 1][index] = RandomManager.GetValue(1, 7);
                    }

                    DiceSum[eventGame.PlayerLocalID - 1] = DiceResults[eventGame.PlayerLocalID - 1].ToList().Sum();
                }
                //--- When round ended : compute score
                else if (roundEnded != null)
                {
                    int? bestSum = DiceSum.ToList().Max();

                    for (int i = 0; i < countPlayers; i++)
                    {
                        if (DiceSum[i] == bestSum)
                        {
                            Scores[i]++;
                        }
                    }
                }
            }

            object value = null;

            switch (evaluation)
            {
                case Evaluation.GetScore:
                    value = Scores[playerLocalId - 1];

                    break;
                case Evaluation.GetLastDiceSum:
                    value = DiceSum[playerLocalId - 1];


                    break;
                case Evaluation.GestLastDiceValues:
                    value = DiceResults[playerLocalId - 1].ToList();

                    break;
                case Evaluation.GetWinnerLastRound:

                    value = new List<int>();

                    int? bestSum = DiceSum.ToList().Max();

                    for (int i = 0; i < countPlayers; i++)
                    {
                        if (DiceSum[i] == bestSum)
                        {
                            ((List<int>)value).Add(i + 1);
                        }
                    }

                    break;
                case Evaluation.GetOrderedWinnerGame:

                    if (Scores.ToList().Exists(s => s >= MaxScore))
                    {
                        value = new List<int>();

                        List<Tuple<int, int>> PlayerScores = new List<Tuple<int, int>>();
                        for (int i = 0; i < countPlayers; i++)
                        {
                            PlayerScores.Add(new Tuple<int, int>(i + 1, Scores[i]));
                        }

                        PlayerScores.Sort((ps1, ps2) => ps1.Item2.CompareTo(ps2.Item2));
                        PlayerScores.Reverse();

                        value = PlayerScores.ConvertAll<int>(ps => ps.Item1);
                    }

                    break;
                default:

                    value = default(T);
                    break;
            }

            return (T)Convert.ChangeType(value, typeof(T));
        }
    }

    /// <summary>
    /// Type of evaluation the Game engine can compute
    /// </summary>
    public enum Evaluation
    {
        /// <summary>
        /// Get the score of a player
        /// </summary>
        GetScore,

        /// <summary>
        /// Get the array of last dice values of a player
        /// </summary>
        GestLastDiceValues,

        /// <summary>
        /// Get the last sum of a player
        /// </summary>
        GetLastDiceSum,

        /// <summary>
        /// Get the local player ID of all winners of the last round. Empty if no winners (if first round is not completed)
        /// </summary>
        GetWinnerLastRound,

        /// <summary>
        /// Get the local player ID of all winners of the game. Empty if no winners
        /// </summary>
        GetOrderedWinnerGame
    }
}
