package com.codingame.game;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

import com.codingame.gameengine.core.AbstractPlayer.TimeoutException;
import com.codingame.gameengine.core.AbstractReferee;
import com.codingame.gameengine.core.MultiplayerGameManager;
import com.codingame.gameengine.module.entities.GraphicEntityModule;
import com.codingame.gameengine.module.endscreen.EndScreenModule;
import com.codingame.view.View;
import com.google.inject.Inject;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import static java.lang.Math.abs;

public class Referee extends AbstractReferee {
    // Uncomment the line below and comment the line under it to create a Solo Game
    // @Inject private SoloGameManager<Player> gameManager;
    @Inject private MultiplayerGameManager<Player> gameManager;
    @Inject private GraphicEntityModule graphicEntityModule;
    @Inject private Game game;
    @Inject private CommandManager commandManager;
    @Inject private EndScreenModule endScreenModule;
    @Inject private View view;

    private int turnId=0;
    boolean gameOverFrame;
    private List<Integer> lastPlayedCards;
    private List<Card> cardsToPlay = new ArrayList<>();;
    private Random random;

    @Override
    public void init() {
        // Initialize your game here.
        turnId=0;
        gameManager.setMaxTurns(500);
        gameManager.setFirstTurnMaxTime(1000);
        gameManager.setTurnMaxTime(100);
        random = gameManager.getRandom();
        game.init(random);
        lastPlayedCards = new ArrayList<>();
        view.displayCards(game,0, cardsToPlay);
        sendGlobalInfo();
    }
    
    //sends initialization data to players
    private void sendGlobalInfo() {
        for (Player player : gameManager.getActivePlayers()) {
            player.sendInputLine(String.format("%d %d",gameManager.getPlayerCount(), player.getIndex()));
            lastPlayedCards.add(-1);
        }
    }

    @Override
    public void gameTurn(int turn) {
        turnId++;
        if (!gameOverFrame) {
            view.displayCards(game, 0, cardsToPlay);
            if (cardsToPlay.isEmpty()) {
                if (gameManager.getActivePlayers().get(0).getCards().size()==10) {
                    //new round
                    lastPlayedCards.clear();
                    for (Player p : gameManager.getPlayers()) {
                        lastPlayedCards.add(-1);
                    }
                }
                //send input to player
                for (Player player : gameManager.getActivePlayers()) {
                    player.sendInputLine(StringUtils.join(lastPlayedCards, " "));
                    for (String line : game.getInputDataForOneTurn()) {
                        player.sendInputLine(line);
                    }
                    player.sendInputLine(Integer.toString(player.getCardsCount()));
                    player.sendInputLine(player.getCardsInHandData());
                    player.sendInputLine("CHOOSE_CARD_TO_PLAY");
                    player.execute();
                }

                lastPlayedCards.clear();
                //parse output
                for (Player player : gameManager.getActivePlayers()) {
                    try {
                        // Check validity of the player output and compute the new game state
                        commandManager.parseCommands(player, player.getOutputs(), random, false);
                    } catch (TimeoutException e) {
                        player.deactivate(String.format("$%d timeout!", player.getIndex()));
                        player.setScore(-999);
                        view.updateScore(player);
                    }
                }

                //update game
                cardsToPlay = new ArrayList<>();
                for (Player player : gameManager.getPlayers()) {
                    if (player.isActive()) {
                        cardsToPlay.add(player.getCardToPlay());
                        lastPlayedCards.add(player.getCardToPlay().getValue());
                    } else {
                        lastPlayedCards.add(-1);
                    }
                }
                Collections.sort(cardsToPlay);

            } else {
                Card card = cardsToPlay.get(0);
                cardsToPlay.remove(0);

                if (!game.playCard(card.getOwner(), card)) {
                    //ask in which line we should put the card
                    Player player = card.getOwner();
                    player.sendInputLine(StringUtils.join(lastPlayedCards, " "));
                    for (String line : game.getInputDataForOneTurn()) {
                        player.sendInputLine(line);
                    }
                    player.sendInputLine(Integer.toString(player.getCardsCount()));
                    player.sendInputLine(player.getCardsInHandData());
                    player.sendInputLine("CHOOSE_LINE_TO_PICK");
                    player.execute();
                    try {
                        // Check validity of the player output and compute the new game state
                        commandManager.parseCommands(player, player.getOutputs(), random, true);
                        if (player.isActive()) {
                            game.playCardAndGetLine(card.getOwner(), card, player.getGetLine());
                        }
                    } catch (TimeoutException e) {
                        player.deactivate(String.format("$%d timeout!", player.getIndex()));
                        player.setScore(-999);
                        view.updateScore(player);
                    }
                }
            }
            view.displayCards(game,0.99, cardsToPlay);

            //check game over
            if (game.isGameOver()) {
                gameOverFrame = true;
            }
        } else {
            game.performGameOver();
            gameManager.endGame();
        }
    }

    @Override
    public void onEnd() {
        endScreenModule.setScores(
                gameManager.getPlayers()
                        .stream()
                        .mapToInt(player -> player.getScore())
                        .toArray(),

                gameManager.getPlayers()
                        .stream()
                        .map(player -> {
                            if (player.isActive()) {
                                return String.format("%d stinking cows", abs(player.getScore()));
                            } else {
                                return "deactivated";
                            }
                        })
                        .collect(Collectors.toList())
                        .toArray(new String[2])
        );
    }
}
