#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef struct{
    char *identifiant,*espece;
    int age,sante;
    float hauteur,diametre,volume;
}Foret;

const char* separateur=";"; //Declaration de separateur qui sera utiliser par le strtok pour decouper les éléments du fichier csv

void viderBuffer() //Fonction qui videe le buffer à l'entrée (donc si on fait un scanf pour un caractere, sa enleve tout autre caractere qu'on peut avoir mis et le \n)
{
    char c;
    while (c != '\n')
    {
        c = getchar();
    }
}

//Fonction qui lit les float du fichier csv, on en a besoin car les floats ont des , au lieu de .
float lecture_float(char* chaine){
    //Prend un pointeur de la chaine pour la parcourir
    char* p=chaine;
    //Boucle qui cherche la virgule et la remplace par un point
    do{
        if(*p==','){
            *p='.';
        }
        p++;
    //Quand le nombre precedent est un point, on a trouver et rempacé la virgule et on sort de la boucle
    //Si on arrive a la fin de la chaine, on assume que la chaine est un flottant qui a deja un .
    }while((*(p-1)!='.' || *p=='\0'));
    //On transforme la chaine en un flottant et on la retourne
    return strtof(chaine,NULL); 
}

//Procedure qui prend un flottant et écrit dans la chaine le flottant en tant que caractere avec une precision maximum de 2 chiffre après la virgule
void Ecriture_float(float reel,char* chaine){
    sprintf(chaine,"%.2f",reel); //Met dans chaine le flottant en tant que caractere
    char* p=chaine; //Declaration d'un pointeur pour parcourir la chaine
    do{ //Parcours la chaine pour chercher le . et le remplace par une virgule
        if(*p=='.'){
            *p=',';
        }
        p++;
    }while((*(p-1)!=',' || *p=='\0'));
}

//Fonction qui prend en parametre un pointeur de tableau de foret et qui met dedans 
void lecture_fichier(Foret **foret,int *compteur){ //Le premier * est la car foret est un tableau (donc un pointeur), le deuxieme et car on fait un passage par adresse
    FILE* fichier=NULL; //Declaration d'un pointeur de fichier
    fichier=fopen("foret_arbres_50_V1.csv","r"); //Ou ouvre le fichier csv en mode lecture et on met son pointeur dans la variable fichier
    if(fichier==NULL){
        printf("Erreur d'ouverture du fichier, verifier que le fichier existe et que vous avez mis son nom correctement"); //Si on n'arrive pas a lire le fichier, met un message d'erreur
    }
    else{
        char chaine[151]; //Declaration d'un tableau de caractère ou on mettra toutes les lignes du fichier csv
        *compteur=-1; //On commence le compteur a -1 car sinon il faudrait enlever 1 après la boucle while car il compte aussi la ligne NULL, si le fichier est vide le compteur sera a 0

        fgets(chaine,150,fichier); //Enleve la premiere ligne avec le nom des colonnes

        do{ //Compte le nombre de ligne a lire dans le fichier (ne compte pas la premiere ligne)
            (*compteur)++;
        }while(fgets(chaine,150,fichier)!=NULL);

        fclose(fichier); //Ferme le fichier car on a finit de l'utiliser
        free(*foret); //Lbibere l'espace memoire de foret avant le malloc au cas ou foret avait deja été initialisé avant
        *foret=(Foret*)malloc(*compteur*sizeof(Foret)); //On créé le tableau de la foret avec assez d'espace pour mettre tout le contenu du fichier csv
        fichier=fopen("foret_arbres_50_V1.csv","r"); //On reouvre le fichier en mode lecture pour lire les elements du fichier
        fgets(chaine,150,fichier); //Enleve la premiere ligne avec le nom des colonnes
        Foret *p=*foret; //Declaration d'un pointeur pour parcourir le tableau foret
        char *token; //Declaration d'un pointeur token ou on mettra tout les elements (identifiant,espece,age...) donner par strtok

        while(fgets(chaine,150,fichier)!=NULL){ //On continue jusqu'a qu'on atteigne la derniere ligne, !!! ATTENTION : c'est le fgets qui retourne un NULL, il ne met pas NULL dans la variable chaine
            token=strtok(chaine,separateur); //On prend le premier élément de la ligne (on prend tout jusqu'à qu'on trouve un caractère contenu dans la chaine separateur, ici juste le ;) et on met le pointeur de tableau de caractere obtenu dans token
            p->identifiant=(char*)malloc(strlen(token)+1); //On créé un tableau dynamique pour mettre les chaine de caractere
            strcpy(p->identifiant, token); //On copie la chaine obtenu avec le strtok et avec comme pointeur token dans identifiant
            token=strtok(NULL,separateur); //On met un NULL pour prendre l'élément suivant, voir comment marche strtok sur l'internet ou demander a gabriel
            p->espece=(char*)malloc(strlen(token)+1);
            strcpy(p->espece, token);
            p->age=strtol(strtok(NULL,separateur),NULL,0); //On utilise un strtol (transforme une chaine en int (techniquement c'est un long mais on l'utilise aussi pour obtenir des int)) car le resultat de strtok est une chaine
            p->hauteur=lecture_float(strtok(NULL,separateur)); //On utilise la fonction lecture_float car le fichier csv a des , au lieu de .
            p->diametre=lecture_float(strtok(NULL,separateur));
            p->volume=lecture_float(strtok(NULL,separateur));
            p->sante=strtol(strtok(NULL,separateur),NULL,0); 
            p++; //On incremente le pointeur du tableau foret p
        }
        fclose(fichier); //On ferme le fichier
        printf("Lecture du fichier fini\n"); //Un message pour qu'on sache qu'on a fini
    }
}

// affichage du contenu de la foret dans un tableau
void affichage(Foret *foret, int enregistres)
{
    printf("\nIdentifiant  |   Espece   |  Age  |  Hauteur  |  Diametre  |  Volume  |  Sante\n");       // En tete du tableau
    printf("-------------|------------|-------|-----------|------------|----------|----------\n");     // Ligne de separation

    for (int i = 0; i < enregistres; i++)   // Boucle qui affiche tout les elements de la foret
    {
        printf("%12s | %10s | %5d | %9.2f | %10.2f | %8.2f | %7d\n",  // Formatage pour que sa soit aligné
               foret[i].identifiant,                        //
               foret[i].espece,                             //
               foret[i].age,                                // 
               foret[i].hauteur,                            //donne les elements de la foret a afficher
               foret[i].diametre,                           //
               foret[i].volume,                             //
               foret[i].sante);                             //
    }
        printf("-------------|------------|-------|-----------|------------|----------|----------\n");   // Ligne de separation
}




void Ecriture_fichier(Foret *foret,int nombre_element){
    FILE* fichier=NULL; //Declaration d'un pointeur de fichier
    char chaine[20];
    fichier=fopen("foret_arbres_50_V1_ecriture.csv","w"); //On reouvre le fichier en mode lecture pour lire les elements du fichier
    fprintf(fichier,"Identifiant;Espece;Age; Hauteur (m) ; Diametre (cm) ; Volume (m3) ;Sante (/10);;;;;;\n"); //On ecrit la premiere ligne du fichier csv
    Foret *p=foret; //Declaration d'un pointeur pour parcourir le tableau foret
    for(int i=1;i<=nombre_element;i++){ //On écrit autant de ligne qu'il y a d'argument
        fprintf(fichier,"%s;%s;%d;",p->identifiant,p->espece,p->age); //On écrits l'identifiant, l'espece et l'age en premier, on ne peut pas tout mettre sur une ligne a cause des float
        Ecriture_float(p->hauteur,chaine); //On appelle la fonction ecriture float pour mettre dans chaine le float avec une virgule
        fprintf(fichier," %s  ;",chaine); //On ecrit la chaine obtenu (elle ressemble a quelque chose comme sa : "20,52")
        Ecriture_float(p->diametre,chaine); //On repete pour les autres float
        fprintf(fichier," %s  ;",chaine); //idem
        Ecriture_float(p->volume,chaine); //idem
        fprintf(fichier," %s  ;",chaine); //idem
        fprintf(fichier,"%d;;;;;;\n",p->sante); //On met la valeur de santé et on met les 6 ; qui reste, le \n est pour passer a la ligne suivante
        p++; //On incremente pour ecrite la prochaine ligne
    }
    fclose(fichier);//On ferme le fichier
}


// fonction de recherche par espèce
// cette fonction demande une espèce à l'utilisateur et affiche tous les arbres correspondants
void recherche_espece(Foret *foret, int nb_element)
{

    // Delcaration des variables
    char espece_saisie[50]; // Chaine pour stocker le nom de l'espèce tapé par l'utilisateur
    int trouve = 0;         // variable qui sert à la fort de compteur (combien d'arbres trouvé quoi)
                    // index pour remplir le tableau temporaire resultats

    // Saisie utilisateur
    printf("Quelle espece recherchez-vous ? : ");
    scanf("%s", espece_saisie); // récupère le mot saisi (exemple genre un chene)

    // Allocation dynamique d'un tableaux temporaire
    Foret *resultats = (Foret *)malloc(nb_element * sizeof(Foret));
    // verification de sécurité demandé par enzo: si malloc echoue, on arrête tout
    if (resultats == NULL)
    {
        printf("Erreur d'allocation memoire \n");
        return;
    }

    for (int i = 0; i < nb_element; i++)
    {
        // strcmp compare deux chaînes.  si elle renvoie 0, les chaînes sont identiques.
        if (strcmp(foret[i].espece, espece_saisie) == 0)
        {
            // si c'estl a bonne espece, on copie l'arbre du tableau principal
            // vers notre tableau temporaire 'resultats' à la position 'trouve'
            resultats[trouve] = foret[i];
            // On augmente la compteur pour préparer la prochaine case du tableau
            trouve++;
        }
    }
    // Affichage des resultats (peut être sa fonctionne cette fois)

    if (trouve > 0)
    {
        printf("Resultats de la recherche pour: %s \n", espece_saisie);
        // pour faire simple sa lui envoie le tableau temporaire créer juste avant et le nombre exact d'arbre trouvé
        affichage(resultats, trouve);
    }
    else
    {
        printf("Aucun arbre de l'espece '%s' n'a ete trouve.\n", espece_saisie);
    }
    // libéré la mémoires
    free(resultats);
}


// fonction de tri par sélection selon l'âge des arbres

void tri_selection_age(Foret *foret, int nombre_element)
{
    // Tri par sélection : on cherche le plus petit élément et on le place au début
    
    int i, j, posmin;
    Foret temp;
    
    // Boucle principale : parcourt chaque position du tableau
    for (i = 0; i < nombre_element - 1; i++)
    {
        posmin = i; // On suppose que le plus petit est à la position i
        
        // Boucle interne : cherche le plus petit élément à partir de i+1
        for (j = i + 1; j < nombre_element; j++)
        {
            // Si on trouve un âge plus petit on mémorise sa position
            if (foret[j].age < foret[posmin].age)
            {
                posmin = j;
            }
        }
        
        // Si on a trouvé un élément plus petit on échange
        if (posmin != i)
        {
            temp = foret[i];           // Sauvegarde l'arbre à la position i
            foret[i] = foret[posmin];        // Place le plus petit à la position i
            foret[posmin] = temp;            // Place l'ancien élément à la position posmin
        }
    }

    affichage(foret, nombre_element); // Affiche la foret triée
}


// fonction de tri à bulle selon la santé des arbres
void tri_bulle_sante(Foret *foret, int nombre_element)
{

    int i = 1, j;
    Foret temp;
    bool echange;

    // Boucle principale : parcourt chaque élément du tableau
    do
    {

        echange = false; // Réinitialise échange à chaque itération
        for (j = 0; j < nombre_element - i; j++)
        {
            // Compare les éléments adjacents
            if (foret[j].sante < foret[j + 1].sante)
            {
                // Échange si l'élément courant est plus grand que le suivant
                temp = foret[j];
                foret[j] = foret[j + 1];
                foret[j + 1] = temp;
                echange = true; // Indique qu'un échange a eu lieu
            }
        }
        i = i + 1;

    } while (echange); // Continue tant qu'il y a eu des échanges
    affichage(foret, nombre_element); // Affiche la foret triée
}




int main(){
    Foret *laforet = NULL; //Declaration de la foret
    int nb_element=0;//Declaration d'une variable nb_element avec le nombre d'arbre dans la foret car c'est plus simple que de calculer la taille du tableau plus tard
    char choix_menu;//Variable ou on mettra le caractère choisit quand on est dans le menu
    bool continue_menu=true;//Variable qui decide si on reste dans le menu ou on quitte le programme
    do{ 
        //Affichage du menu (ajout de nouvelles options possible plus tard)
        printf("========================================\n");
        printf("   GESTION DE FORET - MENU PRINCIPAL    \n");
        printf("========================================\n");
        printf("  l  -  Charger un fichier CSV\n");
        printf("  w  -  Sauvegarder les changements\n");
        printf("  A  -  Afficher la foret\n");
        printf("  R  -  Rechercher une espece\n");
        printf("  tri1 -  Trier la foret par age (t)\n");
        printf("  tri2 -  Trier la foret par sante (b)\n");
        printf("  Q  -  Quitter\n");
        printf("========================================\n");
        printf("\nVotre choix : ");
        scanf("%c",&choix_menu);
        viderBuffer(); // On vide le buffer pour enleve le \n que scanf n'enleve pas et au cas l'utilisateur met plusieurs caractere
        switch (choix_menu){ //Menu qui permet de choisir ce que l'on veut faire (voir menu au dessus)
            case 'l':
            {
                lecture_fichier(&laforet, &nb_element);
                printf("test ligne debut fichier : '%s', '%s', %d, %.2f, %.2f, %.2f, %d\n", laforet[0].identifiant, laforet[0].espece, laforet[0].age, laforet[0].hauteur, laforet[0].diametre, laforet[0].volume, laforet[0].sante);
                printf("Test ligne fin fichier : '%s', '%s', %d, %.2f, %.2f, %.2f, %d\n", laforet[nb_element - 1].identifiant, laforet[nb_element - 1].espece, laforet[nb_element - 1].age, laforet[nb_element - 1].hauteur, laforet[nb_element - 1].diametre, laforet[nb_element - 1].volume, laforet[nb_element - 1].sante);
                break;
            }
            case 'Q' : {continue_menu=false;break;}
            case 'w' : {Ecriture_fichier(laforet,nb_element);break;}
            case 'A' :
            case 'a' : {
                if (nb_element>0) // Verifie qu'il y a des elements a afficher
                { 
                    affichage(laforet,nb_element);break;
                } 
                else 
                {
                    printf("Aucun element a afficher, veuillez d'abord charger un fichier csv\n");
                    break;
                }
            }
            case 'R':
            case 'r':
            {
                if (nb_element > 0) // Verifie qu'il y a des elements a rechercher
                {
                    recherche_espece(laforet, nb_element);
                    break;
                }
                else
                {
                    printf("Veuillez d'abord charger le fichier (Option l).\n");
                    break;
                }
            }
            case 'T':
            case 't': {
                if (nb_element > 0) // Verifie qu'il y a des elements a trier
                {
                    tri_selection_age(laforet, nb_element);
                    break;
                }
                else
                {
                    printf("Aucun element a trier, veuillez d'abord charger un fichier csv\n");
                    break;
                }
            }
            case 'B':
            case 'b':
            {
                if (nb_element > 0) // Verifie qu'il y a des elements a trier
                {
                    tri_bulle_sante(laforet, nb_element);
                    break;
                }
                else
                {
                    printf("Aucun element a trier, veuillez d'abord charger un fichier csv\n");
                    break;
                }
            }
            default : {printf("Erreur, choix non reconnu\n");break;}
        }
    }while(continue_menu);
    printf("Arret du programme");
    return 0;
}


