using QwirkleClassLibrary.Boards;
using QwirkleClassLibrary.Games;
using QwirkleClassLibrary.Players;
using QwirkleClassLibrary.Tiles;
using QwirkleConsoleApp;
using System.Text;
using QwirkleClassLibrary.Persistences;
using static System.Console;


static void AddPlayers(Game game)
{
    NotificationClass nc = new NotificationClass();
    game.PlayerAddNotified += nc.NotificationPlayerAdd;

    List<string> playerstag = [];

    while (game.PlayerList.Count < 4)
    {
        Write("Enter player tag : ");
        string? tag = ReadLine();

        playerstag.Add(tag!);


        Write("Do you want to add another player ? (y/n) : ");
        string? answer = ReadLine();

        if (answer == "n" && game.AddPlayerInGame(playerstag))
        {
            break;
        }
    }
}


static void ShowTiles(Game game)
{
    WriteLine("\n --------------------- YOUR TILES ------------------------");

    var currentPlayer = game.GetPlayingPlayer();

    var stringBuilder = new StringBuilder();

    for (int i = 0; i < currentPlayer.Tiles.Count; i++)
    {
        stringBuilder.Append("[" + (i + 1) + "] ");
        stringBuilder.AppendLine(currentPlayer.Tiles[i].ToString());
    }

    Write(stringBuilder);
}

static void AddTile(Game game)
{

    NotificationClass nc = new NotificationClass();

    game.PlaceTileNotified += nc.NotificationAddTile;

    Tile? tile = null;

    WriteLine("Enter the number of the tile you want to place : ");

    int no = -1;
    int x = -1;
    int y = -1;

    try
    {
        while (no is < 1 or > 6)
        {

            no = Convert.ToInt32(ReadLine());

            if (no is < 1 or > 6)
            {
                ForegroundColor = ConsoleColor.Red;
                WriteLine();
                WriteLine("ERROR : Enter a number between 1 and 6 ! : ");
                ResetColor();
            }
            else
            {
                tile = game.TileOfPlayerWithPos(no - 1);

                Write("Enter the x of the cell: ");
                try
                {
                    x = Convert.ToInt32(ReadLine());
                }
                catch
                {
                    ForegroundColor = ConsoleColor.Red;
                    WriteLine();
                    WriteLine("ERROR : You must type. Please retry : ");
                    ResetColor();
                }
                Write("Enter the y of the cell: ");
                try
                {
                    y = Convert.ToInt32(ReadLine());
                }
                catch
                {
                    ForegroundColor = ConsoleColor.Red;
                    WriteLine();
                    WriteLine("ERROR : You must type. Please retry : ");
                    ResetColor();
                }
                game.PlaceTile(game.GetPlayingPlayer(), tile, x, y);
                game.PlaceTileNotified -= nc.NotificationAddTile;
            }
        }
    }
    catch
    {
        ForegroundColor = ConsoleColor.Red;
        WriteLine();
        WriteLine("ERROR : You must type. Please retry : ");
        ResetColor();
    }
}

static void SwapTile(Game game)
{
    var nc = new NotificationClass();
    game.SwapTilesNotified += nc.NotificationSwapTile;
    
    var tilesToSwap = new List<Tile>();
    bool continueSwap = true;

    ShowTiles(game);

    while (continueSwap)
    {
        Write("Enter the number of the tile you want to swap : ");
        int no = Convert.ToInt32(ReadLine());

        if (no is < 1 or > 6)
        {
            ForegroundColor = ConsoleColor.Red;
            WriteLine();
            WriteLine("ERROR : Enter a number between 1 and 6 !");
            ResetColor();
        }
        else
        {
            tilesToSwap.Add(game.TileOfPlayerWithPos(no - 1));
            game.PlayerList[game.GetPlayingPlayerPosition()].RemoveTileToPlayer(game.TileOfPlayerWithPos(no - 1));
        }

        Write("Do you want to swap another tile ? (y/n) : ");
        string? answer = ReadLine();

        if (answer == "n")
        {
            continueSwap = false;
        }
    }

    game.SwapTiles(game.GetPlayingPlayer(), tilesToSwap);
    game.SwapTilesNotified -= nc.NotificationSwapTile;
}

static void MenuSwitch(Game game)
{
    int enter = 0;

    while (enter != 3 && game.GameRunning)
    {
        ShowBoard(game);

        ShowTiles(game);

        ShowScoreBoard(game);

        WriteLine("\n --------------------- CHOICES ------------------------");

        WriteLine("[1] Place your tiles");
        WriteLine("[2] Swap your tiles");
        WriteLine("[3] End your turn / Skip your turn");
        WriteLine("[4] Save the game");
        Write("Enter your choice : ");

        try
        {
            enter = Convert.ToInt32(ReadLine());
        }
        catch
        {
            ForegroundColor = ConsoleColor.Red;
            WriteLine();
            WriteLine("ERROR : You must type (1 / 2 / 3). Please retry : ");
            ResetColor();
        }


        switch (enter)
        {
            case 1:
                AddTile(game);
                break;
            case 2:
                SwapTile(game);
                enter = 3;
                break;
            case 3:
                WriteLine("Your score on this turn : " + game.GetPlayerScore(game.GetPlayingPlayer(), game.CellsUsed, game.GetBoard()!));
                game.EmptyCellUsed();
                game.DrawTiles(game.GetPlayingPlayer());
                game.CheckGameOver(game.GetPlayingPlayer());

                IGamePersistence gameSave = new GamePersistenceXml();
                gameSave.SaveGame(game);
                
                return;
            case 4:
                IGamePersistence endGameSave = new GamePersistenceXml();
                endGameSave.SaveGame(game);
                
                game.GameRunning = false;
                break;
        }
    }
}

static void ShowBoard(Game game)
{
    Board board = game.GetBoard()!;

    for (int i = 0; i < board.Rows; i++)
    {
        for (int y = 0; y < board.Columns; y++)
        {
            Cell? cell = board.GetCell(y, i);
            if (cell != null && !cell.IsFree)
            {
                Tile? tile = cell.Tile;
                if (tile != null)
                {
                    Write("| " + tile.GetShape.ToString()[0] + tile.GetShape.ToString()[1] +
                                  tile.GetColor.ToString()[0] + " |");
                }
            }
            else
            {
                Write("|     |");
            }
        }

        WriteLine();
    }
}

static void ShowScoreBoard(Game g)
{
    WriteLine(" --------------------- THE SCORE BOARD : ---------------------");

    int i = 0;

    var sb = g.ScoreBoard.OrderByDescending(x => x.Value).ThenBy(x => x.Key);

    foreach (KeyValuePair<string, int> pair in sb)
    {
        i++;
        WriteLine("[" + i + "] " + pair.Key + " with " + pair.Value.ToString() + " points.");
    }
}

static void ShowLeaderboard(Leaderboard leaderboard)
{
    WriteLine(" --------------------- THE LEADERBOARD : ---------------------");
    WriteLine("Position : | PlayerTag : | Last Date : | Points : | Victories :");

    for (int i=0; i<leaderboard.Lb.Count; i++)
    {
        WriteLine("[" + (i+1) + "] " + leaderboard.Lb[i].ToString());
    }

    WriteLine();
    WriteLine();
}

static void GameLoop(Game game)
{
    NotificationClass nc = new NotificationClass();

    do
    {
        game.NextPlayerNotified += nc.NotificationNextPlayer;
        game.EndOfGameNotified += nc.NotificationEndOfGame;

        game.SetNextPlayer();

        WriteLine(" --------------------- GAME ! ------------------------");

        MenuSwitch(game);

        game.NextPlayerNotified -= nc.NotificationNextPlayer;
        game.EndOfGameNotified -= nc.NotificationEndOfGame;

    } while (game.GameRunning);
}

static void MainMenu(Game game)
{
    game.GiveTilesToPlayers();

    ForegroundColor = ConsoleColor.Green;
    WriteLine("Game is starting !");
    ResetColor();

    NotificationClass nc = new NotificationClass();

    do
    {
        game.NextPlayerNotified += nc.NotificationNextPlayer;
        game.EndOfGameNotified += nc.NotificationEndOfGame;

        game.SetNextPlayer();

        WriteLine(" --------------------- GAME ! ------------------------");

        MenuSwitch(game);

        game.NextPlayerNotified -= nc.NotificationNextPlayer;
        game.EndOfGameNotified -= nc.NotificationEndOfGame;

    } while (game.GameRunning);

    WriteLine(" --------------------- END OF GAME ! ------------------------");
    WriteLine(" --------------------- THE BOARD : --------------------------");
    ShowBoard(game);
    ShowScoreBoard(game);
}

static void MainMenuContinue(Game game)
{
    ForegroundColor = ConsoleColor.Green;
    WriteLine("Game loaded ! You can continue the game !");
    ResetColor();

    NotificationClass nc = new NotificationClass();

    WriteLine(" --------------------- GAME ! ------------------------");

    MenuSwitch(game);

    GameLoop(game);
}

static void MainGame()
{
    Directory.SetCurrentDirectory(Path.Combine(Directory.GetCurrentDirectory(), "..\\..\\..\\..\\Files"));
    
    Leaderboard leaderboard = new Leaderboard();
    
    ForegroundColor = ConsoleColor.DarkGray;
    WriteLine(" ===================== WELCOME TO QWIRKLE ! =====================");
    ResetColor();

    int enter = 0;

    while (enter != 3)
    {
        ForegroundColor = ConsoleColor.DarkCyan;
        WriteLine("[1] Create game");
        WriteLine("[2] Load game");
        WriteLine("[3] Show leaderboard");
        WriteLine("[4] Exit");
        WriteLine();
        ResetColor();
        WriteLine("Enter your choice : ");

        try
        {
            enter = Convert.ToInt32(ReadLine());
        }
        catch
        {
            ForegroundColor = ConsoleColor.Red;
            WriteLine();
            WriteLine("ERROR : You must type (1 / 2 / 3). Please retry : ");
            ResetColor();
        }


        switch (enter)
        {
            case 1:
                ForegroundColor = ConsoleColor.DarkYellow;
                WriteLine("Enter minimun 2 player and max 4 player !\n");
                ResetColor();
                
                Game game = new Game();
                AddPlayers(game);
                
                game.StartGame();
                MainMenu(game);

                if (!game.GameRunning)
                {
                    leaderboard.AddScoreInLead(game.ScoreBoard);
                    break;
                }
                
                ILeaderboardPersistence leaderboardSave = new LeaderboardPersistenceJson();
                leaderboardSave.SaveLeaderboard(leaderboard);
                
                break;
            
            case 2:
                Game loadedGame;
                
                IGamePersistence gameLoad = new GamePersistenceXml();

                try
                {
                    loadedGame = gameLoad.LoadGame();
                }
                catch
                {
                    ForegroundColor = ConsoleColor.Yellow;
                    WriteLine("WARNING : No game saved ! Creating a new game !");
                    ResetColor();
                    loadedGame = new Game();
                }

                ILeaderboardPersistence leaderboardLoad = new LeaderboardPersistenceJson();
                leaderboard = leaderboardLoad.LoadLeaderboard();

                if (loadedGame.GameRunning)
                {
                    MainMenuContinue(loadedGame);
                }
                else
                {
                    loadedGame = new Game();
                    ForegroundColor = ConsoleColor.Yellow;
                    WriteLine("WARNING : The previous saved game was over ! Creating a new game !");
                    ResetColor();
                    AddPlayers(loadedGame);
                    loadedGame.StartGame();
                    MainMenu(loadedGame);
                }

                if (!loadedGame.GameRunning)
                {
                    leaderboard.AddScoreInLead(loadedGame.ScoreBoard);
                }

                ILeaderboardPersistence leaderboardSave2 = new LeaderboardPersistenceJson();
                leaderboardSave2.SaveLeaderboard(leaderboard);
                
                break;
            
            case 3:
                ILeaderboardPersistence leaderboardLoad2 = new LeaderboardPersistenceJson();
                leaderboard = leaderboardLoad2.LoadLeaderboard();
                ShowLeaderboard(leaderboard);
                break;
            
            case 4:
                return;
        }

    }
}

MainGame();