#include "Battleship.h"

#include <utility>

#include <iostream>

namespace Battleship{

Battleship::Battleship(const Board& board, Ship*& shipList) : mainBoard(board){
    knownShips = mainBoard.computeCompletedShips();
    unknownShips = shipList;
    this->shipList = NULL;

    int n[8] = {0, 0, 0, 0, 0, 0, 0, 0};

    for (shipList = knownShips; shipList != NULL; shipList = shipList->getNextShip()){
        n[shipList->getShip()]++;
    }

    Ship *s[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    Ship *b[8] = {0, 0, 0, 0, 0, 0, 0, 0};

    for (shipList = unknownShips; shipList != NULL;){
        Ship* current = shipList;
        shipList = shipList->getNextShip();
        uint8_t currentShip = current->getShip();

        if (n[currentShip]){
            n[currentShip]--;
            delete current;
            continue;
        }
        current->setNextShip(s[currentShip]);
        s[currentShip] = current;
        if (!b[currentShip]) b[currentShip] = current;
    }

    for (short i = 1; i <= 7; i++){
        if (b[i] != NULL){
            b[i]->setNextShip(this->shipList);
            this->shipList = s[i];
        }
    }

    unknownShips = s[0];
    for (short i = 1; i <= 7; i++){
        for (short j = 0; j < n[i]; j++){
            if (unknownShips != NULL){
                s[0] = unknownShips;
                unknownShips = unknownShips->getNextShip();
                delete s[0];
            }
        }
    }
}

uint64_t Battleship::findSolutions(Board* board, Ship* ships, bool all, uint8_t last_size, int j, int i){
    if (ships == NULL){
        //solutionStates.push_back(board);
        //solutionStates.back().addShips(shipList);
        //solutionStates.back().printSolution(std::cout);
        int largest = board->getLargestLeft();
        return checkUnknowns(board, unknownShips, all, ((largest > 7 || largest == 0) ? 7 : largest));
        //return true;
    }
    uint64_t count = 0;
    Board* newBoard;
   // bool success = false;
    int x = (ships->getShip() == last_size) ? j : 0;
    int y = (ships->getShip() == last_size) ? i : 0;
    bool canVertical;
    for (int j = x; j < board->getCols(); j++){
        if (!board->getColSpaceLeft(j)) continue;
        canVertical = (board->getColSpaceLeft(j) >= ships->getShip());
        for (int i = ((j == x) ? y : 0); i < board->getRows(); i++){
            if (!board->getRowSpaceLeft(i)) continue;
            //std::cout << "Trying horizontal " << ships->getShipString() << std::endl;
            if (board->getRowSpaceLeft(i) >= ships->getShip() && board->tryPlace(j, i, ships->getShip(), true, newBoard)){
                //std::cout << "Success!" << std::endl;
                //newBoard->printBoard(std::cout);
                ships->setX(j);
                ships->setY(i);
                ships->setHorizontal();
                searchPositions[ships->getShip()-1][0] = j;
                searchPositions[ships->getShip()-1][1] = i;
                count += findSolutions(newBoard, ships->getNextShip(), all, ships->getShip(), j, i);
                delete newBoard;
                //if (ships->getShip() == 1) return 0;
                if (count && !all) return count;
            }
            if (ships->getShip() != 1 && canVertical){
                //std::cout << "Trying vertical " << ships->getShipString() << std::endl;
                if (board->tryPlace(j, i, ships->getShip(), false, newBoard)){
                    //std::cout << "Success!" << std::endl;
                    //newBoard->printBoard(std::cout);
                    ships->setX(j);
                    ships->setY(i);
                    ships->setVertical();
                    searchPositions[ships->getShip()-1][0] = j;
                    searchPositions[ships->getShip()-1][1] = i;
                    count += findSolutions(newBoard, ships->getNextShip(), all, ships->getShip(), j, i);
                    delete newBoard;
                    //if (success && !all) return true;
                    if (count && !all) return count;
                }
            }
        }
    }
    return count;
}

uint64_t Battleship::checkUnknowns(Board* board, Ship* ships, bool all, uint8_t ship, uint8_t last_size, int x, int y){
    if (ships == NULL){
        if (!board->isFilled()) return 0;
        solutionStates.push_back(board);
        solutionStates.back().addShips(shipList);
        solutionStates.back().addShips(knownShips);
        solutionStates.back().addShips(unknownShips);
        return 1;
    }
    if (ship == 0) {
        //std::cout << "failed";
        return 0;
    }

    uint64_t count = 0;
    Board* newBoard;
   // bool success = false;
    x = (ship == last_size) ? x : searchPositions[ship-1][0];
    y = (ship == last_size) ? y : searchPositions[ship-1][1];
    bool canVertical;
    for (int j = x; j < board->getCols(); j++){
        if (!board->getColSpaceLeft(j)) continue;
        canVertical = (board->getColSpaceLeft(j) >= ship);
        for (int i = ((j == x) ? y : 0); i < board->getRows(); i++){
            if (!board->getRowSpaceLeft(i)) continue;
            //std::cout << "Trying horizontal " << ships->getShipString() << std::endl;
            if (board->getRowSpaceLeft(i) >= ship && board->tryPlace(j, i, ship, true, newBoard)){
                //std::cout << "Success!" << std::endl;
                //newBoard->printBoard(std::cout);
                ships->setX(j);
                ships->setY(i);
                ships->setHorizontal();
                ships->setShip(ship);
                count += checkUnknowns(newBoard, ships->getNextShip(), all, ship, ship, j, i);
                delete newBoard;
                //if (ships->getShip() == 1) return 0;
                if (count && !all) return count;
            }
            if (ship != 1 && canVertical){
                //std::cout << "Trying vertical " << ships->getShipString() << std::endl;
                if (board->tryPlace(j, i, ship, false, newBoard)){
                    //std::cout << "Success!" << std::endl;
                    //newBoard->printBoard(std::cout);
                    ships->setX(j);
                    ships->setY(i);
                    ships->setVertical();
                    ships->setShip(ship);
                    count += checkUnknowns(newBoard, ships->getNextShip(), all, ship, ship, j, i);
                    delete newBoard;
                    //if (success && !all) return true;
                    if (count && !all) return count;
                }
            }
        }
    }
    int largest = board->getLargestLeft();
    count += checkUnknowns(board, ships, all, ((largest > ship-1) ? ship-1 : largest), ship);
    return count;
}

};
