//
// Created by nepushiro on 5/29/26.
//

#include "FakeObject.h"

#include <string.h>

#include "decomp/global_state.h"
#include "decomp/engine/math_util.h"
#include "decomp/game/interaction.h"
#include "decomp/game/object_stuff.h"
#include "decomp/include/object_fields.h"
#include "libsm64.h"
#include <stdint.h>
#include <stdbool.h>
#include "decomp/shim.h"

#define o gCurrentObject

struct Object *cur_obj_find_nearest_pole(void)
{
    struct Object *closestObj = NULL;
    struct Object *obj;
    f32 minDist = 0x20000;

    for (s32 i = 0; i < s_fakeobj_instance_pool.size; i++)
    {
        obj = s_fakeobj_instance_pool.objects[i];

        if (obj == NULL)
            continue;

        if (obj->activeFlags == ACTIVE_FLAG_DEACTIVATED)
            continue;

        if (!(obj->oInteractType & INTERACT_POLE))
            continue;

        if (obj == o)
            continue;

        f32 objDist = dist_between_objects(o, obj);

        if (objDist < minDist)
        {
            closestObj = obj;
            minDist = objDist;
        }
    }

    return closestObj;
}

f32 dist_between_objects(struct Object *obj1, struct Object *obj2)
{
    if (obj1 == NULL || obj2 == NULL) { return 0; }
    f32 dx = obj1->oPosX - obj2->oPosX;
    f32 dy = obj1->oPosY - obj2->oPosY;
    f32 dz = obj1->oPosZ - obj2->oPosZ;

    return sqrtf(dx * dx + dy * dy + dz * dz);
}

void fake_object_init(struct FakeObject *fake, float x, float y, float z)
{
    memset(fake, 0, sizeof(*fake));

    struct Object *obj = &fake->object;
    obj->activeFlags = ACTIVE_FLAG_ACTIVE | ACTIVE_FLAG_UNK8;
    obj->parentObj = NULL;
    obj->prevObj = NULL;
    obj->collidedObjInteractTypes = 0;
    obj->numCollidedObjs = 0;

    for (s32 i = 0; i < 0x50; i++)
    {
        #ifdef _WIN32
        obj->rawData.asS32[i] = 0;
        #endif
        #ifdef _WIN64
        obj->ptrData.asVoidPtr[i] = NULL;
        #endif
    }

    obj->unused1 = 0;
    obj->bhvStackIndex = 0;
    obj->bhvDelayTimer = 0;

    obj->hitboxRadius = 50.0f;
    obj->hitboxHeight = 100.0f;
    obj->hurtboxRadius = 0.0f;
    obj->hurtboxHeight = 0.0f;
    obj->hitboxDownOffset = 0.0f;
    obj->unused2 = 0;

    obj->platform = NULL;
    obj->collisionData = NULL;
    obj->oIntangibleTimer = -1;
    obj->oDamageOrCoinValue = 0;
    obj->oHealth = 2048;

    obj->oCollisionDistance = 1000.0f;
    obj->oDrawingDistance = 4000.0f;

    mtxf_identity(obj->transform);

    obj->respawnInfoType = RESPAWN_INFO_TYPE_NULL;
    obj->respawnInfo = NULL;

    obj->oDistanceToMario = 19000.0f;
    obj->oRoom = -1;
    obj->oInteractType = 0;
    obj->oInteractionSubtype = 0;
    obj->oHeldState = 0;
    obj->oAction = 0;

    obj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
    obj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
    obj->header.gfx.node.flags &= ~GRAPH_RENDER_BILLBOARD;
    obj->header.gfx.pos[0] = x;
    obj->header.gfx.pos[1] = y;
    obj->header.gfx.pos[2] = z;
    obj->header.gfx.angle[0] = 0;
    obj->header.gfx.angle[1] = 0;
    obj->header.gfx.angle[2] = 0;
    obj->header.gfx.throwMatrix = NULL;
    obj->header.gfx.sharedChild = NULL;
    obj->header.gfx.animInfo.curAnim = NULL;
    obj->header.gfx.animInfo.animID = 0;
    obj->header.gfx.animInfo.animFrame = 0;
    obj->header.gfx.animInfo.animTimer = 0;

    obj->oPosX = x;
    obj->oPosY = y;
    obj->oPosZ = z;
    obj->oMoveAnglePitch = 0;
    obj->oMoveAngleYaw = 0;
    obj->oMoveAngleRoll = 0;
    obj->oFaceAnglePitch = 0;
    obj->oFaceAngleYaw = 0;
    obj->oFaceAngleRoll = 0;
}

void fake_object_sync_position(struct FakeObject *fake)
{
    struct Object *obj = &fake->object;
    obj->header.gfx.pos[0] = obj->oPosX;
    obj->header.gfx.pos[1] = obj->oPosY;
    obj->header.gfx.pos[2] = obj->oPosZ;
}

bool fake_object_overlaps_mario(struct FakeObject *fake, struct MarioState *m)
{
    struct Object *obj = &fake->object;
    float dx = obj->oPosX - m->pos[0];
    float dz = obj->oPosZ - m->pos[2];
    float radius = obj->hitboxRadius;
    float top = obj->oPosY + obj->hitboxHeight;
    float bottom = obj->oPosY - obj->hitboxDownOffset;

    if ((dx * dx) + (dz * dz) > radius * radius)
    {
        return false;
    }

    if (m->pos[1] < bottom || m->pos[1] > top)
    {
        return false;
    }

    return true;
}

void fake_object_add_collision(struct FakeObject *fake, struct MarioState *m)
{
    struct Object *obj = &fake->object;

    if (obj->oHeldState != 0)
    {
        return;
    }

    m->marioObj->collidedObjInteractTypes |= obj->oInteractType;
    if (m->marioObj->numCollidedObjs < 4)
    {
        m->marioObj->collidedObjs[m->marioObj->numCollidedObjs++] = obj;
    }
}

void fake_object_apply_pole(struct FakeObject *fake)
{
    (void)fake;
}

void fake_object_set_preset(struct FakeObject *fake, uint32_t preset)
{
    struct Object *obj = &fake->object;

    switch (preset)
    {
        case 1:
            obj->oInteractType = INTERACT_POLE;
            obj->oInteractionSubtype = 0;
            obj->hitboxRadius = 60.0f;
            obj->hitboxHeight = 1000.0f;
            obj->hitboxDownOffset = 0.0f;
            break;
        case 0: default:
            obj->oInteractType = 0;
            obj->oInteractionSubtype = 0;
            obj->hitboxRadius = 50.0f;
            obj->hitboxHeight = 100.0f;
            obj->hitboxDownOffset = 0.0f;
            break;
    }
}
