#define ACCION_GLOBAL_VOLVER 15

typedef enum{
    ACCION_A_15,
    ACCION_A_30,
    ACCION_SETEAR_1V1,
    ACCION_SETEAR_2V2,
    ACCION_SETEAR_3V3,
} AccionMenuConfiguracionSala;

typedef enum{
    ACCION_EMPEZAR_PARTIDA,
    ACCION_CAMBIAR_ASIENTO,
    ACCION_CAMBIAR_ASIENTO_OTRO,
    ACCION_DAR_ANFITRION,
    ACCION_CONFIGURAR_SALA,
    ACCION_EXPULSAR_JUGADOR,
} AccionMenuSala;

typedef enum{
    ESTADO_MENU_MENU,

    ESTADO_MENU_CONECTANDO,

    ESTADO_MENU_EN_VESTIBULO,

    ESTADO_MENU_UNIENDO_SALA,
    
    ESTADO_MENU_EN_SALA,
    ESTADO_MENU_CONFIGURANDO_SALA,

    ESTADO_MENU_DANDO_ANFITRION,
    ESTADO_MENU_CAMBIANDO_ASIENTO,
    ESTADO_MENU_CAMBIANDO_ASIENTO_OTRO,
    ESTADO_MENU_EXPULSANDO_JUGADOR,

    ESTADO_MENU_EN_PARTIDA
} EstadoMenu;


EstadoPartida estado_partida = {0};
uint8_t estado_menu = ESTADO_MENU_MENU;
uint8_t asiento_cambio_elegido = JUGADOR_INVALIDO;
uint8_t asientos_ocupados = 0;
uint8_t hosts = 0;
uint8_t numero_jugador = JUGADOR_INVALIDO;
uint8_t max_jugadores_sala = 0;
uint8_t max_puntaje_sala = 0;

uint8_t es_host(){
    return IS_BIT_SET(hosts, numero_jugador);
}

static uint8_t encontrar_indice_opcion_menu(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;
}

typedef void RenderIFunc(uint8_t indice_opcion, uint8_t opcion_i);

static void setear_y_renderizar_con_opcion_i(uint8_t opcion_i, RenderIFunc *func){
    SET_BIT(opciones, opcion_i);
    func(encontrar_indice_opcion_menu(opcion_i), opcion_i);
}

typedef void RenderFunc(uint8_t indice_opcion);

static void setear_y_renderizar(uint8_t opcion_i, RenderFunc *func){
    SET_BIT(opciones, opcion_i);
    func(encontrar_indice_opcion_menu(opcion_i));
}

void TCIC_manejar_nuevo_jugador_en_sala(uint8_t jugador_i);

void TCIC_render_unido_a_sala();
void TCIC_render_empezar_partida(uint8_t numero_jugadores);
void TCIC_render_conectado();
void TCIC_render_cambio_disponible(uint8_t indice_opcion, uint8_t opcion_i);
void TCIC_render_empezar_partida_opcion(uint8_t indice_opcion);
void TCIC_render_cambiar_asiento(uint8_t indice_opcion);
void TCIC_render_cambiar_asiento_otro(uint8_t indice_opcion);
void TCIC_render_cambiar_host(uint8_t indice_opcion);
void TCIC_render_cliente_es_host();
void TCIC_render_otro_host(uint8_t anfitrion_i);
void TCIC_render_nuevo_jugador_en_sala(uint8_t asiento_i);
void TCIC_render_jugador_sale_de_sala(uint8_t asiento_i);
void TCIC_render_cambio_asiento(uint8_t jugador_i, uint8_t asiento_i);
void TCIC_render_unir_mesa(uint8_t numero_mesa);
void TCIC_render_asiento_cliente(uint8_t asiento_i);
void TCIC_render_cambiando_otro_jugador(uint8_t asiento_cambio_elegido);
void TCIC_render_informacion_opciones_sala();
void TCIC_render_configurar_sala(uint8_t indice_opcion);
void TCIC_render_expulsar_jugador(uint8_t indice_opcion);

void TCIC_render_volver(uint8_t opcion_i);

void TCIC_renderizando_opciones_sala();

void TCIC_empezar_partida_accion();
void TCIC_cambiar_asiento(uint8_t asiento_i);
void TCIC_cambiar_asiento_otro(uint8_t asiento_1, uint8_t asiento_2);
void TCIC_dar_anfitrion(uint8_t jugador_i);
void TCIC_setear_puntaje(uint8_t puntaje);
void TCIC_setear_max_jugadores(uint8_t max_jugadores);
void TCIC_expulsar_jugador(uint8_t jugador_i);

//renderizar y pasar el indice a la vez fue una estupidez. primero deberia setear los bits y despues ver qué puedo renderizar, si no depende de que renderice en el órden de los enums. básicamente una L.

uint8_t TCIC_obtener_jugadores_en_sala(){
    uint8_t set = 0;
    for(uint8_t i = 0; i < max_jugadores_sala; i++) if(IS_BIT_SET(asientos_ocupados, i)) set++;
    return set;
}

void TCIC_render_menu_opciones_sala(){
    TCIC_renderizando_opciones_sala();

    if(es_host() && TCIC_obtener_jugadores_en_sala() == max_jugadores_sala) setear_y_renderizar(ACCION_EMPEZAR_PARTIDA, TCIC_render_empezar_partida_opcion);

    if(TCIC_obtener_jugadores_en_sala() != max_jugadores_sala || es_host()) setear_y_renderizar(ACCION_CAMBIAR_ASIENTO, TCIC_render_cambiar_asiento);

    if(es_host()){
        if(TCIC_obtener_jugadores_en_sala() > 1){
            setear_y_renderizar(ACCION_CAMBIAR_ASIENTO_OTRO, TCIC_render_cambiar_asiento_otro);
            setear_y_renderizar(ACCION_DAR_ANFITRION, TCIC_render_cambiar_host);
        }
        setear_y_renderizar(ACCION_CONFIGURAR_SALA, TCIC_render_configurar_sala);

        if(TCIC_obtener_jugadores_en_sala() > 1) setear_y_renderizar(ACCION_EXPULSAR_JUGADOR, TCIC_render_expulsar_jugador);
    }
}

void TCIC_manejar_cambiando_asiento(){
    for(uint8_t i = 0; i < max_jugadores_sala; i++){
        if((IS_BIT_SET(asientos_ocupados, i) && !es_host()) ||
        i == numero_jugador
        ) continue;

        setear_y_renderizar_con_opcion_i(i, TCIC_render_cambio_disponible);
    }
    setear_y_renderizar(ACCION_GLOBAL_VOLVER, TCIC_render_volver);
}

void TCIC_manejar_cambiar_asiento_otro(){
    if(asiento_cambio_elegido != JUGADOR_INVALIDO) TCIC_render_cambiando_otro_jugador(asiento_cambio_elegido);
    for(uint8_t i = 0; i < max_jugadores_sala; i++){
        if(i == asiento_cambio_elegido) continue;

        //salteo si todavia no elegi un jugador al que cambiar de asiento
        if(asiento_cambio_elegido == JUGADOR_INVALIDO &&
        !IS_BIT_SET(asientos_ocupados, i)) continue;

        setear_y_renderizar_con_opcion_i(i, TCIC_render_cambio_disponible);
    }
    setear_y_renderizar(ACCION_GLOBAL_VOLVER, TCIC_render_volver);
}

void TCIC_manejar_dar_anfitrion(){
    for(uint8_t i = 0; i < max_jugadores_sala; i++){
        if(i == numero_jugador || !IS_BIT_SET(asientos_ocupados, i)) continue;

        setear_y_renderizar_con_opcion_i(i, TCIC_render_cambio_disponible);
    }
    setear_y_renderizar(ACCION_GLOBAL_VOLVER, TCIC_render_volver);
}

void TCIC_manejar_expulsar_jugador(){
    for(uint8_t i = 0; i < max_jugadores_sala; i++){
        if(i == numero_jugador || !IS_BIT_SET(asientos_ocupados, i)) continue;

        setear_y_renderizar_con_opcion_i(i, TCIC_render_cambio_disponible);
    }
    setear_y_renderizar(ACCION_GLOBAL_VOLVER, TCIC_render_volver);
}


void TCIC_manejar_conectado(){
    estado_menu = ESTADO_MENU_EN_VESTIBULO;
    TCIC_render_conectado();
}

void TCIC_manejar_unir_mesa(uint8_t numero_mesa){
    estado_menu = ESTADO_MENU_UNIENDO_SALA;
    asientos_ocupados = 0;
    TCIC_render_unir_mesa(numero_mesa);
}

void TCIC_manejar_asiento_cliente(uint8_t asiento_i){
    numero_jugador = asiento_i;
    SET_BIT(asientos_ocupados, asiento_i);
    TCIC_render_asiento_cliente(asiento_i);
}




void TCIC_render_a_15_opcion(uint8_t opcion_i);
void TCIC_render_a_30_opcion(uint8_t opcion_i);

void TCIC_render_1v1_opcion(uint8_t opcion_i);
void TCIC_render_2v2_opcion(uint8_t opcion_i);
void TCIC_render_3v3_opcion(uint8_t opcion_i);

void TCIC_manejar_menu_configurando_sala(){
    if(max_puntaje_sala != 15) setear_y_renderizar(ACCION_A_15, TCIC_render_a_15_opcion);
    if(max_puntaje_sala != 30) setear_y_renderizar(ACCION_A_30, TCIC_render_a_30_opcion);

    if(max_jugadores_sala != 2 && TCIC_obtener_jugadores_en_sala() <= 2) setear_y_renderizar(ACCION_SETEAR_1V1, TCIC_render_1v1_opcion);
    if(max_jugadores_sala != 4 && TCIC_obtener_jugadores_en_sala() <= 4) setear_y_renderizar(ACCION_SETEAR_2V2, TCIC_render_2v2_opcion);
    if(max_jugadores_sala != 6 && TCIC_obtener_jugadores_en_sala() <= 6) setear_y_renderizar(ACCION_SETEAR_3V3, TCIC_render_3v3_opcion);

    setear_y_renderizar(ACCION_GLOBAL_VOLVER, TCIC_render_volver);

    TCIC_render_informacion_opciones_sala();
}

void TCIC_render_menu_segun_estado(){
    opciones = 0;


    if(estado_menu == ESTADO_MENU_UNIENDO_SALA) return;

    switch(estado_menu){
        case ESTADO_MENU_EN_SALA: {
            TCIC_render_menu_opciones_sala();
            break;
        }
        case ESTADO_MENU_CONFIGURANDO_SALA: {
            TCIC_manejar_menu_configurando_sala();
            break;
        }
        case ESTADO_MENU_CAMBIANDO_ASIENTO: {
            TCIC_manejar_cambiando_asiento();
            break;
        }
        case ESTADO_MENU_CAMBIANDO_ASIENTO_OTRO: {
            TCIC_manejar_cambiar_asiento_otro();
            break;
        }
        case ESTADO_MENU_DANDO_ANFITRION: {
            TCIC_manejar_dar_anfitrion();
            break;
        }
        case ESTADO_MENU_EXPULSANDO_JUGADOR: {
            TCIC_manejar_expulsar_jugador();
            break;
        }
    }
}

void TCIC_manejar_cambio_configuracion_sala(){
    if(estado_menu == ESTADO_MENU_UNIENDO_SALA) return;

    TCIC_render_informacion_opciones_sala();
    TCIC_render_menu_segun_estado();
}

void TCIC_manejar_terminar_unir_mesa(){
    estado_menu = ESTADO_MENU_EN_SALA;

    TCIC_render_unido_a_sala();
    TCIC_render_informacion_opciones_sala();
    TCIC_render_menu_segun_estado();
}

void TCIC_manejar_nuevo_anfitrion(uint8_t anfitrion_i){
    SET_BIT(hosts, anfitrion_i);
    if(es_host()){
        TCIC_render_cliente_es_host();
        TCIC_render_menu_segun_estado();
    } 
    else TCIC_render_otro_host(anfitrion_i);
}


void TCIC_manejar_cambio_asiento(uint8_t jugador_i, uint8_t asiento_i){

    if(jugador_i == numero_jugador) numero_jugador = asiento_i;
    else if(asiento_i == numero_jugador) numero_jugador = jugador_i;

    if(IS_BIT_SET(asientos_ocupados, asiento_i)){
        TCIC_render_cambio_asiento(asiento_i, jugador_i);
    } 
    else{
        asientos_ocupados ^= (1u << jugador_i) | (1u << asiento_i);
    }

    if(IS_BIT_SET(hosts, jugador_i) != IS_BIT_SET(hosts, asiento_i)){
        hosts ^= (1u << jugador_i) | (1u << asiento_i);
    }

    TCIC_render_cambio_asiento(jugador_i, asiento_i);
    TCIC_render_menu_segun_estado();
}

void TCIC_manejar_nuevo_jugador_en_sala(uint8_t asiento_i){
    SET_BIT(asientos_ocupados, asiento_i);
    TCIC_render_nuevo_jugador_en_sala(asiento_i);
    TCIC_render_menu_segun_estado();
}

void TCIC_manejar_jugador_sale_de_sala(uint8_t asiento_i){
    CLEAR_BIT(asientos_ocupados, asiento_i);
    CLEAR_BIT(hosts, asiento_i);
    TCIC_render_jugador_sale_de_sala(asiento_i);
    TCIC_render_menu_segun_estado();
}

void TCIC_manejar_fin_partida(){
    estado_menu = ESTADO_MENU_EN_SALA;

    TCIC_render_menu_segun_estado();
}

void TCIC_manejar_volver_a_vestibulo(){
    estado_menu = ESTADO_MENU_EN_VESTIBULO;

    estado_partida = (EstadoPartida){0};
    asiento_cambio_elegido = JUGADOR_INVALIDO;
    asientos_ocupados = 0;
    hosts = 0;
    numero_jugador = JUGADOR_INVALIDO;
    max_jugadores_sala = 0;
    max_puntaje_sala = 0;

    TCIC_render_menu_segun_estado();
}

void TCIC_manejar_empezar_partida(uint8_t numero_jugadores){
    estado_menu = ESTADO_MENU_EN_PARTIDA;
    opciones = 0;
    TCIC_render_empezar_partida(numero_jugadores);
}

void TCIC_manejar_accion_menu(uint8_t accion){
    //ES-PAN-TO-SO
    if(estado_menu == ESTADO_MENU_EN_PARTIDA){
        if(obtener_jugador_turno_actual(&estado_partida) == numero_jugador){
            TCIC_manejar_accion_turno(&estado_partida, accion, numero_jugador);
            return;
        }
    }

    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;}
    }


    if(bits_seteados != accion) return;

    opciones = 0;

    if(comando == ACCION_GLOBAL_VOLVER){
        if(estado_menu >= ESTADO_MENU_CONFIGURANDO_SALA && estado_menu <= ESTADO_MENU_EXPULSANDO_JUGADOR){
            asiento_cambio_elegido = JUGADOR_INVALIDO;
            estado_menu = ESTADO_MENU_EN_SALA;
        }else return;
    }

    switch(estado_menu){
        case ESTADO_MENU_EN_SALA: {
            switch(comando){
                case ACCION_EMPEZAR_PARTIDA: {
                    TCIC_empezar_partida_accion(); 
                    break;
                }
                case ACCION_CAMBIAR_ASIENTO: {
                    estado_menu = ESTADO_MENU_CAMBIANDO_ASIENTO;
                    break;
                }
                case ACCION_CAMBIAR_ASIENTO_OTRO: {
                    estado_menu = ESTADO_MENU_CAMBIANDO_ASIENTO_OTRO;
                    break;
                }
                case ACCION_DAR_ANFITRION: {
                    estado_menu = ESTADO_MENU_DANDO_ANFITRION; 
                    break;
                }
                case ACCION_CONFIGURAR_SALA: {
                    estado_menu = ESTADO_MENU_CONFIGURANDO_SALA; 
                    break;
                }
                case ACCION_EXPULSAR_JUGADOR: {
                    estado_menu = ESTADO_MENU_EXPULSANDO_JUGADOR;
                    break;
                }
            }
            break;
        }
        case ESTADO_MENU_CAMBIANDO_ASIENTO: {
            TCIC_cambiar_asiento(comando);
            estado_menu = ESTADO_MENU_EN_SALA;
            break;
        }
        case ESTADO_MENU_CAMBIANDO_ASIENTO_OTRO: {
            if(asiento_cambio_elegido != JUGADOR_INVALIDO){
                TCIC_cambiar_asiento_otro(asiento_cambio_elegido, comando);
                estado_menu = ESTADO_MENU_EN_SALA;
                asiento_cambio_elegido = JUGADOR_INVALIDO;
            }else{
                asiento_cambio_elegido = comando;
            }
            break;
        }
        case ESTADO_MENU_DANDO_ANFITRION: {
            TCIC_dar_anfitrion(comando);
            estado_menu = ESTADO_MENU_EN_SALA;
            break;
        }
        case ESTADO_MENU_CONFIGURANDO_SALA: {
            switch(comando){
                case ACCION_A_15: {
                    TCIC_setear_puntaje(15);
                    break;
                }
                case ACCION_A_30: {
                    TCIC_setear_puntaje(30);
                    break;
                }
                case ACCION_SETEAR_1V1: {
                    TCIC_setear_max_jugadores(2);
                    break;
                }
                case ACCION_SETEAR_2V2: {
                    TCIC_setear_max_jugadores(4);
                    break;
                }
                case ACCION_SETEAR_3V3: {
                    TCIC_setear_max_jugadores(6);
                    break;
                }
                default: break;
            }
            break;
        }
        case ESTADO_MENU_EXPULSANDO_JUGADOR: {
            TCIC_expulsar_jugador(comando);
            estado_menu = ESTADO_MENU_EN_SALA;
            break;
        }
    }

    TCIC_render_menu_segun_estado();
}