/* 
 * File:   json_parser.c
 * Author: Arthur Brandao
 *
 * Created on 28 octobre 2018
 */
#include <stdio.h>
#include <stdlib.h>
#include "json.h"

/* --- Fonctions privées --- */

/**
 * Passe un objet json dans une chaine json
 * @param char* La chaine json
 * @return int Le nombre de caractères à passer
 */
int skip_object(char* json) {
    int compteur = 1;
    json++;
    //On compte le nombre caractere à sauter
    while (*json && *json != '}') {
        //Si on trouve un autre objet dans l'objet
        if (*json == '{') {
            skip_object(json);
        }
        compteur++;
        json++;
    }
    return compteur;
}

/**
 * Passe un tableau json dans une chaine json
 * @param char* La chaine json
 * @return int Le nombre de caractères à passer
 */
int skip_array(char* json) {
    int compteur = 1;
    json++;
    //On compte le nombre caractere à sauter
    while (*json && *json != ']') {
        json++;
        compteur++;
    }
    return compteur;
}

/**
 * Compte le nombre de clef d'une chaine json
 * @param char* La chaine json 
 * @return int Le nombre de clef
 */
int key_counter(char* json){
    //Declaration variable
    int compteur = 0;
    boolean key = true;
    //On parcours la chaine une 1er fois pour compter le nombre de clef
    while (*json && *json != '}') {
        //Si on trouve le debut d'une clef
        if (*json == '"' && key) {
            compteur++;
            key = false;
        }
        //Si on tombe sur un autre objet json
        else if (*json == '{' && !key) {
            json += skip_object(json);
        }            
        //Si on tombe sur un tableau
        else if (*json == '[' && !key) {
            json += skip_array(json);
        }            
        //Si on trouve un separateur de valeur
        else if (*json == ',' && !key) {
            //Le prochain element est une clefS
            key = true;
        }
        json++;
    }
    //Retour
    return compteur;
}

/**
 * Extraction d'une clef
 * @param JsonParser* La structure pour sauvegarder l'extraction
 * @param int La position dans la structure
 * @param char* La chaine json
 * @return int JSON_ERROR ou le nombre de caractère de la clef
 */
int parse_key(JsonParser* this, int index, char* json) {
    //Declaration variable
    int compteur = 0;
    //On parcours jusqu'a la fin de la clef
    json++;
    while (*json && *json != '"') {
        json++;
        compteur++;
    }
    //Si json mal formé
    if (!*json) {
        return JSON_ERROR;
    }
    //Setup les infos
    this->key[index] = json - compteur;
    this->key_length[index] = compteur;
    //Sinon retourne ok
    return ++compteur;
}

/**
 * Extraction d'une valeur
 * @param JsonParser* La structure pour sauvegarder l'extraction
 * @param int La position dans la structure
 * @param char* La chaine json
 * @return int JSON_ERROR ou le nombre de caractère de la valeur
 */
int parse_val(JsonParser* this, int index, char* json){
    //Declaration variable
    int compteur = 0;
    //Regarde le type de valeur
    switch(*json){
        //String
        case '"':
            //Cherche le guillement de fin
            json++;
            while (*json && *json != '"') {
                json++;
                compteur++;
            }
            //Si json mal formé
            if (!*json) {
                return JSON_ERROR;
            }
            //Setup les infos
            this->val[index] = json - compteur;
            this->val_length[index] = compteur;
            this->type[index] = JSON_STRING;
            break;
        //Boolean
        case 't':
            compteur = 4;
            this->val[index] = json;
            this->val_length[index] = compteur;
            this->type[index] = JSON_BOOLEAN;
            break;
        case 'f':
            compteur = 5;
            this->val[index] = json;
            this->val_length[index] = compteur;
            this->type[index] = JSON_BOOLEAN;
            break;
        //Nombre
        case '-':
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            //Cherche espace de fin ou fin json ou suite json
            while (*json && *json != ' ' && *json != '}' && *json != ',') {
                json++;
                compteur++;
            }
            //Si json mal formé
            if (!*json) {
                return JSON_ERROR;
            }
            //Setup les infos
            this->val[index] = json - compteur;
            this->val_length[index] = compteur;
            this->type[index] = JSON_NUMBER;
            break;
        //Tableau
        case '[':
            compteur = skip_array(json) + 1;
            this->val[index] = json;
            this->val_length[index] = compteur;
            this->type[index] = JSON_ARRAY;
            break;
        //Objet
        case '{':
            compteur = skip_object(json) + 1;
            this->val[index] = json;
            this->val_length[index] = compteur;
            this->type[index] = JSON_OBJECT;
            break;
        //Autre
        default:
            return JSON_ERROR;
    }
    //Retour
    return ++compteur;
}

/**
 * Erreur lors du la procedure de parse
 * Supprime la structure et retourne un code d'erreur
 * @param JsonParser* structure traitée pendant l'erreur
 * @return int JSON_ERROR
 */
int parse_error(JsonParser* this){
    clean_json_parser(this);
    return JSON_ERROR;
}

/**
 * Cherche l'index d'une clef
 * @param JsonParser* La structure initialisé par json_parse
 * @param char* La clef à rechercher
 * @return int La position
 */
int search_index(JsonParser* this, char* key){
    int length = strlen(key);
    //Cherche la clef
    for(int i = 0; i < this->elt; i++){
        if(strncmp(this->key[i], key, length) == 0){
            return i;
        }
    }
    //Si rien trouver
    return JSON_ERROR;
}

/* --- Fonctions publiques --- */

int json_parse(JsonParser* this, char* json) {
    //Declaration variable
    char* tmp;
    int temp, compteur;
    boolean key = true;
    //Compte le nombre de clef
    compteur = key_counter(json);
    //Allocation de la taille des tableaux
    this->str = malloc(strlen(json) * sizeof (char));
    strcpy(this->str, json);
    this->elt = compteur;
    this->key = malloc(compteur * sizeof (char*));
    this->key_length = malloc(compteur * sizeof (int));
    this->val = malloc(compteur * sizeof (char*));
    this->val_length = malloc(compteur * sizeof (int));
    this->type = malloc(compteur * sizeof (int));
    //On reparcours le tableau pour parser
    tmp = this->str;
    compteur = 0;
    while (*tmp && *tmp != '}') {
        //Si on trouve une clef
        if (*tmp == '"') {
            //Lecture clef de la clef
            if((temp = parse_key(this, compteur, tmp)) == JSON_ERROR){ return parse_error(this); }
            tmp += temp;
            key = false;
        }
        //Si on trouve une valeur
        else if(*tmp == ':'){
            //Si pas de claf avant
            if(key){ return parse_error(this); }
            //Saute les espaces
            tmp++;
            while(*tmp == ' ') { tmp++; }
            //Lecture valeur
            if((temp = parse_val(this, compteur, tmp)) == JSON_ERROR){ return parse_error(this); }
            tmp += temp;
            key = true;
            compteur++;
        }
        tmp++;
    }
    //Si on s'arrete sur une clef
    if(!key){
        return parse_error(this);
    }
    //Sinon ok
    return JSON_OK;
}

char* key_index(JsonParser* this, int index){
    //Index incorrect
    if(index < 0 || index >= this->elt){
        return NULL;
    }
    //Creation string d'accueil
    char* val;
    val = malloc((this->key_length[index] + 1) * sizeof(char)); //+1 pour \0
    memset(val, 0, this->key_length[index] + 1);
    //Copie valeur
    strncpy(val, this->key[index], this->key_length[index]);
    //Retour
    return val;
}

char* get_index(JsonParser* this, int index){
    //Index incorrect
    if(index < 0 || index >= this->elt){
        return NULL;
    }
    //Creation string d'accueil
    char* val;
    val = malloc((this->val_length[index] +1) * sizeof(char)); //+1 pour \0
    memset(val, 0, this->val_length[index] + 1);
    //Copie valeur
    strncpy(val, this->val[index], this->val_length[index]);
    //Retour
    return val;
}

int get_pos(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return JSON_ERROR;
    }
    return index;
}

int get_type(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return JSON_ERROR;
    }
    return this->type[index];
}

char* get_value(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return NULL;
    }
    return get_index(this, index);
}

double get_number(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return JSON_ERROR;
    }
    //Verif type
    if(this->type[index] != JSON_NUMBER){
        return JSON_ERROR;
    }
    //Recup valeur
    char* val;
    val = get_index(this, index);
    //Cast et retoure
    double res;
    res = atof(val);
    free(val);
    return res;
}

int get_integer(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return JSON_ERROR;
    }
    //Verif type
    if(this->type[index] != JSON_NUMBER){
        return JSON_ERROR;
    }
    //Recup valeur
    char* val;
    val = get_index(this, index);
    //Cast et retoure
    int res;
    res = atoi(val);
    free(val);
    return res;
}

boolean get_boolean(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return false;
    }
    //Verif type
    if(this->type[index] != JSON_BOOLEAN){
        return false;
    }
    //Recup valeur
    char* val;
    val = get_index(this, index);
    //Cast et retoure
    if(val[0] == 't'){
        return true;
    }
    return false;
}

char* get_array(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return NULL;
    }
    //Verif type
    if(this->type[index] != JSON_ARRAY){
        return NULL;
    }
    //Retour pas de gestion de tableau actuellement
    return get_index(this, index);
}

JsonParser* get_object(JsonParser* this, char* key){
    //Recup index
    int index;
    if((index = search_index(this, key)) == JSON_ERROR){
        return NULL;
    }
    //Verif type
    if(this->type[index] != JSON_OBJECT){
        return NULL;
    }
    //Recup valeur
    char* val;
    val = get_index(this, index);
    //Parse
    JsonParser* json;
    json = malloc(sizeof(JsonParser));
    if(json_parse(json, val) == JSON_ERROR){
        return NULL;
    }
    //Retour JSON utilisable
    free(val);
    return json;
}

void clean_json_parser(JsonParser* this) {
    free(this->key);
    free(this->key_length);
    free(this->val);
    free(this->val_length);
    free(this->type);
    free(this->str);
}