package entity;


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

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


import static jeu.Jeu.getObstacleTable;
//import static jeu.Jeu.gameCharacter;


public class Character extends Entity{

    private double vitesseActuX = 0; // vitesse actuelle supposée nulle au départ
    private double vitesseActuZ = 0;
    private final double impulsionSaut;
    private double masse;
    public int key;

    protected Map<String,Double> tableCommande; //table des commandes du perso

    //private Obstacle obstacleBeneath = Jeu.sol;

    //private BufferedImage sprite;

    public Character(Coordonnees c, double vitesseMax, double m, double impSaut){
        super(c,vitesseMax,30,60,1); // comme tous les entities ont une hitbox, j'ai factorise
        this.masse = m;
        this.impulsionSaut = impSaut;
        this.tableCommande = new HashMap<>();
        tableCommande.put("CommandX",(double)0);
        tableCommande.put("CommandZ",(double)0);
        tableCommande.put("CommandAttack",(double)0); // 1 si attack, 0 sinon  
        nbMaxFramesInvicible = 500; 
    }

    void deplacements(){ // alpha indique si il est au sol ou non, il vaut 0 (en l'air) ou 1 (au sol) en fonction
        
        double delta = Math.pow(10, -3); // pas de temps remis en secondes

        //on actualise les positions
        double newX,newZ;
        //on se souvient de ses anciennes positions
        this.oldCoord = this.getCoord();

        newX = tableCommande.get("CommandX")*delta + this.getCoord().getX();

        newZ = ((1-alpha)*Physique.g - tableCommande.get("CommandZ"))/2*delta*delta + vitesseActuZ*delta + this.getCoord().getZ();
        if (Math.abs(newZ-this.coord.getZ()) <= 1) newZ += Math.signum(newZ-this.coord.getZ());
        this.setCoord(new Coordonnees((int)newX, (int)newZ));
 
        //on actualise les vitesses
        vitesseActuX = tableCommande.get("CommandX");
        //System.out.println(vitesseActuX);
        if (vitesseActuX != 0){
            direction = (int) Math.signum(vitesseActuX);
        }

        vitesseActuZ = vitesseActuZ + 8*((1-alpha)*Physique.g - tableCommande.get("CommandZ"))*delta;
        //System.out.println(vitesseActuZ);
        if (Math.abs(vitesseActuZ) > vitesseMax*500)vitesseActuZ = Math.signum(vitesseActuZ)*vitesseMax*500;
    }

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

        

        //on gere celles du bas
        //on set alpha a 0 (on suppose qu'il est en l'air)
        alpha = 0;

            //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), on n'étudie qui si on ne donne pas de commande
                if (tableCommande.get("CommandZ") != 0){
                    break;
                }
                if (collisionsPresentes.get(listeObstaclesInVoisinage.get(i)) == BAS ){
                    this.vitesseActuZ = 0;
                    this.setCoord(new Coordonnees(this.getCoord().getX(), getObstacleTable().get(i).getHitbox().getExtremites().get("HautGauche").getZ()-entityHEIGHT/2));
                    alpha = 1;

                    break;
                }
            }
            /*for (Obstacle obs : listeObstaclesInVoisinage){ //on cherche l'obstacle en-dessous du perso
                if (obs.getHitbox().getExtremites().get("HautGauche").getZ() <= obstacleBeneath.getHitbox().getExtremites().get("HautGauche").getZ() && obs.getHitbox().getExtremites().get("HautGauche").getZ() >= hitBox.getExtremites().get("BasDroite").getZ()){
                    obstacleBeneath = obs;
                }
            }
            
            if (collisionsPresentes.get(obstacleBeneath) == BAS && tableCommande.get("CommandAttack") == 0){
                this.vitesseActuZ = 0;
                    this.setCoord(new Coordonnees(this.getCoord().getX(), obstacleBeneath.getHitbox().getExtremites().get("HautGauche").getZ()-entityHEIGHT/2-1));
                    alpha = 1;
                    onGround = true;
            }*/


        //}
        //on gere les collisions laterales et celle du haut
        for (int i=0;i<collisionsPresentes.size();i++){

            if (collisionsPresentes.get(listeObstaclesInVoisinage.get(i)) == DROITE && this.vitesseActuX < 0){
                this.setCoord(new Coordonnees(listeObstaclesInVoisinage.get(i).getHitbox().getExtremites().get("HautDroite").getX()+entityWIDTH/2, this.getCoord().getZ()));
                break;
            }

            else if (collisionsPresentes.get(listeObstaclesInVoisinage.get(i)) == GAUCHE && this.vitesseActuX > 0){
                this.setCoord(new Coordonnees(listeObstaclesInVoisinage.get(i).getHitbox().getExtremites().get("HautGauche").getX()-entityWIDTH/2, this.getCoord().getZ()));
                break;
            }

        }
        
        //on gere celles du haut
        for (int i=0;i<collisionsPresentes.size();i++){
            if (collisionsPresentes.get(listeObstaclesInVoisinage.get(i)) == HAUT){
                this.vitesseActuZ = 0;
                this.setCoord(new Coordonnees(this.getCoord().getX(), listeObstaclesInVoisinage.get(i).getHitbox().getExtremites().get("BasGauche").getZ()+entityHEIGHT/2+1));
                break;
            }
        }    
    }
    
    void keyGestion() {
    	for (Coffre cof : Jeu.getCoffreTable()){
            if (HitBox.collision(this.getHitBox(), cof.getHitbox())){
            	if (cof.key) {
            		this.key += 1;
            		cof.key = false;
            		System.out.print("Tu as trouvé une clée!");
            	}
            }
        }
    }    

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


    void attaque(){
        Coordonnees attackCenter = new Coordonnees(coord.getX() + direction*entityWIDTH/2, coord.getZ());
        Attack characterAttack = new Attack(attackStat, attackCenter, new HitBox(attackCenter,entityHEIGHT,entityWIDTH+20));
        for (Monster monster : Jeu.getMonsterList()){
            if (HitBox.collision(characterAttack.getHitBox(), monster.getHitBox())){
                //si collision, l'attaque fait ses dmg
                characterAttack.dealDamage(monster);
            }
        }
    }
    

    void getGameCommand(){

        Map<String,Boolean> commandeJeu = Jeu.getDirectionJeu();
        if (commandeJeu.get("Right") && commandeJeu.get("Left")){tableCommande.replace("CommandX", (double)0);}
        else if (commandeJeu.get("Right")) tableCommande.replace("CommandX", vitesseMax+500);
        else if (commandeJeu.get("Left")) tableCommande.replace("CommandX", -vitesseMax);

        
        if (commandeJeu.get("Jump")) {
            if ((alpha == 1) && (this.getVitesseActuZ() == 0)){
                
                tableCommande.replace("CommandZ", impulsionSaut);
            }      
        }

        if (commandeJeu.get("Attack"))tableCommande.replace("CommandAttack", (double)1);
    }

    void resetCommand(){
        for (String key : tableCommande.keySet()){
            tableCommande.replace(key, (double)0);
        }
    }

    public void evolveCharacter(){
        getGameCommand();
        deplacements();
        collisionGestion();
        keyGestion();
        updateVoisinage();
        this.setHitBox(new HitBox(this.getCoord(),entityHEIGHT,entityWIDTH)); // mise à jour moins crade pour l'instant
        if (tableCommande.get("CommandAttack") == 1) attaque();
        updateNbFrameInvincible();
        resetCommand();
        
    }

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

    public double getVitesseActuX() {
        return vitesseActuX;
    }

    public double getVitesseActuZ() {
        return vitesseActuZ;
    }

    public double getM() {
        return masse;
    }

    public double getImpulsionSaut() {
        return impulsionSaut;
    }

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

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

    public Map<String, Double> getTableCommande() {
        return tableCommande;
    }

    public Character clone(){
        return this;
    }
}
