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

#define CONF "mol.conf"
#define LINE_MAX_LEN 128
#define MOL_MAX_LEN 128

void fatal(char* msg) {
    printf("%s\n", msg);
    exit(1);
}

/* Récupère la masse d'un élément connu dans le
fichier de configuration */
float getElemMass(char* elem, FILE* conf) {
    char line[LINE_MAX_LEN];
    char cur_elem[LINE_MAX_LEN];
    float mass;

    rewind(conf);
    while(fgets(line, LINE_MAX_LEN, conf)) {
        sscanf(line, "%s %f", cur_elem, &mass);
        if(strcmp(elem, cur_elem) == 0) {
            return mass;
        }
    }

    return 0;
}

/* Retourne la masse d'une molécule */
float getMolMass(char* mol, FILE* conf) {
    int i = 0;
    float molmass = 0.0;   /* La masse de la molécule */
    float cur_item_mass = 0.0;  /* Contient la masse du dernier élément/groupement trouvé */
    char cur_item[MOL_MAX_LEN];
    int mul;

    while(mol[i] != '\0') {
        if(isupper(mol[i])) { /* Début d'un élément */
            int j = 0;

            cur_item[j++] = mol[i++];
            while(islower(mol[i]) && mol[i] != '\0') /* Ajout de la suite de l'élément */
                cur_item[j++] = mol[i++];
            cur_item[j] = '\0';

            if(cur_item_mass != 0.0) /* Un élément précédent n'a pas été ajouté à la masse totale */
                molmass += cur_item_mass; /* On l'ajoute avant de modifier cur_item_mass */

            cur_item_mass = getElemMass(cur_item, conf);
        } else if(isdigit(mol[i])) { /* Multiplicateur */
            mul = mol[i++] - '0';
            while(isdigit(mol[i]) && mol[i] != '\0') { /* On ajoute la suite du multiplicateur */
                mul *= 10;
                mul += mol[i++] - '0';
            }
            molmass += cur_item_mass * mul; /* On ajoute l'élément précédemment détecté à la masse totale en le 
                                               multipliant par le multiplicateur */
            cur_item_mass = 0.0;
        } else if(mol[i] == '(') { /* Début d'un groupement */
            int j = 0;
            int num_par = 0; /* Compte le nombre de parenthèses ouvrantes trouvées */
            i++;
            while(!(mol[i] == ')' && num_par == 0) && mol[i] != '\0') {
                if(mol[i] == '(')
                    num_par++;
                else if(mol[i] == ')')
                    num_par--;
                cur_item[j++] = mol[i++];
            }
            i++;
            cur_item[j] = '\0';

            if(cur_item_mass != 0.0)
                molmass += cur_item_mass;

            cur_item_mass = getMolMass(cur_item, conf); /* Récupération de la masse du groupe par appel récursif */
        }
    }
    if(cur_item_mass != 0.0)
        molmass += cur_item_mass;

    return molmass;
}

int main(int argc, char* argv[]) {
    if(argc == 1) {
        FILE* conf;
        char mol[MOL_MAX_LEN];

        if((conf = fopen(CONF, "r")) == NULL)
            fatal("Unable to open configuration file for reading");

        printf("> ");
        fgets(mol, MOL_MAX_LEN, stdin);
        mol[strlen(mol)-1] = '\0'; /* Élimination du '\n' final */
        printf("\n%f\n", getMolMass(mol, conf));
        fclose(conf);
    } else if((strcmp(argv[1], "-a") == 0) || (strcmp(argv[1], "--add-element") == 0)) {
        /* Ajout d'un élément au fichier de configuration */
        FILE* conf;
        if(argc != 4)
            fatal("Syntax : mol -a|--add-element <Element name> <Mass>");

        if((conf = fopen(CONF, "a+")) == NULL)
            fatal("Unable to open configuration file for writing");

        fprintf(conf, "%s %s\n", argv[2], argv[3]);
        fclose(conf);
    } else if(argv[1][0] == '-') {
        fatal("Unknow option");
    } else {
        FILE* conf;
        if((conf = fopen(CONF, "r")) == NULL)
            fatal("Unable to open configuration file for reading");

        printf("%f\n", getMolMass(argv[1], conf));
        fclose(conf);
    }
    return 0;
}