package entity;

import environnement.Coordonnees;
import environnement.HitBox;
import environnement.Physique;
import environnement.PositionCollision;
import static environnement.PositionCollision.*;
import jeu.Jeu;
import map.Obstacle;

import static jeu.Jeu.getGameCharacter;
import static jeu.Jeu.getObstacleTable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Monster extends Entity{

    
    double vitesseActuZ = 0;
    Obstacle obstacleBeneath = Jeu.getSol(); //obstacle en dessous
    int isAttacking = 0;//pour qu'il s'arrete si il attaque
    Coordonnees coordInit; // coordonnees initiales
    PositionCollision collisionOnSide = NONE;
    int impulsionSaut = 0;

    public Monster(Coordonnees c, double vitesseMax,double width,double height,int atkStt,int nbLP) {
        super(c, vitesseMax,width,height,atkStt);
        maxLifePoints = nbLP;
        nbLifePoints = maxLifePoints;
        coordInit = coord;
    }

    public void deplacements(){
        double delta = Math.pow(10, -3);

        //on se souvient de ses anciennes positions
        oldCoord = coord;

        //pour faire un deplacement cyclique
        if ((coordInit.getX()-coord.getX() >= 500 && direction == -1) || coord.getX() <= obstacleBeneath.getHitbox().getExtremites().get("HautGauche").getX())direction = 1;
        else if ((coordInit.getX()-coord.getX() <= -500 && direction == 1) || coord.getX() >= obstacleBeneath.getHitbox().getExtremites().get("HautDroite").getX())direction = -1;
        //******************

        double newX = coord.getX() + (1-Math.min(isAttacking,1))*direction*vitesseMax*delta;
        
        double newZ = (1-alpha)*Physique.g/2*delta*delta + this.vitesseActuZ*delta + this.getCoord().getZ();
        setCoord(new Coordonnees(newX, newZ));
        vitesseActuZ = vitesseActuZ + 8*(1-alpha)*Physique.g*delta;
    }

    public void collisionGestion(){
        //recupere la liste des obstacles dans le voisinage
        List<Obstacle> listeObstaclesInVoisinage = getObstacleTable();
        //stocke l'obstacle avec lequel il y a eu collision
        HashMap<Obstacle,PositionCollision> collisionsPresentes = new HashMap<>();
        
        //recupere les collisions
        for (int i=0;i<listeObstaclesInVoisinage.size();i++) {
            collisionsPresentes.put(listeObstaclesInVoisinage.get(i),Obstacle.collision(this, listeObstaclesInVoisinage.get(i), i));
        }

        //on set alpha a 0 (on le suppose dans les airs)
        alpha = 0;
        obstacleBeneath = Jeu.getSol();
        //on parcourt pour tester si il est sur un block
        for (int i=0;i<collisionsPresentes.size();i++){
            //si on trouve on sort de la boucle (pas besoin d'aller plus loin)
            if (collisionsPresentes.get(listeObstaclesInVoisinage.get(i)) == BAS){  
                //si on a donné une impulsion, on regarde pas les collisions bas         
                this.vitesseActuZ = 0;
                this.setCoord(new Coordonnees(this.getCoord().getX(), listeObstaclesInVoisinage.get(i).getHitbox().getExtremites().get("HautGauche").getZ()-entityHEIGHT/2));
                alpha = 1;
                obstacleBeneath = listeObstaclesInVoisinage.get(i);
                break;
            }
        }

        //on gere les collisions laterales
        for (int i=0;i<collisionsPresentes.size();i++){
            if (collisionsPresentes.get(listeObstaclesInVoisinage.get(i)) == DROITE && this.direction == -1){
                this.setCoord(new Coordonnees(listeObstaclesInVoisinage.get(i).getHitbox().getExtremites().get("HautDroite").getX()+entityWIDTH/2, this.getCoord().getZ()));
                this.direction = 1;
                break;
            }
            else if (collisionsPresentes.get(listeObstaclesInVoisinage.get(i)) == GAUCHE && this.direction == 1){
                this.setCoord(new Coordonnees(listeObstaclesInVoisinage.get(i).getHitbox().getExtremites().get("HautGauche").getX()-entityWIDTH/2, this.getCoord().getZ()));
                this.direction = -1;
                break;
            }
        }
    }

    
    public void attaque(){//isAttacking > 0 pour dire que si il est entrain d'attaquer, il finit son attaque
        if ((Math.abs(this.coord.getX()-getGameCharacter().coord.getX()) <= 2+entityWIDTH/2+getGameCharacter().entityWIDTH/2 && Math.abs(this.coord.getZ()-getGameCharacter().coord.getZ()) <= entityHEIGHT && characInFrontOf()) || isAttacking > 0){ //n'attaque que si perso pas invincible
            //si il n'est pas immobile il s'arrete
            if (isAttacking < 100){
                isAttacking++;
            }
            //sinon on cree l'attaque
            else{
                Coordonnees coordAttack = new Coordonnees(this.coord.getX() + direction*entityWIDTH/2,this.coord.getZ());
                Attack monsterAttack = new Attack(attackStat, coordAttack, new HitBox(coordAttack, entityHEIGHT, entityWIDTH+20));

                //si il ya collision entre le perso et l'attaque
                if (HitBox.collision(monsterAttack.getHitBox(), getGameCharacter().hitBox)){
                    //l'attaque fait ses degats
                    monsterAttack.dealDamage(getGameCharacter());
                }
                isAttacking++;//il peut rebouger
                if (isAttacking > 200)isAttacking = 0; //animation d'attaque finie
            }
        }
    }

    private void updateNbFrameInvincible(){
        nbFramesInvincible = Math.max(0, nbFramesInvincible-1);
    }

    private Boolean characInFrontOf(){
        return (direction == 1 && getGameCharacter().getCoord().getX() >= this.coord.getX()) || (direction == -1 && getGameCharacter().getCoord().getX() <= this.coord.getX());
    }

    public void evolveMonster(){ //evolution du monstre
        deplacements();
        collisionGestion(); // ca merde
        attaque();
        //updateVoisinage();
        this.setHitBox(new HitBox(this.getCoord(),entityHEIGHT,entityWIDTH));
        updateNbFrameInvincible();
        if (death()) {
            int nbRand = (int)(2*Math.random()); //on cree une loi de bernouilli de parametre 1/2
            if (nbRand == 0)dropHearth();
        }
        
    }

    private void dropHearth(){
        Jeu.getHearthList().add(new Hearth(coord));
    }

    public static void evolveAllMonsters(List<Monster> liste){ // evolution de tous les monstres
        if (!liste.isEmpty()){
            for (Monster monster : liste)monster.evolveMonster();
        }
        //on supprime tous les monstres morts
        int i = 0;
        while (i < liste.size()){
            if (liste.get(i).death())liste.remove(i);
            else i++;
        }

    }

    public List<Obstacle> obstacleInVoisinage(){ //recupere la liste des obstacles presents dans le voisinage
        List<Obstacle> liste = new ArrayList<>();
        for (Obstacle obs : Jeu.getObstacleTable()){
            if (HitBox.collision(voisinage, obs.getHitbox())){
                liste.add(obs);
            }
        }
        return liste;
    }

    public void updateVoisinage(){
        voisinage = new HitBox(coord, 1000, 1000);
    }

    public Boolean death(){
        return nbLifePoints == 0;
    }
    
}
