#include "tetris-struct.h"

Grid gridInit(){
    Grid g;
    g.width = GRID_WIDTH;
    g.height = GRID_HEIGHT;
    
    g.grid = calloc(g.width, sizeof(Square*));

    for(int i = 0; i < g.width; i++){
        g.grid[i] = calloc(g.height, sizeof(Square));
    }
    
    return g;
}

Piece pieceInit(){
    Piece p;
    p.size = PIECE_SIZE;
    p.squares = calloc(p.size, sizeof(Square));

    return p;
}

Square squareInit(){
    Square sq = malloc(sizeof(square_s));
    
    sq->color = RAYWHITE;
    sq->posX = 0;
    sq->posY = 0;
    

    return sq;
}

Square copySquare(Square sq){
    Square newSq = squareInit();

    newSq->color = sq->color;
    newSq->posX = sq->posX;
    newSq->posY = sq->posY;

    return newSq;
}

void setSquareColor(Square sq, int colorIndex){
    Color colorList[7] = {SKYBLUE, YELLOW, PURPLE, ORANGE, BLUE, RED, GREEN};

    sq->color = colorList[colorIndex];
}

void setPieceColor(Piece p, int colorIndex){
    for(int i = 0; i < p.size; i++){
        setSquareColor(p.squares[i], colorIndex);
    }
}

Square getSquare(char* line, int index){
    Square sq = squareInit();
    int k = 0;      // Compteur de '('
    int shift = 0;  // Décalage d'indice

    while(k != index){
        if(line[shift] == '('){
            k++;
        }
        shift+=5;
    }
    shift++;        // On obtient l'indice du premier chiffre
    
    int x = line[shift] - '0';
    int y = line[shift+2] - '0';
    
    sq->posX = x;
    sq->posY = y;

    return sq;
}

Piece getPieceFromLine(char* line){
    Piece p = pieceInit();
    Square sq;
    
    for(int i = 0; i < p.size; i++){
        sq = getSquare(line, i);
        p.squares[i] = sq;
    }
    return p;
}

Piece* getPiecesData(char* fileName){

    FILE* dataFile = NULL;
    unsigned int numPieces;

    
    dataFile = fopen(fileName, "r");
    if(dataFile == NULL){
        exit(EXIT_FAILURE);
    }

    // Initialize number of pieces
    fscanf(dataFile, "%u\n", &numPieces);

    // Initialize piecesList
    char* line = calloc(30, sizeof(char));
    Piece* pieceList = calloc(numPieces, sizeof(Piece));
    for(int i = 0; i < numPieces; i++){
        pieceList[i] = pieceInit();
    }

    for(int i = 0; i < numPieces; i++){
        fgets(line, 30, dataFile);
        pieceList[i] = getPieceFromLine(line);
        setPieceColor(pieceList[i], i);
    }
    free(line);

    return pieceList;
}

int randInt(int lower, int upper){
    srand(time(0));
    int num = (rand() % (upper-lower+1)) + lower;

    return num;
}

Piece spawnPiece(Piece* pieceList){
    int numPiece = randInt(0, 6);

    Piece pieceFromList = pieceList[numPiece];
    Piece newPiece = pieceInit();
    
    // Copy the piece from the piece list
    for(int i = 0; i < newPiece.size; i++){
        newPiece.squares[i] = copySquare(pieceFromList.squares[i]);
    }
    
    return newPiece;
}

bool canMoveVert(Grid g, Piece p, int moveNum){
    for(int i = 0; i < p.size; i++){
        int x = p.squares[i]->posX;
        int y = p.squares[i]->posY + moveNum;

        if(y < 0 || y >= g.height || g.grid[x][y] != NULL){
            return false;
        }
    }
    return true;
}

bool canMoveHztl(Grid g, Piece p, int moveNum){
    for(int i = 0; i < p.size; i++){
        int x = p.squares[i]->posX + moveNum;
        int y = p.squares[i]->posY;

        if(x < 0 || x >= g.width || g.grid[x][y] != NULL){
            return false;
        }
    }
    return true;
}

void moveSquareVert(Square sq, int shift){
    sq->posY += shift;
}

void movePieceVert(Piece piece, int shift){
    for(int i = 0; i < piece.size; i++){
        moveSquareVert(piece.squares[i], shift);
    }
}

void moveSquareHztl(Square sq, int shift){
    sq->posX += shift;
}

void movePieceHztl(Piece p, int shift){
    for(int i = 0; i < p.size; i++){
        moveSquareHztl(p.squares[i], shift);
    }
}

bool rotateCollideLeft(Piece p, bool rotation){
    int refX = p.squares[1]->posX;
    int refY = p.squares[1]->posY;

    for(int i = 0; i < p.size; i++){
        int posY = p.squares[i]->posY;
        int newPosX;

        if(rotation){
            newPosX = refX - posY + refY;
        }
        else{
            newPosX = refX + posY - refY;
        }

        if(newPosX < 0){
            return true;
        }
    }
    return false;
}

bool rotateCollideRight(Piece p, bool rotation){
    int refX = p.squares[1]->posX;
    int refY = p.squares[1]->posY;

    for(int i = 0; i < p.size; i++){
        int posX = p.squares[i]->posX;
        int newPosY;

        if(rotation){
            newPosY = refY - posX + refX;
        }
        else{
            newPosY = refY + posX - refX;
        }

        if(newPosY >= GRID_WIDTH){
            return true;
        }
    }
    return false;
}

bool rotateCollideTop(Piece p, bool rotation){
    int refX = p.squares[1]->posX;
    int refY = p.squares[1]->posY;
    int newPosX, newPosY;
    int posX, posY;

    for(int i = 0; i < p.size; i++){
        posX = p.squares[i]->posX;
        posY = p.squares[i]->posY;

        if (rotation){
            newPosX = refX - posY + refY;
            newPosY = refY + posX - refX;
        }
        else{
            newPosX = refX + posY - refY;
            newPosY = refY - posX + refX;
        }

        if (newPosY < 0){
            return true;
        }
    }
    return false;
}

bool rotateCollideBottom(Piece p, bool rotation){
    int refX = p.squares[1]->posX;
    int refY = p.squares[1]->posY;
    int newPosX, newPosY;
    int posX, posY;

    for(int i = 0; i < p.size; i++){
        posX = p.squares[i]->posX;
        posY = p.squares[i]->posY;

        if (rotation){
            newPosX = refX - posY + refY;
            newPosY = refY + posX - refX;
        }
        else{
            newPosX = refX + posY - refY;
            newPosY = refY - posX + refX;
        }

        if (newPosY >= GRID_HEIGHT){
            return true;
        }
    }
    return false;
}

bool rotateCollideGrid(Grid g, Piece p, bool rotation){
    int refX = p.squares[1]->posX;
    int refY = p.squares[1]->posY;

    for(int i = 0; i < p.size; i++){
        int posX = p.squares[i]->posX;
        int posY = p.squares[i]->posY;
        int newPosX, newPosY;

        if(rotation){
            newPosX = refX - posY + refY;
            newPosY = refY + posX - refX;
        }
        else{
            newPosX = refX + posY - refY;
            newPosY = refY - posX + refX; 
        }

        if(g.grid[newPosX][newPosY] != NULL){
            return true;
        }
    }
    return false;
}

void rotateSquare(Square sq, Square ref, bool rotation){
    int refX = ref->posX;
    int refY = ref->posY;

    // Keep memory
    int posX = sq->posX;
    int posY = sq->posY;

    if(rotation){
        sq->posX = refX - posY + refY;
        sq->posY = refY + posX - refX;
    }
    else{
        sq->posX = refX + posY - refY;
        sq->posY = refY - posX + refX; 
    }
}

void rotatePiece(Grid g, Piece p, bool rotation){
    for(int i = 0; i < p.size; i++){
        if(i != 1){
            rotateSquare(p.squares[i], p.squares[1], rotation);
        }
    }
}

void addSquareToGrid(Square sq, Grid g){
    Square newSq = copySquare(sq);
    
    g.grid[newSq->posX][newSq->posY] = newSq;
}

void addPieceToGrid(Piece piece, Grid grid){
    for(int i = 0; i < piece.size; i++){
        addSquareToGrid(piece.squares[i], grid);
    }
}

int removeLinesIfCompleted(Grid grid){
    int nbLinesRemoved = 0;
    bool isLineCompleted;

    int y = 0;
    while(y < grid.height){
        isLineCompleted = true;
        for(int x = 0; x < grid.width; x++){
            if(grid.grid[x][y] == NULL){
                isLineCompleted = false;
            }
        }

        if(isLineCompleted){
            nbLinesRemoved++;
            int lineToRemove = y;

            //free
            for(int x = 0; x < grid.width; x++){
                free(grid.grid[x][lineToRemove]);
            }
            //shift
            for(int x = 0; x < grid.width; x++){
                for(int i = lineToRemove; i > 0; i--){
                    grid.grid[x][i] = grid.grid[x][i-1];
                }   
            }
            //initTop
            for(int x = 0; x < grid.width; x++){
                grid.grid[x][0] = malloc(sizeof(square_s));
                grid.grid[x][0] = NULL;
            }
        }
        else{
            y++;
        }
    }
    return nbLinesRemoved;
}

int heightColumn(Square* column, int size){
    int height = size;

    while(height > 0 && column[size-height] == NULL){
        height--;
    }
    return height;
}

int height(Grid g){
    int height = 0;
    int height_p;

    for(int i = 0; i < g.width; i++){
        height_p = heightColumn(g.grid[i], g.height);

        if(height_p > height){
            height = height_p;
        }
    }
    return height;
}

bool gameOver(Grid grid){
    return height(grid) == grid.height;
}