#include "Board.h"

#include "../Pieces/Piece.h"

#include <vector>
#include <set>
#include <iostream>


Board::Board(int width, int height) :
    width(width),
    height(height) {

    this->emptyRow = std::vector<Block>(width);
    for (int i = 0; i < width; i ++) {
        this->emptyRow.push_back(NOTHING);
    }
    
    this->clearBoard();
}

void Board::changeBlock(const Position& position, Block block) {
    if (position.x < 0 || static_cast<unsigned>(position.x) >= this->width || position.y < 0) return;

    // resize the grid if needed
    if (static_cast<unsigned>(position.y) >= this->grid.size()) {
        for (int j = this->grid.size(); j <= position.y; j++) {
            this->grid.push_back(this->emptyRow);
        }
    }
    
    this->grid.at(position.y).at(position.x) = block;
}

void Board::insertRow(int height, int holePosition, Block blockType) {
    std::vector<Block> insertedRow;
    for (unsigned i = 0; i < this->width; i++) {
        if (i == holePosition) {
            insertedRow.push_back(NOTHING);
        }
        else {
            insertedRow.push_back(blockType);
        }
    }

    this->grid.insert(this->grid.begin() + height, insertedRow);
}

int Board::clearRows() {
    // check from top to bottom, so that erasing lines don't screw up the looping
    int clearedLines = 0;
    for (int j = this->grid.size() - 1; j >= 0; j--) {
        bool lineIsFull = true;
        unsigned i = 0;
        while (lineIsFull && (i < width)) {
            if (this->grid.at(j).at(i) == NOTHING) {
                lineIsFull = false;
            }
            i++;
        }

        if (lineIsFull) {
            this->grid.erase(this->grid.begin() + j);
            if(this->grid.size() < this->height) {
                this->grid.push_back(this->emptyRow);
            }
            clearedLines++;
        }
    }

    return clearedLines;
}

void Board::clearBoard() {
    this->grid.clear();
    for (int j = 0; j < this->height; j++) {
        this->grid.push_back(this->emptyRow);
    }
}

Block Board::getBlock(const Position& position) const {
    if (position.x < 0 || position.x >= this->width || position.y < 0) return OUT_OF_BOUNDS;

    if (position.y >= this->grid.size()) return NOTHING;

    return this->grid.at(position.y).at(position.x);
}

const std::vector<std::vector<Block>>& Board::getBlocks() const {
    return this->grid;
}

int Board::getWidth() const {
    return this->width;
}

int Board::getGridHeight() const {
    return this->grid.size();
}

int Board::getBaseHeight() const {
    return this->height;
}

std::ostream& operator<<(std::ostream& os, const Board& board) {
    for (int y = board.grid.size() - 1; y >= 0; y--) {
        for (int x = 0; x < board.width; x++) {
            Block block = board.grid.at(y).at(x);
            os << getConsoleColorCode(block);
            if (block != NOTHING) {
                os << "*";
            }
            else {
                os << "-";
            }
        }
        os << std::endl;
    }

    os << getResetConsoleColorCode();
    
    return os;
}
