#include <iostream>

#include "Game.h"
#include "Types.h"

Game::Game() : activeX(-1), activeY(-1) {
    reset();
}

void Game::reset() {
    activeX = -1;
    activeY = -1;
    for(uint x = 0; x < 9; x++) {
        for(uint y = 0; y < 5; y++) {
            fields[x][y] = (y > 2 ? FieldState::WHITE : FieldState::BLACK);
        }
    }
    fields[0][2] = FieldState::WHITE;
    fields[2][2] = FieldState::WHITE;
    fields[4][2] = FieldState::EMPTY;
    fields[5][2] = FieldState::WHITE;
    fields[7][2] = FieldState::WHITE;
}

void Game::reset(String& output) {
    reset();
    output.append("Fanorona Game\n\r");
    print(output);
    output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rHuman turn\n\rselect stone: ");
}

void Game::print(String& s) const {
    s.append("\n\r  0 1 2 3 4 5 6 7 8\n\r");
    printLine(s, 0);
    s.append("  |\\|/|\\|/|\\|/|\\|/|\n\r");
    printLine(s, 1);
    s.append("  |/|\\|/|\\|/|\\|/|\\|\n\r");
    printLine(s, 2);
    s.append("  |\\|/|\\|/|\\|/|\\|/|\n\r");
    printLine(s, 3);
    s.append("  |/|\\|/|\\|/|\\|/|\\|\n\r");
    printLine(s, 4);
    s.append("\n\r");
}

void Game::printLine(String& s, int index) const {
    static const char map[] = {
        '#', 'O', '.'
    };
    s.append(index + '0').append(' ');
    for(int x = 0; x < 8; x++) {
        if(x == activeX && index == activeY) {
            s.append("*-");
            continue;
        }
        s.append(map[fields[x][index]]).append('-');
    }
    if(8 == activeX && index == activeY) {
        s.append('*');
    } else {
        s.append(map[fields[8][index]]);
    }
    s.append("\n\r");
}

void Game::parse(const String& input, String& output) {
    if(input.getLength() <= 2 || !isDigit(input[0]) || input[1] != ' ' || !isDigit(input[2])) {
        revertToSelection(output);
        return;
    }
    uint x = input[0] - '0';
    uint y = input[2] - '0';
    if(x >= 9 || y >= 6) {
        revertToSelection(output);
        return;
    }
    if(activeX == -1 || activeY == -1) {
        markActive(x, y, output);
    } else {
        move(x, y, output);
    }
}

bool Game::isDigit(char c) const {
    return c >= '0' && c <= '9';
}

void Game::markActive(int x, int y, String& output) {
    if(fields[x][y] != FieldState::WHITE) {
        revertToSelection(output);
        return;
    }
    activeX = x;
    activeY = y;
    print(output);
    output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rselect location to move: ");
}

void Game::move(int x, int y, String& output) {
    if(fields[x][y] != FieldState::EMPTY || !areNeighbours(activeX, activeY, x, y)) {
        revertToSelection(output);
        return;
    }
    fields[activeX][activeY] = FieldState::EMPTY;
    fields[x][y] = FieldState::WHITE;
    removeLine(x, y, activeX, activeY, FieldState::BLACK);
    removeLine(activeX, activeY, x, y, FieldState::BLACK);
    activeX = x;
    activeY = y;
    print(output);
    output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\r");
    print(output);
    output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rAI turn\n\r");
    activeX = -1;
    activeY = -1;
    botMove(output);
}

bool Game::areNeighbours(int x, int y, int x2, int y2) const {
    if(x == x2 && y == y2) {
        return false;
    }
    int diffX = x - x2;
    int diffY = y - y2;
    if(diffX < -1 || diffX > 1 || diffY < -1 || diffY > 1) {
        return false;
    }
    if(diffX == 0 || diffY == 0) {
        return true;
    }
    return !((x & 1) ^ (y & 1));
}

void Game::revertToSelection(String& output) {
    activeX = -1;
    activeY = -1;
    print(output);
    output.append("Human Color: White(O)\n\rAIColor: Black(#)\n\rHuman turn\n\rselect stone: ");
}

void Game::removeLine(int x, int y, int x2, int y2, FieldState remove) {
    int diffX = x2 - x;
    int diffY = y2 - y;
    while(true) {
        x2 += diffX;
        y2 += diffY;
        if(x2 < 0 || x2 >= 9 || y2 < 0 || y2 >= 5 || fields[x2][y2] != remove) {
            return;
        }
        fields[x2][y2] = FieldState::EMPTY;
    }
}

void Game::botMove(String& output) {
    while(true) {
        int x = 0;
        int y = 0;
        while(true) {
            x = rand() % 9;
            y = rand() % 5;
            if(fields[x][y] == FieldState::EMPTY) {
                break;
            }
        }
        for(uint i = 0; i < 10; i++) {
            int mx = x + (rand() % 3) - 1;
            int my = y + (rand() % 3) - 1;
            if((mx == x && my == y) || mx < 0 || mx >= 9 || my < 0 || my >= 5 || fields[mx][my] != FieldState::BLACK) {
                continue;
            }
            fields[mx][my] = FieldState::EMPTY;
            fields[x][y] = FieldState::BLACK;
            removeLine(mx, my, x, y, FieldState::WHITE);
            removeLine(x, y, mx, my, FieldState::WHITE);
            
            activeX = x;
            activeY = y;
            print(output);
            activeX = -1;
            activeY = -1;
            print(output);
            revertToSelection(output);
            return;
        }
    }
}