import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.sql.Connection;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.Timer;

public class Jeu implements KeyListener, ActionListener{
	//-------------------------------------------------------------------------
	// PROPRIETES
	//-------------------------------------------------------------------------
	
	// Ecran : instance de ZoneDessin, qui contiendra tous les objets graphiques
	// On ne vapas créer directement l'écran car ça va créer un flash 
	// Dès le lancement du jeu avec l'image de fond par défaut et l'imga du niveau 
	// et les animera
	protected ZoneDessin ecran;
	
	// Le cercle : en fait 2 demis-cercles, un qui passera sous la ligne
	// un qui passera dessus
	protected Cercle demiCercleAvant = new Cercle(90,-180); // celui qui est sur la ligne
	protected Cercle demiCercleArriere = new Cercle(90,180); // celui qui est derrière la ligne
	
	// A FAIRE : ajouter les objets graphiques manquants, s'il y en a 
	protected  Ligne laLigne = new Ligne(); // Ligne infinie du jeu 
	
	// Timer : un objet qui émet des événements à un intervalle choisi, 
	// il sert à donner le pas de l'animation
	protected Timer horloge;
	
	// Une variable qui contiendra le score 
	protected double score=0; 

	// un label qui servira à afficher le score
	protected JLabel labScore;

	protected JLabel labTriche;

	protected boolean jeuCommence = false;

	protected boolean modeTriche = false;

	// Propriété de ma connexion à ma base de données
	Connection conn = null;

	private int utilisateurId;

	// Variables actuelles du début de jeu
	protected int idNiveauActuel = 1;
	protected Niveau niveauEnCours;
	protected int segmentsTermines = 0; // Variable compteur de segments
	protected Segment dernierSegmentValide = null;
	
	//-------------------------------------------------------------------------
	// METHODES
	//-------------------------------------------------------------------------
		
	//-------------------------------------------------------------------------
	// Constructeur de la classe
	//-------------------------------------------------------------------------
	public Jeu(int utilisateurId){
		JFrame fenetre = new JFrame();

		this.utilisateurId = utilisateurId;

		Background premierFond = null;
		// On crée le jeu avec sa base de données
		try {
			conn = NiveauxDataConnect.getInstance().getConnection();
			System.out.println("Connexion réussie !!!");
		} catch (Exception ex) {
			ex.printStackTrace();
			javax.swing.JOptionPane.showMessageDialog(fenetre,"Erreur de connexion à la base de données. Le jeu ne peut pas démarrer.",
				"Erreur Fatale",
				javax.swing.JOptionPane.ERROR_MESSAGE
			);
		}

		// On récupère le niveau 1
		Niveau n1 = NiveauxDataConnect.recupererNiveau(conn, 1);

		if( n1 != null ){
			try {
				// On charge l'mage du niveau 1
				java.net.URL url = Jeu.class.getResource("/" + n1.getImage());
				if(url != null){
					java.awt.Image img = javax.imageio.ImageIO.read(url);
					premierFond = new Background(img);
				}
			} catch (Exception e) {
			}
		}

		this.ecran = new ZoneDessin(premierFond);


		// Gestion du score : a réactiver en fin de TP, inutile au début
		ecran.setLayout(null);

		labScore = new JLabel();
		labScore.setText("<html><h3>score : 0</h3></html>");
		labScore.setBounds(20, 0, 200, 50);
	    ecran.add(labScore);

	    labTriche = new JLabel();
        labTriche.setText("<html><h3 style='color:red;'>Mode de triche activé</h3></html>");
        labTriche.setBounds(600, 0, 200, 50); // Placé en haut à droite (la fenêtre fait 800 de large)
        labTriche.setVisible(false); // Caché par défaut au lancement
        ecran.add(labTriche);
	}
	
	
	//-------------------------------------------------------------------------
	// Méthodes qu'il faut implémenter pour être 
	// conforme à un KeyListener
	//-------------------------------------------------------------------------
	
	//-------------------------------------------------------------------------
	// Appui sur une touche
	// -> l'événement est émis lorsqu'on appuie, puis selon le rythme de 
	// répétition du clavier
	//-------------------------------------------------------------------------
	@Override
	public  void keyPressed(KeyEvent e){
		// keyCode 38 : up
		// keyCode 40 : down
		int keyCode = e.getKeyCode();
		
		if (keyCode==38){ // touche "flèche vers le haut"
			// On demande aux deux demi-cercle de  "monter"
			demiCercleAvant.Monter();
			demiCercleArriere.Monter();
		}
		
		if(keyCode==32){//touche espace
			modeTriche =!modeTriche;
			labTriche.setVisible(modeTriche); //afficher ou cacher le label
			demiCercleAvant.setModeTriche(modeTriche);
			demiCercleArriere.setModeTriche(modeTriche);
		}
	}

	//-------------------------------------------------------------------------
	// Relâchement de la touche 
	//-------------------------------------------------------------------------
	@Override
	public  void keyReleased(KeyEvent e){
		// keyCode 38 : up
		// keyCode 40 : down
		int keyCode = e.getKeyCode();
		
		if (keyCode==38){
			// On demande aux deux demi-cercle "d'arrêter de monter"
			demiCercleAvant.ArreterMonter();
			demiCercleArriere.ArreterMonter();
		}
	}

	//-------------------------------------------------------------------------
	// Une méthode que nous n'utilisons pas
	//-------------------------------------------------------------------------
	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}
	
	//-------------------------------------------------------------------------
	// Démarrage du jeu : 
	//	création de diverses instances et 
	//-------------------------------------------------------------------------
	public void demarrer(){
		
		// Création d'une fenêtre
		JFrame fenetre = new JFrame();
		
		// A FAIRE : 
		// placer dans l'instance de l'écran tous les objets graphiques nécessaires 
		// par exemple : ecran.ajouterObjet(demiCerleArriere);
		this.ecran.ajouterObjet(demiCercleArriere);
		this.ecran.ajouterObjet(laLigne);
		this.ecran.ajouterObjet(demiCercleAvant);


		// on indique que c'est le jeu qui traitera les appuis sur une touche
		ecran.addKeyListener(this);
		ecran.setFocusable(true);
		fenetre.setContentPane(ecran);
		fenetre.pack();
		fenetre.setLocation(100, 100);
		fenetre.setVisible(true);
		// s'assurer que l'écran reçoit le focus clavier
		ecran.requestFocusInWindow();
		fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		javax.swing.JButton boutonscores = BoutonsManager.creerBoutonScores(fenetre, utilisateurId);

		this.ecran.add(boutonscores);

		// Création du timer
		horloge = new Timer(40, this);

		//propriétés dont on aura besoin pour utiliser NiveauxDataConnect
		//---------------------------------------------------------------------------------
		// C'est ici qu'on appelle NiveauxDataConnect pour initialiser la connexion à la base de données des niveaux
		// On fera une synchronisation pour éviter que le jeu se fige
		// pendant l'initialisation de la base de données, qui peut prendre un peu de temps
		//--------------------------------------------------------------------------------
		
		// démarrer le timer sur l'EDT même si l'initialisation BDD a échoué
		if (horloge != null && !horloge.isRunning()) {
			chargerNiveau(idNiveauActuel);
			horloge.start();
		}

		
		// A FAIRE :
		// donner la couleur des 2 demi-cercles, par exemple :  demiCerleArriere.setCouleur(new Color(0.8f,0.0f,0.0f));
		//demiCercleArriere.setCouleur(new Color(26, 95, 161));
		//demiCercleAvant.setCouleur(new Color(26, 95, 161));
	}
	


	//------------------------------------------------------------------------------------------------
	// Méthode permettant de charger le niveau et de l'appliquer
	//------------------------------------------------------------------------------------------------
	public void chargerNiveau(int id){
		System.out.println(conn);
		this.niveauEnCours = NiveauxDataConnect.recupererNiveau(conn, id); 

		if(this.niveauEnCours != null){
			// Remise à zéro de l'état propre au niveau
			this.segmentsTermines = 0;
			this.dernierSegmentValide = null;
			this.ecran.partiePerdue = false;


			// On corse le jeu avec son ID
			//On applique les paramètres aux options de jeu
			this.laLigne.generer(niveauEnCours.getNbSegments(), niveauEnCours.getId());
			this.laLigne.setVitesseLigne(niveauEnCours.getVitesseLigne()); // On modifie la vitesse de déplacement
			this.demiCercleAvant.setCouleur(niveauEnCours.getCouleurCercle());
			this.demiCercleArriere.setCouleur(niveauEnCours.getCouleurCercle());
			this.ecran.changerImageFond(niveauEnCours.getImage());


		}else{
			JFrame fenetre = new JFrame();
			JOptionPane.showMessageDialog(fenetre, "CONGRATULATIONS !!! BRAVO VOUS AVEZ TERMINE LE JEU");
			System.out.println("Fin du jeu !!! BRAVO VOUS AVEZ TERMINE LE JEU");
			horloge.stop();
		}
	}



	//------------------------------------------------------------------------------------------------
	// Méthode permettant de passer au niveau suivant à travers l'id
	//------------------------------------------------------------------------------------------------
	private void passerAuNiveauSuivant(){
		// Nettoyage de l'état de validation avant de charger le niveau suivant
		this.segmentsTermines = 0;
		this.dernierSegmentValide = null;
		// On incrémente l'ID du niveau actuel
		idNiveauActuel++;
		JFrame fenetre = new JFrame();

		// Petite pause avec un message 
		horloge.stop();
		JOptionPane.showMessageDialog(fenetre, "Niveau terminé ! Préparez-vous pour le niveau suivant ;)");

		chargerNiveau(idNiveauActuel);
		horloge.start();
	}


	//------------------------------------------------------------------------------------------------
	// Méthode appelée lorsqu'un événement timer se produit
	// -> C'est ici que la loqique et l'animation du jeu sont traitées
	// -> C'est ici que la vérification de la collision entre la ligne et le cercle doit être faite
	//------------------------------------------------------------------------------------------------
	@Override
	public void actionPerformed(ActionEvent e) {
		// 1. Animer et recalculer le segment courant
		this.ecran.traiterBoucleAnimation();
		this.laLigne.actualiserSegCourant();
		
		// Mode triche : centrer le cercle sur le milieu du segment courant
		if (modeTriche && this.laLigne.getSegCourant() != null) {
			double milieuY = this.laLigne.getSegCourant().getMilieuY();
			this.demiCercleAvant.y = milieuY;
			this.demiCercleArriere.y = milieuY;
		}
		
		// 2. On vérifie si la ligne traverse bien le cercle
		boolean verification = this.laLigne.estDansCercle(this.demiCercleAvant);

		// NB: Le nombre de segment s'incrémente qu'il y est collision ou pas
		if(verification){
			// On compte seulement lorsqu'on change de segment
			Segment segmentCourant = this.laLigne.getSegCourant();
			if(segmentCourant != null && segmentCourant != this.dernierSegmentValide){
				this.dernierSegmentValide = segmentCourant;
				this.segmentsTermines++;
				// Ajouter les points continuellement tant que le segment est vert (cyan)
				this.score += this.niveauEnCours.getMultiplicateurScore();
				this.jeuCommence = true;
			}

			// Vérification si on a atteint l'objectif du niveau : Nb de Segments du niveau atteint
			if(this.segmentsTermines >= this.niveauEnCours.getNbSegments()){
				passerAuNiveauSuivant();
			}

		}else{

			// On compte seulement lorsqu'on change de segment
			Segment segmentCourant = this.laLigne.getSegCourant();
			if(segmentCourant != null && segmentCourant != this.dernierSegmentValide){
				this.dernierSegmentValide = segmentCourant;
				this.segmentsTermines++;
			}

			//-------------------------------------------------------------------------------------------------------------------
			// A FAIRE: Le jeu peut s'arrêter si la ligne n'est pas dans le cercle
			// NB: Le jeu commence quand la ligne est dans le cercle
			// Au début le ligne n'est pas dans le cercle car la ligne est à droite du cercle, et elle se déplace vers la gauche
			//-------------------------------------------------------------------------------------------------------------------
			if (this.laLigne.getSegCourant()!=null && !this.modeTriche) { // Le jeu s'arrête si le joueur n'a pas traversé la première ligne
				this.horloge.stop();        // 1. Arrêter le temps
				this.ecran.partiePerdue = true; // 2. Signaler à l'écran
				this.ecran.repaint();       // 3. Forcer l'affichage du texte
				
				//  GestionBDD.ajouterScore((int)this.score);// enregistrement du score dans la base de donne
			}
		}
		
		labScore.setText("<html><h3>score : " + this.score + "</h3></html>");
		
	}
		
}
