#include "PiecesList.h"

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

#include <vector>
#include <utility>


PiecesList::PiecesList() {
    this->highestLoadedSize = 0;
    this->selectedPieces.clear();

    this->distributionMode = DEFAULT;
    this->proportionsPerSize.clear();
    this->customProportionsPerSize.clear();

    // we need to have something at index 0 even if there is no pieces of size 0
    this->loadedPieces.clear();
    this->convexPieces.clear();
    this->holelessPieces.clear();
    this->otherPieces.clear();
    this->pushBackEmptyVectors();

    // we always prepare vectors of the next size to be generated
    this->pushBackEmptyVectors();
}

bool PiecesList::loadPieces(unsigned max_size) {
    if (max_size < 1) return false;
    if (max_size <= this->highestLoadedSize) return true;

    PiecesFiles piecesFiles;
    for (unsigned i = this->highestLoadedSize + 1; i <= max_size; i++) {
        if (!piecesFiles.loadPieces(i, this->loadedPieces.at(i), this->convexPieces.at(i), this->holelessPieces.at(i), this->otherPieces.at(i))) {
            return false;
        }
        else {
            this->highestLoadedSize++;
            this->pushBackEmptyVectors();
        }
    }

    return true;
}

bool PiecesList::selectPiece(unsigned size, unsigned number) {
    if (size < 1 || size > this->highestLoadedSize || number >= this->loadedPieces.at(size).size()) return false;

    this->selectedPieces.push_back(std::pair<int, int>(size, number));
    return true;
}

bool PiecesList::selectAllPieces(unsigned size) {
    if (size < 1 || size > this->highestLoadedSize) return false;

    for (int i = 0; i < this->loadedPieces.at(size).size(); i++) {
        this->selectedPieces.push_back(std::pair<int, int>(size, i));
    }
    return true;
}

bool PiecesList::selectConvexPieces(int size) {
    if (size < 1 || size > this->highestLoadedSize) return false;

    for (int index : this->convexPieces.at(size)) {
        this->selectedPieces.push_back(std::pair<int, int>(size, index));
    }
    return true;
}

bool PiecesList::selectHolelessPieces(int size) {
    if (size < 1 || size > this->highestLoadedSize) return false;

    for (int index : this->holelessPieces.at(size)) {
        this->selectedPieces.push_back(std::pair<int, int>(size, index));
    }
    return true;
}

bool PiecesList::selectOtherPieces(int size) {
    if (size < 1 || size > this->highestLoadedSize) return false;

    for (int index : this->otherPieces.at(size)) {
        this->selectedPieces.push_back(std::pair<int, int>(size, index));
    }
    return true;
}

void PiecesList::unselectAll() {
    this->selectedPieces.clear();
}

bool PiecesList::setDistributionMode(DistributionMode distributionMode) {
    if (distributionMode == DEFAULT || distributionMode == UNIFORM || distributionMode == CUSTOM) {
        this->distributionMode = distributionMode;
        return true;
    }
    return false;
}

int PiecesList::getHighestLoadedSize() const {
    return this->highestLoadedSize;
}

int PiecesList::getNumberOfPieces(int size) const {
    if (size < 1 || size > this->highestLoadedSize) return 0;

    return this->loadedPieces.at(size).size();
}

std::vector<std::pair<int, int>> PiecesList::getSelectedPieces() const {
    return this->selectedPieces;
}

DistributionMode PiecesList::getDistributionMode() const {
    return this->distributionMode;
}

bool PiecesList::changeCustomDistribution(int size, double distribution) {
    if (size < 1 || size > this->highestLoadedSize || distribution > 1) {
        return false;
    }

    this->customProportionsPerSize.at(size) = distribution;
    return true;
}

std::vector<double> PiecesList::getProportionsPerSize() const {
    if (this->distributionMode == CUSTOM) {
        return this->customProportionsPerSize;
    }
    else {
        return this->proportionsPerSize;
    }
}

Piece PiecesList::getPiece(const std::pair<int, int>& pieceIndex) const {
    return this->loadedPieces.at(pieceIndex.first).at(pieceIndex.second);
}

const Piece& PiecesList::lookAtPiece(const std::pair<int, int>& pieceIndex) const {
    return this->loadedPieces.at(pieceIndex.first).at(pieceIndex.second);
}

void PiecesList::pushBackEmptyVectors() {
    this->loadedPieces.push_back(std::vector<Piece>());
    this->convexPieces.push_back(std::vector<int>());
    this->holelessPieces.push_back(std::vector<int>());
    this->otherPieces.push_back(std::vector<int>());

    this->proportionsPerSize.push_back(1);
    this->customProportionsPerSize.push_back(1);
}
