package linea;

import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.Timer;

public class Jeu implements KeyListener, ActionListener, MouseListener {

	protected ZoneDessin ecran = new ZoneDessin();

	protected GestionnaireNiveau gestionnaireNiveau = new GestionnaireNiveau();
	protected Ligne lili = new Ligne(new NoiseGenerator(0), gestionnaireNiveau);

	protected List<Joueur> joueurs = new ArrayList<>();
	protected boolean deuxJoueurs = false;

	protected Timer horloge;

	protected double score = 1;
	protected JLabel labScore;

	protected GestionnaireScore gestionnaireBDD = new GestionnaireScore();

	// pour gestionnaire historique
	protected GestionnaireHistorique gestionnaireHistorique = new GestionnaireHistorique();

	protected GestionnaireFinDePartie gestionnaireFinDePartie;

	protected int compteurFrames = 0;
	protected boolean estGameOver = false;
	protected JLabel labGameOver;

	protected String pseudo = "";
	protected JFrame fenetre;
	protected MenuPrincipal menu;

	public boolean cheatMode = false;

	public Jeu() {
		labScore = new JLabel();
		labScore.setForeground(Color.WHITE);
		labScore.setText("<html><h3>" + (int) score + "</h3></html>");
		labScore.setBounds(20, 0, 600, 50);
		ecran.add(labScore);

		labGameOver = new JLabel("GAME OVER", SwingConstants.CENTER);
		labGameOver.setForeground(Color.WHITE);
		labGameOver.setFont(new Font("Arial", Font.BOLD, 40));
		labGameOver.setBounds(0, 200, 800, 100);
		labGameOver.setVisible(false);
		ecran.add(labGameOver);

		gestionnaireFinDePartie = new GestionnaireFinDePartie(labGameOver, gestionnaireBDD, gestionnaireHistorique);

		ecran.addMouseListener(this);
		ecran.traiterBoucleAnimation();
	}

	private Joueur creerJoueur1() {
		return new Joueur(
				new Color(0.0f, 0.5f, 0.0f), new Color(0.8f, 0.0f, 0.0f),
				KeyEvent.VK_UP, KeyEvent.VK_DOWN
		);
	}

	private Joueur creerJoueur2() {
		return new Joueur(
				new Color(0.0f, 0.3f, 0.8f), new Color(0.6f, 0.0f, 0.6f),
				KeyEvent.VK_Z, KeyEvent.VK_S
		);
	}

	@Override
	public void keyPressed(KeyEvent e) {
		for (Joueur joueur : joueurs) {
			joueur.appliquerTouche(e.getKeyCode(), true);
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		for (Joueur joueur : joueurs) {
			joueur.appliquerTouche(e.getKeyCode(), false);
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {
	}

	public void demarrer() {
		fenetre = new JFrame("Linea");

		menu = new MenuPrincipal(fenetre, this);
		menu.afficher(pseudo);

		fenetre.setSize(800, 600);
		fenetre.setResizable(false);
		fenetre.setLocationRelativeTo(null);
		fenetre.setVisible(true);
		fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public void lancerPartie(String pseudoSaisi, boolean modeDeuxJoueurs) {
		pseudo = pseudoSaisi;
		deuxJoueurs = modeDeuxJoueurs;

		joueurs.clear();
		joueurs.add(creerJoueur1());
		if (deuxJoueurs) {
			joueurs.add(creerJoueur2());
		}

		for (Joueur joueur : joueurs) {
			ecran.ajouterObjet(joueur.demiCercleArriere);
		}

		ecran.ajouterObjet(lili);

		for (Joueur joueur : joueurs) {
			ecran.ajouterObjet(joueur.demiCercleAvant);
		}

		ecran.ajouterObjet(new Bonus(lili, joueurs, this));
		ecran.ajouterObjet(new Malus(lili, joueurs, this));
		ecran.ajouterObjet(new BonusTaille(lili, joueurs, this));

		ecran.addKeyListener(this);
		ecran.setFocusable(true);

		fenetre.setContentPane(ecran);
		fenetre.revalidate();
		fenetre.repaint();
		ecran.requestFocusInWindow();

		score = 1;
		compteurFrames = 0;
		estGameOver = false;
		gestionnaireNiveau.reinitialiser();
		gestionnaireFinDePartie.reinitialiser();

		double posY = deuxJoueurs ? 250 : 200;
		for (int i = 0; i < joueurs.size(); i++) {
			joueurs.get(i).reinitialiser(posY + i * 100);
		}

		ecran.demarrer();

		if (horloge == null) {
			// 40ms par frame, environ 25fps
			horloge = new Timer(40, this);
		}
		horloge.start();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (estGameOver) return;

		gestionnaireNiveau.mettreAJour();
		if (gestionnaireNiveau.aChangeDeNiveau()) {
			for (Joueur joueur : joueurs) {
				joueur.resetInvincibilite();
			}
		}
		ecran.setCouleurFond(gestionnaireNiveau.getCouleurFondInterpolee());

		double rayonBase = gestionnaireNiveau.getRayonCercle();

		for (Joueur joueur : joueurs) {
			joueur.mettreAJourRayon(rayonBase);
			joueur.mettreAJourInvincibilite();
			if (!cheatMode) joueur.verifierCollisionLigne(lili);
			joueur.resterDansLigne(lili);
		}

		ecran.traiterBoucleAnimation();

		compteurFrames++;
		// 25 frames à 40ms = 1 seconde, donc score ≈ durée en secondes
		if (compteurFrames >= 25) {
			score++;
			compteurFrames = 0;
		}

		boolean tousMotrs = true;
		for (Joueur joueur : joueurs) {
			if (!joueur.estMort) {
				tousMotrs = false;
				break;
			}
		}

		if (tousMotrs) {
			// tous les joueurs sont morts, on déclenche le game over
			estGameOver = true;
			gestionnaireFinDePartie.declencherGameOver(ecran, pseudo, (int) score);
		}

		mettreAJourLabel();
	}

	private void mettreAJourLabel() {
		StringBuilder sb = new StringBuilder("<html><h3>Score : " + (int) score + " | ");
		if (deuxJoueurs) {
			sb.append("J1 : ").append(joueurs.get(0).vies).append(" | ");
			sb.append("J2 : ").append(joueurs.get(1).vies).append(" | ");
		} else {
			sb.append("Vies : ").append(joueurs.get(0).vies).append(" | ");
		}
		sb.append("Niv. ").append(gestionnaireNiveau.getNumeroNiveau()).append("</h3></html>");
		labScore.setText(sb.toString());
	}

	private void retourMenu() {
		if (horloge != null) {
			horloge.stop();
		}

		// on repart de zéro : nouvelle ligne, nouveaux joueurs
		gestionnaireNiveau.reinitialiser();
		lili = new Ligne(new NoiseGenerator(0), gestionnaireNiveau);
		joueurs.clear();

		ecran.viderObjets();
		menu.afficher(pseudo);
	}

	@Override
	public void mouseClicked(MouseEvent e) {
	}

	@Override
	public void mousePressed(MouseEvent e) {
		// un clic n'importe où à la fin de la partie pour revenir au menu
		if (estGameOver) {
			retourMenu();
		}
	}

	@Override
	public void mouseReleased(MouseEvent e) {
	}

	@Override
	public void mouseEntered(MouseEvent e) {
	}

	@Override
	public void mouseExited(MouseEvent e) {
	}

	public GestionnaireScore getGestionnaireBDD() {
		return gestionnaireBDD;
	}

	public GestionnaireHistorique getGestionnaireHistorique() {
		return gestionnaireHistorique;
	}
}