#include "PhysicsComponent.h"
#include "../Entitys/Object.h"
#include "imgui.h"

extern "C" {
    #include "base.h"
    #include "math_functions.h"
    #include "types.h"
    #include "body.h"
    #include "shape.h"
    #include "world.h"
}

PhysicsComponent::PhysicsComponent(Object* owner, BodyType type, float width, float height)
    : Component(owner), type(type), width(width), height(height) {}

PhysicsComponent::~PhysicsComponent() {
    DestroyBody();
}

void PhysicsComponent::CreateBody(b2WorldId world, const glm::vec2& position) {
    this->world = world;

    b2BodyDef def = b2DefaultBodyDef();
    def.type = (type == BodyType::Static)   ? b2_staticBody :
               (type == BodyType::Dynamic)  ? b2_dynamicBody :
                                              b2_kinematicBody;
    def.position = { position.x, position.y };
    def.fixedRotation = fixedRotation;

    body = b2CreateBody(world, &def);

    b2Polygon box = b2MakeBox(width * 0.5f, height * 0.5f);
    b2ShapeDef shapeDef = b2DefaultShapeDef();
    shapeDef.density = density;
    

    b2CreatePolygonShape(body, &shapeDef, &box);
}

void PhysicsComponent::DestroyBody() {
    if (!B2_IS_NULL(body)) {
        b2DestroyBody(body);
        body = b2_nullBodyId;
    }
}

void PhysicsComponent::SetVelocity(const glm::vec2& vel) {
    if (!B2_IS_NULL(body))
        b2Body_SetLinearVelocity(body, { vel.x, vel.y });
}

glm::vec2 PhysicsComponent::GetVelocity() const {
    if (!B2_IS_NULL(body)) {
        b2Vec2 v = b2Body_GetLinearVelocity(body);
        return { v.x, v.y };
    }
    return {};
}

void PhysicsComponent::SetFriction(float f) {
    friction = f;
    // Not dynamically supported in Box2D C API. Requires fixture recreation.
}

void PhysicsComponent::SetDensity(float d) {
    density = d;
    // Same here, not dynamically applied.
}

void PhysicsComponent::SetFixedRotation(bool fixed) {
    fixedRotation = fixed;
    if (!B2_IS_NULL(body))
        b2Body_SetFixedRotation(body, fixed);
}

void PhysicsComponent::SyncFromPhysics() {
    if (!B2_IS_NULL(body)) {
        b2Transform xf = b2Body_GetTransform(body);
        owner->SetLocalPosition({ xf.p.x, xf.p.y });
    }
}

void PhysicsComponent::SyncToPhysics() {
    if (!B2_IS_NULL(body)) {
        glm::vec2 pos = owner->GetLocalPosition();
        b2Body_SetTransform(body, { pos.x, pos.y }, b2Body_GetRotation(body));
    }
}

float PhysicsComponent::GetFriction() const { return friction; }
float PhysicsComponent::GetDensity() const { return density; }
bool PhysicsComponent::IsFixedRotation() const { return fixedRotation; }

void PhysicsComponent::SetBodyType(BodyType t) { type = t; }
PhysicsComponent::BodyType PhysicsComponent::GetBodyType() const { return type; }

void PhysicsComponent::SetSize(float w, float h) { width = w; height = h; }
float PhysicsComponent::GetWidth() const { return width; }
float PhysicsComponent::GetHeight() const { return height; }

bool PhysicsComponent::HasBody() const { return (!B2_IS_NULL(body)); }
