package linea;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.ArrayList;

import javax.swing.JPanel;

public class ZoneDessin extends JPanel {

    private static final long serialVersionUID = 1L;
    
    // un booleen qui permet d'arreter l'animation (suspendre)
    protected boolean estArrete = false;
    
    // liste des objets graphiques
    private ArrayList<ObjetGraphique> listeObjets = new ArrayList<ObjetGraphique>();
    // indicateur qu'une défaite (perte de contact) est survenue
    private boolean collisionOccur = false;
    // indique si le cercle a déjà été sur la ligne (début de la phase de maintien)
    private boolean hadBeenOnLine = false;
    // mode immortel - ignore les collisions
    private boolean immortel = false;
    // liste des boules bonus
    private ArrayList<BouleBonus> boolesBonus = new ArrayList<BouleBonus>();
    // compteur pour générer les boules à intervalle régulier
    private int compteurBoule = 0;
    private int intervalleBouleBase = 165; // moins de boules sur la durée
    private int delaiInitialBoule = 97; // apparition initiale avancée de 3%
    private int niveauActuel = 1;
    private Double dernierJoueurY = null;
    // type de bonus récupéré (-1 = rouge, 1 = vert, 0 = aucun)
    private int bonusRecupere = 0;
    
    public ZoneDessin(){
        setLayout(new BorderLayout());
        setPreferredSize(new Dimension(800, 600));
        setBackground(new Color(220,170,0));		
    }

    public void setCouleurFond(Color couleurFond) {
        setBackground(couleurFond);
    }
    
    public void setImmortel(boolean immortel) {
        this.immortel = immortel;
    }

    public void setNiveau(int niveau) {
        if (niveau < 1) {
            niveauActuel = 1;
        } else if (niveau > 100) {
            niveauActuel = 100;
        } else {
            niveauActuel = niveau;
        }
    }
    
    public int getBonusRecupere() {
        return bonusRecupere;
    }
    
    public void reinitialiserBonus() {
        bonusRecupere = 0;
    }

    // Ajout d'un objet graphique à la zone de dessin
    public void ajouterObjet(ObjetGraphique unObjet) {
        listeObjets.add(unObjet);
    }
    
    public void arreter(){
        estArrete = true;
    }
    
    public void demarrer(){
        estArrete = false;
    }
    
    public void traiterBoucleAnimation(){
        if (estArrete==true) {
            return;
        }

        Cercle cercleReference = null;
        for (ObjetGraphique obj : listeObjets) {
            if (obj instanceof Cercle) {
                cercleReference = (Cercle) obj;
                break;
            }
        }
        double joueurY = (cercleReference != null) ? cercleReference.getY() : 300;
        double vitesseJoueurY = 0.0;
        if (dernierJoueurY != null) {
            vitesseJoueurY = joueurY - dernierJoueurY;
        }
        dernierJoueurY = joueurY;
        
        // --- 0. Récupérer la ligne pour synchroniser les boules ---
        Ligne ligneObjet = null;
        for (ObjetGraphique obj : listeObjets) {
            if (obj instanceof Ligne) {
                ligneObjet = (Ligne) obj;
                break;
            }
        }

        // --- 1. Générer une boule au bord droit sur la ligne ---
        double progression = Math.min(1.0, (niveauActuel - 1) / 25.0);
        int intervalleBoule = (int) Math.round(intervalleBouleBase - progression * 20.0); // ~165 -> 145
        double probaVerteBase = 0.35 - progression * 0.06; // ~35% -> 29%
        double variationAleatoire = (Math.random() - 0.5) * 0.10; // +/- 5%
        double probaVerte = Math.max(0.24, Math.min(0.47, probaVerteBase + variationAleatoire + 0.02));

        compteurBoule++;
        if (compteurBoule >= intervalleBoule && ligneObjet != null) {
            compteurBoule = 0;
            boolean estVerte = Math.random() < probaVerte;

            double spawnX = 800;
            double spawnY;
            if (estVerte) {
                // Vertes: proches de la trajectoire du joueur pour être récupérables.
                double margeVerte = 95.0 + progression * 45.0;
                spawnY = joueurY + (Math.random() * (2.0 * margeVerte)) - margeVerte;
            } else {
                // Rouges: visent davantage le joueur, mais démarrent avec une marge d'esquive.
                double offset = 220 + progression * 20.0 + Math.random() * 150.0;
                spawnY = joueurY + (Math.random() < 0.5 ? -offset : offset);
            }

            if (spawnY < 20) spawnY = 20;
            if (spawnY > 580) spawnY = 580;

            if (!estVerte) {
                // Marge suffisante pour esquiver, sans rendre les rouges inoffensives.
                double distanceMin = 125.0;
                if (Math.abs(spawnY - joueurY) < distanceMin) {
                    if (spawnY >= joueurY) {
                        spawnY = Math.min(580, joueurY + distanceMin);
                    } else {
                        spawnY = Math.max(20, joueurY - distanceMin);
                    }
                }
            }
        
            // On crée la boule au bord droit (800)
            BouleBonus boule = new BouleBonus(spawnX, spawnY, estVerte);
            boolesBonus.add(boule);
        }

        // --- 2. Animation des objets ---
        for (ObjetGraphique obj : listeObjets) obj.Animer();

        // Mettre à jour et animer les boules (une seule fois par frame)
        if (ligneObjet != null) {
            for (BouleBonus boule : boolesBonus) {
                boule.animerAvecCible(ligneObjet.getVitesse(), joueurY, vitesseJoueurY);
            }
        }
        
        // 2. vérifier collision entre la Ligne et le Cercle (une seule passe de segments)
        if (ligneObjet != null && cercleReference != null) {
            double[] info = ligneObjet.contactInfo(
                    cercleReference.getX(), cercleReference.getY(), cercleReference.getRayon());
            
            boolean enContact = (info != null) && (info[1] <= cercleReference.getRayon());
            double ligneY = (info != null) ? info[0] : ligneObjet.getYAuX(cercleReference.getX());
            
            // Phase initiale en mode immortel : force d'attraction depuis le début
            if (!hadBeenOnLine && immortel) {
                double delta = ligneY - cercleReference.y;
                cercleReference.vitesse += delta * 0.12;
                
                // Marquer le premier contact
                if (enContact) {
                    hadBeenOnLine = true;
                }
            } else if (hadBeenOnLine && immortel) {
                // Phase maintenance : après le premier contact, maintenir le cercle sur la ligne
                double rayon = cercleReference.getRayon();
                double limite = ligneY - rayon;
                double limiteBas = ligneY + rayon;
                
                // Clamping : empêcher le cercle de sortir au-dessus
                if (cercleReference.y < limite) {
                    cercleReference.y = limite;
                    if (cercleReference.vitesse < 0) cercleReference.vitesse *= -0.3;
                }
                // Clamping : empêcher le cercle de sortir en-dessous
                else if (cercleReference.y > limiteBas) {
                    cercleReference.y = limiteBas;
                    if (cercleReference.vitesse > 0) cercleReference.vitesse *= -0.3;
                }
            } else if (info != null && !enContact && !immortel) {
                // Mode normal : mort si hors contact
                collisionOccur = true;
                estArrete = true;
            }
            
            // Synchroniser tous les autres Cercle en mode immortel
            if (immortel) {
                for (ObjetGraphique obj : listeObjets) {
                    if (obj instanceof Cercle && obj != cercleReference) {
                        Cercle autre = (Cercle) obj;
                        autre.y = cercleReference.y;
                        autre.vitesse = cercleReference.vitesse;
                    }
                }
            }
        }
        
        // 3. vérifier collisions entre les boules bonus et le cercle
        for (int i = boolesBonus.size() - 1; i >= 0; i--) 
        {
            BouleBonus boule = boolesBonus.get(i);
            boolean collision = false;

            for (ObjetGraphique obj : listeObjets) {
                if (obj instanceof Cercle) {
                    Cercle c = (Cercle) obj;
                    if (boule.collisionAvec(c)) {
                        // Si touché : on définit le bonus et on détruit la boule
                        bonusRecupere = boule.isVerte() ? 1 : -1;
                        boolesBonus.remove(i);
                        collision = true;
                        break;
                    }
                }
            }

        //4. Détruire la boule si elle dépasse le joueur sans être touchée ---
        // Le cercle est à X = 400. Si la boule est à X < 350, elle est "passée".
            if (!collision && boule.getX() < 320) {
                boolesBonus.remove(i);
            // On ne change pas bonusRecupere ici (donc rien ne se passe)
            }
        }

        // 5. on demande à redessiner
        repaint();	
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);		

        Graphics2D g2D = (Graphics2D) g;
        g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        
        // Afficher tous les objets
        for (ObjetGraphique obj : listeObjets) {
            obj.Afficher(g);
        }
        
        // Afficher les boules bonus
        for (BouleBonus boule : boolesBonus) {
            boule.Afficher(g);
        }
    }

    // Indique si une collision est survenue
    public boolean aCollision() {
        return collisionOccur;
    }

    // Réinitialise l'état de la zone de dessin et supprime les objets graphiques
    public void reinitialiser() {
        listeObjets.clear();
        boolesBonus.clear();
        estArrete = false;
        collisionOccur = false;
        hadBeenOnLine = false;
        bonusRecupere = 0;
        compteurBoule = -delaiInitialBoule;
        dernierJoueurY = null;
        repaint();
    }
}