package de.miaurizius.jgame2d.entity;

import de.miaurizius.jgame2d.core.*;
import de.miaurizius.jgame2d.core.enums.Direction;
import de.miaurizius.jgame2d.core.enums.EntityType;
import de.miaurizius.jgame2d.core.enums.GameState;
import de.miaurizius.jgame2d.core.handlers.KeyHandler;
import de.miaurizius.jgame2d.entity.item.KeyObj;
import de.miaurizius.jgame2d.entity.item.ShieldWoodObj;
import de.miaurizius.jgame2d.entity.item.SwordNormalObj;
import de.miaurizius.jgame2d.entity.projectile.FireballObj;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;

public class Player extends Entity {

    KeyHandler keyH;
    public final int screenX;
    public final int screenY;
    private int standCount;

    // STATE
    public boolean attackCancel;
    public boolean lightUpdated;

    public Player(GamePanel panel, KeyHandler keyH) {
        super(panel);
        this.keyH = keyH;

        name = "player";
        type = EntityType.PLAYER;

        screenX = panel.screenWidth/2 - panel.tileSize/2;
        screenY = panel.screenHeight/2 - panel.tileSize/2;

        solidArea = new Rectangle();
        solidArea.x = 12;
        solidArea.y = 20;
        solidAreaDefaultX = solidArea.x;
        solidAreaDefaultY = solidArea.y;
        solidArea.width = 24;
        solidArea.height = 24;

        setDefaultValues();
        getPlayerImage();
        getPlayerAttackImage();
        getGuardImages();
        setDialogue();
    }

    // DEFAULT
    public void update() {
        if(life > maxLife) life = maxLife;

        if(knockback) {
            collisionOn = false;
            panel.collisionH.checkTile(this);
            panel.collisionH.checkObject(this, false);
            panel.collisionH.checkEntity(this, panel.npc[panel.currentMap.getIndex()]);
//            panel.collisionH.checkEntity(this, panel.monster[panel.currentMap.getIndex()]);
            panel.collisionH.checkEntity(this, panel.iTile[panel.currentMap.getIndex()]);
            if(collisionOn) {
                knockbackCount = 0;
                knockback = false;
                speed = defaultSpeed;
                invincibleCounting();
                return;
            }

            switch(knockbackDirection) {
                case UP -> worldY -= speed;
                case DOWN -> worldY += speed;
                case LEFT -> worldX -= speed;
                case RIGHT -> worldX += speed;
            }

            knockbackCount++;
            if(knockbackCount != 10) {
                invincibleCounting();
                return;
            }

            knockback = false;
            knockbackCount = 0;
            speed = defaultSpeed;
            invincibleCounting();
            return;
        } else if(attacking) {
            attacking();
            invincibleCounting();
            return;
        }

        // BLOCKING
        if(keyH.CTLKeyPressed) {
            guarding = true;
            transparent = false;
            invincibleCounting();
            return;
        }

        // MOVEMENT
        if(keyH.upPressed ||  keyH.downPressed || keyH.leftPressed || keyH.rightPressed || keyH.spacePressed) {
            if(!keyH.spacePressed) {
                if(keyH.upPressed) direction = Direction.UP;
                else if(keyH.downPressed) direction = Direction.DOWN;
                else if(keyH.leftPressed) direction = Direction.LEFT;
                else direction = Direction.RIGHT;
            }

            // CHECK TILE COLLISION
            collisionOn = false;
            panel.collisionH.checkTile(this);

            // CHECK OBJECT COLLISION
            int objIndex = panel.collisionH.checkObject(this, true);
            pickObject(objIndex);

            // CHECK ENTITY COLLISION
            int npcIndex  = panel.collisionH.checkEntity(this, panel.npc[panel.currentMap.getIndex()]);
            interactNPC(npcIndex);

            // CHECK MONSTER COLLISION
            int monsterIndex = panel.collisionH.checkEntity(this, panel.monster[panel.currentMap.getIndex()]);
            interactMonster(monsterIndex);

            // CHECK INTERACTIVE TILE COLLISION
            int iTileIndex = panel.collisionH.checkEntity(this, panel.iTile[panel.currentMap.getIndex()]);

            // CHECK EVENT
            panel.eventH.checkEvent();

            if(!collisionOn && !keyH.spacePressed) {
                switch (direction) {
                    case UP -> worldY -= speed;
                    case DOWN -> worldY += speed;
                    case LEFT -> worldX -= speed;
                    case RIGHT -> worldX += speed;
                }
            }

            if(keyH.spacePressed && !attackCancel) {
                panel.playSE(7);
                attacking = true;
                spriteCount = 0;
            }

            attackCancel = false;
            panel.keyH.spacePressed = false;
            guarding = false;

            spriteCount++;
            if(spriteCount > 12) {
                if(spriteNum == 1) spriteNum = 2;
                else if(spriteNum == 2) spriteNum = 1;
                else spriteNum = 0;
                spriteCount = 0;
            }
        } else {
            standCount++;
            if(standCount == 20) {
                spriteNum = 1;
                standCount = 0;
            }
            guarding = false;
        }

        if(panel.keyH.shotKeyPressed && !projectile.alive) {
            projectile.set(worldX, worldY, direction, true, this);
            panel.projectileList.add(projectile);
            panel.playSE(10);
        }

        // INVINCIBLE COUNTER
        if(!invincible) return;
        invincibleCounting();

        if(life <= 0) {
            panel.gameState = GameState.GAMEOVER;
            invincibleCount = 0;
            panel.ui.commandNum = -1;
            panel.stopMusic();
            panel.playSE(12);
        }
    }

    // INTERACTION
    public void pickObject(int index) {
        if(index == 999 || panel.obj[panel.currentMap.getIndex()][index] == null) return;

        // PICKUP ONLY ITEMS
        if(panel.obj[panel.currentMap.getIndex()][index].type == EntityType.PICKUP) {
            panel.obj[panel.currentMap.getIndex()][index].use(this);
            panel.obj[panel.currentMap.getIndex()][index] = null;
            return;
        }
        // OBSTACLES
        if(panel.obj[panel.currentMap.getIndex()][index].type == EntityType.OBSTACLE) {
            if(!keyH.spacePressed) return;
            attackCancel = true;
            panel.obj[panel.currentMap.getIndex()][index].interact();
            return;
        }
        // INVENTORY ITEMS
        else {
            if(!canObtainItem(panel.obj[panel.currentMap.getIndex()][index])) {
                panel.ui.addMessage("Your inventory is full!");
                return;
            }
            panel.playSE(1);
            panel.ui.addMessage("Picked up " + panel.obj[panel.currentMap.getIndex()][index].name + "!");
        }
        panel.obj[panel.currentMap.getIndex()][index] = null;
    }

    public void interactMonster(int index) {
        if(index == 999) return;
        if(invincible || panel.monster[panel.currentMap.getIndex()][index].dying || !panel.monster[panel.currentMap.getIndex()][index].alive) return;

        int damage = panel.monster[panel.currentMap.getIndex()][index].attack - defense;

        panel.playSE(6);
        life -= Math.max(damage, 0);
        if(damage != 0) panel.player.transparent = true;
        invincible = true;

    }
    public void damageMonster(int index, Entity attacker, int attack, int knockbackVal) {
        if(index == 999) return;
        if(panel.monster[panel.currentMap.getIndex()][index].invincible) return;

        int damage = attack - panel.monster[panel.currentMap.getIndex()][index].defense;

        if(damage > 0) {
            panel.playSE(5);
            if(knockbackVal > 0) setKnockback(panel.monster[panel.currentMap.getIndex()][index], attacker, knockbackVal);
            panel.monster[panel.currentMap.getIndex()][index].life -= damage;
            panel.monster[panel.currentMap.getIndex()][index].transparent = true;
            panel.monster[panel.currentMap.getIndex()][index].invincible = true;
        }

        panel.monster[panel.currentMap.getIndex()][index].damageReaction();
        if(panel.monster[panel.currentMap.getIndex()][index].life <= 0) {
            panel.monster[panel.currentMap.getIndex()][index].dying = true;
            panel.ui.addMessage("Gained +" + panel.monster[panel.currentMap.getIndex()][index].exp + " XP!");
            exp += panel.monster[panel.currentMap.getIndex()][index].exp;
            checkLevelUp();
        }
    }

    public void interactTile(int index) {
        if(index == 999 || !panel.iTile[panel.currentMap.getIndex()][index].destructible || panel.iTile[panel.currentMap.getIndex()][index].invincible) return;
        if(!panel.iTile[panel.currentMap.getIndex()][index].meetItemReq(this)) return;
        panel.iTile[panel.currentMap.getIndex()][index].playSE();
        panel.iTile[panel.currentMap.getIndex()][index].life--;
        panel.iTile[panel.currentMap.getIndex()][index].invincible = true;
        generateParticle(panel.iTile[panel.currentMap.getIndex()][index], panel.iTile[panel.currentMap.getIndex()][index]);
        if(panel.iTile[panel.currentMap.getIndex()][index].life == 0) panel.iTile[panel.currentMap.getIndex()][index] = panel.iTile[panel.currentMap.getIndex()][index].getDestroyedForm();
    }

    public void interactNPC(int index) {
        if(index == 999) return;
        //if(!panel.keyH.spacePressed) return; //Only uncomment if text should only appear if player hits space
        attackCancel = true;
        panel.npc[panel.currentMap.getIndex()][index].speak();
    }
    public void speak() {
        //This method only exists for character specific things later...
        super.speak();
    }

    // BACKGROUND JOBS
    public void checkLevelUp() {
        if(exp < nextLevelExp) return;
        level++;
        nextLevelExp = nextLevelExp*2;
        maxLife += 2;
        strength++;
        dexterity++;
        attack = getAttack();
        defense = getDefense();
        panel.playSE(8);
        setDialogue();
        startDialogue(this,0);
    }
    public void selectItem() {
        int itemIndex = panel.ui.getItemIndex(panel.ui.playerSlotCol, panel.ui.playerSlotRow);
        if(itemIndex >= inventory.size()) return;
        Entity selectedItem = inventory.get(itemIndex);
        if(selectedItem.type == EntityType.WEAPON) {
            currentWeapon = selectedItem;
            getAttack();
            getPlayerAttackImage();
        }
        if(selectedItem.type == EntityType.SHIELD) {
            currentShield = selectedItem;
            getDefense();
        }
        if(selectedItem.type == EntityType.LIGHT) {
            if(currentLight == selectedItem) currentLight = null; else currentLight = selectedItem;
            lightUpdated = true;
        }
        if(selectedItem.consumable)
            if(selectedItem.use(this))
                if(selectedItem.amt > 1) selectedItem.amt--; else inventory.remove(selectedItem);
    }
    public void setDefaultPositions() {
        worldX = panel.tileSize * 23;
        worldY = panel.tileSize * 21;
        direction = Direction.DOWN;
    }
    public void restoreStatus() {
        life = maxLife;
        speed = defaultSpeed;
        invincible = false;
        transparent = false;
        attacking = false;
        guarding = false;
        knockback = false;
        lightUpdated = true;
    }
    public void getSleepingImage(BufferedImage image) {
        down1 = image;
        down2 = image;
        up1 = image;
        up2 = image;
        left1 = image;
        left2 = image;
        right1 = image;
        right2 = image;
    }

    // SETTING THINGS UP
    public void setDefaultValues() {
        worldX = panel.tileSize * 23;
        worldY = panel.tileSize * 21;

        defaultSpeed = 4;
        speed = defaultSpeed;
        direction = Direction.DOWN;

        // PLAYER STATUS (1 heart = 2 lives)
        maxLife = 6;
        life = maxLife;
        level = 1;
        strength = 1;
        dexterity = 1;
        exp = 0;
        nextLevelExp = 5;
        coins = 500;
        currentWeapon = new SwordNormalObj(panel);
        currentShield = new ShieldWoodObj(panel);
        currentLight = null;
        projectile = new FireballObj(panel);
        attack = getAttack();
        defense = getDefense();

        // INVENTORY
        inventory.clear();
        inventory.add(currentWeapon);
        inventory.add(currentShield);
        inventory.add(new KeyObj(panel));
    }
    public void setDialogue() {
        dialogue[0][0] = "You are level " + level + " now!\nYou feel stronger!";
    }
    public int getAttack() {
        attackArea = currentWeapon.attackArea;
        return attack = strength * currentWeapon.attackValue;
    }
    public int getDefense() {
        return defense = dexterity * currentShield.defenseValue;
    }
    public int getCurrentWeaponSlot() {
        for(int i = 0; i < inventory.size(); i++) {
            if(inventory.get(i) == currentWeapon) return i;
        }
        return -1;
    }
    public int getCurrentShieldSlot() {
        for(int i = 0; i < inventory.size(); i++) {
            if(inventory.get(i) == currentShield) return i;
        }
        return -1;
    }
    public void getPlayerImage()  {
        up1 = initEntitySprites("player/boy_up_1");
        up2 = initEntitySprites("player/boy_up_2");
        down1 = initEntitySprites("player/boy_down_1");
        down2 = initEntitySprites("player/boy_down_2");
        left1 = initEntitySprites("player/boy_left_1");
        left2 = initEntitySprites("player/boy_left_2");
        right1 = initEntitySprites("player/boy_right_1");
        right2 = initEntitySprites("player/boy_right_2");
    }
    public void getPlayerAttackImage() {
        switch(currentWeapon.weaponType) {
            case SWORD:
                attackUp1 = initEntitySprites("player/attack/boy_attack_up_1", panel.tileSize, panel.tileSize * 2);
                attackUp2 = initEntitySprites("player/attack/boy_attack_up_2", panel.tileSize, panel.tileSize * 2);
                attackDown1 = initEntitySprites("player/attack/boy_attack_down_1", panel.tileSize, panel.tileSize * 2);
                attackDown2 = initEntitySprites("player/attack/boy_attack_down_2", panel.tileSize, panel.tileSize * 2);
                attackLeft1 = initEntitySprites("player/attack/boy_attack_left_1", panel.tileSize * 2, panel.tileSize);
                attackLeft2 = initEntitySprites("player/attack/boy_attack_left_2", panel.tileSize * 2, panel.tileSize);
                attackRight1 = initEntitySprites("player/attack/boy_attack_right_1", panel.tileSize * 2, panel.tileSize);
                attackRight2 = initEntitySprites("player/attack/boy_attack_right_2", panel.tileSize * 2, panel.tileSize);
                break;
            case AXE:
                attackUp1 = initEntitySprites("player/attack/boy_axe_up_1", panel.tileSize, panel.tileSize * 2);
                attackUp2 = initEntitySprites("player/attack/boy_axe_up_2", panel.tileSize, panel.tileSize * 2);
                attackDown1 = initEntitySprites("player/attack/boy_axe_down_1", panel.tileSize, panel.tileSize * 2);
                attackDown2 = initEntitySprites("player/attack/boy_axe_down_2", panel.tileSize, panel.tileSize * 2);
                attackLeft1 = initEntitySprites("player/attack/boy_axe_left_1", panel.tileSize * 2, panel.tileSize);
                attackLeft2 = initEntitySprites("player/attack/boy_axe_left_2", panel.tileSize * 2, panel.tileSize);
                attackRight1 = initEntitySprites("player/attack/boy_axe_right_1", panel.tileSize * 2, panel.tileSize);
                attackRight2 = initEntitySprites("player/attack/boy_axe_right_2", panel.tileSize * 2, panel.tileSize);
                break;
        }
    }
    public void getGuardImages() {
        guardUp = initEntitySprites("player/guarding/boy_guard_up");
        guardDown = initEntitySprites("player/guarding/boy_guard_down");
        guardLeft = initEntitySprites("player/guarding/boy_guard_left");
        guardRight = initEntitySprites("player/guarding/boy_guard_right");
    }
    public int searchItemInInventory(String itemName) {
        for(int i = 0; i < inventory.size(); i++) {
            if(inventory.get(i).name.equals(itemName)) {
                return i;
            }
        }
        return 999;
    }
    public boolean canObtainItem(Entity item) {
        int i = searchItemInInventory(item.name);
        try {
            if(item.stackable) {
                if(i != 999) {
                    inventory.get(i).amt++;
                    return true;
                } else if(inventory.size() != maxInvSize) {
                    inventory.add((Entity) Class.forName(item.getClass().getName()).getDeclaredConstructor(GamePanel.class).newInstance(panel));
                    return true;
                }
            } else if(inventory.size() != maxInvSize) {
                inventory.add((Entity) Class.forName(item.getClass().getName()).getDeclaredConstructor(GamePanel.class).newInstance(panel));
                return true;
            }
        } catch(ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return false;
    }
    @Override
    public void invincibleCounting() {
        if(!invincible) return;
        invincibleCount++;
        if(invincibleCount > 60) {
            invincible = false;
            invincibleCount = 0;
        }
    }

}
