#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "structure.h"

const int WIDTH = 1000;
const int HEIGHT = 1000;

const int ROOM_SIZE = 100;
const int MAX_SPEED = 220;               // m/s

const double DIST_EDGE = 0.2;
const double START_EDGE = 0.05;
const double RESTITUTION_WALL = 0.8;
const double RESTITUTION_PLAYER = 0.8;  // keep equal to previous constant

const int PLAYER_R = 5;
const int MAX_THETA_SPAWN = 80;         // degrees

const int BARRIERS = 0;                 // enables/disables barriers
const int ONEATATIME = 0;               // 0 = everyone move at the same time, 1 = turn-based movement (one player at a time)
const double BARRIER_WIDTH = 0.05;

const double FRICTION = 0.90;
const double DV = 2.2;                  // m/s
const double DT = 1.0/100.0;
const double EPSILON = 1.0/4096.0;

const double DELTA_V = 7.0;         // %
const double DELTA_THETA = 7.0;     // degrees

const double WIND_X = 0.0;
const double WIND_Y = 0.0;

const double STR2_W = 0.2;

double B = 0.0;   // 0.6 works fine

// ------------------------------------------------------------------------------------------ //

void write_output(char* stream, level* lvl, int nPl) {
    FILE* ptr = fopen(stream, "w");
    fprintf(ptr, "%d\n", nPlayers);
    for(int p = 0; p < nPlayers; p++) {
        fprintf(ptr, "%d (%d %d) (%.2lf %.2lf)\n", p, players[p].c->chx, players[p].c->chy, players[p].c->pos.fx, players[p].c->pos.fy);
    }
    fprintf(ptr, "%d\n\n", nPl);
    fprintf(ptr, "%d %d\n", lvl->lines, lvl->cols);
    for(int l = 0; l < lvl->lines; l++) {
        for(int c = 0; c < lvl->cols; c++) {
            switch (lvl->map[l][c]){
                case NONE:
                    fprintf(ptr, ".");
                    break;

                case START:
                    fprintf(ptr, "S");
                    break;

                case END:
                    fprintf(ptr, "E");
                    break;
                
                default:
                    fprintf(ptr, "%d", (int)lvl->map[l][c]-3);
                    break;
            }
        }
        fprintf(ptr, "\n");
    }
    fprintf(ptr, "\n");
    fprintf(ptr, "%d %d %d %.2lf %.2lf %.2lf %.2lf %.2lf\n", ROOM_SIZE, MAX_SPEED, PLAYER_R, FRICTION, RESTITUTION_WALL, DIST_EDGE, B, STR2_W);
    fprintf(ptr, "\nRoom Size, Maximum speed, Player Radius, Friction, Restitution factor, Track Distance to Edge, Magnetic Field, Slow zone Width");
    fclose(ptr);
}

// ------------------------- //

bool str_equal(char* s1, char* s2) {
    if(s1[0] == '\0' || s2[0] == '\0') {
        return (s1[0] == '\0' && s2[0] == '\0');
    } else if(s1[0] != s2[0]) {
        return false;
    }
    return str_equal(&s1[1], &s2[1]);
}

int* get_elo_diffs(char** execs) {
    FILE* ptr = fopen("leaderboard.txt", "r");
    int* elos = malloc(sizeof(int)*nPlayers);
    for(int p = 0; p < nPlayers; p++) {
        elos[p] = 500;
    }

    // ignore first line
    char c = fgetc(ptr);
    while(c != EOF && c != '\n') {
        c = fgetc(ptr);
    }

    char* name = malloc(sizeof(char)*64);
    int elo;
    int scanres = fscanf(ptr, "%s %d", name, &elo);
    while(scanres > 0) {
        for(int p = 0; p < nPlayers; p++) {
            if(str_equal(name, execs[p])) {
                elos[p] = elo;
            }
        }

        scanres = fscanf(ptr, "%s %d", name, &elo);
    }

    fclose(ptr);
    free(name);

    int* diffs = malloc(sizeof(int)*nPlayers);
    for(int p = 0; p < nPlayers; p++) {
        diffs[p] = 0;
        for(int q = 0; q < nPlayers; q++) {
            if(p != q) {
                diffs[p] += elos[q] - elos[p];
            }
        }
        diffs[p] = diffs[p]/(nPlayers-1+((nPlayers==1)?(1):(0)));
    }

    free(elos);
    return diffs;
}

int smax(int a, int b) {return (a>b)?(a):(b);}
int smin(int a, int b) {return (a<b)?(a):(b);}
double fsmax(double a, double b) {return (a>b)?(a):(b);}
double fsmin(double a, double b) {return (a<b)?(a):(b);}

// +20 for #1 no matter what
// up to -70 (past 2500) for last
// if diff < -250, no bonus
// if diff > 250, 2x bonus
int gains(int elo, int rank, int diff) {
    return 5+(int)(
        (fsmax(0.1, fsmin(2.0, (diff+250.0)/250.0)))*20-
            (smax(40,smin(90, (90*elo)/1900)))*
            (((rank-1)*1.0)/((nPlayers-1+((nPlayers==1)?(1):(0)))*1.0))
    );
}

void updateLeaderBoard(char** execs, int* ranks) {
    FILE* ptr = fopen("leaderboard.txt", "r");
    FILE* ptrOut = fopen("tempLeader.txt", "w");

    // ignore first line
    char c = fgetc(ptr);
    while(c != EOF && c != '\n') {
        fprintf(ptrOut, "%c", c);
        c = fgetc(ptr);
    }
    fprintf(ptrOut, "\n");

    int* diffs = get_elo_diffs(execs);

    bool* found = malloc(sizeof(bool)*nPlayers);
    for(int p = 0; p < nPlayers; p++) {
        found[p] = false;
    }
    char* entry = malloc(sizeof(char)*64);
    int elo;
    int res = fscanf(ptr, "%s %d", entry, &elo);
    while(res > 0) {
        for(int p = 0; p < nPlayers; p++) {
            if(!found[p] && str_equal(execs[p], entry)) {
                found[p] = true;
                printf("[%s - rank %d/%d] : %d -", execs[p], ranks[p], nPlayers, elo);
                int de = gains(elo, ranks[p], diffs[p]);
                printf("(%d)-> ", de);
                elo += de;
                printf("%d\n", elo);
            }
        }

        fprintf(ptrOut, "%s %d\n", entry, elo);
        res = fscanf(ptr, "%s %d\n", entry, &elo);
    }
    fclose(ptr);
    fclose(ptrOut);

    // copy data to main file
    FILE* tempRead = fopen("tempLeader.txt", "r");
    FILE* mainWrite = fopen("leaderboard.txt", "w");

    c = fgetc(tempRead);
    while(c != EOF) {
        fprintf(mainWrite, "%c", c);
        c = fgetc(tempRead);
    }
    for(int k = 0; k < nPlayers; k++) {
        if(!found[k]) {
            fprintf(mainWrite, "%s %d\n", execs[k], 500+gains(500, ranks[k], diffs[k]));
        }
    }

    fclose(tempRead);
    fclose(mainWrite);

    free(entry);
    free(found);
    free(diffs);
}