#pragma once

#include "state.hpp"
#include "player.hpp"
#include "ship.hpp"
#include "planet.hpp"
#include "missile.hpp"

#include <iostream>

namespace game {
    /**
     * Base class for commands.
     * must derive from this.
     */
    class Command {
    public:
        Command()
        {
        }

        virtual ~Command()
        {
        }

        // check whether the command is ready to execute.
        // if not, wait.
        // note: stuff like for admin should be done in execute() to discard the
        // command and not block the queue.
        virtual bool ready(const Player *player, const State *state) const
        {
            (void) player;
            (void) state;

            return true;
        }

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

            std::cout<<"Command '" << name() << "' not yet implemented!" << std::endl;
        }

        virtual std::string name() const { return "<unnamed>"; }
    };

    /**********************************************************************/
    /* Implemented commands */
    /**********************************************************************/

    /*
     * Shoot at angle and optional speed.
     * TODO
     */
    class ShootCommand : public Command {
    public:
        ShootCommand(float angle) : m_angle(angle)
        {
        }

        std::string name() const { return "<shoot>"; }

        void apply(      Player *player,       State *state) const;
        bool ready(const Player *player, const State *state) const;

    private:
        float m_angle;
    };

    /**
     * Change the name of the player if it will be uniuqe.
     */
    class ChangeNameCommand : public Command {
    public:
        ChangeNameCommand(const std::string &name) : m_name(name)
        {
        }

        std::string name() const { return "<change name>"; }
        void apply(Player *player, State *state) const;

    private:
        std::string m_name;
    };

    /**
     * clear all traces of this player that are done
     */
    class ClearTracesCommand : public Command {
    public:
        ClearTracesCommand()
        {
        }

        std::string name() const { return "<clear traces>"; }
        void apply(Player *player, State *state) const;
    };

    /**
     * Set default speed of next shots for this player.
     */
    class SetSpeedCommand : public Command {
    public:
        SetSpeedCommand(float speed) : m_speed(speed)
        {
        }

        std::string name() const { return "<set speed>"; }
        void apply(Player *player, State *state) const;

    private:
        float m_speed;
    };

    /**
     * Developer command.
     */
    class DeveloperCommand : public Command {
    public:
        DeveloperCommand(const std::string &payload) : m_payload(payload)
        {
        }

        std::string name() const { return "<dev command>"; }
        void apply(Player *player, State *state) const;

    private:
        std::string m_payload;
    };

    class SetDeveloperModeCommand : public Command {
        public: SetDeveloperModeCommand(bool enable) : m_enable(enable)
        {
        }

        std::string name() const { return "<set dev. mode>"; }
        void apply(Player *player, State *state) const;

    private:
        bool m_enable;
    };

#if 0
    /**
     * Take over a session for a player that left.
     * An admin must confirm this command.
     */
    class TakeOverPlayerCommand : public Command {
    public:
        TakeOverPlayerCommand(int otherPlayerId) : m_otherPlayerId(otherPlayerId)
        {
        }

        std::string name() const { return "<take over player>"; }

        void apply(      Player *player,       State *state) const;
        bool ready(const Player *player, const State *state) const;

    private:
        int m_otherPlayerId;
    };
#endif
}
