#include <string.h>
#include "pico/platform/panic.h"
#include "pico/stdlib.h"
#include "messagerie.h"
#include <stdio.h>

#define NB_MAX_CDC_CONNEXION 10
#define TAMPON_TAILLE 1020
#define INVALID_ID  ((char)-1)

struct log_buffer_t{
    char idx;
    char tampon[TAMPON_TAILLE];     // Tampon tournant - 1er niveau de tampon
    unsigned int index_tampon_ecriture, index_tampon_lecture;
}log_buffer[NB_MAX_CDC_CONNEXION];

static struct log_buffer_t * get_buffer(const char idx);

static struct log_buffer_t * get_buffer(const char idx){
    for (int i=0; i<NB_MAX_CDC_CONNEXION; i++){
        if(log_buffer[i].idx == idx){
            return &(log_buffer[i]);
        }
    }
    /// TODO: panic ?
    panic("Buffer IDX not found: %d\n", idx);
    return NULL;
}

/// @brief Initialisation des structure de reception des données USB-CDC
void log_init(){
    for (int i=0; i<NB_MAX_CDC_CONNEXION; i++){
        log_buffer[i].idx = INVALID_ID;
        log_buffer[i].index_tampon_ecriture = 0;
        log_buffer[i].index_tampon_lecture = 0;
    }
}

/// @brief Affectation des tampons de reception à une liaison USB-CDC
/// @param idx : 
void log_create(const char idx){
    for (int i=0; i<NB_MAX_CDC_CONNEXION; i++){
        if(log_buffer[i].idx == INVALID_ID){
            log_buffer[i].idx = idx;
            log_buffer[i].index_tampon_ecriture = 0;
            log_buffer[i].index_tampon_lecture = 0;
            return;
        }
    }
    panic("Creation impossible: idx: %d\n", idx);
}

void log_destroy(const char idx){
    struct log_buffer_t * p_log_bufffer;
    p_log_bufffer = get_buffer(idx);
    p_log_bufffer->idx = INVALID_ID;
    printf("Destruction: idx: %d\n", idx);
}

/// @brief Ajoute les données reçu au tampon tournant
/// @param idx Numéro de la liaison CDC
/// @param buffer Pointeur vers les données
/// @param buffer_size Taille des données
void log_add(const char idx, const char * buffer, uint16_t buffer_size){
    struct log_buffer_t * log_buffer;
    log_buffer = get_buffer(idx);

    for(int i=0; i<buffer_size; i++){
        log_buffer->index_tampon_ecriture = log_buffer->index_tampon_ecriture+1;
        if(log_buffer->index_tampon_ecriture >= TAMPON_TAILLE){
            log_buffer->index_tampon_ecriture = 0;
        }
        log_buffer->tampon[log_buffer->index_tampon_ecriture] = buffer[i];
    }
}

/// @brief Incrémente l'index du tampon tournant
/// @param index 
/// @return 
void increment_index(unsigned int *index){
    *index = 1 + *index ;
    if((*index) >= TAMPON_TAILLE){
        *index = 0;
    }
}

/// @brief augmente la position l'index du tampon tournant
/// @param index 
/// @return 
void augmente_index(unsigned int *index, unsigned int offset){
    *index = offset + *index ;
    if((*index) >= TAMPON_TAILLE){
        *index -= TAMPON_TAILLE;
    }
}

void log_analyse(const char idx){
    // Les données sont dans le tampon tournant
    // Nous les copions dans une seconde mémoire pour les analyser.
    // Si un message est trouvé :
    // - Celui-ci est ajouté à la liste des message à traiter
    // - Nous avançons l'index de lecture du tampon cyclique
    // Si nous arrivons à la fin du tampon cyclique, il n'y a plus rien à récupérer.
    enum etat_message_t{
        ETAT_MESSAGE_DEBUT,
        ETAT_MESSAGE_TEXTE,
        ETAT_MESSAGE_VALIDE
    }etat_message;

    struct log_buffer_t * p_log_bufffer;
    char chaine[TAMPON_TAILLE];
    char affiche_message[TAMPON_TAILLE];
    struct message_t message;

    unsigned int index, index_chaine;
    p_log_bufffer = get_buffer(idx);

    index = p_log_bufffer->index_tampon_lecture;
    index_chaine = 0;

    // Copie des données
    do{
        chaine[index_chaine] = p_log_bufffer->tampon[index];
        index_chaine++;
        increment_index(&index);
    }while(index != log_buffer->index_tampon_ecriture);
    
    // Lecture du message
    message.type = 0;
    message.idx = idx;
    unsigned int fin_message = 0;
    for(int i=0; i< index_chaine; i++){
        char index_fin_message;
        switch(etat_message){
            case ETAT_MESSAGE_DEBUT:
                if(chaine[i] == '>'){
                    message.type = '>';
                    message.taille_donnees = 0;
                    etat_message = ETAT_MESSAGE_TEXTE;
                }
                if(chaine[i] == 'd'){
                    // Est-ce que nous avons reçu le message en entier ?
                    if(i + 4 < index_chaine){
                        if(chaine[i+4] == 0){
                            // Message valide
                            message.type = 'd';
                            message.id_carte = chaine[i+1];
                            message.adresse_registre = chaine[i+2];
                            message.taille_donnees = chaine[i+3];
                            i = i + 4; // On se met à la fin du message
                            fin_message = i;
                            put_message(message);                            
                        }
                    }
                }
                if(chaine[i] == 'r'){
                    // Est-ce que nous avons reçu l'entête du message ?
                    if(i + 3 < index_chaine){
                        message.type = 'r';
                        message.adresse_registre = chaine[i+1];
                        message.taille_donnees = chaine[i+2];
                        index_fin_message = i+ message.taille_donnees + 3;
                        if(index_fin_message < index_chaine){
                            if(chaine[index_fin_message] == 0){
                                // Message valide
                                for(int j=i+3; j<index_fin_message; j++){
                                    message.donnees[j - (i+3)] = chaine[j];
                                }
                                i = i + index_fin_message; // On se met à la fin du message 
                                fin_message = index_fin_message;
                                etat_message = ETAT_MESSAGE_DEBUT;
                                
                                put_message(message);
                            }
                        }
                    }
                }
                break;
            
            case ETAT_MESSAGE_TEXTE:
                message.donnees[message.taille_donnees] = chaine[i];
                message.taille_donnees++;
                if(chaine[i] == '\n'){
                    // message valide.
                    message.donnees[message.taille_donnees] = '\0';
                    fin_message = i;
                    put_message(message);

                    etat_message = ETAT_MESSAGE_DEBUT;
                }
                break;

        }
        
    }
    augmente_index(&(p_log_bufffer->index_tampon_lecture), fin_message);
}
