#include "hardware/gpio.h"
#include "i2c_annexe.h"
#include "Asser_Position.h"
#include "Balise_VL53L1X.h"
#include "Geometrie_robot.h"
#include "Localisation.h"
#include "Moteurs.h"
#include "Strategie_prise_cerises.h"
#include "Strategie.h"
#include "Trajet.h"
#include "math.h"

#define SEUIL_RECAL_DIST_MM 75
#define SEUIL_RECAL_ANGLE_RADIAN (5 * DEGREE_EN_RADIAN)
#define DISTANCE_OBSTACLE_CM 50
#define DISTANCE_PAS_OBSTACLE_MM 2000

enum etat_action_t calage_angle(enum longer_direction_t longer_direction, double x_mm, double y_mm, double angle_radian);
enum etat_action_t lance_balles(uint32_t step_ms);

enum etat_strategie_t etat_strategie=STRATEGIE_INIT;

void Homologation(uint32_t step_ms){
    

    enum etat_action_t etat_action;
    enum etat_trajet_t etat_trajet;

    struct trajectoire_t trajectoire;

    switch(etat_strategie){
        case STRATEGIE_INIT:
            Localisation_set(775., 109., -60. * DEGREE_EN_RADIAN);
            etat_strategie = ATTENTE_TIRETTE;
            break;

        case ATTENTE_TIRETTE:
            if(attente_tirette() == 0){
                etat_strategie = APPROCHE_CERISE_1_A;
            }
            break;

        case APPROCHE_CERISE_1_A:
            Trajet_config(250, 500);
            Trajectoire_droite(&trajectoire,775, 109, 857, 156, -60. * DEGREE_EN_RADIAN, +30. * DEGREE_EN_RADIAN);
            if(parcourt_trajet_simple(trajectoire, step_ms) == TRAJET_TERMINE){
                etat_strategie = ATTRAPE_CERISE_1;
            }
            break;
        
        case ATTRAPE_CERISE_1:
            etat_action = cerise_attraper_bordure(LONGER_VERS_C, step_ms);
            if(etat_action == ACTION_TERMINEE){
                etat_strategie = APPROCHE_PANIER_1;
            }
            break;

        case APPROCHE_PANIER_1:
            Trajet_config(500, 500);
            Trajectoire_bezier(&trajectoire,Localisation_get().x_mm, Localisation_get().y_mm, 
                                485, Localisation_get().y_mm,
                                465, 857,
                                465,2830,
                                +30. * DEGREE_EN_RADIAN, +120. * DEGREE_EN_RADIAN);

            if(parcourt_trajet_simple(trajectoire, step_ms) == ACTION_TERMINEE){
                etat_strategie = CALAGE_PANIER_1;
            }
            break;

        case APPROCHE_PANIER_2:
            if( depose_cerises(step_ms) == ACTION_TERMINEE){
                etat_strategie = APPROCHE_CERISE_2;
            }
            break;

        case APPROCHE_CERISE_2:
            Trajet_config(250, 500);
            Trajectoire_droite(&trajectoire,Localisation_get().x_mm, Localisation_get().y_mm, 
                                857, 3000 - 156,
                                +270. * DEGREE_EN_RADIAN, 300. * DEGREE_EN_RADIAN);
            if(parcourt_trajet_simple(trajectoire, step_ms) == ACTION_TERMINEE){
                etat_strategie = ATTRAPPE_CERISE_2;
            }
            break;
        
        case ATTRAPPE_CERISE_2:
            etat_action = cerise_attraper_bordure(LONGER_VERS_A, step_ms);
            if(etat_action == ACTION_TERMINEE){
                etat_strategie = APPROCHE_PANIER_3;
            }
            break;

        case APPROCHE_PANIER_3:
            if( depose_cerises(step_ms) == ACTION_TERMINEE){
                etat_strategie = STRATEGIE_FIN;
            }
            break;

        case STRATEGIE_FIN:
            Moteur_Stop();
            i2c_annexe_desactive_propulseur();
            break;
    }
}

enum etat_action_t depose_cerises(uint32_t step_ms){
    static enum {
        APPROCHE_ANGLE,
        CALAGE_PANIER_1,
        RECULE_PANIER,
        LANCE_DANS_PANIER,
    }etat_depose = APPROCHE_ANGLE;
    enum etat_action_t etat_action = ACTION_EN_COURS;

    struct trajectoire_t trajectoire;

    switch(etat_depose){

        case APPROCHE_ANGLE:
            Trajectoire_droite(&trajectoire,Localisation_get().x_mm, Localisation_get().y_mm, 
                                265, 2830,
                                Localisation_get().angle_radian, +120. * DEGREE_EN_RADIAN);

            if(parcourt_trajet_simple(trajectoire, step_ms) == ACTION_TERMINEE){
                etat_depose = CALAGE_PANIER_1;
            }

            break;

        case CALAGE_PANIER_1:
            if(calage_angle(LONGER_VERS_A, RAYON_ROBOT, 3000 - (RAYON_ROBOT/(RACINE_DE_3/2.)), 120. *DEGREE_EN_RADIAN) == ACTION_TERMINEE){
                etat_depose = RECULE_PANIER;
            }
            break;
        
        case RECULE_PANIER:
            Trajet_config(250, 500);
            Trajectoire_droite(&trajectoire,Localisation_get().x_mm, Localisation_get().y_mm, 
                                180, 3000 - (RAYON_ROBOT/(RACINE_DE_3/2)) - 80,
                                120. * DEGREE_EN_RADIAN, +270. * DEGREE_EN_RADIAN);

            if(parcourt_trajet_simple(trajectoire, step_ms) == ACTION_TERMINEE){
                etat_depose = LANCE_DANS_PANIER;
            }
            break;

        case LANCE_DANS_PANIER:
            Asser_Position_maintien();
            if(lance_balles(step_ms) == ACTION_TERMINEE){
                etat_depose = APPROCHE_ANGLE;
                etat_action = ACTION_TERMINEE;
            }
            break;
    }
    return etat_action;

}


/// @brief Active le propulseur, ouvre la porte, attend qql secondes.
/// @param step_ms : pas de temps.
/// @return ACTION_EN_COURS ou ACTION_TERMINEE
enum etat_action_t lance_balles(uint32_t step_ms){
    enum etat_action_t etat_action = ACTION_EN_COURS;
    uint32_t tempo_ms;

    static enum{
        LANCE_PROPULSEUR_ON,
        LANCE_TEMPO_PROP_ON,
        LANCE_PORTE_OUVERTE,
    } etat_lance_balle = LANCE_PROPULSEUR_ON;

    switch(etat_lance_balle){
        case LANCE_PROPULSEUR_ON:
            i2c_annexe_active_propulseur();
            tempo_ms = 2000;
            etat_lance_balle = LANCE_TEMPO_PROP_ON;
            break;
        
        case LANCE_TEMPO_PROP_ON:
            if (tempo_ms < step_ms){
                //etat_lance_balle = LANCE_PORTE_OUVERTE;
                etat_lance_balle = LANCE_TEMPO_PROP_ON;
                i2c_annexe_ouvre_porte();
                tempo_ms = 6000;
            }else{
                tempo_ms -= step_ms;
            }
            break;
        
        case LANCE_PORTE_OUVERTE:
            if (tempo_ms < step_ms){
                etat_lance_balle = LANCE_PROPULSEUR_ON;
                i2c_annexe_desactive_propulseur();
                etat_action = ACTION_TERMINEE;
            }else{
                tempo_ms -= step_ms;
            }
            break;


    }
    return etat_action;
}

/// @brief Envoie le robot se caler dans l'angle en face de lui, recale la localisation
enum etat_action_t calage_angle(enum longer_direction_t longer_direction, double x_mm, double y_mm, double angle_radian){
    enum etat_action_t etat_action = ACTION_EN_COURS;
    struct position_t position;

    avance_puis_longe_bordure(longer_direction);
    if( ((longer_direction == LONGER_VERS_A) && (i2c_annexe_get_contacteur_butee_A() == CONTACTEUR_ACTIF) ) ||
        ((longer_direction == LONGER_VERS_C) && (i2c_annexe_get_contacteur_butee_C() == CONTACTEUR_ACTIF) ) ){
        etat_action = ACTION_TERMINEE;

        position = Localisation_get();
        if(fabs(position.x_mm - x_mm) < SEUIL_RECAL_DIST_MM){
            Localisation_set_x(x_mm);
        }
        if(fabs(position.y_mm - y_mm) < SEUIL_RECAL_DIST_MM){
            Localisation_set_y(y_mm);
        }
        if(fabs(position.angle_radian - angle_radian) < SEUIL_RECAL_ANGLE_RADIAN){
            Localisation_set_angle(angle_radian);
        }
    }
    return etat_action;
}

enum etat_action_t parcourt_trajet_simple(struct trajectoire_t trajectoire, uint32_t step_ms){
    enum etat_action_t etat_action = ACTION_EN_COURS;
    enum etat_trajet_t etat_trajet;
    static enum {
        PARCOURS_INIT,
        PARCOURS_AVANCE,
    } etat_parcourt=PARCOURS_INIT;

    switch (etat_parcourt){
        case PARCOURS_INIT:
            Trajet_debut_trajectoire(trajectoire);
            etat_parcourt = PARCOURS_AVANCE;
            break;
        
        case PARCOURS_AVANCE:
            if(Balise_VL53L1X_get_min_distance() <DISTANCE_OBSTACLE_CM){
                Trajet_stop(step_ms);
                break;
            }
            etat_trajet = Trajet_avance(step_ms/1000.);
            if(etat_trajet == TRAJET_TERMINE){
                etat_action = ACTION_TERMINEE;
                etat_parcourt = PARCOURS_INIT;
            }
            break;
    }

    return etat_action;
}

enum etat_action_t parcourt_trajet_simple_sans_evitement(struct trajectoire_t trajectoire, uint32_t step_ms){
    enum etat_action_t etat_action = ACTION_EN_COURS;
    enum etat_trajet_t etat_trajet;
    static enum {
        PARCOURS_INIT,
        PARCOURS_AVANCE,
    } etat_parcourt=PARCOURS_INIT;

    switch (etat_parcourt){
        case PARCOURS_INIT:
            Trajet_debut_trajectoire(trajectoire);
            etat_parcourt = PARCOURS_AVANCE;
            break;
        
        case PARCOURS_AVANCE:
            etat_trajet = Trajet_avance(step_ms/1000.);
            if(etat_trajet == TRAJET_TERMINE){
                etat_action = ACTION_TERMINEE;
                etat_parcourt = PARCOURS_INIT;
            }
            break;
    }

    return etat_action;
}

/// @brief Renvoi 1 si on doit attendre le déclenchement de la tirette
uint attente_tirette(void){
    return !gpio_get(TIRETTE);
}

/// @brief Renvoi COULEUR_VERT ou COULEUR_BLEU
enum couleur_t lire_couleur(void){
    if (gpio_get(COULEUR))
        return COULEUR_VERT;
    return COULEUR_BLEU;

}