#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "symboles.h"
#include "analyseur_lexical.h"
#include "util.h"

#define YYTEXT_MAX 100
#define is_num(c)(('0' <= (c)) && ((c) <= '9'))
#define is_maj(c)(('A' <= (c)) && ((c) <= 'Z'))
#define is_min(c)(('a' <= (c)) && ((c) <= 'z'))
#define is_alpha(c)(is_maj(c) || is_min(c) || (c) == '_' || (c) == '$')
#define is_alphanum(c)(is_num((c)) || is_alpha((c)))

extern FILE *yyin;

char tableSymbole[] = {
  ';', ',', '(', ')', '[', ']', '{', '}', // Code
  '+', '-', '*', '/',                     // Arithmetique
  '<', '=', '?', ':',                     // Comparaison
  '&','|', '!',                           // Logique
  '\0'
};

int codeSymbole[] = {
  POINT_VIRGULE, VIRGULE, PARENTHESE_OUVRANTE, PARENTHESE_FERMANTE, CROCHET_OUVRANT, CROCHET_FERMANT, ACCOLADE_OUVRANTE, ACCOLADE_FERMANTE,
  PLUS, MOINS, FOIS, DIVISE, 
  INFERIEUR, EGAL, INTERROGATION, DEUXPOINTS,
  ET, OU, NON
};

char *tableMotsClefs[] = {
  "si", "alors", "sinon", "tantque", "faire", "entier", "retour", "lire", "ecrire", "pour", '\0'
};

int codeMotClefs[] = { 
  SI, ALORS, SINON, TANTQUE, FAIRE, ENTIER, RETOUR, LIRE, ECRIRE, POUR
};

char yytext[YYTEXT_MAX];
int yyleng;
int nbMotsClefs = 10;
/* Compter les lignes pour afficher les messages d'erreur avec numero ligne */
int nb_ligne = 1;

/*******************************************************************************
 * Fonction qui ignore les espaces et commentaires. 
 * Renvoie -1 si arrivé à la fin du fichier, 0 si tout va bien 
 ******************************************************************************/
int mangeEspaces()
{ 
  char c = fgetc(yyin);
  int comment = 0;
  while( comment || (c == ' ') || (c == '\n') || (c == '\t') || (c == '#' ) ) {
    if( c == '#' ) {
        comment = 1;
    }
    if( c == '\n' ) {
      nb_ligne++;
      comment = 0;
    }
    c = fgetc(yyin);    
  }
  if ( feof(yyin) ) {
    return -1;
  }    
  ungetc(c, yyin);
  return 0;
}

/*******************************************************************************
 * Lit un caractère et le stocke dans le buffer yytext 
 ******************************************************************************/
char lireCar(void)
{
  yytext[yyleng++] = fgetc(yyin);
  yytext[yyleng] = '\0';
  return yytext[yyleng - 1];
}

/*******************************************************************************
 * Remet le dernier caractère lu au buffer clavier et enlève du buffer yytext 
 ******************************************************************************/
void delireCar()
{
  char c;
  c = yytext[yyleng - 1];
  yytext[--yyleng] = '\0';
  ungetc(c, yyin);
}
/*******************************************************************************
 * Fonction principale de l'analyseur lexical, lit les caractères de yyin et
 * renvoie les tokens sous forme d'entier. Le code de chaque unité est défini 
 * dans symboles.h sinon (mot clé, idententifiant, etc.). Pour les tokens de 
 * type ID_FCT, ID_VAR et NOMBRE la valeur du token est dans yytext, visible 
 * dans l'analyseur syntaxique.
 ******************************************************************************/
int yylex(void)
{
  char c;
  int i;
  yytext[yyleng = 0] = '\0';
  
  if (mangeEspaces() == -1)
    return FIN;

  c = lireCar();


  // Symbole simple
  for( int i = 0; tableSymbole[i] != '\0'; ++i ) {
    if( c == tableSymbole[i] ) {
      return codeSymbole[i];
    }
  }

  // Nombre
  if( is_num( c ) )
  {
    do {
      lireCar();
    } while( is_num( yytext[yyleng-1] ) );
    delireCar();

    return NOMBRE;
  }

  //id_var
  if( c == '$' ) {
    i = 1; // 1 pour $

    do {
      lireCar();
      i++;
    } while( is_alphanum( yytext[yyleng-1] ) );
    i--;
    delireCar();


    if(i > 99) {
      return -1;
    }
    
    return ID_VAR;
  }

  // Mot clefs
  do {
    lireCar();
  } while( is_alphanum( yytext[yyleng-1] ) );
  delireCar();

  for( int i = 0; tableMotsClefs[i] != '\0'; ++i )
  {
    if( strcmp( tableMotsClefs[i], yytext ) == 0 ) {
        return codeMotClefs[i];
    }
  }

  for(; yyleng != 1; delireCar() );

  //id_fct
  if( is_alpha( c ) ) {
    int i = 1; // 1 pour $

    do {
      lireCar();
      i++;

    } while( is_alphanum( yytext[yyleng-1] ) );
    i--;
    delireCar();

    if(i > 99) {
      return -1;
    }

    return ID_FCT;
  }

  erreur("Aucune Correspondance");  
  return -1;
}

/*******************************************************************************
 * Fonction auxiliaire appelée par l'analyseur syntaxique tout simplement pour 
 * afficher des messages d'erreur et l'arbre XML 
 ******************************************************************************/
void nom_token( int token, char *nom, char *valeur ) {
  int i;
  
  strcpy( nom, "symbole" );
  if(token == POINT_VIRGULE) strcpy( valeur, "POINT_VIRGULE");
  else if(token == PLUS) strcpy(valeur, "PLUS");
  else if(token == MOINS) strcpy(valeur, "MOINS");
  else if(token == FOIS) strcpy(valeur, "FOIS");
  else if(token == DIVISE) strcpy(valeur, "DIVISE");
  else if(token == PARENTHESE_OUVRANTE) strcpy(valeur, "PARENTHESE_OUVRANTE");
  else if(token == PARENTHESE_FERMANTE) strcpy(valeur, "PARENTHESE_FERMANTE");
  else if(token == CROCHET_OUVRANT) strcpy(valeur, "CROCHET_OUVRANT");
  else if(token == CROCHET_FERMANT) strcpy(valeur, "CROCHET_FERMANT");
  else if(token == ACCOLADE_OUVRANTE) strcpy(valeur, "ACCOLADE_OUVRANTE");
  else if(token == ACCOLADE_FERMANTE) strcpy(valeur, "ACCOLADE_FERMANTE");
  else if(token == EGAL) strcpy(valeur, "EGAL");
  else if(token == INFERIEUR) strcpy(valeur, "INFERIEUR");
  else if(token == ET) strcpy(valeur, "ET");
  else if(token == OU) strcpy(valeur, "OU");
  else if(token == NON) strcpy(valeur, "NON");
  else if(token == FIN) strcpy(valeur, "FIN");
  else if(token == VIRGULE) strcpy(valeur, "VIRGULE");
  else if(token == INTERROGATION) strcpy(valeur, "INTERROGATION");
  else if(token == DEUXPOINTS) strcpy(valeur, "DEUXPOINTS");

  else if( token == ID_VAR ) {
    strcpy( nom, "id_variable" );  
    strcpy( valeur, yytext );        
  }
  else if( token == ID_FCT ) {
    strcpy( nom, "id_fonction" );    
    strcpy( valeur, yytext );    
  }
  else if( token == NOMBRE ) {
    strcpy( nom, "nombre" );
    strcpy( valeur, yytext ); 
  }
  else {
    strcpy( nom, "mot_clef" );
    for(i=0; i < nbMotsClefs; i++){
      if( token ==  codeMotClefs[i] ){
        strcpy( valeur, tableMotsClefs[i] );
        break;
      }
    }
  }  
}
/*******************************************************************************
 * Fonction auxiliaire appelée par le compilo en mode -l, pour tester l'analyseur
 * lexical et, étant donné un programme en entrée, afficher la liste des tokens.
 ******************************************************************************/

void test_yylex_internal(FILE *yyin) {
  int uniteCourante;
  char nom[100];
  char valeur[100];  
  do {
    uniteCourante = yylex();  
    nom_token( uniteCourante, nom, valeur );
    printf("%s\t%s\t%s\n", yytext, nom, valeur);
  } while (uniteCourante != FIN);
}
