#include "commands.hpp"

#include <iostream>

#include "trace.hpp"
#include "util.hpp"

#include "developer_console.hpp"

namespace game {
    void ShootCommand::apply(Player *player, State *state) const
    {
        // TODO: idea
        // shoot multiple rockets at once or from different positions after
        // level up / upgrade ...

        // angles are supplied in degrees and are CCW
        Missile *missile = new Missile(
                state->generateId(),
                player,
                player->ship->position,
                -util::deg2rad(m_angle),
                0.005*player->speed);

        player->energy -= player->speed;
        player->missiles.push_back(missile);

        state->addMissile(missile);
    }

    bool ShootCommand::ready(const Player *player, const State *state) const
    {
        (void) state;

        return player->alive && player->energy >= player->speed;
        //return player->alive;
    }

    void ChangeNameCommand::apply(Player *player, State *state) const
    {
        // discard if not unique
        for (const Player *other : state->players) {
            if (m_name == other->name) {
                std::cout << "name '" << m_name << "' already given to player #" << other->id << std::endl;
                return;
            }
        }

        player->name = m_name;
    }

    void SetSpeedCommand::apply(Player *player, State *state) const
    {
        (void) state;

        if (m_speed < 0.0) {
            return;
        }

        player->speed = m_speed;
    }

    void ClearTracesCommand::apply(Player *player, State *state) const
    {
        std::cout<<"clearing traces!!!" << std::endl;

        std::vector<Trace*> rm;

        for (Trace *trace : state->traces) {
            if (trace->missile == nullptr && trace->player == player) {
                rm.push_back(trace);
            }
        }

        for (Trace *trace : rm) {
            state->traces.remove(trace);
        }
    }

    void DeveloperCommand::apply(Player *player, State *state) const
    {
        (void) player;

        if (!state->developerMode()) {
            std::cout<<"ignoring dev command while not in developer mode: '"
                << m_payload << "'" << std::endl;

            return;
        }

        // TODO: give developer mode to game
        // TODO: forward to application in some way

        std::cout << "got developer command: '" << m_payload << "'" << std::endl;

        std::string nextToken;
        std::string nextPayload;

        if (util::splitIntoTokenAndRest(m_payload, nextToken, nextPayload)) {
            developer::DeveloperConsole::CallbackResult result =
                developer::DeveloperConsole::instance().dispatchInput(
                    nextToken,
                    nextPayload);

            if (result.okay) {
                std::cout<<"[commands] developer callback success: " << result.answer << std::endl;
            } else {
                std::cout<<"[commands] developer callback failure: " << result.answer << std::endl;
            }
        }
    }

    void SetDeveloperModeCommand::apply(Player *player, State *state) const
    {
        (void) player;
        // TODO: check if player is admin

        state->setDeveloperMode(m_enable);
    }

#if 0
    bool TakeOverPlayerCommand::ready(const Player *player, const State *state) const
    {
        (void) state;
        return state->havePlayerGrantFor(player->id, m_otherPlayerId);
    }

    void TakeOverPlayerCommand::apply(Player *player, State *state) const
    {
        if (
        state->playerGrant(player->id, m_otherPlayerId);
    }
#endif
}
