#include "game.h"
Game::Game() 
{
    // initialize the number of columns that each "color" will contain
    lastColumnIndex = CHECKBOXES - 1;
    // set up lockOut
    for(int i = 0; i<white1;i++)
    {
        lockOut.push_back(false);
    }
    // set the state of game [how to move from state to state ("game logic?")]
    state = NOT_STARTED;
    // dice have been initialized with random seed, copy this into entropyPool
    entropyPool = dice.roll();
};
bool Game::addPlayer(std::string name)
{
    if(NOT_STARTED == state)
    {
        // add a new player with a name they choose and a secret
        ScoreSheet newplayer(name, dice.roll());
        players.push_back(newplayer);
        fprintf (stdout, "added: %s to the game\n", name.c_str());
        return true;
    }
    else
    {
        fprintf(stdout, "player %s was not added to the game\n", name.c_str());
        return false;
    }
};
void Game::score()
{
    // **CALCULATE EACH PLAYERS' SCORE**
    std::vector<int> pointsGuide = {0,1,3,6,10,15,21,28,36,45,55,66,78};
    // check players' card for marked boxes
    for(int i = 0;i <players.size();i++)
    {
        // clear each players' previous score
        players[i].score = 0;
        // calculate penalty amount
        players[i].score -= players[i].cumulativeOlok.penaltyCount*PENALTY_VALUE;
        // count the Xs of each "color" and give me a score
        for(int j = 0;j <white1;j++)
        {
            // determine if player qualifies for the lockout point (this is a rule)
            int lockoutPoint = players[i].cumulativeOlok.getLastIndex(j) == lastColumnIndex;
            // use pointsGuide to get the score for this user's "color" row
            players[i].score += pointsGuide[players[i].cumulativeOlok.getXCount(j) + lockoutPoint];
        }
    }
    // **CHECK IF GAME IS DONE**
    // check each player's card for penalties
    for(int i = 0;i<players.size();i++)
    {
        // check for maximum penalty to see if game is over
        if(players[i].cumulativeOlok.penaltyCount == MAX_PENALTY){state = FINISHED;}      
    }
    // check for lockout to see if game is over
    int lockCount = 0;
    for(int i = 0;i<lockOut.size();i++)
    {
        if(true == lockOut[i]){lockCount++;}
    }
    if(MAX_LOCKOUT <= lockCount)
    {
        state = FINISHED;
    }
}
void Game::round()
{
    // start all players' turns
    for(int i = 0; i <players.size();i++)
    {
        players[i].isTurnDone = false;
    }
    // roll the dice
    dice.roll();
    // check to see if all players are done with turn
    while(true)
    {
        bool temp = true;
        for(int i = 0; i <players.size();i++)
        {
            temp = temp && players[i].isTurnDone;
        }
        if(temp){break;}
        // technically this is a sleep, milliseconds is for how long
		somethingNeedDoing(1000 /* milliseconds */, storage);
    }
    // save data from currentTurnOlok into cumulativeOlok
    for(int i = 0; i <players.size();i++)
    {
        players[i].cumulativeOlok.addOlok(players[i].currentTurnOlok);
    }
    // score the cards
    score();
    // go to next player's turn
    activePlayer++;
    activePlayer %= players.size();
    // adding in entropy from human players of THIS round to dice so they are slightly more random
    dice.diceTropy(entropyPool);
}

// generate text: per player(48boxes, penalty count), locked off rows, dice roll, game end(when it occurs) send it
std::string Game::send()
{
    std::string output;
    if(dice.dice.size()==0){fprintf(stdout, "kyle is cool\n");}
    // add dice values to output
    for(int i = 0; i < dice.dice.size(); i++)
    {
        output.push_back(dice.dice[i] + '0');
    }
    // look at each scoresheet
    for(int i = 0; i < players.size(); i++)
    {
        output.append(players[i].cumulativeOlok.toString());
    }
    // add locked off row to output
    for(int i = 0; i < lockOut.size();i++)
    {
        output.push_back(lockOut[i] ? 'T' : 'F');
    }
    // add game end to output
    output.push_back(state == FINISHED ? 'T' : 'F');
    // print everything needed to send current game state to players
    fprintf(stdout, "the output is: %s\n", output.c_str());
    return output;
}

// kyle's intention is to check off red 12 and blue 5
// " kylesecret,1143"
// emma's intention is to take a penalty
// "emmasecret,1"
// convert a user string into a turn
struct Turn Game::receive(std::string userInput)
{
    Turn turnInProgress;
    std::string playerSecret, original = userInput;
    int playerNumber = -1, pos = 0;
    // check for user request to add themselves to game
    // userInput == add+stringOfAnyLength
    if("add" == userInput.substr(0,3))
    {
        // add player to game
        addPlayer(userInput.substr(3));
        ScoreSheet lastPlayer = players.back();
        // send player secret -> for now it is only printed below
        fprintf(stdout, "Player %s has a secret of: %d",lastPlayer.savedName, lastPlayer.secretNumber);
        return turnInProgress;
    }
    // look for the position of a comma in userInput
    for(int i = 0;i < userInput.size();i++)
    {
        // a comma is found, set position and stop
        if(',' == userInput[i])
        {
            pos = i; break;
        }
    }
    // assuming there is a comma and it is not the first character, split userInput into two parts on the comma
    if(0<pos)
    {
        playerSecret = userInput.substr(0,pos-1);
        userInput = userInput.substr(pos);
        int secret = stoi(playerSecret);
        // check each player's secret against submitted secret to find player number
        for(int i = 0;i < players.size(); i++)
        {
            if(secret == players[i].secretNumber)
            {
                playerNumber = i; break;
            }
        }
    }
    if(-1 == playerNumber)
    {
        fprintf(stdout, "Could not find player: %s", original);
        return turnInProgress;
    }
    // userinput is one, add a penalty
    if(1 == userInput.size())
    {
        turnInProgress.penalty = 1;
    }
    // userinput size is two, add one moves
    else if(2 == userInput.size())
    {
        turnInProgress.moves[0] = translateToMove(userInput);
        turnInProgress.numberOfMoves = 1;
    }
    // userinput size is four, add two moves
    else if(4 == userInput.size())
    {
        turnInProgress.moves[0] = translateToMove(userInput.substr(0,2));
        turnInProgress.moves[1] = translateToMove(userInput.substr(2,2));
        turnInProgress.numberOfMoves = 2;
    }
    else {fprintf(stdout, "Form an opinion: WRONG!");}
    // check the rules to make sure turn is valid
    if(true == checkTurn(playerNumber, turnInProgress))
    {
        // currentTurnOlok can only have one valid turn in it at a time
         players[playerNumber].currentTurnOlok.clear();
        // check the moves in turn
        for(int i = 0;i < turnInProgress.numberOfMoves;i++)
        {
            // add turn to currentTurnOlok
            players[playerNumber].currentTurnOlok.addX(turnInProgress.moves[i]);
        }
        // player chose to take a penalty
        players[playerNumber].currentTurnOlok.penaltyCount += turnInProgress.penalty;
    }
    // adding time to entropyPool to increase entropy in the system
    entropyPool += time(NULL);
}

bool Game::isMoveRepresentedInDie(int player, Turn turn)
{
    // is this the non-active player
    if(player != activePlayer) 
    {
        // if the move equals the sum of the two white die return true, if not return false
        return turn.moves[0].index == dice.dice[white1] + dice.dice[white2];
    }
    // otherwise this is the active player
    else
    {
        // if one move
        if(1 == turn.numberOfMoves)
        { 
            // check sum of white die
            if(turn.moves[0].index != (dice.dice[white1] + dice.dice[white2]))
            {
                int c = turn.moves[1].color;
                // if any white die plus the colored die equals the move
                if(turn.moves[1].index == (dice.dice[white1] + dice.dice[c]) || turn.moves[1].index == (dice.dice[white2] + dice.dice[c]))
                {
                    return true;
                }
                // colored die plus either white die was not a move
                else {return false;}
            }
            // otherwise the white sum die was a move
            else {return true;}
        }
        // if two moves
        else if(2 == turn.numberOfMoves)
        {            
            // check first input with white die sum
            if(turn.moves[0].index != (dice.dice[white1] + dice.dice[white2]))
            {
                // white die sum was not a move
                return false;
            }
            else
            {
                    int c = turn.moves[1].color;
                // check to see if color die + either white die sum equals a move
                if(turn.moves[1].index == (dice.dice[white1] + dice.dice[c]) || turn.moves[1].index == (dice.dice[white2] + dice.dice[c]))
                {
                    return true;
                }
                else {return false;}
            }           
                
        }           
    }      
};

bool Game::lockOutRule(int player, Turn turn)
{
    // if the player has selected the last column, check it
    for(int i = 0; i<turn.moves.size(); i++)
    {
        // player can take their move in the last column if there are 5 recorded moves in that color
        if(LOCKOUT_QUALIFIER > players[player].cumulativeOlok.getXCount(turn.moves[i].color)
        {
            fprintf(stdout, "Player[%d] %s tried to check off column 12 without minimum X\n", player, players[player].savedName);
            return false;
        }
    }
    return true;
}

bool Game::leftToRightRule(int player, Turn turn)
{
    for(int i = 0; i<turn.moves.size(); i++)
    {
        // player must add an x to olok from left to right
        if(players[player].cumulativeOlok.getLastIndex(turn.moves[i].color) > turn.moves[i].index)
        {
            fprintf(stdout,"Player[%d] %s not following left to right rule X\n", player, players[player].savedName);
            return false;
        }
    }
    return true;
}

bool Game::checkTurn (int player, Turn turn)
{
    // check all rules for turn verification
    return lockOutRule( player, turn) && leftToRightRule( player, turn) && isMoveRepresentedInDie(player, turn);
}

void Game::addX(int player, int color, int index)
{
}
struct Move Game::translateToMove(std::string temp)
{
    int temp2 = stoi(temp);
    struct Move newMove;
    // find the color
    newMove.color = (temp2/colors);
    // find the index, subtracted 1 from checkboxes because it is lockout bonus box - only interested in scored boxes
    newMove.index = (temp2%(CHECKBOXES-1));
    return newMove;
}