#include "SparCraft.h"

using namespace SparCraft;

// IMPORTANT:
// Consult BaseTypes.hpp for all important variable typedefs
// Consult Common.h for all important constant and enum values 

Unit getSampleUnit()
{
    // Unit has several constructors
    // You will typically only be using this one to construct a 'starting' unit

    // Unit(const BWAPI::UnitType unitType, const IDType & playerID, const Position & pos)

    // The BWAPI::UnitType of the unit to be added
    BWAPI::UnitType marine = BWAPI::UnitTypes::Terran_Marine;

    // The player to add this unit to, specified by an IDType
    IDType player = Players::Player_One;

    // A Position, measured in Pixel coordinates
    Position p(0,0);

    // Simple unit constructor
    Unit marineAtOrigin(marine, player, p);
    
    return marineAtOrigin;
}

GameState getSampleState()
{
    // GameState only has a default constructor, you must add units to it manually
    GameState state;

    // The recommended way of adding a unit to a state is to just construct a unit and add it with:
    state.addUnit(getSampleUnit());

    // Or it can be added to the state via unit construction parameters
    state.addUnit(BWAPI::UnitTypes::Terran_Marine, Players::Player_One, Position(10,10));
    state.addUnit(BWAPI::UnitTypes::Protoss_Dragoon, Players::Player_Two, Position(40,40));

    // Units added with those 2 functions will be given a unique unitID inside GameState
    // If you require setting your own unique unitID for a unit, for example when translating a BWAPI::Broodwar state to GameState

    // Construct the unit
    Unit u(BWAPI::UnitTypes::Terran_Marine, Players::Player_One, Position(0,0));

    // Set the unitID
    u.setUnitID(5);

    // Add it to the state and tell it not to change the unitID.
    // If a state contains two units with the same ID, an error will occur
    state.addUnitWithID(u);

    return state;
}

Map getSampleMap()
{
    // Maps are used to constrain the movement of Units on a battlefield

    // There are 3 resolution scales at which positions operate in StarCraft maps
    // Pixel Resolution = 1x1 pixel = StarCraft smallest movement resolution
    // WalkTile Resolution = 8x8 pixels = StarCraft 'walkable' resolution
    // BuildTile Resolution = 32x32 pixels, or 4x4 WalkTiles = StarCraft "map size" resolution

    // Example: A Map of size 32*32 BuildTiles has size 128*128 WalkTiles or 1024*1024 pixels
    
    // The Map object constructor takes in size coordinates in BWAPI BuildTile resolution    
    Map smallMap(32, 32);

    // We can set the walkable values of WalkTile resolution via
    // void setMapData(const size_t & buildTileX, const size_t & buildTileY, const bool val)
    smallMap.setMapData(21, 98, false);

    // The default map sets all tiles to walkable, with an upper-left boundary of (0,0) and a lower-right boundary of (x,y)
    // We can query whether or not a unit can walk at a given position 
    bool canWalkHere = smallMap.isWalkable(Position(100, 30));

    // You can also construct a Map from a BWAPI::Game object, if you are using this code from within a bot
    // Map gameMap(BWAPI::BroodWar)

    // Once constructed, maps can be saved or loaded to files
    // A sample map (Destination) is provided under the sample_experiment directory
    // smallMap.load("mapname.txt");

    // We can set the Map of a GameState via a pointer to the map, as Map objects can be quite large:
    GameState state(getSampleState());
    state.setMap(&smallMap);

    return smallMap;
}

// When dealing with players, use a shared pointer, it's safer
// PlayerPtr is a boost::shared_pointer wrapper for Player *

PlayerPtr getSamplePlayer(const IDType playerID)
{
    // Player is the base class for all Player objects
    //
    // Scripted players all have the same constructor which is just the player ID which will be using this script
    // It is imoprtant to set that player ID correctly, as that player will only be generating and returning moves for that player

    PlayerPtr attackClosest(new Player_AttackClosest(playerID));

    return attackClosest;
}

std::vector<Action> getSamplePlayerActionsFromState()
{
    // get our sample player
    IDType currentPlayerID = Players::Player_One;
    PlayerPtr myPlayer = getSamplePlayer(currentPlayerID);

    // Construct a blank vector of Actions, which are individual unit moves
    std::vector<Action> move;

    // Get a state
    GameState state = getSampleState();

    // Construct a MoveArray. This structure will hold all the legal moves for each unit possible for this state
    MoveArray moveArray;

    // Generate the moves possible by currentPlayer from state into moveArray
    state.generateMoves(moveArray, currentPlayerID);

    // Call getMoves with these arguments
    myPlayer->getMoves(state, moveArray, move);
    
    return move;
}

void runSampleGame()
{
    // running a game is quite simple, you just need 2 players and an initial state
    GameState initialState = getSampleState();

    // get the players
    PlayerPtr p1 = getSamplePlayer(Players::Player_One);
    PlayerPtr p2 = getSamplePlayer(Players::Player_Two);

    // enter a maximum move limit for the game to go on for
    int moveLimit = 1000;

    // contruct the game
    Game g(initialState, p1, p2, moveLimit);

    // play the game
    g.play();

    // you can access the resulting game state after g has been played via getState
    GameState finalState = g.getState();

    // you can now evaluate the state however you wish. let's use an LTD2 evaluation from the point of view of player one
    StateEvalScore score = finalState.eval(Players::Player_One, EvaluationMethods::LTD2);

    // StateEvalScore has two components, a numerical score and a number of Movement actions performed by each player
    // with this evaluation, positive val means win, negative means loss, 0 means tie
    if (score.val() > 0)
    {
        std::cout << "Player One Wins!\n";
    }
    else if (score.val() < 0)
    {
        std::cout << "Player Two Wins!\n";
    }
    else
    {
        std::cout << "Game is a draw!\n";
    }
}