#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>

#include "Board.h"
#include "Ship.h"
#include "Solution.h"
#include "Battleship.h"

/*Battleship::Ship* shipss;

void tests(Battleship::Board* board, Battleship::Ship* ships, short last_size = 0, int j = -1, int i = -1){
    Battleship::Board* newBoard;
    if (ships == NULL){
        //board->printBoard(std::cout);
        Battleship::Solution soln(board);
        soln.addShips(shipss);
        soln.printSolution(std::cout);
        return;
    }
    int x = (ships->getShip() == last_size) ? j : 0;
    int y = (ships->getShip() == last_size) ? i : 0;
    for (j = x; j < board->getCols(); j++){
        for (i = y; i < board->getRows(); i++){
            //std::cout << "Trying horizontal " << ships->getShipString() << std::endl;
            if (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();
                tests(newBoard, ships->getNextShip(), ships->getShip(), j, i);
                delete newBoard;
                if (ships->getShip() == 1) return;
            }
            if (ships->getShip() != 1){
                //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();
                    tests(newBoard, ships->getNextShip(), ships->getShip(), j, i);
                    delete newBoard;
                }
            }
        }
    }
}*/

int main(int argc, char** args){
    if (argc > 3){
        std::cerr << "Error: Too many arguments." << std::endl
                  << " Usage: " << args[0] << " [gamefile] find_all_solutions(optional)" << std::endl;
        return 1;
    }
    if (argc < 2){
        std::cerr << "Error: Not enough arguments." << std::endl
                  << " Usage: " << args[0] << " [gamefile] find_all_solutions(optional)" << std::endl;
        return 1;
    }
    bool find_all_solutions = false;
    if ((find_all_solutions = argc == 3) && std::string(args[2]) != "find_all_solutions"){
        std::cerr << "Error: Invalid arguments." << std::endl
                  << " Usage: " << args[0] << " [gamefile] find_all_solutions(optional)" << std::endl;
        return 1;
    }

    std::ifstream puzzle(args[1]);
    if (!puzzle.good()){
        std::cerr << "Error: Unable to access game file " << args[1] << std::endl;
        return 1;
    }

    //Set a bunch of state variables to create the game with.
    Battleship::Ship* shipList = NULL;
    Battleship::Cell* constraints = NULL;
    int rows = 0, cols = 0;
    int *r_sums = NULL, *c_sums = NULL;

    //Big ugly processing loop
        //get the next thingy
    for (std::string word; puzzle >> word;){
        //Make sure it's not blank
        if (word.length() == 0) continue;
        //Check word cases
        if (word == "board"){
            //Check if it's a repeat command
            if (rows != 0 || cols != 0){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                if (r_sums) delete [] r_sums;
                if (c_sums) delete [] c_sums;
                return 1;
            }
            //it's setting the board size
            puzzle >> word;
            //Check there are any non-numeric characters
            if (word.find_first_not_of("0123456789") != std::string::npos){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                return 1;
            }
            rows = atoi(word.c_str());
            //Repeat for columns
            puzzle >> word;
            if (word.find_first_not_of("0123456789") != std::string::npos){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                return 1;
            }
            cols = atoi(word.c_str());
        }
        else if (word == "rows"){
            //Initial check
            if (rows == 0 || r_sums != NULL){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                if (r_sums) delete [] r_sums;
                if (c_sums) delete [] c_sums;
                return 1;
            }
            //Allocate memory for the sums
            r_sums = new int[rows];
            //Same as above, just loop and check validity.
            for (int i = 0; i < rows; i++){
                puzzle >> word;
                //Check for the question mark
                if (word == "?"){
                    r_sums[i] = cols+1;
                    continue;
                }
                if (word.find_first_not_of("0123456789") != std::string::npos){
                    std::cerr << "Invalid file." << std::endl;
                    Battleship::deleteLinkedShips(shipList);
                    Battleship::Cell::deleteLinkedCells(constraints);
                    delete [] r_sums;
                    delete [] c_sums;
                    return 1;
                }
                r_sums[i] = atoi(word.c_str());
            }
        }
        else if (word == "cols"){
            //Actually the same as above
            if (cols == 0 || c_sums != NULL){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                if (r_sums) delete [] r_sums;
                if (c_sums) delete [] c_sums;
                return 1;
            }
            //Allocate memory for the sums
            c_sums = new int[cols];
            for (int i = 0; i < cols; i++){
                puzzle >> word;
                //Check for the question mark
                if (word == "?"){
                    c_sums[i] = rows+1;
                    continue;
                }
                if (word.find_first_not_of("0123456789") != std::string::npos){
                    std::cerr << "Invalid file." << std::endl;
                    Battleship::deleteLinkedShips(shipList);
                    Battleship::Cell::deleteLinkedCells(constraints);
                    delete [] r_sums;
                    delete [] c_sums;
                    return 1;
                }
                c_sums[i] = atoi(word.c_str());
            }
        }
        else if (word == "constraint"){
            //get coordinates, y
            puzzle >> word;
            if (word.find_first_not_of("0123456789") != std::string::npos){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                if (r_sums) delete [] r_sums;
                if (c_sums) delete [] c_sums;
                return 1;
            }
            int y = atoi(word.c_str());
            //x
            puzzle >> word;
            if (word.find_first_not_of("0123456789") != std::string::npos){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                if (r_sums) delete [] r_sums;
                if (c_sums) delete [] c_sums;
                return 1;
            }
            int x = atoi(word.c_str());
            //Validate char
            puzzle >> word;
            if (word.find_first_not_of("o<>^vX_") != std::string::npos && word.size() != 1){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                if (r_sums) delete [] r_sums;
                if (c_sums) delete [] c_sums;
                return 1;
            }
            constraints = new Battleship::Cell(((word[0] == '_') ? ' ' : word[0]), x, y, constraints);
        }
        else {
            //It's either a ship or it's invalid.
            //So let's try making a ship, and only check the string here
            //if ship marks it as unknown.
            shipList = new Battleship::Ship(0, shipList);
            shipList->setShip(word);
            if (shipList->getShip() == 0 && word != "unknown"){
                std::cerr << "Invalid file." << std::endl;
                Battleship::deleteLinkedShips(shipList);
                Battleship::Cell::deleteLinkedCells(constraints);
                if (r_sums) delete [] r_sums;
                if (c_sums) delete [] c_sums;
                return 1;
            }
        }
    }

    //Final validation
    if (rows == 0 || cols == 0 || shipList == NULL){
        std::cerr << "Invalid file." << std::endl;
        Battleship::deleteLinkedShips(shipList);
        Battleship::Cell::deleteLinkedCells(constraints);
        if (r_sums) delete [] r_sums;
        if (c_sums) delete [] c_sums;
        return 1;
    }

    //Make the initial board state
    Battleship::Board board(rows, cols, r_sums, c_sums, constraints);
    //Note that those pointers are now nullified since the object took them
    //Added for sanity
    r_sums = c_sums = NULL;
    constraints = NULL;

    //Create the game
    Battleship::Battleship game(board, shipList);
    //Again shipList is taken over by the object.

    int found;
    if (find_all_solutions)
        found = game.findAllSolutions();
    else
        found = game.findSingleSolution();

    for (std::list<Battleship::Solution>::iterator itr = game.getSolutions().begin(); itr != game.getSolutions().end(); itr++){
        itr->printSolution(std::cout);
        std::cout << std::endl;
    }

    if (find_all_solutions && found)
        std::cout << "Found " << found << " solution(s)" << std::endl;
    else if (!found)
        std::cout << "No solution" << std::endl;

    /*int rows = 8;
    int cols = 10;
    int* r_sums = new int[rows];
    int* c_sums = new int[cols];
    r_sums[0] = 4;
    r_sums[1] = 0;
    r_sums[2] = 2;
    r_sums[3] = 1;
    r_sums[4] = 4;
    r_sums[5] = 0;
    r_sums[6] = 2;
    r_sums[7] = 1;
    c_sums[0] = 1;
    c_sums[1] = 2;
    c_sums[2] = 1;
    c_sums[3] = 2;
    c_sums[4] = 1;
    c_sums[5] = 1;
    c_sums[6] = 2;
    c_sums[7] = 1;
    c_sums[8] = 2;
    c_sums[9] = 1;
//    Battleship::Cell* cell = new Battleship::Cell('<', 0, 0);
//    cell = new Battleship::Cell('X', 1, 0, cell);
//    cell = new Battleship::Cell('>', 2, 0, cell);
//    cell = new Battleship::Cell('o', 4, 0, cell);
    Battleship::Board test(rows, cols, r_sums, c_sums);

    Battleship::Ship* knowns = test.computeCompletedShips();
    for (Battleship::Ship* temp = knowns; temp != NULL; temp=temp->getNextShip()){
        temp->printShip(std::cout);
    }
    Battleship::deleteLinkedShips(knowns);

    //Battleship::Ship* ships;
    shipss = new Battleship::Ship(0);
    shipss = new Battleship::Ship(0, shipss);
    shipss = new Battleship::Ship(0, shipss);
    shipss = new Battleship::Ship(0, shipss);
    shipss = new Battleship::Ship(0, shipss);
    shipss = new Battleship::Ship(0, shipss);
    shipss = new Battleship::Ship(0, shipss);
    shipss = new Battleship::Ship(0, shipss);

    test.printBoard(std::cout);

    //tests(&test, shipss);

    Battleship::Battleship testing(test, shipss);

    std::cout << testing.findAllSolutions();

    for (std::list<Battleship::Solution>::iterator itr = testing.getSolutions().begin(); itr != testing.getSolutions().end(); itr++){
        //itr->printSolution(std::cout);
    }

    Battleship::deleteLinkedShips(shipss);
*/
    /*Battleship::Board* newBoard;
    for (int i = 0; i < rows; i++){
        for (int j = 0; j < cols; j++){
            if (test.tryPlace(j, i, 3, true, newBoard)){
                std::cout << "Success!" << std::endl;
                newBoard->printBoard(std::cout);
                delete newBoard;
            }
            if (test.tryPlace(j, i, 3, false, newBoard)){
                std::cout << "Success!" << std::endl;
                newBoard->printBoard(std::cout);
                delete newBoard;
            }
        }
    }*/
    return 0;
}
