#ifndef PHYSICSCOMPONENT_HPP
#define PHYSICSCOMPONENT_HPP

#include "Component.hpp"
#include "TransformComponent.hpp"
#include "TinyMath.hpp"

/**
 * An interface for all @link Component components @endlink that will be interacting with the PhysicEngine
 */
class IPhysicsComponent : public Component {
public:
    /**
     * Construct a new IPhysicsComponent object
     * 
     * @param pos the position of the GameObject the Component is attached to
     * @param dim the dimensions (width, height) of the GameObject the Component is attached to
     */
    IPhysicsComponent(TransformComponent* pos, Vec2D dim);

    /**
     * Destroy the IPhysicsComponent object
     */
    ~IPhysicsComponent();

    /**
     * Check for a collision with the given IPhysicsComponent.
     * 
     * @param other the Component to check for collisions
     * @return whether other is colliding with this object 
     */
    bool IsColliding(IPhysicsComponent* other);

    /**
     * Apply a force to the Component.
     * Handles forces outside of collisions (such as from scripts).
     * 
     * @param force the force to be applied to the IPhysicsComponent
     * @see #SetVelocity() to directly change the velocity
     * @see #ApplyAcceleration() to modify the acceleration
     * @see #ApplyVelocity() to finalize movement
     */
    virtual void ApplyForce(Vec2D force);

    /**
     * Change the velocity of the Component.
     * Adds the acceleration value to the current velocity.
     */
    virtual void ApplyAcceleration(Vec2D acceleration);

    /**
     * Moves the Component.
     * The IPhysicsComponent will not move until this is called.
     * 
     * @see #ApplyForce() to use force to change the velocity
     * @see #SetVelocity() to directly change the velocity
     * @see #ApplyAcceleration() to modify the acceleration
     */
    virtual void ApplyVelocity();

    // SETTERS

    /**
     * Set the velocity of the IPhysicsComponent.
     * 
     * @param vel the new velocity
     */
    virtual void SetVelocity(Vec2D vel);

    // GETTERS

    /**
     * Get whether the IPhysicsComponent is kinematic.
     * Allows for Colliders to only get collision data, without applying forces to other Components
     * 
     * @return whether the IPhysicsComponent uses kinematic collisions
     * @see PhysicsEngine#Update()
     */
    virtual bool IsKinematic();

    /**
     * Get whether the IPhysicsComponent bounces or just stops on collision.
     * 
     * @return whether the IPhysicsComponent uses elastic collisions
     * @see PhysicsEngine#Update()
     */
    virtual bool IsElastic();
    
    /**
     * Get whether the PhysicsEngine will apply gravity forces to the IPhysicsComponent.
     * 
     * @return whether the IPhysicsComponent uses gravity
     */
    virtual bool UsesGravity();

    /**
     * Get the position of the IPhysicsComponent.
     * 
     * @return the position of the Component
     * @see TransformComponent
     */
    Vec2D GetPosition();

    /**
     * Get the dimensions of the IPhysicsComponent.
     * 
     * @return the dimensions of the Component 
     */
    Vec2D GetDimensions();

    /**
     * Get the velocity of the IPhysicsComponent.
     * 
     * @return the velocity of the Component
     */
    virtual Vec2D GetVelocity();

    /**
     * Get the mass of the IPhysicsComponent.
     * 
     * @return the mass of the Component 
     */
    virtual float GetMass();

protected:
    TransformComponent* m_position; /** pointer to the TransformComponent used by the shared parent GameObject */
    Vec2D dimensions;
    std::vector<ComponentTag> tags = {ComponentTag::Physics};
};

#endif
