/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef DEF_PLAYER
#define DEF_PLAYER

#include "StateDescription.hpp"
#include "PlayerMoves.hpp"
#include "Weapon/WeaponInventory.hpp"
#include "Engines/Physics/PhysicsObject.hpp"
#include "Structs/Vector.hpp"
#include <string>

class SoundInterface;
class DamageTracker;
class PlayerStatus;

/// To notify when the player is hit.
/// Used by the AI controller to switch targets.
class PlayerCallbackReceiver
{
    public:
        virtual ~PlayerCallbackReceiver() {}

        /// The player was hit by a local source
        virtual void playerWasHit(float strength, HitDirection side,
                                  const Player *source) = 0;

        /// The player hit animation was triggered
        /// (either following a hit by a local source or
        ///  directly after a server update in a networked fight)
        virtual void playerHitTriggered() = 0;
};

/// PhysicsObject's inheriting class representing a player.
/// It can move, jump, attack, ...
class Player : public PhysicsObject, public AnimationCallbackReceiver
{
    public:
        Player();
        Player(const std::string &name, float health, float strength,
               float speed, float jumpEnergy);

        void initWeapons(WeaponEther &ether);
        void loadMoves();

        /// Resets the player to its default state
        /// Maintains only the resource handlers, i.e., physics world,
        /// callback receiver, damage tracker, sound interface,
        /// and the position if \p maintainPosition
        void reset(bool maintainPosition = false);
        void applyPlayerStatus(const PlayerStatus &status);
        void update(float frameTime);

        void addCallbackReceiver(PlayerCallbackReceiver &receiver);
        void setDamageTracker(DamageTracker &damageTracker);
        void setSoundInterface(SoundInterface *soundInterface);

        //Moves
        void setMoving(MovingState direction);
        void jump();
        void crouch();
        void uncrouch();

        void cancelAttack();
        void punch();
        void kick();
        void throwShuriken();

        //Attacks callback
        void hit(const HitBox &hitBox);
        void playSoundEffect(const std::string &effectName);

        //Animations callback
        void stepImpactCallback() override;

        //Physics callback
        void collideWorld(bool /*ground*/) override;
        void collide(PhysicsObject& /*obj*/) override;
        void takeAHit(float strength, HitDirection side,
                      const Player *source) override;
        void pushDamage(float strength, HitDirection side,
                        const Player &source, DamageTracker &damage) override;

        //Get and set methods
        const std::string &getName() const;
        float getHealth() const;
        float getBaseSpeed() const;

        void setSpeed(float speed);
        float getSpeed() const;

        unsigned int getWeaponsRemaining(WeaponType weaponType) const;

        //Moves state
        AttackIndex getAttack() const;
        StateIndex getState() const;
        const Animation *getAnimation() const;
        std::map<StateIndex, const State*> getStateInfo() const;

        bool isKo() const;
        bool isHit() const;
        bool isMoving() const;
        MovingState getMovingState() const;
        MovingState getAheadDirection() const;
        MovingState getBehindDirection() const;

        void setLooksLeft(bool looksLeft, bool force = false);
        bool looksLeft() const;

        static Player *allocFromXML(const std::string &name,
                                    const std::string &xmlRelPath);

    private:
        void updateState(StateIndex state, bool force = false);
        void updateAttack(AttackIndex attack, bool force = false);
        void updatePhysicsBox();

        void setHealth(float health);
        void moveStep(float frameTime);

        std::string m_name;

        float m_health; ///< Health points (/100)
        const float m_strength; ///< Strength in health impact

        const float m_baseSpeed; ///< Base speed in pixels / second
        float m_speed; ///< Current speed in pixels / second

        const float m_jumpEnergy; ///< Jump energy in pixels / second

        /// Whether the player is looking to the left or to the right
        bool m_lookingLeft;

        PlayerMoves m_moves; ///< Animations and attacks
        WeaponInventory m_weapons; ///< Weapons storage

        SoundInterface *m_soundInterface;
        DamageTracker *m_damageTracker;

        /// Whether the player wishes to move right, left, or not at all
        MovingState m_requestsMove;
        /// Whether the player is expecting a ground impact
        bool m_awaitsImpact;

        std::vector<PlayerCallbackReceiver*> m_receivers;
};

#endif
