package com.codingame.game;

import com.codingame.gameengine.core.MultiplayerGameManager;
import com.codingame.view.View;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import java.util.*;

@Singleton
public class Game {
    @Inject private MultiplayerGameManager<Player> gameManager;
    @Inject private View view;
    public static int CARD_COUNT=104;
    private static int CARD_PER_PLAYER=10;
    private static int MAX_CARDS_PER_LINE=6;
    private static int LINE_COUNT=4;
    private List<Card> cards;
    private List<Card>[] lines = new ArrayList[LINE_COUNT];
    private Random random;
    private int nextCardIndex=0;
    private int round=0;

    public static String getExpected(boolean pickPhase) {
        if (pickPhase) {
            return "PICK <lineId> | RANDOM";
        } else {
            return "PLAY <cardId> | RANDOM";
        }
    }

    public List<Card> getCards() {
        return cards;
    }
    public List<Card>[] getLines() { return lines; }

    public void init(Random secureRandom) {
        createCards();
        random = secureRandom;
        shuffleCards();
        //Deal cards to players
        for (Player player : gameManager.getActivePlayers()) {
            for (int i=0;i<CARD_PER_PLAYER;++i) {
                player.addCard(getOneRandomCard());
            }
        }
        //init lines
        for (int i=0;i<LINE_COUNT;++i) {
            lines[i] = new ArrayList<>();
            lines[i].add(getOneRandomCard());
        }
        view.init();
        view.initTooltips(this);
    }

    private void shuffleCards() {
        Collections.shuffle(cards);
        nextCardIndex = 0;
    }

    private void createCards() {
        cards = new ArrayList<>();
        for (int i=0;i<CARD_COUNT;++i) {
            cards.add(new Card(i+1));
        }
    }

     private Card getOneRandomCard() {
        Card card = cards.get(nextCardIndex++);
        return card;
    }

    public List<String> getInputDataForOneTurn() {
        StringBuilder sb;
        List<String> data = new ArrayList<>();
        for(List<Card> line : lines) {
            data.add(Integer.toString(line.size()));
            sb = new StringBuilder();
            for (Card card : line) {
                sb.append(card.getValue());
                sb.append(" ");
            }
            data.add(sb.toString());
        }

        //scores
        sb = new StringBuilder();
        for (Player player : gameManager.getPlayers()) {
            sb.append(player.getScore());
            sb.append(" ");
        }
        data.add(sb.toString());

        return data;
    }

    public boolean playCard(Player player, Card card) {
        player.removeCard(card);
        //find right pile
        int lastPileCardValue[] = new int[LINE_COUNT];
        for(int i=0;i<LINE_COUNT;i++) {
            lastPileCardValue[i] = lines[i].get(lines[i].size()-1).getValue();
        }
        int rightPile=-1;
        int maxValue=-1;
        for (int i=0;i<LINE_COUNT;++i) {
            if (lastPileCardValue[i] < card.getValue()
            && (rightPile<0 || lastPileCardValue[rightPile] < lastPileCardValue[i])) {
                rightPile = i;
            }
            if (lastPileCardValue[i]>maxValue) {
                maxValue = lastPileCardValue[i];
            }
        }
        if (rightPile>=0) {
            //play in pile
            view.playCard(card.getValue(), 0);
            if (lines[rightPile].size() + 1 >= MAX_CARDS_PER_LINE) {
                //get all cards because line is full
                int oldScore = player.getScore();
                view.playerGetsAllCards(lines[rightPile], player);
                player.getAllCowCards(lines[rightPile], card);
                view.updateScore(player);
                gameManager.addToGameSummary(String.format(
                        "%s got %d cows! They put the 6th card in the row.",
                        player.getNicknameToken(),
                        oldScore - player.getScore()
                        )
                );
                gameManager.addTooltip(player, String.format("%s completed a line", player.getNicknameToken()));
            } else {
                lines[rightPile].add(card);
            }
        }
        else {
            return false;
            //no good pile. We'll ask the player which one they want
        }
        return true;
    }

    public void performGameOver() {
        // one player deactivated
        List<Player> activePlayers = gameManager.getActivePlayers();
        if (activePlayers.size() <= 1) {
            if (activePlayers.size() == 1) {
                gameManager.addToGameSummary(
                        String.format(
                                "Only %s is still playing!",
                                activePlayers.get(0).getNicknameToken()
                        )
                );
            } else {
                gameManager.addToGameSummary("No player remaining!");
            }
            return;
        }
    }

    public boolean isGameOver() {
        // only one player is still activate
        List<Player> activePlayers = gameManager.getActivePlayers();
        if (activePlayers.size() <= 1) {
            return true;
        }

        int cardsLeft = 0;
        for (Player activePlayer : activePlayers) {
            cardsLeft += activePlayer.getCardsCount();
        }
        if (cardsLeft == 0) {
            if (round<4) {
                //reinit new round
                nextRound();
            }
            else {
                return true; //end of the game
            }
        }
        return false;
    }

    private void nextRound() {
        shuffleCards();
        //Deal cards to players
        for (Player player : gameManager.getActivePlayers()) {
            for (int i=0;i<CARD_PER_PLAYER;++i) {
                player.addCard(getOneRandomCard());
            }
        }
        //init lines
        for (int i=0;i<LINE_COUNT;++i) {
            lines[i] = new ArrayList<>();
            lines[i].add(getOneRandomCard());
        }
        round++;
        view.hideCards(1);
        view.changeBackground(round);
    }

    public void playCardAndGetLine(Player player, Card card, int getLine) {
        view.playCard(card.getValue(), 0);
        int oldScore=player.getScore();
        view.playerGetsAllCards(lines[getLine], player);
        player.getAllCowCards(lines[getLine], card);
        view.updateScore(player);
        gameManager.addToGameSummary(String.format(
                "%s got %d cows! his card was less than all the lines.",
                player.getNicknameToken(),
                oldScore - player.getScore()
                )
        );
        gameManager.addTooltip(player, String.format("%s took an incomplete line", player.getNicknameToken()));
    }
}
