package it.polimi.ingsw.gc14.Model;

import it.polimi.ingsw.gc14.Model.Cards.Building.EffectType;
import it.polimi.ingsw.gc14.Model.Cards.BuildingCard;
import it.polimi.ingsw.gc14.Model.Cards.TribeCard;
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.Character;
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.EventCard;
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.EventType;
import it.polimi.ingsw.gc14.Model.GamePackage.Board;
import it.polimi.ingsw.gc14.Model.GamePackage.CurrentState;
import it.polimi.ingsw.gc14.Model.Orders.Order2;
import it.polimi.ingsw.gc14.Model.Orders.Order3;
import it.polimi.ingsw.gc14.Model.Orders.Order4;
import it.polimi.ingsw.gc14.Model.Orders.Order5;
import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;

import java.util.*;
import java.util.stream.Collectors;

public class Game {

    private ArrayList<Player> playersList;
    private CurrentState currentState;
    private HashMap<Slot,Player> slotMap;
    private int nPlayers;
    private Queue<Player> OptionalCardQueue;
    private OrderLogicCard orderLogicCard;
    private Board board;

    public Player getPlayerByIndex(int playerIndex) throws IndexOutOfBoundsException {
        if(playerIndex >= playersList.size())
            throw new IndexOutOfBoundsException("Player index out of bounds");
        return playersList.get(playerIndex);
    }
    /*
    public TribeCard getUpperTribeCardByIndex(int playerIndex) throws IndexOutOfBoundsException
    {
    }
    public TribeCard getLowerTribeCardByIndex()
    {

    }
    public BuildingCard getUpperBuildingCardByIndex(int playerIndex) throws IndexOutOfBoundsException
    {

    }
    public BuildingCard getLowerBuildingCardByIndex(int playerIndex) throws IndexOutOfBoundsException
    {

    }
     */

    public int getNPlayers() {
        return nPlayers;
    }
    public Game(int nPlayers) {
        this.nPlayers = nPlayers;
        board=new Board(nPlayers);
        slotMap = new LinkedHashMap<>();
        for(Slot s :board.getSlotList())
        {
            slotMap.put(s,null);
        }
        currentState= new CurrentState();
        playersList = new ArrayList<>();
        OptionalCardQueue = new LinkedList<>();
    }

    public boolean addPlayer(Player player) {
        if(currentState.getGameStage()!= GameStages.WAITING)
            return false;
        playersList.add(player);

        if(playersList.size()>=nPlayers)
        {
            init();
            return true;
        }
        return false;
    }
    public void init() {

        switch (nPlayers) {
            case 2:
                orderLogicCard=new Order2(playersList);
                break;
            case 3:
                orderLogicCard=new Order3(playersList);
                break;
            case 4:
                orderLogicCard=new Order4(playersList);
                break;
            case 5:
                orderLogicCard=new Order5(playersList);
                break;
        }
        currentState.PlayerUpdate(orderLogicCard.pull(),null);
        currentState.GameStageUpdate(GameStages.SLOT_CHOICE);
    }

    //region Cotroller Methods
    public boolean SlotChoice(Player player, Slot slot) {
        if(currentState.getGameStage()!= GameStages.SLOT_CHOICE)
        {
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer()))
        {
            return false;
        }

        if(slotMap.get(slot)!=null)
        {
            return false;
        }
        if(!board.getSlotList().contains(slot))
        {
            return false;
        }
        slotMap.put(slot,player);
        nextPlayerSetup();
        return true;

    }

    //region Drawing Methods
    public boolean DrawUpperTribeCard(Player player,TribeCard tribeCard) {
        if(currentState.getGameStage()!= GameStages.RESOLVING_ACTIONS)
        {
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer()))
        {
            return false;
        }
        if(!board.containsUpperTribeCard(tribeCard))
            return false;
        if(currentState.getNUpper() <1)
            return false;
        if(!tribeCard.IsEventCard())
            return false;

        Character tempCard = (Character) tribeCard;
        tempCard.insert(player);
        board.removeUpperTribeCard(tempCard);
        currentState.UpperDrawn();
        if(currentState.getNUpper() ==0 && currentState.getNLower() ==0)
            nextPlayerSetup();
        return true;

    }
    public boolean DrawLowerTribeCard(Player player,TribeCard tribeCard) {
        if(currentState.getGameStage()!= GameStages.RESOLVING_ACTIONS)
        {
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer()))
        {
            return false;
        }
        if(!board.containsLowerTribeCard(tribeCard))
            return false;
        if(currentState.getNLower() <1)
            return false;
        if(!tribeCard.IsEventCard())
            return false;

        Character tempCard = (Character) tribeCard;
        tempCard.insert(player);
        board.removeLowerTribeCard(tempCard);
        currentState.LowerDrawn();
        if(currentState.getNLower() ==0 && currentState.getNUpper() ==0)
            nextPlayerSetup();
        return true;

    }

    public boolean DrawUpperBuildingCard(Player player,BuildingCard buildingCard) {
        if(currentState.getGameStage()!= GameStages.RESOLVING_ACTIONS)
        {
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer()))
        {
            return false;
        }
        if(!board.containsUpperBuildingCard(buildingCard))
        {
            return false;
        }
        if(currentState.getNUpper() <1)
            return false;
        if(buildingCard.buy(player))
        {
            currentState.UpperDrawn();
            board.removeUpperBuildingCard(buildingCard);
        }
        else
            return false;
        if(currentState.getNLower() ==0 && currentState.getNUpper() ==0)
            nextPlayerSetup();

        return true;
    }
    public boolean DrawLowerBuildingCard(Player player,BuildingCard buildingCard) {
        if(currentState.getGameStage()!= GameStages.RESOLVING_ACTIONS)
        {
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer()))
        {
            return false;
        }
        if(!board.containsLowerBuildingCard(buildingCard))
        {
            return false;
        }
        if(currentState.getNLower() <1)
            return false;
        if(buildingCard.buy(player))
        {
            currentState.LowerDrawn();
            board.removeLowerBuildingCard(buildingCard);
        }
        else
            return false;
        if(currentState.getNLower() ==0 && currentState.getNUpper() ==0)
            nextPlayerSetup();

        return true;

    }

    //endregion

    //region Optional Card Methods
    // TODO fare mappa per contare più volte lo stesso player
    public boolean PickOptionalTribeCard(Player player,TribeCard tribeCard) {
        if(currentState.getGameStage() != GameStages.OPTIONAL_CARD_EFFECT){
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer())){
            return false;
        }
        if(!board.containsUpperTribeCard(tribeCard)){
            return false;
        }
        if(tribeCard.IsEventCard()){
            return false;
        }
        Character tempCard = (Character) tribeCard;
        tempCard.insert(player);
        board.removeUpperTribeCard(tempCard);
        nextPlayerSetup();

        return true;
    }
    public boolean PickOptionalBuildingCard(Player player, BuildingCard buildingCard) {
        if(currentState.getGameStage() != GameStages.OPTIONAL_CARD_EFFECT){
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer())){
            return false;
        }
        if(!board.containsUpperBuildingCard(buildingCard)){
            return false;
        }
        if(buildingCard.buy(player)) {
            board.removeUpperBuildingCard(buildingCard);
        }
        else {
            return false;
        }
        nextPlayerSetup();

        return true;
    }

    public boolean NoOptionalCard(Player player) {
        if(currentState.getGameStage() != GameStages.OPTIONAL_CARD_EFFECT){
            return false;
        }
        if(!player.equals(currentState.getCurrentPlayer())){
            return false;
        }
        return true;
    }
    //endregion

    //endregion

    private void nextPlayerSetup() {
        if(GameStages.SLOT_CHOICE==currentState.getGameStage()) {
            currentState.PlayerUpdate(orderLogicCard.pull(), null);
            if(currentState.getCurrentPlayer()==null)
            {
                currentState.GameStageUpdate(GameStages.RESOLVING_ACTIONS);
            }
        }
        if(GameStages.RESOLVING_ACTIONS==currentState.getGameStage()) {
            orderLogicCard.push(currentState.getCurrentPlayer());
            slotMap.put(currentState.getSlot(), null);
            for (Slot s : board.getSlotList()) {
                if (slotMap.get(s) != null) {
                    currentState.PlayerUpdate(slotMap.get(s), s);
                }
            }
            if(slotMap.values().stream().allMatch(v -> v == null))
            {
                currentState.GameStageUpdate(GameStages.OPTIONAL_CARD_EFFECT);
                OptionalCardQueue=(playersList.stream().filter(x->x.buildingCards.stream().anyMatch(y->y.getEffectId()==12)).collect(Collectors.toCollection(LinkedList::new)));
                currentState.PlayerUpdate(OptionalCardQueue.remove(), null);
                if(currentState.getCurrentPlayer()==null)
                {
                    currentState.GameStageUpdate(GameStages.RESOLVING_EVENT);
                    if(currentState.getRound()<10)
                    {
                        nextRound();
                    }
                    else
                    {
                        EventResolution();
                        currentState.GameStageUpdate(GameStages.ENDING);
                        endGame();
                    }

                    return;
                }
            }
        }
        if(GameStages.OPTIONAL_CARD_EFFECT==currentState.getGameStage()) {
            currentState.PlayerUpdate(OptionalCardQueue.remove(), null);
            if(currentState.getCurrentPlayer()==null)
            {
                currentState.GameStageUpdate(GameStages.RESOLVING_EVENT);
            }
        }
    }


    private void EventResolution() {

        if(currentState.getGameStage()!= GameStages.RESOLVING_EVENT)
        {
            return;
        }
        Queue<EventCard> events;
        events=board.getPendingEvents();
        ArrayList<EventCard>sustenance=events.stream().filter(x->x.getType().equals(EventType.SUSTENANCE)).collect(Collectors.toCollection(ArrayList::new));
        events.removeAll(sustenance);
        for(EventCard event:events)
        {
            event.activateEvent(playersList);
        }

        for(EventCard e : sustenance)
        {
            e.activateEvent(playersList);
        }

    }
    private void nextRound() {

        EventResolution();
        if(currentState.getRound()==10)
        {
            currentState.GameStageUpdate(GameStages.ENDING);
            endGame();
        }
        if(currentState.getEra()!= board.nextRound())
            currentState.EraUpdate();
        currentState.RoundUpdate();

    }

    // TODO pulire game ?
    private void endGame() {
        playersList.forEach(
            p -> p.buildingCards.stream().filter(x -> x.getEffectType() == EffectType.FINAL).
            forEach(x -> x.applyEffect(p))
        );
        currentState.GameStageUpdate(GameStages.ENDED);
    }









}
