package org.project;

import org.project.entity.enemies.*;
import org.project.entity.players.Assassin;
import org.project.entity.players.Knight;
import org.project.entity.players.Player;
import org.project.entity.players.Wizard;
import org.project.location.Location;

import java.util.Random;
import java.util.Scanner;

public class Main {

    public static final String RESET = "\u001B[0m";
    public static final String RED_TEXT = "\u001B[31m";
    public static final String YELLOW_TEXT = "\u001B[33m";
    public static final String BLUE_TEXT = "\u001B[34m";

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);

        // Enemies
        Enemy goblin = new Goblin();
        Enemy skeleton = new Skeleton();
        Enemy vampire = new Vampire();
        Enemy[] enemies = {goblin, vampire, skeleton}; // An array of the enemies except Dragon which is the final boss

        // Locations, one for each enemy
        Location forest = new Location("Whispering forest", goblin);
        Location catacombs = new Location("forgotten catacombs", skeleton);
        Location castle = new Location("crimson castle", vampire);
        Location[] locations = {forest, catacombs, castle}; // An array of the location except peak where is the Dragon(final boss) located

        System.out.println(BLUE_TEXT + "\t=============== Java Knight ===============\n" + RESET);
        boolean running = true;
        while(running){

            System.out.println(YELLOW_TEXT + "\tChoose your option:");
            System.out.println("\t1. Start game" +
                    "   2. Exit" + RESET);
            int option = scanner.nextInt();
            if(option == 1){
                startGame(locations);
            } else if (option == 2) {
                running = false;
                break;
            }


        }


    }

    public static void startGame(Location[] locations){
        //
        Scanner scanner = new Scanner(System.in);
        Random random = new Random();

        // Set up
        System.out.print("\tEnter your name: ");
        String name = scanner.next();
        Player player = null;
        int option = 0;
        while(option > 3 || option < 1){
            System.out.println("\n    Select your character: (Enter number)");
            System.out.println("\t1. ⚔\uFE0F Knight (fights with a sword)\n" +
                               "\t2. \uD83D\uDD2A Assassin (fights with dual daggers)\n" +
                               "\t3. \uD83E\uDDD9 Wizard (fights with his magic staff)");
            if (scanner.hasNextInt()) {
                option = scanner.nextInt();
            } else {
                System.out.println(RED_TEXT + "\tInvalid input! Please enter a number." + RESET);
                scanner.next();
            }
        }
        switch (option){
            case 1:
                player = new Knight(name);
                System.out.printf("\n\tYour Character: %s, Your Name: %s\n\n", " ⚔\uFE0F Knight", player.getName());
                break;
            case 2:
                player = new Assassin(name);
                System.out.printf("\n\tYour Character: %s, Your Name: %s\n\n", " \uD83D\uDD2A Assassin", player.getName());
                break;
            case 3:
                player = new Wizard(name);
                System.out.printf("\n\tYour Character: %s, Your Name: %s\n\n", " \uD83E\uDDD9 Wizard", player.getName());
                break;
            default:
                player = null;
                break;
        }


        // Choosing location and enemy
        Location loc = locations[random.nextInt(locations.length)];
        loc = setLocation(loc, locations, player, random, scanner);
        Enemy enemy = loc.getEnemy();

        System.out.println(BLUE_TEXT + "\t=============== Prepare to fight ===============" + RESET);

        while(player.isAlive()){

            while(enemy.isAlive()){
                playerMove(player, enemy, scanner);
                if(!enemy.isStunned() && enemy.getAlive()) {
                    enemyMove(enemy, player, random);
                }
                else{
                    enemy.stun(false);
                }
            }

            player.addXP(30);
            enemy.heal();
            // Key dropping (50% chance)
            boolean[] logics = {false, true};
            boolean drop = logics[random.nextInt(logics.length)];
            if(enemy.hasKey() && drop){
                player.addKey(enemy);
            }

            // resetting player and enemy for the next battle
            player.fillMana();
            enemy.fillHP();

            // the final battle, Dragon fight
            if(player.getKeys() == 3){
                System.out.println(BLUE_TEXT + "\t=============== Entering Ashen Peak to fight the Dragon \uD83D\uDC09 ===============" + RESET);
                enemy = new Dragon();
                loc = new Location("ashen peak", enemy);

                while(enemy.isAlive() && player.isAlive()){
                    playerMove(player, enemy, scanner);
                    if(!enemy.isStunned() && enemy.isAlive()) {
                        enemyMove(enemy, player, random);
                    }
                    else{
                        enemy.stun(false);
                    }
                }

                break;
            }
            else{
                loc = setLocation(loc, locations, player, random, scanner);
                enemy = loc.getEnemy();
            }

        }

    }

    public static Location setLocation(Location currentLocation, Location[] locations, Player player, Random random, Scanner scanner){
        System.out.printf("\t%s should fight %s who fights with %s in %s\n",
                player.getName(), currentLocation.getEnemy().getName(), currentLocation.getEnemy().getWeapon().getName(), currentLocation.getName());
        int option = 0;
        while(option > 2 || option < 1){
            System.out.println(YELLOW_TEXT + "\tChoose your option: ");
            System.out.println("\t1. Fight" + currentLocation.getEnemy().getName() +
                    "\t\t2. Change location" + RESET);
            if (scanner.hasNextInt()) {
                option = scanner.nextInt();
            } else {
                System.out.println(RED_TEXT + "\tInvalid input! Please enter a number." + RESET);
                scanner.next();
            }
        }

        if(option == 1){
            return currentLocation;
        }
        else{
            Location[] temp_locations = new Location[locations.length - 1];
            int counter = 0;
            for(Location l : locations){
                if (l != currentLocation) {
                    temp_locations[counter] = l;
                    counter++;
                }
            }
            currentLocation = temp_locations[random.nextInt(temp_locations.length)];
            System.out.println("\tChanging locations...\n");
            setLocation(currentLocation, locations, player, random, scanner);
        }
        return currentLocation;
    }

    public static void playerMove(Player player, Enemy enemy, Scanner scanner){

        player.defend(false);

        int option = 0;
        while(option > 5 || option < 1){
            System.out.println("\n    Your turn.  Choose your move: \n" +
                    "   1. Light attack" +
                    "   2. Heavy attack" +
                    "   3. Defend" +
                    "   4. Heal" +
                    "   5. Special Ability");
            if (scanner.hasNextInt()) {
                option = scanner.nextInt();

                if(option != 1 && player.getMp() <= 0){ // when mana runs out the only available action is light attack
                    System.out.println(RED_TEXT + "\tYou are out of Mana. You can only choose \"1. Light attack\"" + RESET);
                    option = 0;
                }
                else if(option == 4 && player.getHp() == player.getMaxHP()){
                    System.out.println(RED_TEXT + "\tYour HP is at its maximum. Choose another option." + RESET);
                    option = 0;
                }

            } else {
                System.out.println(RED_TEXT + "\tInvalid input! Please enter a number." + RESET);
                scanner.next();
            }
        }

        switch (option){
            case 1: // light attack
                if(!enemy.isDefending()){
                    player.lightAttack(enemy);
                }
                break;
            case 2: // heavy attack
                if(player.getMp() >= player.getWeapon().getManaCost()) {
                    player.heavyAttack(enemy);
                }
                else{
                    System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
                    playerMove(player, enemy, scanner);
                }
                break;
            case 3: // defend
                if(player.getMp() >= player.getDefendMP()) {
                    player.defend(true);
                }
                else{
                    System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
                    playerMove(player, enemy, scanner);
                }
                break;
            case 4: // heal
                if(player.getMp() >= player.getDefendMP()) {
                    player.heal( (int)((0.2)*player.getMaxHP()) );
                }
                else{
                    System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
                    playerMove(player, enemy, scanner);
                }
                break;
            case 5: // special ability
                if(player.getMp() >= player.getSpecialAbilityMP()) {
                    player.specialAbility(enemy);
                }
                else{
                    System.out.println(RED_TEXT + "\tYou don't have enough Mana" + RESET);
                    playerMove(player, enemy, scanner);
                }
                break;
        }

        showStatus(player, enemy);

    }

    public static void enemyMove(Enemy enemy, Player player, Random random){

        enemy.defend(false);
        enemy.stun(false); // have been checked before calling this method

        System.out.println("\t" + enemy.getName() + "'s turn: ");

        int move_id = random.nextInt(5) + 1;
        switch (move_id){
            case 1: // attack (light attack)
                enemy.attack(player);
                break;
            case 2: // special ability (heavy attack)
                if(enemy instanceof Skeleton && enemy.isRevived()){
                    enemyMove(enemy, player, random);
                }
                enemy.specialAbility(player);
                break;
            case 3: // defend
                enemy.defend(true);
                break;
            case 4: // heal
                enemy.heal( (int)((0.2)*player.getMaxHP()) );
                break;
        }

        showStatus(player, enemy);

    }

    public static void showStatus(Player player, Enemy enemy){
        System.out.printf("\n\t[%s - %d/%d HP | %d/%d Mana]\n\t[%s - %d/%d HP]\n\n",
                player.getName(), player.getHp(), player.getMaxHP(), player.getMp(), player.getMaxMP(),
                enemy.getName(), enemy.getHp(), enemy.getMaxHP()
        );
    }


}