#include <stdint.h>

#define PALO_ESPADA 0
#define PALO_BASTO  1
#define PALO_ORO    2
#define PALO_COPA   3

#define TOTAL_CARTAS 40
#define MAX_JUGADORES 6
#define MAX_CARTAS_MANO 3
#define MAX_BAZAS_RONDA 3

#define CARTA_INVALIDA (TOTAL_CARTAS + 1)
#define JUGADOR_INVALIDO (MAX_JUGADORES +1)
#define EQUIPO_INVALIDO 2

typedef struct{
    uint8_t palo;
    uint8_t valor_carta;
} Carta;

const Carta cartas[TOTAL_CARTAS] = {
    {PALO_ESPADA, 1},
    {PALO_BASTO,  1},
    {PALO_ESPADA, 7},
    {PALO_ORO,    7},

    {PALO_ESPADA, 3},
    {PALO_BASTO,  3},
    {PALO_ORO,    3},
    {PALO_COPA,   3},

    {PALO_ESPADA, 2},
    {PALO_BASTO,  2},
    {PALO_ORO,    2},
    {PALO_COPA,   2},

    {PALO_ORO,    1},
    {PALO_COPA,   1},

    {PALO_ESPADA, 12},
    {PALO_BASTO,  12},
    {PALO_ORO,    12},
    {PALO_COPA,   12},

    {PALO_ESPADA, 11},
    {PALO_BASTO,  11},
    {PALO_ORO,    11},
    {PALO_COPA,   11},

    {PALO_ESPADA, 10},
    {PALO_BASTO,  10},
    {PALO_ORO,    10},
    {PALO_COPA,   10},

    {PALO_BASTO,  7},
    {PALO_COPA,   7},

    {PALO_BASTO,  6},
    {PALO_ORO,    6},
    {PALO_COPA,   6},
    {PALO_ESPADA, 6},

    {PALO_BASTO,  5},
    {PALO_ORO,    5},
    {PALO_COPA,   5},
    {PALO_ESPADA, 5},

    {PALO_BASTO,  4},
    {PALO_ORO,    4},
    {PALO_COPA,   4},
    {PALO_ESPADA, 4},
};

uint8_t seed = 13;

uint8_t rand8() {
    seed ^= (uint8_t)(seed << 7);
    seed ^= (uint8_t)(seed >> 5);
    seed ^= (uint8_t)(seed << 3);
    return seed;
}

uint8_t jugadores_cartas[MAX_JUGADORES][MAX_CARTAS_MANO];

uint8_t cartas_bazas[MAX_BAZAS_RONDA][MAX_JUGADORES];

uint8_t numero_jugadores = 2;

uint8_t jugador_mano = 0;
uint8_t jugador_turno_actual = 0;
uint8_t jugador_inicial_baza = 0;

uint8_t baza_actual = 0;
uint8_t baza_puntos = 0;
uint8_t parda = 0;


#define EVENTO_JUGADA_INVALIDA 0 //default
#define EVENTO_CARTA_JUGADA 1
#define EVENTO_NUEVA_BAZA 2
#define EVENTO_RONDA_TERMINADA 3

typedef struct{
    uint8_t evento;

    uint8_t carta;

    uint8_t ganador_baza;
    uint8_t equipo_ganador;
} ResultadoJugada;


void repartir(){
    uint64_t usadas = 0;

    for(uint8_t jugador_i = 0; jugador_i < numero_jugadores; jugador_i++){
        for(uint8_t carta_i = 0; carta_i < MAX_CARTAS_MANO; carta_i++){
            uint8_t indice;

            do {indice = rand8() % TOTAL_CARTAS;} while(usadas & (1ULL << indice));

            usadas |= (1ULL << indice);

            jugadores_cartas[jugador_i][carta_i] = indice;
        }
    }
}

void iniciar_ronda(){
    for(uint8_t b = 0; b <= baza_actual; b++){
        for(uint8_t j = 0; j < numero_jugadores; j++){
            cartas_bazas[b][j] = CARTA_INVALIDA;
        }
    }
    baza_actual = 0;
    jugador_turno_actual = jugador_mano;
    baza_puntos = 0;
    parda = 0;
    repartir();
}

uint8_t iniciar_partida(uint8_t jugadores){
    //rechazar jugadores impar, 0, 1 (redundante pero porque por ahi despues meto gallo) o mayores
    //a MAX_JUGADORES.
    if(jugadores <= 1 || jugadores > MAX_JUGADORES || jugadores % 2 != 0) return 0;
    numero_jugadores = jugadores;
    jugador_mano = 0;
    jugador_turno_actual = 0;
    jugador_inicial_baza = 0;
    baza_actual = 0;
    parda = 0;

    for(uint8_t b = 0; b < MAX_BAZAS_RONDA; b++){
        for(uint8_t j = 0; j < MAX_JUGADORES; j++){
            cartas_bazas[b][j] = CARTA_INVALIDA;
        }
    }

    for(uint8_t j = 0; j < MAX_JUGADORES; j++){
        for(uint8_t c = 0; c < MAX_CARTAS_MANO; c++){
            jugadores_cartas[j][c] = CARTA_INVALIDA;
        }
    }

    iniciar_ronda();
    return 1;
}


void nueva_baza(uint8_t jugador_inicial){
    jugador_inicial_baza = jugador_inicial;
    jugador_turno_actual = jugador_inicial_baza;
    baza_actual++;
}

void terminar_ronda(uint8_t equipo_ganador_i){
    jugador_mano++;
    if(jugador_mano == numero_jugadores) jugador_mano = 0;
    iniciar_ronda();
}

ResultadoJugada jugar_baza(uint8_t jugador_i, uint8_t carta_mano_i){

    ResultadoJugada resultado = {
        .evento = EVENTO_JUGADA_INVALIDA,
        .carta = CARTA_INVALIDA,
        .ganador_baza = JUGADOR_INVALIDO,
        .equipo_ganador = JUGADOR_INVALIDO,
    };

    //si el jugador o la carta son invalidos, salir
    if(jugador_i >= numero_jugadores || carta_mano_i >= MAX_CARTAS_MANO) return resultado;

    uint8_t carta_i = jugadores_cartas[jugador_i][carta_mano_i];
    if(jugador_turno_actual != jugador_i || 
    jugador_i >= numero_jugadores || 
    carta_i == CARTA_INVALIDA) return resultado;

    resultado.carta = carta_i;

    //poner la carta en la mesa en la posición del jugador y sacarsela.
    cartas_bazas[baza_actual][jugador_i] = carta_i;
    jugadores_cartas[jugador_i][carta_mano_i] = CARTA_INVALIDA;

    /*
        ahora es turno del siguiente jugador. si llegamos al máximo de jugadores de la partida,
        empezamos por el primer jugador de la mesa, pero si es el del primer jugador que jugó,
        eso significa que terminó la baza.
    */
    jugador_turno_actual++;
    
    if(jugador_turno_actual >= numero_jugadores) jugador_turno_actual = 0;

    if(jugador_turno_actual == jugador_inicial_baza){
        //evaluar y terminar baza/ronda/partida
        uint8_t mayor_carta_i = CARTA_INVALIDA;
        uint8_t ganador_baza = JUGADOR_INVALIDO;
        
        for(uint8_t i = 0; i < numero_jugadores; i++){
            uint8_t carta_actual = cartas_bazas[baza_actual][i];
            if(carta_actual < mayor_carta_i){
                mayor_carta_i = carta_actual;
                ganador_baza = i;
            } 
        }

        // revisamos que no sea parda. 
        // si la carta es una de las cuatro primeras, obvio que no es.
        // de lo contrario, me fijo si hay una carta del mismo valor
        // numérico que la "ganadora". si la hay, (y es de alguien de otro equipo), es parda.
        if(mayor_carta_i > 3){
            for(uint8_t i = 0; i < numero_jugadores; i++){
                uint8_t carta_actual = cartas_bazas[baza_actual][i];

                if(carta_actual != CARTA_INVALIDA && 
                    cartas[carta_actual].valor_carta == cartas[mayor_carta_i].valor_carta &&
                   (i % 2 != ganador_baza % 2)){
                    mayor_carta_i = CARTA_INVALIDA;
                    break;
                }
            }
        }



        //pasar de baza o terminar ronda
        if(baza_actual < MAX_BAZAS_RONDA - 1){

            uint8_t es_primera_baza = baza_actual == 0;
            uint8_t jugador_siguiente_baza = ganador_baza;
            uint8_t hay_mayor_carta = mayor_carta_i != CARTA_INVALIDA;
            uint8_t equipo_ganador = ganador_baza % 2;

            if(hay_mayor_carta){
                //caso normal. hay carta ganadora.

                //solo si el ganador es el equipo 1 seteamos el bit de la baza actual.
                if(equipo_ganador == 1) baza_puntos |= (1 << baza_actual);
                resultado.ganador_baza = ganador_baza;
                resultado.equipo_ganador = equipo_ganador;
            }
            else{
                //parda. mano empieza la siguiente baza
                parda = 1;
                jugador_siguiente_baza = jugador_mano;
            }

            //si es la primera baza, si los últimos 2 resultados de la baza estan intercalados (osea no gano nadie)
            //o si se esta en parda y no hay carta considerada ganadora,
            //jugar nueva baza. de lo contrario, terminar la ronda.
            if(es_primera_baza || 
            (!(((baza_puntos >> baza_actual) & 1) && ((baza_puntos >> (baza_actual - 1)) & 1)) &&
            (parda && !hay_mayor_carta))
            ){
                resultado.evento = EVENTO_NUEVA_BAZA;
                nueva_baza(jugador_siguiente_baza);
            } 
            else{
                resultado.evento = EVENTO_RONDA_TERMINADA;
                terminar_ronda(equipo_ganador);
            } 

        }else{
            uint8_t equipo_ganador = jugador_mano % 2;

            resultado.evento = EVENTO_RONDA_TERMINADA;
            resultado.equipo_ganador = equipo_ganador;
            terminar_ronda(equipo_ganador);
        }
    }
    return resultado;
}