#include "Actor/ActorManager.hpp"
#include "System/OverlayManager.hpp"
#include "Unknown/UnkStruct_027e09a0.hpp"
#include "Unknown/UnkStruct_027e09a4.hpp"
#include "Unknown/UnkStruct_027e09b8.hpp"
#include "Unknown/UnkStruct_027e0cd8.hpp"
#include "Unknown/UnkStruct_027e0cf4.hpp"
#include "Unknown/UnkStruct_027e0d70.hpp"
#include "flags.h"
#include "global.h"
#include <nitro/mi.h>

extern "C" {
void func_ov000_020977e4();
void func_ov021_020f8818();
void func_ov031_020ea100();
void func_ov071_0215e8d4();
}

struct UnkStruct_ov000_020ab1ac {
    /* 00 */ u32 mUnk_00;
    /* 04 */ u32 mUnk_04;
    /* 08 */ u8 mUnk_08;
    /* 09 */ u8 pad[3];
    /* 0C */
};
extern UnkStruct_ov000_020ab1ac data_ov000_020ab1ac[30];

const ActorId data_ov001_020c2638[] = {ActorId_FOMC, ActorId_FOMA, ActorId_FOMB, ActorId_FOPD, ActorId_FOMR};
const u16 data_ov001_020c2624[]     = {0x01AE, 0x01AF, 0x01B0, 0x01B1, 0x01B2};
const u16 data_ov001_020c262e[]     = {0x01CB, 0x01CC, 0x01CD, 0x01CE, 0x01CF};

ActorManager *ActorManager::Create() {
    return new(1, 4) ActorManager();
}

ActorManager::ActorManager() {
    this->mUnk_20         = 0;
    this->mUnk_21         = 0;
    this->mUnk_22         = 0;
    this->mUnk_23         = 0;
    this->mUnk_32         = 0;
    this->mActorTable     = NULL;
    this->mActorTableEnd  = NULL;
    this->mUnk_08         = NULL;
    this->mActorCount     = 0;
    this->mNextActorId    = 0;
    this->mUnk_10         = 0;
    this->mUnk_14.mUnk_00 = NULL;
    this->mUnk_14.mUnk_04 = 0;
    this->mUnk_24         = 0;
    this->mUnk_28         = 0;
    this->mUnk_2C         = 0;
    this->mUnk_30         = 0;
    this->mUnk_34         = 0;
}

ActorManager::~ActorManager() {
    if (this->mActorTable != NULL) {
        delete this->mActorTable;
    }
}

void ActorManager::func_ov001_020bafdc() {
    Actor **ppActorTable = this->mActorTable;

    while (ppActorTable != this->mUnk_08) {
        Actor *pActor = *ppActorTable;

        if (pActor != NULL) {
            // alive and uninitialized?
            if (GET_FLAG(pActor->mFlags, ActorFlag_Alive) && !GET_FLAG(pActor->mFlags, ActorFlag_4)) {
                pActor->vfunc_1C();
                SET_FLAG(pActor->mFlags, ActorFlag_4);
            }
        }

        ppActorTable++;
    }
}

void ActorManager::func_ov001_020bb018(ZOBHeader *pHeader) {
    u16 unk_0A = pHeader->unk_0A;
    u16 unk_08 = pHeader->unk_08;

    this->mActorCount  = 0;
    this->mNextActorId = 0;
    this->mUnk_10      = 0;
    this->mUnk_34      = 0;

    int aligned0A = ALIGN_NEXT(unk_0A, 8);
    int aligned08 = ALIGN_NEXT(unk_08, 8);

    int iVar5 = data_027e09a0->func_ov000_020702a8(data_027e09a4->CurrentSceneIndex())->mUnk_20;

    s32 allocCount;
    if (data_027e09a4->mUnk_60 == 0) {
        allocCount = aligned0A + 0x40;
    } else {
        allocCount = aligned0A + 0x80;
    }

    if (this->mActorTable != NULL) {
        delete this->mActorTable;
    }

    this->mActorTable    = (Actor **) ::operator new(allocCount * 4, HeapIndex_1);
    this->mActorTableEnd = (Actor **) ((uintptr_t) this->mActorTable + allocCount * 4);

    MI_CpuFill32(0, this->mActorTable, (this->mActorTableEnd - this->mActorTable) * 4);
    this->mUnk_08 = this->mActorTable;
    data_0204999c.func_ov001_020ba588(aligned08 + iVar5, 0x100);

    // it's 2 when we are on the title screen and 0 during normal gameplay, is it the game mode ?
    if (data_027e09a4->mUnk_60 != 2) {
        if (data_027e09a4->IsLand() != 0) {
            unk32 iVar5 = data_027e09a4->CurrentSceneIndex();
            OverlayManager *pMgr;

            if (iVar5 == SceneIndex_f_rabbit) {
                this->mUnk_34 = 0xFFFFECCD; // ~0x1332
            }

            this->mUnk_14.func_ov000_0205fc20(0x5D, 0x22, NULL, 0);

            {
                UnkStruct_StackTitleScreen auStack_28("Npc/Tex.bin", 1);
                auStack_28.func_ov000_02059270(0x07, "zdf_sw_trace", 0x39200000);
                auStack_28.func_ov000_02059270(0x08, "zdf_sword02", 0x19150000);
                auStack_28.func_ov000_02059270(0x0A, "whip", 0x28000000);

                char *str = "rupy0";
                auStack_28.func_ov000_02059270(0x0C, str, 0x2D200000);
                auStack_28.func_ov000_02059288(0x0D, "rupy1", 0x0C);
                auStack_28.func_ov000_02059288(0x0E, "rupy2", 0x0C);
                auStack_28.func_ov000_02059288(0x0F, "rupy3", 0x0C);

                auStack_28.func_ov000_02059270(0x10, "life0", 0x2D200000);
                auStack_28.func_ov000_02059270(0x1D, "arrow", 0x2D200000);

                pMgr = &gOverlayManager;

                auStack_28.func_ov000_02059270(0x1E, "bomb", 0x2D200000);
                auStack_28.func_ov000_02059270(0x1F, "drug", 0x2D200000);
                auStack_28.func_ov000_02059270(0x20, "drop0", 0x2D200000);
                auStack_28.func_ov000_02059270(0x21, "drop1", 0x2D200000);
                auStack_28.func_ov000_02059270(0x22, "drop2", 0x2D200000);
                auStack_28.func_ov000_02059270(0x23, "drop3", 0x2D200000);
                auStack_28.func_ov000_02059270(0x24, "drop4", 0x2D200000);
                auStack_28.func_ov000_02059270(0x25, "drop5", 0x2D200000);

                CourseListEntry *ptr = data_027e09a0->GetCourseEntry(iVar5);
                if ((ptr->unk_1D - 1) == 1) {
                    auStack_28.func_ov000_02059270(0x38, "mic_0", 0x35B00000);
                    auStack_28.func_ov000_02059270(0x39, "mic_1", 0x35B00000);
                    auStack_28.func_ov000_02059270(0x3A, "mic_on", 0x35B00000);
                    auStack_28.func_ov000_02059270(0x3B, "mic_ok", 0x35B00000);
                    auStack_28.func_ov000_02059270(0x3C, "mic_ng", 0x35B00000);
                }

                if (pMgr->IsBossDeago()) {
                    auStack_28.func_ov000_02059270(0x09, "bdga_chain", 0x28000000);
                }

                if (pMgr->IsBossLast1()) {
                    auStack_28.func_ov000_02059270(0x0B, "kimrat", 0x28000000);
                }
            }

            if (pMgr->IsTower()) {
                UnkStruct_StackTitleScreen auStack_3c("Screen/tex2d.bin", 1);
                auStack_3c.func_ov000_02059270(0x34, "baloon", 0x28A00000);
                auStack_3c.func_ov000_02059270(0x35, "dot", 0x28000000);
            }

            if (pMgr->IsBossDeago()) {
                UnkStruct_StackTitleScreen auStack_50("Screen/tex2d.bin", 1);
                auStack_50.func_ov000_02059270(0x30, "deagohit", 0x29200000);
                auStack_50.func_ov000_02059270(0x31, "deagotarget", 0x29200000);
            }

            if (pMgr->IsBossLast2()) {
                UnkStruct_StackTitleScreen auStack_64("Screen/tex2d.bin", 1);
                auStack_64.func_ov000_02059270(0x32, "zeldahit", 0x29200000);
                char *str = "zeldatarget";
                auStack_64.func_ov000_02059270(0x33, str, 0x2D200000);
            }
        } else if (data_027e09a4->IsTrain()) {
            UnkStruct_StackTitleScreen auStack_78("Npc/Tex.bin", 1);
            auStack_78.func_ov000_02059270(0x12, "rupy0", 0x2D200000);
            auStack_78.func_ov000_02059288(0x13, "rupy1", 0x12);
            auStack_78.func_ov000_02059288(0x14, "rupy2", 0x12);

            auStack_78.func_ov000_02059270(0x11, "life1", 0x2D200000);
        }
    }

    if (data_027e09a4->UnkCheck(data_027e09a4->CurrentSceneIndex())) {
        func_ov071_0215e8d4();
    }

    // 1 for battle mode
    if (data_027e09a4->mUnk_60 == 1) {
        func_ov021_020f8818();
    }
}

void ActorManager::func_ov001_020bb414() {
    data_0204999c.func_ov001_020ba59c();

    if (data_027e09a4->UnkCheck(data_027e09a4->CurrentSceneIndex())) {
        this->func_ov001_020bb844();
    }

    if (this->mActorTable != 0) {
        delete this->mActorTable;
    }

    this->mActorTable    = NULL;
    this->mActorTableEnd = NULL;
}

void ActorManager::func_ov001_020bb488() {
    if (data_027e09a4->IsNotCutscene()) {
        switch (data_027e09a4->GetCurrentCourseEntry()->unk_10) {
            case 0x00:
            case 0x01:
            case 0x03:
            case 0x06:
                if (data_027e0cd8->GetUnk0C()->mUnk_128 & 0x800) {
                    func_ov031_020ea100();
                }
                break;
            default:
                break;
        }
    }

    if (data_027e09a4->mUnk_60 == 1) {
        data_027e0cf4->func_ov021_020f8cdc();
    }

    if (data_027e09a4->UnkCheck(data_027e09a4->CurrentSceneIndex())) {
        data_027e0d70->func_ov071_0215e8f8();
    }
}

void ActorManager::func_ov001_020bb548() {
    Actor **ppActorTable = this->mActorTable;
    int i                = 0;

    while (ppActorTable < this->mUnk_08) {
        Actor *pActor = *ppActorTable;

        if (pActor != NULL && pActor->vfunc_34() != 0) {
            this->func_ov000_02096e44(i);
        }

        ppActorTable++;
        i++;
    }

    unk32 value = data_027e09a4->CurrentSceneIndex();
    if (data_027e09a4->UnkCheck(value)) {
        data_027e0d70->func_ov071_0215e9ac();

        if (data_027e09a4->UnkCheck(value)) {
            data_027e0d70->func_ov071_0215eac8();
        }
    }

    if (data_027e09a4->mUnk_60 == 1) {
        ActorManager::func_ov001_020bb824();
    }
}

void ActorManager::func_ov001_020bb630() {
    ActorProfile **piVar2 = data_ov000_020b539c_eur.func_ov000_02073dc();
    ActorProfile **piVar3 = data_ov000_020b539c_eur.func_ov000_02073e8();

    while (piVar2 != piVar3) {
        ActorProfile *iVar4 = *piVar2;

        if (iVar4 != NULL) {
            bool run_vfunc_08 = false;

            if (iVar4->mUnk_38 != 0) {
                run_vfunc_08 = true;
            } else {
                if (iVar4->mActorId == ActorId_BSFC && data_027e09a4->GetCurrentCourseEntry()->unk_1B & 0x10) {
                    run_vfunc_08 = true;
                }
            }

            if (run_vfunc_08) {
                (*piVar2)->vfunc_08();
            }
        }

        piVar2++;
        piVar3 = data_ov000_020b539c_eur.func_ov000_02073e8();
    }

    if (data_027e09a4->mUnk_60 == 1) {
        data_027e0cf4->func_ov021_020f8d20();
    }
}

void ActorManager::func_ov001_020bb6b0(EntranceInfo *param1) {
    ActorProfile **piVar1 = data_ov000_020b539c_eur.func_ov000_02073dc();
    ActorProfile **piVar2 = data_ov000_020b539c_eur.func_ov000_02073e8();

    while (piVar1 != piVar2) {
        ActorProfile *iVar5 = *piVar1;

        if (iVar5 != NULL && iVar5->mUnk_38 == 0) {
            for (int i = 0; i < ARRAY_LEN(data_ov000_020ab1ac); i++) {
                UnkStruct_ov000_020ab1ac *pEntry = &data_ov000_020ab1ac[i];

                if (pEntry->mUnk_00 == iVar5->mActorId && pEntry->mUnk_04 == param1->sceneIndex &&
                    pEntry->mUnk_08 == param1->roomIndex) {
                    (*piVar1)->mUnk_39 = 1;
                    (*piVar1)->vfunc_08();
                }
            }
        }

        piVar1++;
        piVar2 = data_ov000_020b539c_eur.func_ov000_02073e8();
    }
}

bool ActorManager::func_ov001_020bb728(ActorId actorId) {
    EntranceInfo *piVar1 = data_027e09a4->func_ov000_02070560();

    if (piVar1->sceneIndex != SceneIndex_f_water || piVar1->roomIndex != 0) {
        return false;
    }

    for (u32 i = 0; i < 5; i++) {
        int value;
        int value2;

        if (actorId == data_ov001_020c2638[i]) {
            value  = data_ov001_020c2624[i] & 0x1F;
            value2 = data_027e09b8->mAdventureFlags[(u32) data_ov001_020c2624[i] >> 5];

            if ((1 << value) & value2) {
                value  = data_ov001_020c262e[i] & 0x1F;
                value2 = data_027e09b8->mAdventureFlags[(u32) data_ov001_020c262e[i] >> 5];

                if (!((1 << value) & value2)) {
                    continue;
                }
            }

            return true;
        }
    }

    return false;
}

void ActorManager::func_ov001_020bb7b0(ZeldaObjectList *pObjList) {
    for (s32 i = 0; i < pObjList->header.nEntries; i++) {
        u32 id                      = pObjList->aIdList[i];
        ActorProfile *pActorProfile = data_ov000_020b539c_eur.GetProfileFromId(id);

        if (!this->func_ov001_020bb728(id) && pActorProfile != NULL) {
            pActorProfile->vfunc_08();
        }
    }
}

void ActorManager::func_ov001_020bb7f0() {
    ActorProfile **ptr1 = data_ov000_020b539c_eur.func_ov000_02073dc();
    ActorProfile **ptr2 = data_ov000_020b539c_eur.func_ov000_02073e8();

    while (ptr1 != ptr2) {
        if (*ptr1 != NULL) {
            func_ov000_020977e4();
        }

        ptr1++;
        ptr2 = data_ov000_020b539c_eur.func_ov000_02073e8();
    }
}

void ActorManager::func_ov001_020bb824() {
    if (data_027e0cf4 != NULL) {
        delete data_027e0cf4;
    }
}

void ActorManager::func_ov001_020bb844() {
    if (data_027e0d70 != NULL) {
        delete data_027e0d70;
    }
}

DECL_INSTANCE(ActorManager, gpActorManager);
