#include <inttypes.h>

#define MAX_OPCIONES 16

#define OFFSET_RONDA_ESTADO_JUGANDO_BAZA_CANTO 3

uint16_t opciones = 0;

ResultadoJugada TCIC_jugar_baza(EstadoPartida *partida, uint8_t jugador_i, uint8_t carta_i, uint16_t partida_i);
ResultadoJugada TCIC_cantar(EstadoPartida *partida, uint8_t jugador_i, uint8_t canto, uint16_t partida_i);
ResultadoJugada TCIC_cantar_tanto(EstadoPartida *partida, uint8_t jugador_i, uint16_t partida_i);

void TCIC_mostrando_controles_en_canto(EstadoPartida *partida, uint16_t partida_i);

uint8_t TCIC_recibir_input(uint16_t partida_i);
void TCIC_avisar_accion(uint16_t partida_i);

void TCIC_render_controles_carta(uint8_t carta_i, uint8_t carta_id, uint8_t control_i, uint16_t partida_i);
void TCIC_render_controles_canto(uint8_t canto_i, uint8_t control_i, uint16_t partida_i);
void TCIC_render_controles_canto_tantos(uint8_t tantos, uint8_t control_i, uint16_t partida_i);

void TCIC_render_carta(uint8_t carta_i, uint16_t partida_i);

void TCIC_turno_baza(uint8_t jugador_i, uint16_t partida_i);
void TCIC_turno_canto(uint8_t jugador_i, uint8_t canto_i, uint16_t partida_i);
void TCIC_turno_tantos(uint8_t jugador_i, uint16_t partida_i);

void TCIC_render_jugada_invalida(uint8_t jugador_i, uint16_t partida_i);
void TCIC_render_carta_jugada(uint8_t jugador_i, uint8_t carta_i, uint16_t partida_i);
void TCIC_render_canto_realizado(uint8_t jugador_i, uint8_t canto_i, uint16_t partida_i);
void TCIC_render_tantos_cantados(uint8_t jugador_i, uint8_t tantos, uint16_t partida_i);
void TCIC_render_puntaje(uint8_t numero_jugadores, uint8_t puntos_1, uint8_t puntos_2, uint16_t partida_i);
void TCIC_render_envido_en_mesa(uint8_t mayor_tanto, uint16_t partida_i);
void TCIC_render_carta_envido_en_mesa(uint8_t carta_i, uint16_t partida_i);
void TCIC_render_puntos_ganados(uint8_t numero_jugadores, uint8_t equipo_puntero, uint8_t puntos_ganados, uint16_t partida_i);
void TCIC_render_ronda_terminada(uint8_t numero_jugadores, uint8_t ganador_i, uint16_t partida_i);
void TCIC_render_nueva_ronda(uint8_t mano_i, uint16_t partida_i);
void TCIC_render_victoria(uint8_t numero_jugadores, uint8_t equipo_i, uint16_t partida_i);
void TCIC_render_nueva_baza(uint8_t baza_i, uint16_t partida_i);
void TCIC_render_flag_activada(uint8_t flag_i, uint16_t partida_i);
void TCIC_render_envido_no_disponible(uint16_t partida_i);
void TCIC_render_tiene_retruco(uint8_t equipo_i, uint16_t partida_i);

static uint8_t encontrar_indice_opcion(uint8_t opcion_i){
    uint8_t indice_opcion = 0;

    for(uint8_t i = 0; i < MAX_OPCIONES; i++){
        if(IS_BIT_SET(opciones, i)) indice_opcion++;
        if(i == opcion_i) break;
    }

    return indice_opcion;
}

void TCIC_render_cartas_disponibles_jugador(EstadoPartida *estado_partida, uint8_t jugador_i, uint16_t partida_i){
    for(uint8_t i = 0; i < MAX_CARTAS_MANO; i++){
        uint8_t carta_id = estado_partida->jugadores_cartas[jugador_i][i];

        if(carta_id == CARTA_INVALIDA) continue;

        TCIC_render_carta(carta_id, partida_i);
    }
}

void TCIC_nuevo_turno(EstadoPartida *estado_partida, uint16_t partida_i){

    uint8_t jugador_turno = obtener_jugador_turno_actual(estado_partida);

    switch(estado_partida->ronda_estado){
        case RONDA_ESTADO_JUGANDO_BAZA: {TCIC_turno_baza(jugador_turno, partida_i); break;}
        case RONDA_ESTADO_EN_CANTO: {TCIC_turno_canto(jugador_turno, estado_partida->canto_ofrecido, partida_i); break;}
        case RONDA_ESTADO_RONDA_TANTOS: {TCIC_turno_tantos(jugador_turno, partida_i); break;}
        default: {break;}
    }
}


void TCIC_render_mensaje_por_resultado(ResultadoJugada *resultado_jugada, EstadoPartida *estado_partida, uint16_t partida_i){
    if(!resultado_jugada->eventos){
        TCIC_render_jugada_invalida(resultado_jugada->jugador, partida_i);
        return;
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_CARTA_JUGADA)){
        uint8_t carta_jugada_i = resultado_jugada->carta;
        if(carta_jugada_i == CARTA_INVALIDA){
            TCIC_render_jugada_invalida(resultado_jugada->jugador, partida_i);
            return;
        }

        TCIC_render_carta_jugada(resultado_jugada->jugador, carta_jugada_i, partida_i);
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_CANTO)){
        TCIC_render_canto_realizado(resultado_jugada->jugador, resultado_jugada->canto, partida_i); 
    }
    
    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_TANTOS_CANTADOS)){
        TCIC_render_tantos_cantados(resultado_jugada->jugador, resultado_jugada->tantos_cantados, partida_i); 
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_PUNTOS_SUMADOS)){
        TCIC_render_puntos_ganados(estado_partida->numero_jugadores, resultado_jugada->equipo_ganador, resultado_jugada->puntos_ganados, partida_i);
        TCIC_render_puntaje(estado_partida->numero_jugadores, estado_partida->equipos_puntos[0], estado_partida->equipos_puntos[1], partida_i);
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_ENVIDO_EN_MESA)){
        TCIC_render_envido_en_mesa(resultado_jugada->mayor_envido, partida_i);
        for(uint8_t i = 0; i < 2; i++){
            uint8_t carta_i = resultado_jugada->cartas_mayor_envido[i];
            
            if(carta_i == CARTA_INVALIDA) continue;

            TCIC_render_carta_envido_en_mesa(carta_i, partida_i);
        }
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_VICTORIA)){
        TCIC_render_victoria(estado_partida->numero_jugadores, resultado_jugada->equipo_ganador, partida_i);
        return;
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_RONDA_TERMINADA)){
        TCIC_render_ronda_terminada(estado_partida->numero_jugadores, resultado_jugada->equipo_ganador, partida_i);
        TCIC_render_nueva_ronda(estado_partida->jugador_mano, partida_i);
        return;
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_CANTO_FLAG_ACTIVADA)){
        TCIC_render_flag_activada(resultado_jugada->canto_flag_activada, partida_i);
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_ENVIDO_NO_DISPONIBLE)){
        TCIC_render_envido_no_disponible(partida_i);
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_TIENE_RETRUCO)){
        TCIC_render_tiene_retruco(resultado_jugada->equipo_ganador, partida_i);
    }

    if(IS_BIT_SET(resultado_jugada->eventos, EVENTO_NUEVA_BAZA)){
        TCIC_render_nueva_baza(estado_partida->baza_actual, partida_i);
    }
}

uint8_t TCIC_obtener_truco_disponible(EstadoPartida *estado_partida){
    switch(estado_partida->ronda_estado){
        case RONDA_ESTADO_JUGANDO_BAZA: {
            if(!IS_BIT_SET(estado_partida->flags_ronda, FLAG_TRUCO)) return CANTO_TRUCO;
            else if(IS_BIT_SET(estado_partida->flags_ronda, FLAG_TIENE_RETRUCO) == ((estado_partida->jugador_turno_actual) % 2)){
                if(!IS_BIT_SET(estado_partida->flags_ronda, FLAG_VALE_CUATRO)){
                    if(IS_BIT_SET(estado_partida->flags_ronda, FLAG_RETRUCO)) return CANTO_VALE_CUATRO;
                    return CANTO_RETRUCO;
                }
            }
            break;
        }

        case RONDA_ESTADO_EN_CANTO: {
            if(estado_partida->canto_ofrecido >= CANTO_TRUCO && estado_partida->canto_ofrecido < CANTO_VALE_CUATRO) return estado_partida->canto_ofrecido+1;
            break;
        }

        default: {break;}
    }
    return CANTO_INVALIDO;
}

static void manejar_controles_canto_tantos(uint8_t tantos, uint16_t partida_i){
    SET_BIT(opciones, 0);

    TCIC_render_controles_canto_tantos(tantos, encontrar_indice_opcion(0), partida_i);
}

static void manejar_controles_carta(uint8_t carta_i, uint8_t carta_id, uint16_t partida_i){
    if(carta_id >= CARTA_INVALIDA) return;

    SET_BIT(opciones, carta_i);

    TCIC_render_controles_carta(carta_i, carta_id, encontrar_indice_opcion(carta_i), partida_i);
}

static void manejar_controles_canto(uint8_t ronda_estado, uint8_t canto_i, uint16_t partida_i){
    if(canto_i >= CANTO_INVALIDO) return;

    uint8_t opcion_i = 0;

    switch(ronda_estado){
        case RONDA_ESTADO_JUGANDO_BAZA: {
            //queremos que los cantos esten después de la tercera carta.
            opcion_i = canto_i+OFFSET_RONDA_ESTADO_JUGANDO_BAZA_CANTO;
            break;
        }

        case RONDA_ESTADO_EN_CANTO: {
            //aca no hay offset, todos juntos.
            opcion_i = canto_i;
            break;
        }

        case RONDA_ESTADO_RONDA_TANTOS: {
            //en este caso el unico canto es son buenas, queremos que sea la segunda opción y ya.
            opcion_i = 1;
            break;
        }

        default: {break;}
    }
    SET_BIT(opciones, opcion_i);

    TCIC_render_controles_canto(canto_i, encontrar_indice_opcion(opcion_i), partida_i);
}

void TCIC_mostrar_controles_baza(EstadoPartida *estado_partida, uint16_t partida_i){
    for(uint8_t carta_i = 0; carta_i < 3; carta_i++){
        uint8_t carta = estado_partida->jugadores_cartas[estado_partida->jugador_turno_actual][carta_i];
        if(carta == CARTA_INVALIDA) continue;
        manejar_controles_carta(carta_i, carta, partida_i);
    }

    uint8_t truco_disponible = TCIC_obtener_truco_disponible(estado_partida);

    if(truco_disponible != CANTO_INVALIDO) manejar_controles_canto(estado_partida->ronda_estado, truco_disponible, partida_i);

    if(!IS_BIT_SET(estado_partida->flags_ronda, FLAG_ENVIDO_NO_DISPONIBLE)){
        manejar_controles_canto(estado_partida->ronda_estado, CANTO_ENVIDO, partida_i);
        manejar_controles_canto(estado_partida->ronda_estado, CANTO_REAL_ENVIDO, partida_i);
        manejar_controles_canto(estado_partida->ronda_estado, CANTO_FALTA_ENVIDO, partida_i);
    }

    manejar_controles_canto(estado_partida->ronda_estado, CANTO_ME_VOY_AL_MAZO, partida_i);
}

void iterar_y_manejar_controles_canto(EstadoPartida *estado_partida, uint8_t canto_min, uint8_t canto_max, uint16_t partida_i){
    for(uint8_t i = canto_min; i <= canto_max; i++){
        manejar_controles_canto(estado_partida->ronda_estado, i, partida_i);
    }
}

void TCIC_mostrar_controles_canto(EstadoPartida *estado_partida, uint16_t partida_i){

    uint8_t tipo_canto_ofrecido = obtener_tipo_canto(estado_partida->canto_ofrecido);

    switch(tipo_canto_ofrecido){
        case TIPO_CANTO_TRUCO: {
            if(estado_partida->canto_ofrecido != CANTO_VALE_CUATRO){
                manejar_controles_canto(estado_partida->ronda_estado, TCIC_obtener_truco_disponible(estado_partida), partida_i);
            }

            if(!IS_BIT_SET(estado_partida->flags_ronda, FLAG_ENVIDO_NO_DISPONIBLE)){
                iterar_y_manejar_controles_canto(estado_partida, FLAG_ENVIDO, FLAG_FALTA_ENVIDO, partida_i);
            }
            break;
        }
        case TIPO_CANTO_ENVIDO: {
            if(estado_partida->canto_ofrecido != CANTO_FALTA_ENVIDO){
                iterar_y_manejar_controles_canto(estado_partida, estado_partida->canto_ofrecido+1, FLAG_FALTA_ENVIDO, partida_i);
            }
            break;
        }
        default: break;
    }

    manejar_controles_canto(estado_partida->ronda_estado, CANTO_QUIERO, partida_i);
    manejar_controles_canto(estado_partida->ronda_estado, CANTO_NO_QUIERO, partida_i);

    if(tipo_canto_ofrecido == TIPO_CANTO_TRUCO) manejar_controles_canto(estado_partida->ronda_estado, CANTO_ME_VOY_AL_MAZO, partida_i);

    TCIC_mostrando_controles_en_canto(estado_partida, partida_i);
}

void TCIC_mostrar_controles_tantos(EstadoPartida *estado_partida, uint16_t partida_i){

    uint8_t envido = calcular_envido(estado_partida, estado_partida->jugador_turno_canto).valor_tantos;

    if(puede_cantar_mayor_envido(estado_partida, envido, estado_partida->jugador_turno_canto)){
        manejar_controles_canto_tantos(envido, partida_i);
    }

    if(estado_partida->mayor_envido != 0){
        manejar_controles_canto(estado_partida->ronda_estado, CANTO_SON_BUENAS, partida_i);
    } 
}

void TCIC_mostrar_controles_a_jugador_actual_segun_estado(EstadoPartida *estado_partida, uint16_t partida_i){

    switch(estado_partida->ronda_estado){
        case RONDA_ESTADO_JUGANDO_BAZA: {
            TCIC_mostrar_controles_baza(estado_partida, partida_i);
            break;
        }
        case RONDA_ESTADO_EN_CANTO: {
            TCIC_mostrar_controles_canto(estado_partida, partida_i);
            break;
        }
        case RONDA_ESTADO_RONDA_TANTOS: {
            TCIC_mostrar_controles_tantos(estado_partida, partida_i);
            break;
        }
        default: {break;} 
    }
}

uint8_t TCIC_anunciar_nuevo_turno_y_mostrar_controles_si_es_jugador_indicado(EstadoPartida *estado_partida, uint8_t jugador_i, uint16_t partida_i){

    TCIC_nuevo_turno(estado_partida, partida_i);

    uint8_t jugador_turno = obtener_jugador_turno_actual(estado_partida);

    if(jugador_turno != jugador_i) return 0;

    TCIC_mostrar_controles_a_jugador_actual_segun_estado(estado_partida, partida_i);

    return 1;
}

ResultadoJugada TCIC_manejar_accion_turno(EstadoPartida *partida, uint8_t accion, uint8_t jugador_i, uint16_t partida_i){
    uint8_t bits_seteados = 0;
    uint8_t comando = 0;

    for(uint8_t i = 0; i < MAX_OPCIONES; i++){
        if(IS_BIT_SET(opciones, i)) bits_seteados++;
        if(bits_seteados == accion) {comando = i; break;}
    }

    

    ResultadoJugada resultado_fail = (ResultadoJugada){0};
    SET_BIT(resultado_fail.eventos, EVENTO_JUGADA_INVALIDA);

    if(bits_seteados != accion){
        TCIC_render_jugada_invalida(jugador_i, partida_i);
        return resultado_fail;
    } 

    opciones = 0;

    switch(partida->ronda_estado){
        case RONDA_ESTADO_JUGANDO_BAZA: {
            if(comando < OFFSET_RONDA_ESTADO_JUGANDO_BAZA_CANTO) return TCIC_jugar_baza(partida, jugador_i, comando, partida_i);
            else return TCIC_cantar(partida, jugador_i, comando-OFFSET_RONDA_ESTADO_JUGANDO_BAZA_CANTO, partida_i);
            break;
        }
        case RONDA_ESTADO_EN_CANTO: {
            return TCIC_cantar(partida, jugador_i, comando, partida_i);
            break;
        }
        case RONDA_ESTADO_RONDA_TANTOS: {
            switch(comando){
                case 0: {return TCIC_cantar_tanto(partida, jugador_i, partida_i);}
                case 1: {return TCIC_cantar(partida, jugador_i, CANTO_SON_BUENAS, partida_i);}
                default: {break;}
            }
            break;
        }

        default: {break;}
    }
    
    return resultado_fail;
}