#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <unistd.h>
#include <termios.h>
#include <limits.h>
#include <time.h>
#include <sys/time.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

#include "structure.h"
#include "base.h"
#include "display.h"

imgs* digits;
imgs* letters;

void updateRenderer(SDL_Renderer* renderer) {
    //printf("E");
    SDL_RenderPresent(renderer);
}

void resetRenderer(SDL_Renderer* renderer) {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
}

void drawRectToRenderer(SDL_Renderer* renderer, SDL_Rect* rect, int R, int G, int B, int A) {
    SDL_SetRenderDrawColor(renderer, R, G, B, A);
    SDL_RenderFillRect(renderer, rect);
}

void placeRectToRenderer(SDL_Renderer* renderer, int X, int Y, int W, int H, int R, int G, int B, int A) {
    SDL_Rect rect;
    rect.x = X;
    rect.y = Y;
    rect.w = W;
    rect.h = H;
    SDL_SetRenderDrawColor(renderer, R, G, B, A);
    SDL_RenderFillRect(renderer, &rect);
}

void drawLineWithThicc(SDL_Renderer* renderer, int width, int x1, int x2, int y1, int y2, int R, int G, int B, int A) {
    // draws line with width (native SDL cannot do that)
    double theta = 0.0;
    double seglen = distance_pt(x1, x2, y1, y2);
    while(theta < 1.0) {
        placeRectToRenderer(renderer, convex_seg(x1, x2, theta)-width/2, convex_seg(y1, y2, theta)-width/2, width, width, R, G, B, A);
        theta += 1 / seglen;
    }
}

void drawDigitToRenderer(SDL_Renderer* renderer, int digit, int X, int Y, int W, int H) {
    if(digit == -727) {
        SDL_Rect rect;
        rect.x = X;
        rect.y = Y;
        rect.w = W;
        rect.h = H;
            
        SDL_Texture* texture = digits->arr[10];
        
        SDL_RenderCopy(renderer, texture, NULL, &rect);
    } else if (!(0 <= digit && digit <= 9)) {
        fprintf(stderr, "Illegal digit : '%d'.\n", digit);
        exit(1);
    } else {
        SDL_Rect rect;
        rect.x = X;
        rect.y = Y;
        rect.w = W;
        rect.h = H;
            
        SDL_Texture* texture = digits->arr[digit];
        
        SDL_RenderCopy(renderer, texture, NULL, &rect);
    }
}

void drawCharToRenderer(SDL_Renderer* renderer, char c, int X, int Y, int W, int H) {
    if ((int)c >= 97 && (int)c <= 122) {
        SDL_Rect rect;
        rect.x = X;
        rect.y = Y;
        rect.w = W;
        rect.h = H;
            
        SDL_Texture* texture = letters->arr[(int)c - 97];
        
        SDL_RenderCopy(renderer, texture, NULL, &rect);
    }
}

void drawNumberToRenderer(SDL_Renderer* renderer, int n, int X, int Y, int W, int H, int Woffset) {
    if(n == 0) {
        drawDigitToRenderer(renderer, 0, X + W, Y, W, H);
    } else if(n > 0) {
        int toDraw = 0, remaining = n, nIter = notLN(n, 10);
        while(nIter != 0) {
            toDraw = remaining%10;
            remaining = remaining / 10;
            drawDigitToRenderer(renderer, toDraw, X + (W-Woffset)*nIter, Y, W, H);
            nIter--;
        }
    } else {
        int toDraw = 0, remaining = -n, nIter = notLN(-n, 10);
        drawDigitToRenderer(renderer, -727, X, Y, W, H);
        while(nIter != 0) {
            toDraw = remaining%10;
            remaining = remaining / 10;
            drawDigitToRenderer(renderer, toDraw, X + (W-Woffset)*nIter, Y, W, H);
            nIter--;
        }
    }
}

void drawStringToRenderer(SDL_Renderer* renderer, char* s, int X, int Y, int W, int H) {
    int k = 0;
    while(s[k] != '\0') {
        drawCharToRenderer(renderer, s[k], X + W*k, Y, W, H);
        k += 1;
    }
}

void draw7SegDigitToRenderer(SDL_Renderer* renderer, int n, int X, int Y, int W, int H, int thicc, int R, int G, int B, int A) {
    if(n == 0) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);      // top left
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);    // bottom left
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
        placeRectToRenderer(renderer, X+thicc, Y+2*H+thicc, W+thicc, thicc, R, G, B, A);  // bottom
    } else if(n == 1) {
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
    } else if(n == 2) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, W+thicc, thicc, R, G, B, A);    // mid
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);    // bottom left
        placeRectToRenderer(renderer, X+thicc, Y+2*H+thicc, W+thicc, thicc, R, G, B, A);  // bottom
    } else if(n == 3) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, W+thicc, thicc, R, G, B, A);    // mid
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
        placeRectToRenderer(renderer, X+thicc, Y+2*H+thicc, W+thicc, thicc, R, G, B, A);  // bottom
    } else if(n == 4) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);      // top left
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, W+thicc, thicc, R, G, B, A);    // mid
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
    } else if(n == 5) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);      // top left
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, W+thicc, thicc, R, G, B, A);    // mid
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
        placeRectToRenderer(renderer, X+thicc, Y+2*H+thicc, W+thicc, thicc, R, G, B, A);  // bottom
    } else if(n == 6) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);      // top left
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, W+thicc, thicc, R, G, B, A);    // mid
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);    // bottom left
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
        placeRectToRenderer(renderer, X+thicc, Y+2*H+thicc, W+thicc, thicc, R, G, B, A);  // bottom
    } else if(n == 7) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);      // top left
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
    } else if(n == 8) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);      // top left
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, W+thicc, thicc, R, G, B, A);    // mid
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);    // bottom left
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
        placeRectToRenderer(renderer, X+thicc, Y+2*H+thicc, W+thicc, thicc, R, G, B, A);  // bottom
    } else if(n == 9) {
        placeRectToRenderer(renderer, X+thicc, Y+thicc, W+thicc, thicc, R, G, B, A);      // top
        placeRectToRenderer(renderer, X+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);      // top left
        placeRectToRenderer(renderer, X+W+thicc, Y+thicc, thicc, H+thicc, R, G, B, A);    // top right
        placeRectToRenderer(renderer, X+thicc, Y+H+thicc, W+thicc, thicc, R, G, B, A);    // mid
        placeRectToRenderer(renderer, X+W+thicc, Y+H+thicc, thicc, H+thicc, R, G, B, A);  // bottom right
        placeRectToRenderer(renderer, X+thicc, Y+2*H+thicc, W+thicc, thicc, R, G, B, A);  // bottom
    }
}

// H = size of ont segment (therefore the actual size of the number is W x 2H)
// side is -1 (alignedLeft), 0 (centered) or 1 (alignedRight)
// dot if for placing a dot (for decimals) at a specified place (enter -1 if none)
void draw7SegNumberToRenderer(SDL_Renderer* renderer, int n, int X, int Y, int W, int H, int thicc, int R, int G, int B, int A, int side, int dot) {
    int nlen = 1+ln_baseN(n, 10);
    int incrX = W+3*thicc;
    int curX = X +(side==0)*(incrX*(nlen)/2-incrX) +(side==-1)*incrX*(nlen-1);
    int curN = n;
    int doot = (dot==-1)?(nlen+1):(nlen-(dot-1));
    while(nlen > 0) {
        draw7SegDigitToRenderer(renderer, curN%10, curX, Y, W, H, thicc, R, G, B, A);
        curX -= incrX;
        if(nlen == doot) {
            placeRectToRenderer(renderer, curX-2*thicc+incrX, Y+2*H, 2*thicc, 2*thicc, R, G, B, A);
            curX -= 6*thicc;
        }
        curN /= 10;
        nlen -= 1;
    }
}
//---------------------------------------------------------------------------------------------------------------------//
// circle functions by @Gumichan01 at https://gist.github.com/Gumichan01/364c26f6197a464db91cc4647fcabb1c (not native) //
//---------------------------------------------------------------------------------------------------------------------//
void SDL_RenderDrawCircle(SDL_Renderer * renderer, int x, int y, int radius, int r, int g, int b, int a) {
    SDL_SetRenderDrawColor(renderer, r, g, b, a);
    int offsetx, offsety, d;
    int status;

    offsetx = 0;
    offsety = radius;
    d = radius -1;
    status = 0;

    while (offsety >= offsetx) {
        status += SDL_RenderDrawPoint(renderer, x + offsetx, y + offsety);
        status += SDL_RenderDrawPoint(renderer, x + offsety, y + offsetx);
        status += SDL_RenderDrawPoint(renderer, x - offsetx, y + offsety);
        status += SDL_RenderDrawPoint(renderer, x - offsety, y + offsetx);
        status += SDL_RenderDrawPoint(renderer, x + offsetx, y - offsety);
        status += SDL_RenderDrawPoint(renderer, x + offsety, y - offsetx);
        status += SDL_RenderDrawPoint(renderer, x - offsetx, y - offsety);
        status += SDL_RenderDrawPoint(renderer, x - offsety, y - offsetx);

        if (status < 0) {
            status = -1;
            break;
        }

        if (d >= 2*offsetx) {
            d -= 2*offsetx + 1;
            offsetx +=1;
        } else if (d < 2 * (radius - offsety)) {
            d += 2 * offsety - 1;
            offsety -= 1;
        } else {
            d += 2 * (offsety - offsetx - 1);
            offsety -= 1;
            offsetx += 1;
        }
    }

    //return status;
}


void SDL_RenderFillCircle(SDL_Renderer * renderer, int x, int y, int radius, int r, int g, int b, int a) {
    SDL_SetRenderDrawColor(renderer, r, g, b, a);
    int offsetx, offsety, d;
    int status;

    offsetx = 0;
    offsety = radius;
    d = radius -1;
    status = 0;

    while (offsety >= offsetx) {

        status += SDL_RenderDrawLine(renderer, x - offsety, y + offsetx,
                                     x + offsety, y + offsetx);
        status += SDL_RenderDrawLine(renderer, x - offsetx, y + offsety,
                                     x + offsetx, y + offsety);
        status += SDL_RenderDrawLine(renderer, x - offsetx, y - offsety,
                                     x + offsetx, y - offsety);
        status += SDL_RenderDrawLine(renderer, x - offsety, y - offsetx,
                                     x + offsety, y - offsetx);

        if (status < 0) {
            status = -1;
            break;
        }

        if (d >= 2*offsetx) {
            d -= 2*offsetx + 1;
            offsetx +=1;
        } else if (d < 2 * (radius - offsety)) {
            d += 2 * offsety - 1;
            offsety -= 1;
        } else {
            d += 2 * (offsety - offsetx - 1);
            offsety -= 1;
            offsetx += 1;
        }
    }
    //return status;
}

void drawCircleToRenderer(SDL_Renderer * renderer, int32_t centreX, int32_t centreY, int32_t radius, int r, int g, int b, int a) {
    SDL_SetRenderDrawColor(renderer, r, g, b, a);
    const int32_t diameter = (radius * 2);

    int32_t x = (radius - 1);
    int32_t y = 0;
    int32_t tx = 1;
    int32_t ty = 1;
    int32_t error = (tx - diameter);

    while(x >= y){
        SDL_RenderDrawLine(renderer, centreX + x, centreY - y, centreX, centreY);
        SDL_RenderDrawLine(renderer, centreX + x, centreY + y, centreX, centreY);
        SDL_RenderDrawLine(renderer, centreX - x, centreY - y, centreX, centreY);
        SDL_RenderDrawLine(renderer, centreX - x, centreY + y, centreX, centreY);
        SDL_RenderDrawLine(renderer, centreX + y, centreY - x, centreX, centreY);
        SDL_RenderDrawLine(renderer, centreX + y, centreY + x, centreX, centreY);
        SDL_RenderDrawLine(renderer, centreX - y, centreY - x, centreX, centreY);
        SDL_RenderDrawLine(renderer, centreX - y, centreY + x, centreX, centreY);

        if(error <= 0){
            ++y;
            error += ty;
            ty += 2;
        }

        if(error > 0) {
            --x;
            tx += 2;
            error += (tx - diameter);
        }
    }
}

void drawQuarterCircleToRenderer(SDL_Renderer * renderer, int32_t centreX, int32_t centreY, int32_t radius, bool TL, bool TR, bool BL, bool BR, int r, int g, int b, int a) {
    SDL_SetRenderDrawColor(renderer, r, g, b, a);
    const int32_t diameter = (radius * 2);

    int32_t x = (radius - 1);
    int32_t y = 0;
    int32_t tx = 1;
    int32_t ty = 1;
    int32_t error = (tx - diameter);

    while(x >= y){

        if(TL) {
            SDL_RenderDrawLine(renderer, centreX - x, centreY - y, centreX, centreY);
            SDL_RenderDrawLine(renderer, centreX - y, centreY - x, centreX, centreY);
        }

        if(TR) {
            SDL_RenderDrawLine(renderer, centreX + x, centreY - y, centreX, centreY);
            SDL_RenderDrawLine(renderer, centreX + y, centreY - x, centreX, centreY);
        }

        if(BL) {
            SDL_RenderDrawLine(renderer, centreX - x, centreY + y, centreX, centreY);
            SDL_RenderDrawLine(renderer, centreX - y, centreY + x, centreX, centreY);
        }

        if(BR) {
            SDL_RenderDrawLine(renderer, centreX + x, centreY + y, centreX, centreY);
            SDL_RenderDrawLine(renderer, centreX + y, centreY + x, centreX, centreY);
        }

        if(error <= 0){
            ++y;
            error += ty;
            ty += 2;
            ++y;
            error += ty;
            ty += 2;
        }

        if(error > 0) {
            --x;
            tx += 2;
            error += (tx - diameter);
            --x;
            tx += 2;
            error += (tx - diameter);
        }
    }
}

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

void renderStraights(SDL_Renderer* renderer, level* lvl, int cx, int cy, int range, int rsize) {
    for(int x = -range; x <= range; x++) {
        for(int y = -range; y <= range; y++) {
            if(cx+x >= 0 && cy+y >= 0 && cx+x < lvl->lines && cy+y < lvl->cols) {
                switch (lvl->map[cx+x][cy+y]){
                case NONE:
                    placeRectToRenderer(renderer, (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    break;

                case START:
                case END:
                    placeRectToRenderer(renderer, (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y+(int)(START_EDGE*rsize),
                        (HEIGHT-rsize)/2+rsize*x,
                        rsize-(int)(2*START_EDGE*rsize),
                        rsize,
                    64, 64, 64, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y,
                        (HEIGHT-rsize)/2+rsize*x+(int)(START_EDGE*rsize),
                        rsize,
                        rsize-(int)(2*START_EDGE*rsize),
                    64, 64, 64, 255);
                    break;

                case STR_H:
                    placeRectToRenderer(renderer, (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y,
                        (HEIGHT-rsize)/2+rsize*x+(int)(DIST_EDGE*rsize),
                        rsize,
                        rsize-(int)(2*DIST_EDGE*rsize),
                    64, 64, 64, 255);
                    break;

                case STR_V:
                    placeRectToRenderer(renderer, (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y+(int)(DIST_EDGE*rsize),
                        (HEIGHT-rsize)/2+rsize*x,
                        rsize-(int)(2*DIST_EDGE*rsize),
                        rsize,
                    64, 64, 64, 255);
                    break;

                case STR_VB:
                    placeRectToRenderer(renderer, (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y+(int)(DIST_EDGE*rsize),
                        (HEIGHT-rsize)/2+rsize*x,
                        rsize-(int)(2*DIST_EDGE*rsize),
                        rsize,
                    64, 64, 64, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y+(int)((1.0-STR2_W)/2.0*rsize),
                        (HEIGHT-rsize)/2+rsize*x,
                        (int)(STR2_W*rsize),
                        rsize,
                    64, 0, 0, 255);
                    break;

                case STR_HB:
                    placeRectToRenderer(renderer, (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y,
                        (HEIGHT-rsize)/2+rsize*x+(int)(DIST_EDGE*rsize),
                        rsize,
                        rsize-(int)(2*DIST_EDGE*rsize),
                    64, 64, 64, 255);
                    placeRectToRenderer(renderer,
                        (WIDTH-rsize)/2+rsize*y,
                        (HEIGHT-rsize)/2+rsize*x+(int)((1.0-STR2_W)/2.0*rsize),
                        rsize,
                        (int)(STR2_W*rsize),
                    64, 0, 0, 255);
                    break;
                
                default:
                    break;
                }
            }
        }
    }
}

void renderCircles(SDL_Renderer* renderer, level* lvl, int cx, int cy, int range, int rsize) {
    for(int x = -range; x <= range; x++) {
        for(int y = -range; y <= range; y++) {
            if(cx+x >= 0 && cy+y >= 0 && cx+x < lvl->lines && cy+y < lvl->cols) {
                switch (lvl->map[cx+x][cy+y]){
                case NONE:
                    break;

                case START:
                case END:
                    break;

                case STR_H:
                    break;

                case STR_V:
                    break;

                case TURN_NE:
                    placeRectToRenderer(renderer,  (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    drawQuarterCircleToRenderer(renderer, (WIDTH-rsize)/2+rsize*y+rsize, (HEIGHT-rsize)/2+rsize*x      , (int)(rsize*(1-DIST_EDGE)), 
                        false, false, true, false,
                        64, 64, 64, 192);
                    SDL_RenderFillCircle(renderer,        (WIDTH-rsize)/2+rsize*y+rsize, (HEIGHT-rsize)/2+rsize*x      , (int)(rsize*DIST_EDGE), 0, 0, 0, 255);
                    break;

                case TURN_SE:
                    placeRectToRenderer(renderer,  (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    drawQuarterCircleToRenderer(renderer, (WIDTH-rsize)/2+rsize*y+rsize, (HEIGHT-rsize)/2+rsize*x+rsize, (int)(rsize*(1-DIST_EDGE)),
                        true, false, false, false,
                        64, 64, 64, 192);
                    SDL_RenderFillCircle(renderer,        (WIDTH-rsize)/2+rsize*y+rsize, (HEIGHT-rsize)/2+rsize*x+rsize, (int)(rsize*DIST_EDGE), 0, 0, 0, 255);
                    break;

                case TURN_SW:
                    placeRectToRenderer(renderer,  (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    drawQuarterCircleToRenderer(renderer, (WIDTH-rsize)/2+rsize*y      , (HEIGHT-rsize)/2+rsize*x+rsize, (int)(rsize*(1-DIST_EDGE)),
                        false, true, false, false,
                        64, 64, 64, 192);
                    SDL_RenderFillCircle(renderer,        (WIDTH-rsize)/2+rsize*y      , (HEIGHT-rsize)/2+rsize*x+rsize, (int)(rsize*DIST_EDGE), 0, 0, 0, 255);
                    break;

                case TURN_NW:
                    placeRectToRenderer(renderer,  (WIDTH-rsize)/2+rsize*y, (HEIGHT-rsize)/2+rsize*x, rsize, rsize, 0, 0, 0, 255);
                    drawQuarterCircleToRenderer(renderer, (WIDTH-rsize)/2+rsize*y      , (HEIGHT-rsize)/2+rsize*x      , (int)(rsize*(1-DIST_EDGE)),
                        false, false, false, true,
                        64, 64, 64, 192);
                    SDL_RenderFillCircle(renderer,        (WIDTH-rsize)/2+rsize*y      , (HEIGHT-rsize)/2+rsize*x      , (int)(rsize*DIST_EDGE), 0, 0, 0, 255);
                    break;
                
                default:
                    break;
                }
            }
        }
    }
}

pt project(int p, int cx, int cy, int range, int rsize) {
    if(players[p].c->chx >= cy-range && players[p].c->chx <= cy+range && players[p].c->chy >= cx-range && players[p].c->chy <= cx+range) {
        int cox = (WIDTH-rsize)/2+rsize*(players[p].c->chx-cy) + (int)((players[p].c->pos.fx*1.0)/ROOM_SIZE*rsize);
        int coy = (HEIGHT-rsize)/2+rsize*(players[p].c->chy-cx) + (int)((players[p].c->pos.fy*1.0)/ROOM_SIZE*rsize);
        return (pt){.ix = cox, .iy = coy};
    } else {
        return (pt){.ix = -WIDTH, .iy = -HEIGHT};
    }
}

const int BAR_W = 25;
const int BAR_T = 3;
void renderProgress(SDL_Renderer* renderer, level* lvl, path* pth) {
    for(int p = 0; p < nPlayers; p++) {
        double remD = getTotalRemainingDistance(lvl, pth, p);
        remainingD[p] = remD;
        maxRemD = mfax(maxRemD, remD);
        minRemD = mfin(minRemD, remD);
        //printf("(%lf out of %lf rem)\n", remD, maxD);
        placeRectToRenderer(renderer, 10, BAR_W/8+p*(BAR_W+BAR_W/8), WIDTH-20, BAR_W, players[p].rgb.red/2, players[p].rgb.green/2, players[p].rgb.blue/2, 255);
        placeRectToRenderer(renderer, 10+BAR_T, BAR_W/8+p*(BAR_W+BAR_W/8)+BAR_T, WIDTH-20-2*BAR_T, BAR_W-2*BAR_T, 0, 0, 0, 255);
        placeRectToRenderer(renderer,
            10+BAR_T, BAR_W/8+p*(BAR_W+BAR_W/8)+BAR_T,
            (int)(((pth->maxD-remD)/(pth->maxD))*(WIDTH-20-2*BAR_T)), BAR_W-2*BAR_T,
            players[p].rgb.red, players[p].rgb.green, players[p].rgb.blue, 255
        );
        for(int k = 1; k <= pth->maxDepth; k++) {
            if(k <= pth->nxts[players[p].c->chy][players[p].c->chx].deep) {
                placeRectToRenderer(renderer, 10+BAR_T+(int)(pth->singleDist*k*(WIDTH-20-2*BAR_T))-2, BAR_W/8+p*(BAR_W+BAR_W/8)+BAR_T, 4, BAR_W-2*BAR_T, 0, 0, 0, 255);
            } else {
                k = pth->maxDepth+1;
            }
        }
    }
}

void renderPlayers(SDL_Renderer* renderer, int cx, int cy, int range, int rsize) {
    for(int p = 0; p < nPlayers; p++) {
        if(players[p].c->chx >= cy-range && players[p].c->chx <= cy+range && players[p].c->chy >= cx-range && players[p].c->chy <= cx+range) {
            //printf("[%d]", p);
            int cox = (WIDTH-rsize)/2+rsize*(players[p].c->chx-cy) + (int)((players[p].c->pos.fx*1.0)/ROOM_SIZE*rsize);
            int coy = (HEIGHT-rsize)/2+rsize*(players[p].c->chy-cx) + (int)((players[p].c->pos.fy*1.0)/ROOM_SIZE*rsize);
            int divd = (offTheMap[p])?(2):(1);
            SDL_RenderFillCircle(renderer, cox, coy, PLAYER_R*rsize/ROOM_SIZE, players[p].rgb.red/divd, players[p].rgb.green/divd, players[p].rgb.blue/divd, 255);
        } else {
            //
        }
    }
    //printf("\n");
}

void renderMap(SDL_Renderer* renderer, level* lvl, path* pth, int cx, int cy, int range, int rsize, bool showM) {
    if(showM) {
        renderCircles(renderer, lvl, cx, cy, range, rsize);
        renderStraights(renderer, lvl, cx, cy, range, rsize);
        for(int x = -range; x <= range; x++) {
            for(int y = -range; y <= range; y++) {
                if((cx+x == START_CHX && cy+y == START_CHY) || (cx+x == FINISH_CHX && cy+y == FINISH_CHY)) {
                    SDL_RenderDrawCircle(renderer, (WIDTH)/2+rsize*(y), (HEIGHT)/2+rsize*(x), (int)(rsize/3.5), 192, 192, 192, 255);
                }
            }
        }
        renderPlayers(renderer, cx, cy, range, rsize);
    }
    renderProgress(renderer, lvl, pth);
}

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

void import_digits(SDL_Renderer* renderer) {
    imgs* res = malloc(sizeof(imgs));
    res->arr = malloc(sizeof(SDL_Texture*)*11);
    SDL_Texture* texture;
    SDL_Surface* img;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-0.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[0] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-1.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[1] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-2.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[2] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-3.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[3] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-4.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[4] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-5.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[5] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-6.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[6] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-7.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[7] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-8.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[8] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/digit-9.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[9] = texture;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/digits/sign-minus.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[10] = texture;

    res->len = 11;
    digits = res;
}
void import_letters(SDL_Renderer* renderer) {
    imgs* res = malloc(sizeof(imgs));
    res->arr = malloc(sizeof(SDL_Texture*)*26);
    SDL_Texture* texture;
    SDL_Surface* img;

    int cc = 0;

    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-a.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-b.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-c.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-d.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-e.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-f.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-g.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-h.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-i.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-j.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-k.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-l.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-m.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-n.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-o.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-p.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-q.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-r.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-s.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-t.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-u.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-v.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-w.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-x.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-y.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;
    cc += 1;
    // -------------------------------------------------------- //
    img = SDL_LoadBMP("./res/letters/letter-z.bmp");
    texture = SDL_CreateTextureFromSurface(renderer, img);
    SDL_FreeSurface(img);
    res->arr[cc] = texture;

    res->len = 26;
    letters = res;
}

void free_digits(imgs* dgts) {
    for(int i = 0; i < dgts->len; i++) {
        SDL_DestroyTexture(dgts->arr[i]);
    }
    free(dgts->arr);
    free(dgts);
}