package ch.zhaw.catan;

import java.util.HashMap;
import java.util.Random;

/**
 * This Class manages the game process and contains the Main Method which creates and starts a new Parser and a new Game.
 *
 * @author Leonardo Brandeberger, Roman Schenk
 */
public class Siedler {
    /**
     * The main Method of the game. It creates a Parser Object and calls the Methods foundingPhase (to create a new Game) and gamePhase
     *
     * @param args There are no arguments needed.
     */
    public static void main(String[] args) {
        Parser parser = new Parser();
        SiedlerGame game = foundingPhase(parser);
        gamePhase(parser, game);
    }

    /**
     * The Method for the usual game Process. It contains the Main loop to get new commands from the players and calls the associated methods.
     *
     * @param parser The Parser Object which will interact with the player.
     * @param game   The Game object which is already created and has the founding phase completed.
     */
    private static void gamePhase(Parser parser, SiedlerGame game) {
        boolean running = true;
        boolean diceThrown = false;
        while (running) {
            Config.Faction currentPlayerFaction = game.getCurrentPlayerFaction();
            parser.displayGameboard(game.getBoard().getTextView());
            parser.playerTurn(currentPlayerFaction);
            if (!diceThrown) {
                throwDice(parser, game);
                diceThrown = true;
            }
            parser.displayPlayerInfo(game.getCurrentPlayerResource(), game.getCurrentPlayerWinPoints());
            switch (parser.getAction()) {
                case NEXT_PLAYER:
                    if (caseNextPlayer(parser, game)) {
                        running = false;
                    } else {
                        diceThrown = false;
                    }
                    break;
                case BUILD_SETTLEMENT:
                    caseBuildStructure(parser, game, Config.Structure.SETTLEMENT);
                    break;
                case BUILD_CITY:
                    caseBuildStructure(parser, game, Config.Structure.CITY);
                    break;
                case BUILD_ROAD:
                    caseBuildStructure(parser, game, Config.Structure.ROAD);
                    break;
                case TRADE_WITH_BANK:
                    caseTradeWithBank(parser, game);
                    break;
                case QUIT:
                    running = false;
                    parser.quit();
                    break;
                default:
                    parser.errorMessage();
            }
        }
    }

    /**
     * The Method which is called if the player chooses the command "next Player".
     *
     * @param parser The parser Object which will interact with the player.
     * @param game   The game Object which will be used to execute the Method.
     * @return true: if there is a winner and game is finished. false: if there is no winner (yet).
     */
    private static boolean caseNextPlayer(Parser parser, SiedlerGame game) {
        Config.Faction winner = game.getWinner();
        if (winner == null) {
            game.switchToNextPlayer();
            return false;
        } else {
            parser.displayWinnertext(winner);
            return true;
        }
    }

    /**
     * The Method which is called if the player chooses the command "Trade with bank".
     *
     * @param parser The parser Object which will interact with the player.
     * @param game   The game Object which will be used to execute the Method.
     */
    private static void caseTradeWithBank(Parser parser, SiedlerGame game) {
        Config.Resource offer = parser.trade(true);
        Config.Resource want = parser.trade(false);
        if (!game.tradeWithBankFourToOne(offer, want)) {
            parser.errorMessage();
        }
    }

    /**
     * The Method which is called if the player chooses a command to build any structure.
     *
     * @param parser    The parser Object which will interact with the player.
     * @param game      The game Object which will be used to execute the Method.
     * @param structure The kind of Structure the player selected to build.
     */
    private static void caseBuildStructure(Parser parser, SiedlerGame game, Config.Structure structure) {
        parser.giveCoordinatesForStructures(structure);
        boolean successfully = false;
        if (structure == Config.Structure.SETTLEMENT) {
            successfully = game.buildSettlement(parser.getPoint());
        } else if (structure == Config.Structure.CITY) {
            successfully = game.buildCity(parser.getPoint());
        } else if (structure == Config.Structure.ROAD) {
            successfully = game.buildRoad(parser.getPoint(), parser.getPoint());
        }
        if (!successfully) {
            parser.errorMessage();
        }
    }

    /**
     * The method which calculates a random dice number and calls the method throwDice in the game Object.
     *
     * @param parser The parser Object which will interact with the player.
     * @param game   The game Object which will be used to execute the Method.
     */
    private static void throwDice(Parser parser, SiedlerGame game) {
        Random random = new Random();
        //(sum of two random integers from 0-5) + 2 => (sum of two random integers from 1-6)
        int thrownDices = random.nextInt(6) + random.nextInt(6) + 2;
        parser.thrownDices(thrownDices);
        game.throwDice(thrownDices);
    }

    /**
     * The Method which executes the whole founding phase. It Creates a new SiedlerGame Object and prompts the players to build their first structures.
     *
     * @param parser The parser Object which will interact with the player.
     * @return The new SiedlerGame Object which is created.
     */
    private static SiedlerGame foundingPhase(Parser parser) {
        HashMap<String, Integer> gameInfo = parser.gameStart();
        SiedlerGame game = new SiedlerGame(gameInfo.get("NumberOfWinPoints"), gameInfo.get("NumberOfPlayers"));

        //each Player builds their first Settlement and first Road in reverse order than in the rest of the game
        for (int player = 1; player <= gameInfo.get("NumberOfPlayers"); player++) {
            buildStructuresInFoundingPhase(parser, game, false);
            if (player < gameInfo.get("NumberOfPlayers")) {
                game.switchToPreviousPlayer();
            }
        }

        //each Player builds their second Settlement and second Road and gets a Payout for these Structures in usual order.
        for (int player = 1; player <= gameInfo.get("NumberOfPlayers"); player++) {
            buildStructuresInFoundingPhase(parser, game, true);
            game.switchToNextPlayer();
        }
        return game;
    }

    /**
     * The Method is called by foundingPhase. It prompts the Player to build a Settlement and a Road.
     *
     * @param parser The parser Object which will interact with the player.
     * @param game   The game Object which will be used to execute the Method.
     * @param payout true (for second Settlement in founding Phase): the Player gets a payout for the settlement. false (for first Settlement in founding Phase): The Player gets no payout.
     */
    private static void buildStructuresInFoundingPhase(Parser parser, SiedlerGame game, Boolean payout) {
        parser.displayGameboard(game.getBoard().getTextView());
        parser.playerTurn(game.getCurrentPlayerFaction());

        //build Settlement
        parser.giveCoordinatesForStructures(Config.Structure.SETTLEMENT);
        boolean successful = false;
        do {
            if (game.placeInitialSettlement(parser.getPoint(), payout)) {
                successful = true;
            } else {
                parser.errorMessage();
            }
        } while (!successful);

        //build Road
        parser.displayGameboard(game.getBoard().getTextView());
        parser.giveCoordinatesForStructures(Config.Structure.ROAD);
        successful = false;
        do {
            if (game.placeInitialRoad(parser.getPoint(), parser.getPoint())) {
                successful = true;
            } else {
                parser.errorMessage();
            }
        } while (!successful);
    }
}
