#include <assert.h>
#include <math.h>
#include <stdio.h>
#include "quadboard.h"
#include "state.h"
#include "eval.h"

// evaluation weights
EVAL_SIM_WEIGHTS eval_sim_w;
EVAL_TAC_WEIGHTS eval_tac_w;

// initialise the constants for the simple evaluation
static void eval_sim_init()
{
    PIECETYPE pt;

    // read constants from file
    FILE* fp = fopen(EVAL_SIM_FILE, "r");

    // piece square tables
    for(pt = PAWN; pt <= QUEEN; ++pt)
        eval_pst_scan(fp, &eval_sim_w.pst[pt - 1]);

    // king square table
    eval_pst_scan(fp, &eval_sim_w.kst[EVAL_SIM_K_MID]);
    eval_pst_scan(fp, &eval_sim_w.kst[EVAL_SIM_K_END]);

    // phase values
    for(pt = PAWN; pt <= KING; ++pt)
        fscanf(fp, "%"EVAL_SCAN, &eval_sim_w.phase[pt - 1]);

    // cumulative phase value
    eval_sim_w.phase[EVAL_SIM_P_START] = 16 * eval_sim_w.phase[PAWN - 1] +
                                         4 * eval_sim_w.phase[KNIGHT - 1] +
                                         4 * eval_sim_w.phase[BISHOP - 1] +
                                         4 * eval_sim_w.phase[ROOK - 1] +
                                         2 * eval_sim_w.phase[QUEEN - 1];

    // close the file
    fclose(fp);
}

// initialise the constants for the polish evaluation
static void eval_tac_init()
{
    PIECETYPE pt, pt_atk;
    int def, turn, many, rk, i;

    // read constants from file
    FILE* fp = fopen(EVAL_TAC_FILE, "r");

    // piece square tables
    for(pt = PAWN; pt <= QUEEN; ++pt)
        eval_pst_scan(fp, &eval_tac_w.pst[pt - 1]);

    // king square table
    eval_pst_scan(fp, &eval_tac_w.kst[EVAL_TAC_K_MID]);
    eval_pst_scan(fp, &eval_tac_w.kst[EVAL_TAC_K_END]);

    // phase values
    for(pt = PAWN; pt <= QUEEN; ++pt)
        fscanf(fp, "%lf", &eval_tac_w.phase[pt - 1]);
    fscanf(fp, "%lf", &eval_tac_w.phase_off);

    // attack bonuses
    for(def = 0; def <= 1; ++def)
        for(pt = PAWN; pt <= QUEEN; ++pt)
            for(pt_atk = PAWN; pt_atk <= KING; ++pt_atk)
                fscanf(fp, "%lf", &eval_tac_w.atk[def][pt - 1][pt_atk - 1]);
    fscanf(fp, "%lf", &eval_tac_w.atk_off);
    fscanf(fp, "%lf", &eval_tac_w.atk_mul);

    // passed pawns
    for(turn = 0; turn <= 1; ++turn)
        for(many = 0; many <= 1; ++many)
            for(rk = 0; rk < 8; ++rk)
                fscanf(fp, "%"EVAL_SCAN, &eval_tac_w.pp[turn][many][rk]);

    // checks
    for(i = 0; i < 3; ++i)
        for(pt = PAWN; pt <= KING; ++pt)
            fscanf(fp, "%lf", &eval_tac_w.chk[i][pt - 1]);
    fscanf(fp, "%lf", &eval_tac_w.chk_off);
    fscanf(fp, "%lf", &eval_tac_w.chk_mul);

    // right-to-move advantage
    fscanf(fp, "%"EVAL_SCAN, &eval_tac_w.turn);

    // close the file
    fclose(fp);
}

// initialise the constants
void eval_init()
{
    eval_sim_init();
    eval_tac_init();
}

// relative to absolute mate scores
EVAL eval_ply_2_abs(EVAL scr, int ply)
{
    if(scr > EVAL_MATE)
        return scr + ply;
    if(scr < -EVAL_MATE)
        return scr - ply;
    return scr;
}

// absolute to relative mate scores
EVAL eval_abs_2_ply(EVAL scr, int ply)
{
    if(scr > EVAL_MATE)
        return scr - ply;
    if(scr < -EVAL_MATE)
        return scr + ply;
    return scr;
}

// convert the score in a UCI string
void eval_print_uci(char* uci, EVAL score)
{
    if(score > +EVAL_MATE)
        sprintf(uci, "score mate %d", (int) (EVAL_MAX - score + 1) / 2);
    else if(score < -EVAL_MATE)
        sprintf(uci, "score mate %d", (int) (-EVAL_MAX - score) / 2);
    else
        sprintf(uci, "score cp %d", (int) score);
}

// write the pst on file
void eval_pst_print(FILE* fp, EVAL_PST* pst)
{
    int file, rank;

    // include a line break after every rank
    for(rank = 0; rank < 8; ++rank) {
        for(file = 0; file < 8; ++file)
            fprintf(fp, "%"EVAL_PRINT"\t", pst->w[ALG_2_SQ(file, rank)]);
        fprintf(fp, "\n");
    }
}

// read the pst from file
void eval_pst_scan(FILE* fp, EVAL_PST* pst)
{
    SQUARE sq;

    for(sq = A8; sq <= H1; ++sq)
        fscanf(fp, "%"EVAL_SCAN, &pst->w[sq]);
}

// compute the pst score
EVAL eval_pst_score(EVAL_PST* pst, BITBOARD bb, PLAYER pl)
{
    EVAL scr = 0;

    while(bb) {
        int sq = SQ_MIRROR(bb_lsb_next(&bb), pl);
        scr += pst->w[sq];
    }

    return scr;
}

// compute the attack maps
void eval_attack_init(EVAL_ATTACK* atk, const QUADBOARD* qb)
{
    // occupancy bit-board
    BITBOARD occ_tot = qb_get_bb_tot(qb);

    // piece-wise bit-boards
    BITBOARD bb_p = qb_get_bb_pt(qb, PAWN);
    BITBOARD bb_n = qb_get_bb_pt(qb, KNIGHT);
    BITBOARD bb_b = qb_get_bb_pt(qb, BISHOP);
    BITBOARD bb_r = qb_get_bb_pt(qb, ROOK);
    BITBOARD bb_q = qb_get_bb_pt(qb, QUEEN);

    // attack bit-boards
    PLAYER pl;
    for(pl = WHITE; pl <= BLACK; ++pl) {
        BITBOARD mask_pl = BB_MASK_PL(qb, pl);

        // pawn attacks
        BITBOARD bb_p_pl = bb_p & mask_pl;
        atk->bb_pt[pl][PAWN - 1] = bb_dia_w(bb_p_pl, pl) | bb_dia_e(bb_p_pl, pl);

        // knight attacks
        atk->bb_pt[pl][KNIGHT - 1] = BB_NULL;
        BITBOARD bb_n_pl = bb_n & mask_pl;
        while(bb_n_pl)
            atk->bb_pt[pl][KNIGHT - 1] |= bb_n_leap[bb_lsb_next(&bb_n_pl)];

        // bishop attacks
        atk->bb_pt[pl][BISHOP - 1] = BB_NULL;
        BITBOARD bb_b_pl = bb_b & mask_pl;
        while(bb_b_pl)
            atk->bb_pt[pl][BISHOP - 1] |= bb_b_slide(occ_tot, bb_lsb_next(&bb_b_pl));

        // rook attacks
        atk->bb_pt[pl][ROOK - 1] = BB_NULL;
        BITBOARD bb_r_pl = bb_r & mask_pl;
        while(bb_r_pl)
            atk->bb_pt[pl][ROOK - 1] |= bb_r_slide(occ_tot, bb_lsb_next(&bb_r_pl));

        // queen attacks
        atk->bb_pt[pl][QUEEN - 1] = BB_NULL;
        BITBOARD bb_q_pl = bb_q & mask_pl;
        while(bb_q_pl) {
            int sq = bb_lsb_next(&bb_q_pl);
            atk->bb_pt[pl][QUEEN - 1] |= bb_b_slide(occ_tot, sq) | bb_r_slide(occ_tot, sq);
        }

        // king attacks
        atk->bb_pt[pl][KING - 1] = bb_k_leap[qb_get_k_sq(qb, pl)];

        // cumulative
        atk->bb_tot[pl] = atk->bb_pt[pl][PAWN - 1] |
                          atk->bb_pt[pl][KNIGHT - 1] |
                          atk->bb_pt[pl][BISHOP - 1] |
                          atk->bb_pt[pl][ROOK - 1] |
                          atk->bb_pt[pl][QUEEN - 1] |
                          atk->bb_pt[pl][KING - 1];
    }
}

// a random evaluation
EVAL eval_rand(QUADBOARD* qb, STATE st)
{
    return (qb_hash(qb) ^ st_hash(st)) % (2 * EVAL_RAND_MAX + 1) - EVAL_RAND_MAX;
}

// simple evaluation
EVAL eval_simple(QUADBOARD* qb, STATE st)
{
    EVAL p_scr = 0;
    EVAL phase = 0;
    PIECETYPE pt;

    // colour-blind code
    PLAYER pl = ST_GET_PL(st);
    PLAYER foe = FOE(pl);

    // piece square scores and phase
    for(pt = PAWN; pt <= QUEEN; ++pt) {
        BITBOARD bb_pt = qb_get_bb_pt(qb, pt);
        BITBOARD bb_mask = BB_MASK_PL(qb, pl);
        p_scr += eval_pst_score(&eval_sim_w.pst[pt - 1], bb_pt & bb_mask, pl);
        p_scr -= eval_pst_score(&eval_sim_w.pst[pt - 1], bb_pt & ~bb_mask, foe);
        phase += eval_sim_w.phase[pt - 1] * bb_pop_count(bb_pt);
    }
    EVAL anti_phase = eval_sim_w.phase[EVAL_SIM_P_START] - phase;

    // king score
    int k_sq_pl = SQ_MIRROR(qb_get_k_sq(qb, pl), pl);
    int k_sq_foe = SQ_MIRROR(qb_get_k_sq(qb, foe), foe);
    EVAL k_mid_scr = eval_sim_w.kst[EVAL_SIM_K_MID].w[k_sq_pl] -
                     eval_sim_w.kst[EVAL_SIM_K_MID].w[k_sq_foe];
    EVAL k_end_scr = eval_sim_w.kst[EVAL_SIM_K_END].w[k_sq_pl] -
                     eval_sim_w.kst[EVAL_SIM_K_END].w[k_sq_foe];
    EVAL k_scr = (k_mid_scr * phase + k_end_scr * anti_phase) /
                  eval_sim_w.phase[EVAL_SIM_P_START];

    // final evaluation
    return p_scr + k_scr;
}

// sigmoid unit
static double eval_sigmoid(double x)
{
    return (1.0 / (1.0 + exp(-x)));
}

// passed pawns
static EVAL eval_tactic_pp(const EVAL_ATTACK* atk, const QUADBOARD* qb, PLAYER pl, int turn)
{
    EVAL scr = 0;

    // obstacles
    PLAYER foe = FOE(pl);
    BITBOARD bb_def = atk->bb_tot[foe] | qb_get_bb_tot(qb);

    // does the opponent have any piece left?
    BITBOARD bb_nop_foe = qb_get_bb_pl(qb, foe) & ~qb_get_bb_pt(qb, PAWN);
    BITBOARD bb_k_foe = qb_get_bb_pt(qb, KING) & BB_MASK_PL(qb, foe);
    int many = (bb_k_foe == bb_nop_foe)? 0 : 1;

    // scan the pawns
    BITBOARD bb_p_pl = qb_get_bb_pt(qb, PAWN) & BB_MASK_PL(qb, pl);
    while(bb_p_pl) {
        int sq = bb_lsb_next(&bb_p_pl);
        if((bb_front(BB_SQ(sq), pl) & ~bb_def)) {
            int rk = SQ_RANK(SQ_MIRROR(sq, pl));
            scr += eval_tac_w.pp[turn][many][rk];
        }
    }

    return scr;
}

// tactic evaluation
EVAL eval_tactic(QUADBOARD* qb, STATE st)
{
    EVAL_ATTACK atk;
    PIECETYPE pt;
    EVAL scr = 0;

    // colour-blind code
    PLAYER pl = ST_GET_PL(st);
    PLAYER foe = FOE(pl);

    /*char fen[256];
    FILE* fp = fopen("cippa.txt", "w");
    qb_print_fen(fen, qb);
    fprintf(fp, "%s\n", fen);
    fclose(fp);
    pt = KING;*/

    // piece square scores and phase
    double phase = 0.0;
    for(pt = PAWN; pt <= QUEEN; ++pt) {
        BITBOARD bb_pt = qb_get_bb_pt(qb, pt);
        BITBOARD bb_mask = BB_MASK_PL(qb, pl);
        scr += eval_pst_score(&eval_tac_w.pst[pt - 1], bb_pt & bb_mask, pl);
        scr -= eval_pst_score(&eval_tac_w.pst[pt - 1], bb_pt & ~bb_mask, foe);
        phase += eval_tac_w.phase[pt - 1] * bb_pop_count(bb_pt);
    }
    phase = eval_sigmoid(phase + eval_tac_w.phase_off);

    // king score
    int k_sq_pl = SQ_MIRROR(qb_get_k_sq(qb, pl), pl);
    int k_sq_foe = SQ_MIRROR(qb_get_k_sq(qb, foe), foe);
    scr += eval_tac_w.kst[EVAL_TAC_K_END].w[k_sq_pl] -
           eval_tac_w.kst[EVAL_TAC_K_END].w[k_sq_foe];
    scr += (eval_tac_w.kst[EVAL_TAC_K_MID].w[k_sq_pl] -
            eval_tac_w.kst[EVAL_TAC_K_MID].w[k_sq_foe]) * phase;

    // compute the attack maps
    eval_attack_init(&atk, qb);

    // attack bonuses
    double k = 0.0;
    BITBOARD bb_targ = qb_get_bb_pl(qb, pl) & atk.bb_tot[foe] & ~qb_get_bb_pt(qb, KING);
    while(bb_targ) {
        int sq = bb_lsb_next(&bb_targ);
        BITBOARD bb_sq = BB_SQ(sq);
        pt = qb_get_sq_pt(qb, sq);

        // least attacker
        PIECETYPE pt_atk;
        for(pt_atk = PAWN; pt_atk <= QUEEN; ++pt_atk)
            if((atk.bb_pt[foe][pt_atk - 1] & bb_sq))
                break;

        int def = (atk.bb_tot[pl] & bb_sq) != BB_NULL;
        k += eval_tac_w.atk[def][pt - 1][pt_atk - 1];
    }
    scr += (EVAL) (eval_sigmoid(k + eval_tac_w.atk_off) * eval_tac_w.atk_mul + 0.5);

    // passed pawns
    scr += eval_tactic_pp(&atk, qb, pl, 0);
    scr -= eval_tactic_pp(&atk, qb, foe, 1);

    // checks
    double c = 0.0;
    BITBOARD bb_def = atk.bb_pt[foe][PAWN - 1] |
                      atk.bb_pt[foe][KNIGHT - 1] |
                      atk.bb_pt[foe][BISHOP - 1] |
                      atk.bb_pt[foe][ROOK - 1] |
                      atk.bb_pt[foe][QUEEN - 1];
    BITBOARD bb_k_zone = atk.bb_pt[foe][KING - 1] & ~bb_def;
    for(pt = PAWN; pt <= KING; ++pt) {
        BITBOARD bb_atk = atk.bb_pt[pl][pt - 1] & bb_k_zone;
        if(bb_atk)
            c += eval_tac_w.chk[EVAL_TAC_C_ZONE][pt - 1];
    }
    c = eval_sigmoid(c + eval_tac_w.chk_off);
    int k_sq = qb_get_k_sq(qb, foe);
    BITBOARD bb_occ = qb_get_bb_tot(qb);
    BITBOARD bb_safe = bb_occ | atk.bb_tot[foe]; // occupied or defended
    BITBOARD bb_k_n = bb_n_leap[k_sq] & ~bb_safe;
    BITBOARD bb_k_b = bb_b_slide(bb_occ, k_sq) & ~bb_safe;
    BITBOARD bb_k_r = bb_r_slide(bb_occ, k_sq) & ~bb_safe;
    BITBOARD bb_chk_n = bb_k_n & atk.bb_pt[pl][KNIGHT - 1];
    BITBOARD bb_chk_b = bb_k_b & atk.bb_pt[pl][BISHOP - 1];
    BITBOARD bb_chk_r = bb_k_r & atk.bb_pt[pl][ROOK - 1];
    BITBOARD bb_chk_q = (bb_k_b | bb_k_r) & atk.bb_pt[pl][QUEEN - 1];
    double a = 0.0;
    double b = 0.0;
    if(bb_chk_n) {
        a += eval_tac_w.chk[EVAL_TAC_C_NONE][KNIGHT - 1];
        b += eval_tac_w.chk[EVAL_TAC_C_MANY][KNIGHT - 1];
    }
    if(bb_chk_b) {
        a += eval_tac_w.chk[EVAL_TAC_C_NONE][BISHOP - 1];
        b += eval_tac_w.chk[EVAL_TAC_C_MANY][BISHOP - 1];
    }
    if(bb_chk_r) {
        a += eval_tac_w.chk[EVAL_TAC_C_NONE][ROOK - 1];
        b += eval_tac_w.chk[EVAL_TAC_C_MANY][ROOK - 1];
    }
    if(bb_chk_q) {
        a += eval_tac_w.chk[EVAL_TAC_C_NONE][QUEEN - 1];
        b += eval_tac_w.chk[EVAL_TAC_C_MANY][QUEEN - 1];
    }
    scr += (EVAL) (a + (b + eval_tac_w.chk_mul) * c + 0.5);

    return scr + eval_tac_w.turn;
}

// evaluation hub
EVAL eval(QUADBOARD* qb, STATE st)
{
    //return eval_simple(qb, st);
    return eval_tactic(qb, st);
}
