/*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_PHYSICS_OBJECT
#define DEF_PHYSICS_OBJECT

#include "Structs/Box.hpp"
#include "Structs/Vector.hpp"
#include "PhysicsRoutines.hpp"
#include <vector>
#include <string>

class PhysicsWorld;
class PhysicsObjectStatus;
class DamageTracker;
class Player;

enum class HitDirection { FromLeft, FromRight };

class PhysicsObject
{
    public:
        PhysicsObject();
        virtual ~PhysicsObject() = default;

        virtual void setPhysicsWorld(PhysicsWorld &physicsWorld);
        const PhysicsWorld *getPhysicsWorld() const;

        void applyPhysicsStatus(const PhysicsObjectStatus &status);

        void setPosition(float xPosition, float yPosition);
        void moveXPosition(float distance, bool push = false);
        void moveYPosition(float distance, bool push = false);

        void setWidth(float width, bool reverseSide = false);
        void setHeight(float height, bool reverseSide = false);

        void setVelocity(const Vector &velocity);
        const Vector &getVelocity() const;

        void setAirPenetration(float airPenetration);
        float getAirPenetration() const;

        void setMaterial(const std::string &material);
        const std::string &getMaterial() const;

        void setFixed(bool fixed);
        bool isFixed() const;

        float shadowProjection() const;

        /// Whether the object should intercept shadows of other objects over it
        virtual bool interceptsShadows() const;

        bool isOnGround(std::string *material=nullptr) const;
        const Box &getBox() const;

        virtual void collideWorld(bool ground) = 0;
        virtual void collide(PhysicsObject &obj) = 0;
        virtual void takeAHit(float strength, HitDirection side,
                              const Player *source = nullptr) = 0;

        /// Push a damage to be applied to the object.
        /// The damage can either be applied immediately (Weapon, Platform)
        /// or queued in a damage tracker (Player) to
        /// allow simultaneous hits and draw games
        virtual void pushDamage(float strength,
                                HitDirection side,
                                const Player &source,
                                DamageTracker &damage) = 0;

    protected:
        std::vector<PhysicsObject*> hitTest() const;
        std::vector<PhysicsObject*> hitTest(const Box &box) const;

        PhysicsWorld *m_physicsWorld;

        Box m_box;
        Vector m_velocity;
        bool m_fixed;

        /// From 0.f: regular impact of air friction
        /// to 1.f: no impact of air friction
        float m_airPenetration;

        /// Name of the material of the object (for step sounds)
        std::string m_material;

    private:
        void movePosition(float distance,
                          PhysicsRoutines::BoxView1D view, bool push);
        bool attempt_movePosition(float distance,
                                  PhysicsRoutines::BoxView1D view, bool push);

        void setSize(float size, PhysicsRoutines::BoxView1D view,
                     bool reverseSide);
};

#endif
