package linea;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;

public class Ligne extends ObjetGraphique {

	protected int nbSegments = 50;
	protected Segment SegCourant;

	protected ArrayList<Segment> listeSegments = new ArrayList<Segment>();

	protected Segment dernierSegment;
	private double decalageXDernierSegment = 0;

	private NoiseGenerator noiseGenerator;
	private GestionnaireNiveau gestionnaireNiveau;

	private double xActuel;

	// Curseur dans le noise : avance plus vite quand la frequence du niveau augmente
	private double noiseCurseur = 0;

	public Ligne(NoiseGenerator noiseGenerator, GestionnaireNiveau gestionnaireNiveau) {
		double x = 0;

		this.noiseGenerator = noiseGenerator;
		this.gestionnaireNiveau = gestionnaireNiveau;

		double dx = GetLargeurSegment();

		for (int i = 0; i < nbSegments; i++) {
			double noiseStep = getNoiseStep();
			double yDebut = CalculerY(noiseCurseur);
			double yFin = CalculerY(noiseCurseur + noiseStep);

			Segment s = new Segment(x, yDebut, dx, yFin - yDebut);
			s.setCouleur(new Color(0.2f, 0.2f, 0.2f));

			listeSegments.add(s);
			noiseCurseur += noiseStep;

			if (i == nbSegments - 1) {
				dernierSegment = s;
				xActuel = x + dx;
			}

			x += dx;
		}
	}

	protected double GetLargeurSegment(){
		return (double)ZoneDessin.largeur / nbSegments;
	}

	private double getNoiseStep() {
		return GetLargeurSegment() / gestionnaireNiveau.getNoiseFrequence();
	}

	// Quand un segment sort de l'écran, génère un nouveau segment à la fin de la ligne.
	protected Segment CreerSegmentSuivant(){
		double noiseStep = getNoiseStep();

		double yDebut = CalculerY(noiseCurseur);
		double yFin = CalculerY(noiseCurseur + noiseStep);

		noiseCurseur += noiseStep;

		Segment s = new Segment(0, yDebut, GetLargeurSegment(), yFin - yDebut);
		s.setCouleur(new Color(0.2f, 0.2f, 0.2f));

		return s;
	}

	// Calcule la hauteur de la ligne à un point X.
	protected double CalculerY(double noisePos){
		double limiteHaut = gestionnaireNiveau.getLimiteHaut();
		double limiteBas = gestionnaireNiveau.getLimiteBas();
		double hauteur = limiteBas - limiteHaut;

		double bruit = noiseGenerator.noise(noisePos, 1);
		double normalise = (bruit + 1) / 2;

		return limiteBas - normalise * hauteur;
	}

	@Override
	public void Afficher(Graphics g) {
		Graphics2D g2D = (Graphics2D) g;
		g2D.setStroke(new BasicStroke(3.0f));

		for (Segment s : listeSegments) {
			s.Afficher(g);
			if (SegCourant == null) {
				if (Cercle.xCercle <= s.x + s.xLong && Cercle.xCercle >= s.x) {
					SegCourant = s;
				}
			} else {
				if ((SegCourant.x + SegCourant.xLong) < Cercle.xCercle) {
					if (s.x <= Cercle.xCercle && (s.x + s.xLong) >= Cercle.xCercle) {
						SegCourant = s;
					}
				}
			}
		}
	}

	@Override
	public void Animer() {
		double vitesse = gestionnaireNiveau.getVitesseScroll();

		for (Segment s : listeSegments) {
			s.Animer();
			s.x -= vitesse;
		}

		UpdateSegments();
	}

	// Permet de réutiliser les segments sortis de l'écran au debut
	private void UpdateSegments(){

		if (dernierSegment == null) throw new RuntimeException("dernierSegment n'existe pas");

        for (int i = 0; i < listeSegments.size(); i++) {

            Segment s = listeSegments.get(i);
            if (s.x + s.xLong < 0) { // le segment est ENTIEREMENT hors de l'écran
                Segment nouveauSegment = CreerSegmentSuivant();
				nouveauSegment.x = dernierSegment.x + dernierSegment.xLong;
				listeSegments.set(i, nouveauSegment);

				xActuel += GetLargeurSegment();
				dernierSegment = nouveauSegment;
            }
        }
	}

	// Renvoie la hauteur à laquelle doit etre le cercle. Utilisé pour la collision
	public double GetHauteurPointCercle(){
		double t = (400 - SegCourant.x) / SegCourant.xLong;

		return SegCourant.y + SegCourant.yLong * t;
	}
}
