#include "tree.h"

//Contruction d'un arbre
void cons_tree(struct node **ptr_tree, wchar_t val)
{
  *ptr_tree = malloc(sizeof(struct node));
  (*ptr_tree)->val = val;
  (*ptr_tree)->fin = 0;
  (*ptr_tree)->nbr_fils = 0;
  (*ptr_tree)->fils = malloc(sizeof(struct node *)); //Tableau de taille 1 de pointeur de noeud
  (*ptr_tree)->fils[0] = NULL;                       //Cette node n'a pas de fils
}

//Initialiation de chaque element du dictionnaire à nul
void mk_empty_tree(dico *Dico)
{
  for (int i = 0; i < Dico->taille; i++)
  {
    Dico->tab_ptr_tree[i] = NULL; //Chaque element du dico est vide
  }
}

//Création d'un dictionnaire pour enregister les 26 lettres de l'alphabet
void init_dico(dico *Dico)
{
  Dico->taille = 26;
  Dico->tab_ptr_tree = malloc(Dico->taille * sizeof(struct node *));
  mk_empty_tree(Dico);
}

//ajout d'un mot dans le dictionnaire dans son arbre (liée à la première lettre du mot)
void add(struct node **tab_ptr_tree, wchar_t val[], int taille, int fl)
{
  if (tab_ptr_tree[fl] == NULL)cons_tree(&(tab_ptr_tree[fl]), val[0] + 97); //si l'arbre n'existe pas on le crée

  Node *noeudtest = tab_ptr_tree[fl];
  for (int i = 1; i < taille; i++)//on va travailler sur toutes les lettres du mot
  {
    int trouve = -1;
    for (int j = 0; j < noeudtest->nbr_fils; j++)//On recherche si la lettre existe déja
    {
      if (noeudtest->fils[j]->val == val[i])trouve = j;
    }
    if (trouve == -1)
    {
      //ajouter lettre
      noeudtest->nbr_fils++;
      noeudtest->fils = realloc(noeudtest->fils, (noeudtest->nbr_fils) * sizeof(struct node *));//On ajoute de la place dans le tableau de fils pour y mettre celui ci
      cons_tree(&(noeudtest->fils[(noeudtest->nbr_fils) - 1]), val[i]);
      trouve = noeudtest->nbr_fils - 1;
    }

    noeudtest = noeudtest->fils[trouve]; //on jump au noeud suivant (lettre existante ou venant d'étre créé)
  }
  noeudtest->fin = 1;//On a terminé le mot, cette lettre est la dernière du mot
}

//Calcul de la taille de la chaine de caractère donnée en paramètre
int size(wchar_t val[])
{
  int cpt = 0;
  while (val != NULL && val[cpt] != '\0')
  {
    cpt++;
  }
  return cpt;
}

//Tous les caractères en majuscules sont mis en minuscule (même ceux avec accent)
void toLowerCase(wchar_t mot[])
{
  for (int i = 0; i < size(mot); i++)
  {
    if ((mot[i] <= 'Z' && mot[i] >= 'A') || (mot[i] >= 192 && mot[i] <= 214) || (mot[i] >= 216 && mot[i] <= 222))mot[i] += 32;
    else if (mot[i] == 138 || mot[i] == 140 || mot[i] == 142)mot[i] += 16;
    else if (mot[i] == 159)mot[i] += 96;
  }
}

//Récupération de la première lettre du mot et découpade de celui ci en fonction des séparateurs
void splitcarac(dico *Dico, wchar_t message[], wchar_t separateur[])
{

  wchar_t *buffer;
  wchar_t *token = wcstok(message, separateur, &buffer);//On découpe le mot selon les séparateurs
  if(token == NULL)return;

  //On récupére l'id de la première lettre dans notre dico
  int first_letter = -1;
  if (token[0] >= 'a' && token[0] <= 'z')
  {
    first_letter = (int)token[0] - 97;
  }
  else
  {
    for (int i = 26; i < Dico->taille; i++)//On recherche si elle existe et qu'elle n'est pas de 'a' à 'z'
    {
      if (Dico->tab_ptr_tree[i]->val == token[0])
      {
        first_letter = i;
        break;
      }
    }
    if (first_letter == -1)//Elle n'exite pas, on l'ajoute
    {
      first_letter = Dico->taille;
      Dico->taille++;
      Dico->tab_ptr_tree = realloc(Dico->tab_ptr_tree, (Dico->taille) * sizeof(struct node *));//On laisse la place pour ajouter une première lettre de mot
      Dico->tab_ptr_tree[first_letter] = NULL;
      cons_tree(&(Dico->tab_ptr_tree[first_letter]), token[0]);
    }
  }

  add(Dico->tab_ptr_tree, token, size(token), first_letter);//On ajoute le mot (jusqu'au séparateur) au dictionnaire
  if (buffer != NULL)splitcarac(Dico, buffer, separateur);//S'il reste des mots à ajouter on recommence
}

//Chargement du dictionnaire
void load_dico(FILE *fp, dico *Dico, wchar_t separateur[])
{
  wchar_t val[3000];//Nombre de caractère max sur une page

  while (fwscanf(fp, L"%ls", val) == 1)
  {
    toLowerCase(val);//Les caractères sont mis en miniscule
    splitcarac(Dico, val, separateur);//On ajoute tous les mots au dictionnaire
  }

  //On peut tester la bonne ou mauvaise terminaison de la lecture
  if (ferror(fp))wprintf(L"ERREUR de lecture\n");
}

//On libère toute la mémoire de chaque arbre
void free_tree(struct node *ptr_tree)
{
  if (ptr_tree == NULL)return;//On a terminé
  if (ptr_tree->nbr_fils == 0)//Le noeud n'a pas de fils on peut libérer la mémoire
  {
    free(ptr_tree->fils);
    return;
  }
  for (int i = (ptr_tree->nbr_fils) - 1; i >= 0; i--)//si le noeud à des fils ré-exécute la fontion
  {
    free_tree(ptr_tree->fils[i]);
    free(ptr_tree->fils[i]);
  }
  free(ptr_tree->fils);
}

//On libére la mémoire du dictionnaire
void free_dico(dico Dico)
{
  for (int i = 0; i < Dico.taille; i++)//On libére chaque arbre
  {
    if (Dico.tab_ptr_tree[i] != NULL)
    {
      free_tree(Dico.tab_ptr_tree[i]);
      free(Dico.tab_ptr_tree[i]);
    }
  }
  free(Dico.tab_ptr_tree);
}

//Recherche dans le dictionnaire d'un mot
int find_mot(dico Dico, wchar_t mot[])
{
  if (mot == NULL)return 0;

  int fl = -1;
  if (mot[0] >= 'a' && mot[0] <= 'z')
  {
    fl = (int)mot[0] - 97;
  }
  else
  {
    for (int i = 26; i < Dico.taille; i++)
    {
      if (Dico.tab_ptr_tree[i]->val == mot[0])
      {
        fl = i;
        break;
      }
    }
    if (fl == -1){
      wprintf(mot);
      wprintf(L"\n");
      return 1;
    }
  }

  int taille = size(mot);
  if (taille == 1 && Dico.tab_ptr_tree[fl] != NULL)
  {
    if (Dico.tab_ptr_tree[fl]->fin == 0){
      wprintf(mot);
      wprintf(L"\n");
      return 1;
      }else return 0; //vrais
  }
  if (Dico.tab_ptr_tree[fl] == NULL)return 1; //faux

  struct node *ptr_node = Dico.tab_ptr_tree[fl];
  for (int i = 1; i < taille; i++)
  {
    if (ptr_node->nbr_fils == 0){
      wprintf(mot);
      wprintf(L"\n");
      return 1;
    }
    for (int k = 0; k < (ptr_node->nbr_fils); k++)
    {
      if (ptr_node->fils[k]->val == mot[i])
      {
        ptr_node = ptr_node->fils[k];
        break;
      }
      else if (k + 1 == ptr_node->nbr_fils){
        wprintf(mot);
        wprintf(L"\n");
        return 1;
      }
    }
  }

  if (ptr_node->fin == 0){
    wprintf(mot);
    wprintf(L"\n");
    return 1;
  }else return 0;
}

//Test de présence de chaque mot du fichier de l'utilisateur
int find_erreur(dico Dico, FILE *fp, wchar_t separateur[])
{
  wchar_t val[3000];
  int cpt_erreur = 0;

  wprintf(L"Mots non reconnus :\n");

  while (fwscanf(fp, L"%ls", val) == 1)
  {
    toLowerCase(val);//Fichier mit en minuscule
    cpt_erreur += split_text(Dico, val, separateur);//découpage et analyse du texte selon les séparateur
  }

  //On peut tester la bonne ou mauvaise terminaison de la lecture
  if (ferror(fp))wprintf(L"ERREUR de lecture\n");

  return cpt_erreur;
}

//Découpage et analyse du texte selon les séparateur
int split_text(dico Dico, wchar_t message[], wchar_t separateur[])
{
  if (message[0] == 0)return 0;
  wchar_t *buffer;
  wchar_t *token = wcstok(message, separateur, &buffer);//Découpage selon les caractère de séparation
  int err = find_mot(Dico, token);//recherche de la présence du mot dans le dictionnaire
  if (buffer != NULL)err += split_text(Dico, buffer, separateur);

  return err;
}
