#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "hardware/i2c.h"
#include "i2c_annexe.h"
#include "i2c_maitre.h"
#include "Test_i2c.h"
#include <stdio.h>

#define TEST_TIMEOUT_US 10000000

void affiche_contacteur(void);

int test_i2c_bus(void);
int test_i2c_lecture_pico_annex(void);
int test_i2c_lecture_pico_annex_nb2(void);
int test_i2c_ecriture_pico_annex_nb(void);
int test_i2c_ecriture_pico_annex_nb_2(void);
bool reserved_addr(uint8_t addr);


int test_i2c(){
    int lettre;

    while(1){

        do{
            printf("A : Scan bus I2C\n");
            printf("B : Lecture I2C bloquante\n");
            printf("C : Lecture I2C non bloquante\n");
            printf("D : Ecriture I2C non bloquante\n");
            printf("E : Ecriture I2C non bloquante - fonctions encapsulées\n");            
            printf("Q : Quitter\n");
            
            lettre = getchar_timeout_us(TEST_TIMEOUT_US);
            stdio_flush();
        }while(lettre == PICO_ERROR_TIMEOUT ||lettre == 0);

        switch(lettre){

            case 'A':
            case 'a':
                while(test_i2c_bus());
                break;

            case 'B':
            case 'b':
                while(test_i2c_lecture_pico_annex());
                break;

            case 'C':
            case 'c':
                while(test_i2c_lecture_pico_annex_nb2());
                break;

            case 'D':
            case 'd':
                while(test_i2c_ecriture_pico_annex_nb());
                break;

            case 'E':
            case 'e':
                while(test_i2c_ecriture_pico_annex_nb_2());
                break;

            case 'Q':
            case 'q':
                return 0;
        }
    }
    
}

bool reserved_addr(uint8_t addr) {
    return (addr & 0x78) == 0 || (addr & 0x78) == 0x78;
}

/// @brief Scan le bus I2C
/// @return 0
int test_i2c_bus(){
    uint8_t reception[8];
    uint8_t emission[8];
    //uint8_t adresse = 0b0100000;
    int statu;
    int lettre;

    emission[0]=6; // Registre à lire

    i2c_maitre_init();
    // Scan bus I2C - cf SDK
    printf("\nI2C Bus Scan\n");
    printf("   0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
    for (int addr = 0; addr < (1 << 7); ++addr) {
        if (addr % 16 == 0) {
            printf("%02x ", addr);
        }
        int ret;
        uint8_t rxdata=0x55;
        if (reserved_addr(addr))
            ret = PICO_ERROR_GENERIC;
        else{
            absolute_time_t time_out = get_absolute_time();
            time_out += 100000; // Ajout 100 ms
            ret = i2c_read_blocking_until(i2c_default, addr, &rxdata, 1, false, time_out);
        }
        
        printf(ret < 0 ? "." : "@");
        printf(addr % 16 == 15 ? "\n" : " ");
    }
    printf("Done.\n");
    return 0;

}


/// @brief Test de lecture I2C - Attention cette fonction écrit 1 octet avant de lire... 
/// @return 0
int test_i2c_lecture_pico_annex(){
    i2c_maitre_init();
    uint8_t tampon[10];
    uint8_t registre=2;
    uint8_t adresse = 0x17;
    int ret; 
    
    ret = i2c_write_blocking(i2c0, adresse, &registre, 1, false);
    if(ret < 0){
        printf("Erreur I2C : %d", ret);
        return 0;
    }

    ret = i2c_read_blocking(i2c_default, adresse, tampon, 10, false);
    if(ret < 0){
        printf("Erreur I2C : %d", ret);
    }else{
        for(int i=0; i<10; i++){
            printf("%c", tampon[i]);
        }
        printf("\n");

        for(int i=0; i<10; i++){
            printf("%2x ", tampon[i]);
        }
        printf("\n");
    }
    return 0;
}

/// @brief Lecture I2C non bloquante
/// @return 0
int test_i2c_lecture_pico_annex_nb2(){
    i2c_maitre_init();

    uint8_t tampon[10];
    uint8_t registre=8;
    uint8_t adresse = 0x17;
    uint32_t time_i2c[5];
    const uint8_t T_MAX_I2C = 10;
    enum i2c_resultat_t retour_i2c = I2C_EN_COURS;

    time_i2c[0] = time_us_32();
    time_i2c[2] = 0;

    while(retour_i2c == I2C_EN_COURS){
        time_i2c[1] = time_us_32(); // Pour mesurer le temps d'execution
        i2c_gestion(i2c0);
        retour_i2c = i2c_lire_registre_nb(adresse, registre, tampon, T_MAX_I2C);
        time_i2c[2] += time_us_32() - time_i2c[1]; // Pour mesurer le temps d'execution
        sleep_us(100); // Attente, ou le reste du code
    }
    time_i2c[3] = time_us_32() - time_i2c[0];

    // Affichage
    for(int i=0; i<T_MAX_I2C; i++){
        printf("%c", tampon[i]);
    }
    printf("\n");

    for(int i=0; i<T_MAX_I2C; i++){
        printf("%2x ", tampon[i]);
    }
    printf("\n");

    printf("Temps lecture : %u microsecondes, temps specifique i2c : %u microsecondes.\n", time_i2c[3], time_i2c[2]);

    return 0;
}

/// @brief Ecrit sur le bus I2C 
/// @return 1 pour continuer le test, 0 pour quitter
int test_i2c_ecriture_pico_annex_nb(){
    i2c_maitre_init();

    uint8_t tampon[10];
    uint8_t registre=0x09;
    uint8_t adresse = 0x17;
    uint32_t time_i2c[5];
    const uint8_t T_I2C_ENVOI = 2;
    static uint8_t commande=0;
    enum i2c_resultat_t retour_i2c = I2C_EN_COURS;


    printf("F - Ferme porte\n");
    printf("O - Ouvre porte\n");
    printf("T - Turbine On\n");
    printf("U - Turbine Off\n");
    printf("P - Propulseur On\n");
    printf("M - Propulseur Off\n");
    printf("Q pour quitter\n");

    int lettre;
    do{ 
        lettre = getchar_timeout_us(0);
        stdio_flush();

    }while(lettre == PICO_ERROR_TIMEOUT || lettre == '\0');

    tampon[1] = 0x0;
    switch(lettre){
        case 'F':
        case 'f':
            commande = commande | 0x02; // 0b0000 0010
            printf("=> Ferme porte\n");
            break;

        case 'O':
        case 'o':
            commande = commande & 0xFD; // 0b1111 1101
            printf("=> Ouvre porte\n");
            break;
        
        case 't':
        case 'T':
            commande = commande | 0x01; // 0b0000 0001
            printf("=> Active turbine\n");
            break;
        
        case 'u':
        case 'U':
            commande = commande & 0xFE; // 0b1111 1110
            printf("=> Arrete turbine\n");
            break;
        
        case 'p':
        case 'P':
            commande = commande | 0x08; // 0b0000 1000
            printf("=> Active propulseur\n");
            break;
        
        case 'm':
        case 'M':
            commande = commande & 0xF7; // 0b1111 0111
            printf("=> Arrete propulseur\n");
            break;
            
        case 'q':
        case 'Q':
            return 0;
            break;

    }

    tampon[0] = 54;
    tampon[1] = commande;

    time_i2c[0] = time_us_32();
    time_i2c[2] = 0;

    while(retour_i2c == I2C_EN_COURS){
        time_i2c[1] = time_us_32(); // Pour mesurer le temps d'execution
        i2c_gestion(i2c0);
        retour_i2c = i2c_ecrire_registre_nb(adresse, registre, tampon, T_I2C_ENVOI);
        time_i2c[2] += time_us_32() - time_i2c[1]; // Pour mesurer le temps d'execution
        sleep_us(100); // Attente, ou le reste du code
    }
    time_i2c[3] = time_us_32() - time_i2c[0];

    printf("Temps lecture : %u microsecondes, temps specifique i2c : %u microsecondes.\n", time_i2c[3], time_i2c[2]);

    return 1;
}


/// @brief Test les fonctions définies dans I2C_Annexe
/// @return 0
int test_i2c_ecriture_pico_annex_nb_2(){
    i2c_maitre_init();

    uint32_t time_i2c[5];
    const uint8_t T_I2C_ENVOI = 2;
    static uint8_t commande=0;
    enum i2c_resultat_t retour_i2c = I2C_EN_COURS;


    
    printf("D - Deguisement On\n");
    printf("E - Deguisement Off\n");
    printf("F - Ferme porte\n");
    printf("G - Mi-Ferme porte\n");
    printf("O - Ouvre porte\n");
    printf("T - Turbine On\n");
    printf("U - Turbine Off\n");
    printf("P - Propulseur On\n");
    printf("M - Propulseur Off\n");
    printf("S - Score + 1\n");
    printf("B - Bras deplie\n");
    printf("N - Bras plie 1\n");
    

    int lettre;
    int continue_test=1;
    uint8_t score=0;

    time_i2c[0] = time_us_32();
    time_i2c[1] = time_us_32();
    time_i2c[2] = 0;

    multicore_launch_core1(affiche_contacteur);


    while(1){
        lettre = getchar_timeout_us(0);
        if(lettre != PICO_ERROR_TIMEOUT && lettre != '\0'){
            printf("lettre !\n");
            switch(lettre){
                case 'd':
                case 'D':
                    i2c_annexe_active_deguisement();
                    printf("=> Active déguisement\n");
                    break;

                case 'E':
                case 'e':
                    i2c_annexe_desactive_deguisement();
                    printf("=> Desactive déguisement\n");
                    break;

                case 'F':
                case 'f':
                    i2c_annexe_ferme_porte();
                    printf("=> Ferme porte\n");
                    break;

                case 'G':
                case 'g':
                    i2c_annexe_mi_ferme_porte();
                    printf("=> Ferme porte\n");
                    break;

                case 'O':
                case 'o':
                    i2c_annexe_ouvre_porte();
                    printf("=> Ouvre porte\n");
                    break;
                
                case 't':
                case 'T':
                    i2c_annexe_active_turbine();
                    printf("=> Active turbine\n");
                    break;
                
                case 'u':
                case 'U':
                    i2c_annexe_desactive_turbine();
                    printf("=> Arrete turbine\n");
                    break;

                case 'm':
                case 'M':
                    i2c_annexe_desactive_propulseur();
                    printf("=> Arrete propulseur\n");
                    break;

                case 'p':
                case 'P':
                    i2c_annexe_active_propulseur();
                    printf("=> Active propulseur\n");
                    break;

                case 'q':
                case 'Q':
                    multicore_reset_core1();
                    return 0;

                case 's':
                case 'S':
                    score++;
                    i2c_annexe_envoie_score(score);
                    break;

                case 'b':
                case 'B':
                    i2c_annexe_deplie_bras();
                    printf("=> Deplie bras\n");
                    break;

                case 'n':
                case 'N':
                    i2c_annexe_plie_bras();
                    printf("=> Plie bras\n");
                    break;
                
                default:
                    printf("lettre non reconnue: %d %c\n", lettre, lettre);
            }
        }

        i2c_gestion(i2c0);        
        i2c_annexe_gestion();

    }
    

}

void affiche_contacteur(){
    while(1){
        printf(">contacteur_butee_A:%d\n", i2c_annexe_get_contacteur_butee_A());
        printf(">contacteur_butee_C:%d\n", i2c_annexe_get_contacteur_butee_C());
        printf(">contacteur_longer_A:%d\n", i2c_annexe_get_contacteur_longer_A());
        printf(">contacteur_longer_C:%d\n", i2c_annexe_get_contacteur_longer_C());
    }
}
