#include "library.h"

namespace py = pybind11;

PYBIND11_MODULE(library, m)
{
    m.doc() = "Python API for playing Starcraft II";

    define_typeenums(m);
    define_unit(m);
    define_unittype(m);
    define_util(m);
    define_point(m);
    define_base_location(m);
    define_tech_tree(m);
    define_map_tools(m);
    define_building_placer(m);

    py::class_<Coordinator>(m, "Coordinator")
        .def(py::init())
        .def("set_participants", &sc2::Coordinator::SetParticipants, "participants"_a)
        .def("launch_starcraft", &sc2::Coordinator::LaunchStarcraft)
        .def("start_game", &sc2::Coordinator::StartGame, "map_path"_a)
        .def("update", &sc2::Coordinator::Update);

    py::enum_<sc2::Race>(m, "Race")
        .value("Terran", sc2::Race::Terran)
        .value("Zerg", sc2::Race::Zerg)
        .value("Protoss", sc2::Race::Protoss)
        .value("Random", sc2::Race::Random);

    // Stupid anonymous enum, used as int everywhere. Best work-around I could think of.
    py::module players = m.def_submodule("Players", "Constants for referring to players");
    players.attr("Self")    = (int) Players::Self;
    players.attr("Enemy")   = (int) Players::Enemy;
    players.attr("Neutral") = (int) Players::Neutral;
    players.attr("Ally")    = (int) Players::Ally;
    players.attr("Size")    = (int) Players::Size; // These are probably used for
    players.attr("None")    = (int) Players::None; // creating arrays of fixed length??

    /*
    TODO: These also needs to be defined

    typedef SC2Type<ABILITY_ID>   AbilityID;
    typedef SC2Type<UPGRADE_ID>   UpgradeID;
    typedef SC2Type<BUFF_ID>      BuffID;
    */
    py::class_<sc2::UnitTypeID>(m, "UnitTypeID")
        .def(py::init<sc2::UNIT_TYPEID>());

    py::implicitly_convertible<sc2::UNIT_TYPEID, sc2::UnitTypeID>();

    py::class_<sc2::Agent>(m, "Agent")
        .def(py::init());

    // IDABot is a specialization of Agent
    py::class_<IDABot, PyIDABot, sc2::Agent>(m, "IDABot")
        .def(py::init())
        // TODO: The following 3 are CamelCased, but if the name is changed
        // the name used for inheritance in Pyhon stays the same. Weird.
        .def("OnGameStart", &IDABot::OnGameStart)
        .def("OnStep", &IDABot::OnStep)
        .def("get_all_units", &IDABot::GetAllUnits)
        .def("get_my_units", &IDABot::GetMyUnits)
        .def("get_player_race", &IDABot::GetPlayerRace)
        .def_property_readonly("base_location_manager", &IDABot::Bases)
        .def_property_readonly("tech_tree", &IDABot::TechTree)
        .def_property_readonly("map_tools", &IDABot::Map)
        .def_property_readonly("building_placer", &IDABot::BuildingPlacer)
        .def_property_readonly("start_location", &IDABot::GetStartLocation)
        .def_property_readonly("minerals", &IDABot::GetMinerals)
        .def_property_readonly("current_supply", &IDABot::GetCurrentSupply)
        .def_property_readonly("max_supply", &IDABot::GetMaxSupply)
        .def_property_readonly("gas", &IDABot::GetGas);

    py::class_<sc2::PlayerSetup>(m, "PlayerSetup");

    py::enum_<sc2::Difficulty>(m, "Difficulty")
        .value("VeryEasy", sc2::Difficulty::VeryEasy)
        .value("Easy", sc2::Difficulty::Easy)
        .value("Medium", sc2::Difficulty::Medium)
        .value("MediumHard", sc2::Difficulty::MediumHard)
        .value("Hard", sc2::Difficulty::Hard)
        .value("HardVeryHard", sc2::Difficulty::HardVeryHard)
        .value("VeryHard", sc2::Difficulty::VeryHard)
        .value("CheatVision", sc2::Difficulty::CheatVision)
        .value("CheatMoney", sc2::Difficulty::CheatMoney)
        .value("CheatInsane", sc2::Difficulty::CheatInsane);

    py::class_<sc2::Color>(m, "Color")
        .def(py::init<>())
        .def(py::init<int, int, int>())
        .def_readwrite("r", &sc2::Color::r)
        .def_readwrite("g", &sc2::Color::g)
        .def_readwrite("b", &sc2::Color::b);

    py::module colors = m.def_submodule("Colors", "Constants for common colors");
    colors.attr("White") = sc2::Colors::White;
    colors.attr("Red") = sc2::Colors::Red;
    colors.attr("Green") = sc2::Colors::Green;
    colors.attr("Yellow") = sc2::Colors::Yellow;
    colors.attr("Blue") = sc2::Colors::Blue;
    colors.attr("Teal") = sc2::Colors::Teal;
    colors.attr("Purple") = sc2::Colors::Purple;
    colors.attr("Black") = sc2::Colors::Black;
    colors.attr("Gray") = sc2::Colors::Gray;

    m.def("create_participants", &sc2::CreateParticipant, "Create participant from agent");
    m.def("create_computer", &sc2::CreateComputer, "Create participant from built-in Starcraft computer");
}