#pragma once
#include <string>
#include <MCP.h>
#include <tuple>

//Convenience macro for a trivial create function implementation.
#define REGISTER_GAME(name) \
    Game* Game::create(int players, const std::string & board) { \
        return new name(players,board); \
    }

// The ABI of windows and macos uses leading underscores
#if defined(__WIN32__) || defined(__APPLE__)
#   define CDECL(name)  asm("_" #name)
#else
#   define CDECL(name)  asm(#name)
#endif

/// The class the represents the general game instance
/**
 *  This class implements standard information about the game and methods to
 *  evaluate game boards for validity and pass game changes. Games running on
 *  MCP should implement this interface.
 */
class Game {

public:

    /// An enum describing the result of a move.
    /*  Ok      - The move was succesfully processed
     *  Invalid - An invalid move was passed by the player
     *  Won     - The player won
     *  Draw    - The move by the player lead to a draw
     *  End     - The game ended as neither won, lost nor ended in a draw
     *            The last state is primarily usefull for Replay
     */
    enum class MoveResult { OK, Invalid, Won, Draw, End };

    //A type for the game creation function described below
    using create_func = Game *(*)(int players, const std::string &board);

    /// This creates the game for the given number of players, possibly with the given game state
    /**\param players The number of players playing the game. The game is responsible to check
     *                whether this number is supported
     * \param board The initial state of the game in the custom format
     *              also used for serialize()
     *  This sets the initial game state to the one that is represented by
     *  the board parameter. The game should check the given state for
     *  plausibility and, upon finding an inconsistency, throw a std::runtime_error.
     *  This is especially true if the number of players does not match the number of players
     *  in the game state.
     *  If the board parameter is empty then the initialization should be a default board.
     *
     *  You may use the REGISTER_GAME macro for an easy implementation of this function.
     */
    static Game *create(int players, const std::string &board = "") CDECL(game_create);

    virtual ~Game() {}


    //! Game name
    /*! Returns a descriptive name of the game */
    virtual std::string name() const = 0;

    /// Returns the player whose turn it is
    /*  This funciton returns the player whose turn it is to play. The mcp will check that this is between
     *  minPlayers and the actual number of players passed to Game::initialize. If this check fails then the MCP
     *  will terminate with an internal error.
     */
    virtual unsigned currentPlayer() const = 0;


    /// Processes the move of the player that is currently returned by Game::nextPlayer
    /** This function will get the move output by the player that the MCP determines to be next.
     *  The MCP decides this based on a call to Game::nextPlayer() previous to calling this function.
     *
     *  The function returns the result of the move. Additionally a message can be returned that is output
     *  by the MCP. This can be used to print additional advise for invalid moves for example.
     *  See also the result function below for a helper
     */
    virtual std::tuple<MoveResult,std::string> move(const std::string& move) = 0;

    /// Returns the state of the game that will be processed by the player. This state should describe the
    /*  game state completely so that it can also be loaded by the Game::initialize function. The string
     *  should be human readable.
     */
    virtual std::string serialize() const = 0;

    /// Returns a textual name for a player.
    /* This is used for more helpfull messages by the MCP. By default players are called by their
     * number (+1). So the player 0 is called "1", 1 is called "2" in and so on. You might want to
     * change this to colors for games such as Checkers or similar.
     */
    virtual std::string playerName(int number) const {
        return std::to_string(number);
    }

    /// Prints a graphical representation of the board to the console
    /* This should output a compact, graphical representation of the board to std::cout.
     * The default implementation does nothing.
     */
    virtual void renderBoard() const = 0;

    /// Prints an explanation on the expected move format. Shown for illegal moves
    /* This should output the allowed move formats, together with examples. It should
     * write directly to std::out (just like renderBoard)
     */
    virtual void expectedFormat() const = 0;
protected:

    //Helper for returning the result
    static std::tuple<MoveResult,std::string> result(const MoveResult& m, const std::string& s = "") {
        return std::make_tuple(m,s);
    }
};
