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

/* --- Fonctions privée --- */

/**
 * Ajout un noeud au JsonEncoder
 * @param JsonEncoder* La structure pour encoder 
 * @param char* La chaine à mettre dans le noeud
 */
void add_node(JsonEncoder* this, char* str){
    //Création node
    JsonNode* node;
    node = malloc(sizeof(JsonNode));
    //Allocation node
    node->str = malloc(strlen(str) * sizeof(char));
    //Initialisation node
    strcpy(node->str, str);
    //Si 1er node
    if(this->head == NULL){
        this->head = node;
        node->prev = NULL;
    } else {
        node->prev = this->tail;
        node->prev->next = node;
    }
    this->tail = node;
    node->next = NULL;
}

/**
 * Supprimme un noeud
 * @param JsonNode* Le noeud à supprimer
 */
void delete_node(JsonNode* node){
    free(node->str);
}

/* --- Fonctions publique --- */

void ini_encoder(JsonEncoder* this){
    this->head = NULL;
    this->tail = NULL;
    this->length = 0;
}

void add_value(JsonEncoder* this, char* str){
    //Ajoute la longueur de la chaine au total
    this->length += strlen(str) + 2; //Chaine + ", "
    //Ajoute le noeud
    add_node(this, str);
}

void add_string(JsonEncoder* this, char* key, char* val){
    //Creation chaine
    char* str;
    str = malloc((strlen(key) + strlen(val) + 4 + 2 + 1) * sizeof(char)); //clef + val + 4 guillemet + ": " + \0
    sprintf(str, "\"%s\": \"%s\"", key, val);
    //Ajout
    add_value(this, str);
    free(str);
}

void add_number(JsonEncoder* this, char* key, double val, int ndigit){
    //Double en string
    char nombre[20];
    ftoa(val, nombre, ndigit);
    //Creation chaine
    char* str;
    str = malloc((strlen(key) + strlen(nombre) + 2 + 2 + 1) * sizeof(char)); //clef + val + 2 guillemets + ": " + \0
    sprintf(str, "\"%s\": %s", key, nombre);
    //Ajout
    add_value(this, str);
    free(str);
}

void add_integer(JsonEncoder* this, char* key, int val){
    //Creation chaine
    char* str;
    str = malloc((strlen(key) + ceil(val/10.0) + 2 + 2 + 1) * sizeof(char)); //clef + val + 2 guillemets + ": " + \0
    sprintf(str, "\"%s\": %d", key, val);
    //Ajout
    add_value(this, str);
    free(str);
}

void add_boolean(JsonEncoder* this, char* key, boolean val){
    //On determine le boolean
    char bool[6];
    if(val){
        strcpy(bool, "true");
    } else {
        strcpy(bool, "false");
    }
    //Creation chaine
    char* str;
    str = malloc((strlen(key) + strlen(bool) + 2 + 2 + 1) * sizeof(char)); //clef + val + 2 guillemets + ": " + \0
    sprintf(str, "\"%s\": %s", key, bool);
    //Ajout
    add_value(this, str);
    free(str);
}

void add_array(JsonEncoder* this, char* key, char* val){
    add_string(this, key, val); //Pas de gestion special
}

void add_object(JsonEncoder* this, char* key, JsonEncoder val){
    //Recup string du JsonEncoder
    char* json;
    json = json_encode(&val);
    //Creation chaine
    char* str;
    str = malloc((strlen(key) + strlen(json) + 2 + 2 + 1) * sizeof(char)); //clef + val + 2 guillemets + ": " + \0
    sprintf(str, "\"%s\": %s", key, json);
    //Ajout
    add_value(this, str);
    free(str);
    free(json);
}

char* json_encode(JsonEncoder* this){
    boolean first = true;
    //Allocation chaine
    char* str;
    str = malloc((this->length + 2) * sizeof(char)); // La chaine + {}
    //Creation de la chaine
    JsonNode* node;
    node = this->head;
    str[0] = '{';
    while(node != NULL){
        if(first){
            sprintf(str, "%s%s", str, node->str);
            first = false;
        } else {
            sprintf(str, "%s, %s", str, node->str);
        }    
        node = node->next;
    }
    sprintf(str, "%s}", str);
    //Retour
    return str;
}

void clean_json_encoder(JsonEncoder* this){
    //Parcours les noeuds et les supprimes
    JsonNode* node, * tmp;
    node = this->head;
    while(node != NULL){
        tmp = node->next;
        delete_node(node);
        free(node);
        node = tmp;
    }
    //Reset la structure
    this->head = NULL;
    this->tail = NULL;
    this->length = 0;
}