#define NENT_DEPRECATED
#include "entity.h"
#include "definitions.h"
#include "functions.h"
#include "object.h"
#include "save.h"
#include "projectile.h"
#include "object/deathFx.h"

extern void sub_08049CF4(Entity*);

extern EnemyDefinition gEnemyDefinitions[];

const EnemyDefinition* GetEnemyDefinition(Entity* entity);
bool32 LoadEnemySprite(Entity* entity, const EnemyDefinition* definition);
extern const u16 gUnk_080D3E74[];

const EnemyDefinition* GetEnemyDefinition(Entity* entity) {
    const EnemyDefinition* definition = &gEnemyDefinitions[entity->id];
    if (definition->gfx == 0xffff) {
        definition = &definition->ptr.definition[entity->type];
    }
    return definition;
}

bool32 EnemyInit(GenericEntity* this) {
    if ((super->flags & ENT_DID_INIT) == 0) {
        const EnemyDefinition* definition = GetEnemyDefinition(super);
        if (LoadEnemySprite(super, definition) == FALSE) {
            return FALSE;
        }
        super->flags |= ENT_DID_INIT;
        if (definition->spriteFlags.collision != 0) {
            COLLISION_ON(super);
        }
        super->spriteIndex = definition->spriteIndex;
        if (super->spriteSettings.draw == 0) {
            super->spriteSettings.draw = definition->spriteFlags.draw;
        }
        super->spritePriority.b1 = definition->spriteFlags.spritePriority;
        super->spriteSettings.shadow = definition->spriteFlags.shadow;
        if (super->speed == 0) {
            super->speed = definition->speed;
        }
        super->flags2 = definition->flags2;
        super->hitType = definition->damageType;
        super->hitbox = (Hitbox*)definition->ptr.hitbox;
        super->health = definition->health;
        if (super->hurtType == 0) {
            super->hurtType = 0x41;
        }
        UpdateSpriteForCollisionLayer(super);
        if ((this->field_0x6c.HALF.HI & 0x20) != 0) {
            u32 uVar4 = gUnk_080D3E74[super->id >> 3] >> ((super->id & 7) << 1) & 3;
            if (uVar4 != 0) {
                Entity* object = CreateObject(OBJECT_A9, uVar4 - 1, 0);
                if (object != NULL) {
                    object->actionDelay = super->flags;
                    object->field_0xf = super->spriteSettings.draw;
                    object->spritePriority.b0 = 3;
                    object->parent = super;
                    CopyPosition(super, object);
                    super->flags &= ~ENT_COLLIDE;
                    super->spriteSettings.draw = 0;
                    this->field_0x6c.HALF.HI |= 0x10;
                }
            }
        }
    }
    return TRUE;
}

bool32 LoadEnemySprite(Entity* entity, const EnemyDefinition* definition) {
    bool32 result;
    if (definition->gfx != 0) {
        if ((definition->gfx & 0x8000) != 0) {
            // Common gfx: Reuse sprite that is already in vram? (bitfield 0x8000)
            entity->spriteVramOffset = definition->gfx & 0x3ff;
        } else {
            if ((definition->gfx & 0x4000) != 0) {
                // Swap gfx (bitfield 0x4000)
                result = LoadSwapGFX(entity, (u8)(definition->gfx >> 4), 0);
            } else {
                // Fixed gfx
                result = LoadFixedGFX(entity, definition->gfx);
            }
            if (result == FALSE) {
                return FALSE;
            }
        }
    }
    LoadObjPalette(entity, definition->paletteIndex);
    return TRUE;
}

ASM_FUNC("asm/non_matching/enemyUtils/sub_0804A720.inc", void sub_0804A720(Entity* this))

void CreateDeathFx(GenericEntity* parent, u32 parentId, u32 fixedItem);
void GenericDeath(Entity* this) {
    CreateDeathFx((GenericEntity*)this, this->id, 0);
}

void CreateDeathFx(GenericEntity* parent, u32 parentId, u32 fixedItem) {
    DeathFxObject* deathFx;
    DeathFxObject* deathFx2;
    u8 bVar3;

    if ((parent->field_0x6c.HALF.HI & 1) != 0) {
        if ((parent->field_0x6c.HALF.HI & 2) != 0) {
            return;
        }
        deathFx = (DeathFxObject*)CreateObject(DEATH_FX, parent->base.id, 0);
        if (deathFx == NULL) {
            return;
        }
        deathFx->unk6c = 1;
        PositionRelative(&(parent->base), &(deathFx->base), 0, 1);
        deathFx->base.parent = &(parent->base);
        parent->field_0x6c.HALF.HI |= 2;
        if ((parent->base.id == 0x37) && (gRoomTransition.field_0x39 != 0)) {
            DeleteThisEntity();
        }
        sub_0807CD9C();
        SoundReq(SONG_STOP_BGM);
        DeleteThisEntity();
        return;
    } else {
        int tmp = parent->base.field_0x3a & 2;
        if (tmp == 0) {
            sub_08049CF4(&(parent->base));
            gSave.unk50 += 1;
            parent->base.field_0x3a |= 2;
            parent->base.actionDelay = 0xff;
            SetDefaultPriority(&(parent->base), 3);
            deathFx2 = (DeathFxObject*)CreateObject(DEATH_FX, parent->base.id, 0);
            if (deathFx2 != NULL) {
                deathFx2->unk6c = tmp;
                deathFx2->parentId = parentId;
                deathFx2->item = fixedItem;
                deathFx2->base.parent = &(parent->base);
                deathFx2->base.child = &(parent->base);
                CopyPosition(&(parent->base), &(deathFx2->base));
            }
            if ((parent->field_0x6c.HALF.HI & 8) != 0) {
                deathFx2->unk6c |= 8;
                DeleteEntity(&(parent->base));
                return;
            }
            if ((parent->base.bitfield & 0x7f) == 0x13) {
                bVar3 = parent->base.field_0x1c & 0xf;
                if (bVar3 != 1) {
                    if ((bVar3 == 2) && (deathFx2 != NULL)) {
                        deathFx2->unk6c |= 2;
                    }
                } else {
                    if (deathFx2 != NULL) {
                        deathFx2->unk6c |= 4;
                    }
                }
                deathFx2->base.parent = NULL;
                DeleteThisEntity();
                return;
            }
        }

        if (parent->base.actionDelay == 0) {
            DeleteThisEntity();
        } else {
            if (--parent->base.actionDelay == 0) {
                parent->base.spriteSettings.draw = 0;
                SetDefaultPriority(&(parent->base), 0);
            } else {
                if (parent->base.actionDelay < 9) {
                    if (parent->base.spriteSettings.draw) {
                        parent->base.spriteSettings.draw = 0;
                    } else {
                        parent->base.spriteSettings.draw = 1;
                    }
                }
            }
        }
        return;
    }
}

Entity* CreateProjectileWithParent(Entity* parent, u32 projectileId, u32 projectileType) {
    Entity* projectile;

    projectile = CreateProjectile(projectileId);
    if (projectile != NULL) {
        projectile->type = projectileType;
        CopyPosition(parent, projectile);
    }
    return projectile;
}

void SetChildOffset(Entity* entity, s32 xOffset, s32 yOffset, s32 zOffset) {
    Entity* other;
    GenericEntity* this = (GenericEntity*)entity;

    other = *(Entity**)&this->field_0x68;
    if (other != NULL) {
        other->spriteRendering.b3 = super->spriteRendering.b3;
        other->spriteOrientation.flipY = super->spriteOrientation.flipY;
        other->x.HALF.HI = super->x.HALF.HI + xOffset;
        other->y.HALF.HI = super->y.HALF.HI + yOffset;
        other->z.HALF.HI = super->z.HALF.HI + zOffset;
        other->collisionLayer = super->collisionLayer;
    }
}

Entity* Create0x68FX(Entity* parent, u32 fxType) {
    Entity* fx;
    GenericEntity* this = (GenericEntity*)parent;

    if ((*(Entity**)&this->field_0x68 == NULL) && (fx = CreateFx(super, fxType, 0), fx != NULL)) {
        *(Entity**)&this->field_0x68 = fx;
    } else {
        fx = NULL;
    }
    return fx;
}

void sub_0804AA1C(Entity* entity) {
    GenericEntity* this = (GenericEntity*)entity;
    if (*(Entity**)&this->field_0x68 != NULL) {
        (*(Entity**)&this->field_0x68)->parent = NULL;
        *(Entity**)&this->field_0x68 = NULL;
    }
}
