/**
 * @file enemy64.c
 * @ingroup Enemies
 *
 * @brief Enemy64 enemy
 */

#define NENT_DEPRECATED
#include "global.h"
#include "enemy.h"
#include "functions.h"
#include "fade.h"

typedef struct {
    /*0x00*/ Entity base;
    /*0x68*/ u8 unk_68[0x6];
    /*0x6e*/ u8 unk_6e;
    /*0x6f*/ u8 unk_6f[0x5];
    /*0x74*/ s16 unk_74;
    /*0x76*/ u16 unk_76;
    /*0x78*/ u16 unk_78;
    /*0x7a*/ u16 unk_7a;
    /*0x7c*/ u8 unk_7c;
    /*0x7d*/ u8 unk_7d;
    /*0x7e*/ u16 unk_7e;
    /*0x80*/ u16 unk_80;
    /*0x82*/ u16 unk_82;
} Enemy64Entity;

extern void sub_08047EA4(Entity*, u32); // gyorgMale

extern void (*const Enemy64_Actions[])(Enemy64Entity*);
extern void (*const Enemy64_Action1_SubActions[])(Enemy64Entity*);
extern void (*const Enemy64_Action2_SubActions[])(Enemy64Entity*);
extern void (*const Enemy64_Action3_SubActions[])(Enemy64Entity*);
extern const u8 gUnk_080D29B8[];
extern void (*const Enemy64_Action4_SubActions[])(Enemy64Entity*);

void sub_080499F0(Enemy64Entity*);
void sub_08049998(Enemy64Entity*, u32);
void Enemy64_Action2_SubAction1(Enemy64Entity*);
void sub_08049944(Enemy64Entity*);
bool32 sub_08049A8C(Enemy64Entity*);
void sub_08049AB0(Enemy64Entity*);
void sub_08049B20(Enemy64Entity*);

void Enemy64(Enemy64Entity* this) {
    if (super->action != 0) {
        super->spriteSettings.draw = this->unk_7d;
    }
    if (super->health == 0) {
        super->health = 0xff;
        super->action = 4;
        super->subAction = 0;
    }
    Enemy64_Actions[super->action](this);
    sub_0805EC9C(super, 0xaa, 0xaa, this->unk_78);
    super->animationState = -(this->unk_78 >> 8);
    sub_080499F0(this);
    this->unk_7d = super->spriteSettings.draw;
    if (((super->spriteSettings.draw == 1) && (0x100 < (super->y.HALF.HI - gRoomControls.scroll_y) + 0x30U))) {
        super->spriteSettings.draw = 0;
    }
}

void Enemy64_Init(Enemy64Entity* this) {
    Entity* tail;

    if (CheckFlags(0x7c)) {
        SetTile(0x4081, 10, 2);
        SetTile(0x4081, 0x4a, 2);
        SetTile(0x4081, 0x8a, 2);
        SetTile(0x4081, 0xca, 2);
        DeleteThisEntity();
    } else {
        sub_0807B7D8(0x323, 10, 1);
        sub_0807B7D8(0x323, 0x4a, 1);
        sub_0807B7D8(0x323, 0x8a, 1);
        sub_0807B7D8(0x323, 0xca, 1);
    }
    tail = CreateProjectile(GYORG_TAIL);
    if (tail != NULL) {
        tail->type = 1;
        tail->parent = super;
        super->child = tail;
        super->action = 1;
        super->subAction = 0;
        super->actionDelay = 0x3c;
        super->direction = 0xc0;
        super->animationState = 0xc0;
        super->speed = 0x300;
        super->spriteRendering.b0 = 3;
        super->spriteSettings.draw = 1;
        super->spriteOrientation.flipY = 2;
        super->spriteRendering.b3 = 2;
        super->collisionLayer = 2;
        this->unk_7c = 0;
        this->unk_78 = 0x4000;
        this->unk_7a = 0x4000;
        InitAnimationForceUpdate(super, 0);
    }
}

void Enemy64_Action1(Enemy64Entity* this) {
    Enemy64_Action1_SubActions[super->subAction](this);
    UpdateAnimationSingleFrame(super);
}

void Enemy64_Action1_SubAction0(Enemy64Entity* this) {
    if (super->actionDelay != 0) {
        super->actionDelay--;
    } else {
        sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
        if ((gRoomControls.origin_x - 0x100) > super->x.HALF.HI) {
            super->subAction = 1;
            super->direction = 0x80;
            super->animationState = 0x80;
            super->speed = 0xc0;
            super->x.HALF.HI = gRoomControls.origin_x + 0xa8;
            super->y.HALF.HI = gRoomControls.origin_y - 0x40;
            this->unk_78 = 0x8000;
            this->unk_7a = 0x8000;
        }
    }
}

void Enemy64_Action1_SubAction1(Enemy64Entity* this) {
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
    if ((gRoomControls.origin_y + 0x40) < super->y.HALF.HI) {
        super->action = 2;
        super->subAction = 0;
    }
}

void Enemy64_Action2(Enemy64Entity* this) {
    Enemy64_Action2_SubActions[super->subAction](this);
    UpdateAnimationSingleFrame(super);
}

void Enemy64_Action2_SubAction0(Enemy64Entity* this) {
    u16 uVar1;
    u16* puVar2;

    super->subAction = 1;
    super->speed = 0x120;
    this->unk_6e = 0;
    if ((s32)(super->x.HALF.HI - (u32)gRoomControls.origin_x) < 0xa8) {
        this->unk_80 = 0xd8;
        this->unk_74 = 0x100;
    } else {
        this->unk_80 = 0x78;
        this->unk_74 = 0xff00;
    }
    this->unk_80 += gRoomControls.origin_x;
    if ((s32)(super->y.HALF.HI - (u32)gRoomControls.origin_y) < 0x80) {
        this->unk_82 = 0xb0;
    } else {
        this->unk_82 = 0x50;
        this->unk_74 *= -1;
    }
    this->unk_82 += gRoomControls.origin_y;
    Enemy64_Action2_SubAction1(this);
}

void Enemy64_Action2_SubAction1(Enemy64Entity* this) {
    u32 tmp = sub_080045DA(this->unk_80 - super->x.HALF.HI, this->unk_82 - super->y.HALF.HI);
    if (4 < (((super->direction - tmp) + 2) & 0xff)) {
        if (((tmp - super->direction) & 0x80) != 0) {
            super->direction--;
        } else {
            super->direction++;
        }
    }
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
    if (EntityWithinDistance(super, this->unk_80, this->unk_82, (super->speed >> 0x10) + 0x10)) {
        super->subAction = 2;
        super->speed = 0xc0;
        this->unk_7e = 0x100;
        this->unk_76 = (u16)super->direction << 8;
    }
}

void Enemy64_Action2_SubAction2(Enemy64Entity* this) {
    u32 iVar1;
    u32 uVar2;
    u32 uVar3;

    iVar1 = this->unk_76 += this->unk_74;
    uVar2 = iVar1 * 0x10000;
    super->direction = (u8)(uVar2 >> 0x18);
    if (((uVar2 >> 0x18 & 0x1f) == 0) &&
        (uVar3 = CalculateDirectionTo(super->x.HALF.HI, super->y.HALF.HI, gRoomControls.origin_x + 0xa8,
                                      gRoomControls.origin_y + 0x80),
         ((((uVar3 + 2) & 0x1c) - (super->direction >> 3)) & 0xf) == 8)) {
        super->subAction = 4;
        super->actionDelay = 0x3c;
        super->field_0xf = 7;
    }
    sub_08049944(this);
    sub_08047EA4(super, ((0x100 - super->direction) & 0xff) << 8);
}

void Enemy64_Action2_SubAction3(Enemy64Entity* this) {
    s32 iVar1;
    u32 uVar2;

    iVar1 = this->unk_76 += this->unk_74;
    uVar2 = iVar1 * 0x10000;
    super->direction = (uVar2 >> 0x18);
    if (super->actionDelay == 0) {
        if ((uVar2 >> 0x18 & 0x1f) == 0) {
            super->subAction = 4;
            super->actionDelay = 0x5a;
        }
    } else {
        if (sub_08049A8C(this)) {
            Entity* projectile = CreateProjectile(GYORG_MALE_ENERGY_PROJECTILE);
            if (projectile != NULL) {
                projectile->collisionLayer = 2;
                projectile->parent = super;
                UpdateSpriteForCollisionLayer(projectile);
            }
        }
    }
    sub_08049944(this);
    sub_08047EA4(super, ((0x100 - super->direction) & 0xff) << 8);
}

void Enemy64_Action2_SubAction4(Enemy64Entity* this) {
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
    if (--super->actionDelay == 0) {
        if (--super->field_0xf == 0) {
            super->subAction = 5;
            super->speed += 0x40;
        } else {
            super->subAction = 3;
            super->actionDelay = 0xa;
        }
    } else if (super->actionDelay == 0x50) {
        Entity* enemy = CreateEnemy(GYORG_MALE_EYE, Random() & 3);
        if (enemy != NULL) {
            enemy->parent = super;
        }
    }
}

void Enemy64_Action2_SubAction5(Enemy64Entity* this) {
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
    if (EntityWithinDistance(super, gRoomControls.origin_x + 0xa8, gRoomControls.origin_y + 0x80, 0x70) == FALSE) {
        super->action = 3;
        super->subAction = 0;
    }
}

void Enemy64_Action3(Enemy64Entity* this) {
    Enemy64_Action3_SubActions[super->subAction](this);
    UpdateAnimationSingleFrame(super);
}

void Enemy64_Action3_SubAction0(Enemy64Entity* this) {
    u32 tmp = sub_080045DA(gRoomControls.origin_x + 0xa8 - super->x.HALF.HI,
                           gRoomControls.origin_y + 0x80 - super->y.HALF.HI);
    if (tmp != super->direction) {
        if (((tmp - super->direction) & 0x80) != 0) {
            super->direction--;
        } else {
            super->direction++;
        }
    } else {
        super->subAction = 1;
        super->speed = 0x180;
    }
    sub_08049944(this);
    sub_08047EA4(super, ((0x100 - super->direction) & 0xff) << 8);
}

void Enemy64_Action3_SubAction1(Enemy64Entity* this) {
    if (EntityWithinDistance(super, gRoomControls.origin_x + 0xa8, gRoomControls.origin_y + 0x80, 0x28)) {
        super->subAction = 2;
        super->actionDelay = 0x2d;
        this->unk_74 = gUnk_080D29B8[Random() & 7] << 0x18 >> 0x18;
        this->unk_76 = super->direction << 8;
    }
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
}

void Enemy64_Action3_SubAction2(Enemy64Entity* this) {
    this->unk_76 += this->unk_74;
    super->direction = this->unk_76 >> 8;
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
    if (--super->actionDelay == 0) {
        super->subAction = 3;
        super->actionDelay = 0x1e;
    }
}

void Enemy64_Action3_SubAction3(Enemy64Entity* this) {
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
    if (--super->actionDelay == 0) {
        super->action = 2;
        super->subAction = 0;
    }
}

void Enemy64_Action4(Enemy64Entity* this) {
    Enemy64_Action4_SubActions[super->subAction](this);
    UpdateAnimationSingleFrame(super);
}

void Enemy64_Action4_SubAction0(Enemy64Entity* this) {
    SoundReq(SFX_BOSS_DIE);
    if (sub_08079F8C() && gPlayerEntity.z.HALF.HI == 0) {
        SetPlayerControl(CONTROL_2);
        super->subAction = 2;
        super->actionDelay = 0x1e;
    } else {
        super->subAction = 1;
    }
    sub_08049944(this);
}

void Enemy64_Action4_SubAction1(Enemy64Entity* this) {
    if (sub_08079F8C() && gPlayerEntity.z.HALF.HI == 0) {
        SetPlayerControl(CONTROL_2);
        super->subAction = 2;
        super->actionDelay = 1;
    }
    sub_08049944(this);
}

void Enemy64_Action4_SubAction2(Enemy64Entity* this) {
    if (--super->actionDelay == 0) {
        super->subAction = 3;
        super->actionDelay = 0xb4;
        super->speed = 0x100;
        super->direction -= 0x40;
    }
    sub_08049944(this);
}

void Enemy64_Action4_SubAction3(Enemy64Entity* this) {
    sub_08049AB0(this);
    if (--super->actionDelay == 0) {
        super->actionDelay = 0x1e;
        super->subAction = 4;
        super->speed = 0x400;
        super->direction = super->animationState;
        SoundReq(SFX_BOSS_DIE);
    } else {
        super->direction ^= 0x80;
        sub_08049B20(this);
    }
    sub_08049944(this);
}

void Enemy64_Action4_SubAction4(Enemy64Entity* this) {
    sub_08049AB0(this);
    sub_08049998(this, ((0x100 - super->direction) & 0xff) << 8);
    if (--super->actionDelay == 0) {
        super->subAction = 5;
        SetFade(5, 0x10);
    }
}

void Enemy64_Action4_SubAction5(Enemy64Entity* this) {
    Entity* entity;
    sub_08049944(this);
    if (gFadeControl.active == 0) {
        super->subAction = 6;
        super->spriteSettings.draw = 0;
        entity = super->child;
        entity->flags &= 0x7f;
        entity->spriteSettings.draw = 0;
        entity = entity->child;
        entity->flags &= 0x7f;
        entity->spriteSettings.draw = 0;
        entity = entity->child;
        entity->flags &= 0x7f;
        entity->spriteSettings.draw = 0;
        entity = &gPlayerEntity;
        entity->x.HALF.HI = gRoomControls.origin_x + 0xa8;
        entity->y.HALF.HI = gRoomControls.origin_y + 0x80;
        entity->animationState = 0;
        sub_080809D4();
        SetFade(4, 4);
    }
}

void Enemy64_Action4_SubAction6(Enemy64Entity* this) {
    if (gFadeControl.active == 0) {
        super->subAction = 7;
        super->actionDelay = 0xa0;
        SoundReq(SFX_SECRET);
        SetFlag(0x7c);
        SetPlayerControl(CONTROL_1);
    }
}

void Enemy64_Action4_SubAction7(Enemy64Entity* this) {
    if (--super->actionDelay == 0) {
        DeleteThisEntity();
    } else if (super->actionDelay == 0x10) {
        sub_0807B7D8(0x36, 0xca, 1);
        SetTile(0x4081, 0xca, 2);
        SoundReq(SFX_HEART_GET);
    } else if (super->actionDelay == 0x18) {
        sub_0807B7D8(0x36, 0x8a, 1);
        SetTile(0x4081, 0x8a, 2);
        SoundReq(SFX_HEART_GET);
    } else if (super->actionDelay == 0x20) {
        sub_0807B7D8(0x36, 0x4a, 1);
        SetTile(0x4081, 0x4a, 2);
        SoundReq(SFX_HEART_GET);
    } else if (super->actionDelay == 0x28) {
        sub_0807B7D8(0x36, 10, 1);
        SetTile(0x4081, 10, 2);
        SoundReq(SFX_HEART_GET);
    }
}

void sub_08049944(Enemy64Entity* this) {
    this->unk_7c = (this->unk_7c << 1 & 0x7f) | (this->unk_7c & 0x80);
    if (EntityWithinDistance(super, gPlayerEntity.x.HALF.HI, gPlayerEntity.y.HALF.HI, 0x20)) {
        gPlayerState.field_0x14 = 1;
        if (gPlayerEntity.z.HALF.HI == 0) {
            gPlayerState.flags |= 2;
            this->unk_7c |= 1;
        }
    }
}

void sub_08049998(Enemy64Entity* this, u32 param_2) {
    s32 tmpX;
    s32 tmpY;

    sub_08049944(this);
    sub_08047EA4(super, param_2);
    tmpX = super->x.HALF.HI;
    tmpY = super->y.HALF.HI;
    LinearMoveAngle(super, super->speed, super->direction);
    if (((this->unk_7c & 1) != 0) && sub_08079F8C()) {
        gPlayerEntity.x.HALF.HI = (super->x.HALF.HI - tmpX) + gPlayerEntity.x.HALF.HI;
        gPlayerEntity.y.HALF.HI = (super->y.HALF.HI - tmpY) + gPlayerEntity.y.HALF.HI;
    }
}

void sub_080499F0(Enemy64Entity* this) {
    u32 tmp;

    if ((((super->action != 0) && (gPlayerEntity.z.HALF.HI == 0)) && (sub_08079F8C())) && ((this->unk_7c & 1) == 0)) {
        if (EntityWithinDistance(&gPlayerEntity, super->x.HALF.HI, super->y.HALF.HI, 0x24) &&
            ((this->unk_7c & 2) == 0)) {
            tmp = sub_080045DA((s32)gPlayerEntity.x.HALF.HI - super->x.HALF.HI,
                               (s32)gPlayerEntity.y.HALF.HI - super->y.HALF.HI);
            gPlayerEntity.x.WORD = super->x.WORD + gSineTable[tmp] * 0x2400;
            gPlayerEntity.y.WORD = super->y.WORD + gSineTable[tmp + 0x40] * -0x2400;
        }
        this->unk_7c = 0;
    }
}

bool32 sub_08049A8C(Enemy64Entity* this) {
    if (--super->actionDelay == 0 && (Random() & 0xf) != 0) {
        return TRUE;
    } else {
        return FALSE;
    }
}

void sub_08049AB0(Enemy64Entity* this) {
    u32 rand;

    if ((++super->field_0xf & 0xf) == 0) {
        Entity* effect = CreateFx(super, FX_GIANT_EXPLOSION3, 0);
        if (effect != NULL) {
            rand = Random();
            effect->x.HALF.HI += ((rand & 0x30) - 0x18);
            effect->y.HALF.HI += (((rand >> 8) & 0x30) - 0x18);
            effect->spritePriority.b0 = 3;
            effect->collisionLayer = 2;
            UpdateSpriteForCollisionLayer(effect);
        }
    }
}

void sub_08049B20(Enemy64Entity* this) {
    s32 tmpX;
    s32 tmpY;

    sub_08049944(this);
    tmpX = super->x.HALF.HI;
    tmpY = super->y.HALF.HI;
    LinearMoveAngle(super, super->speed, super->direction);
    if (((this->unk_7c & 1) != 0) && sub_08079F8C()) {
        gPlayerEntity.x.HALF.HI = (super->x.HALF.HI - tmpX) + gPlayerEntity.x.HALF.HI;
        gPlayerEntity.y.HALF.HI = (super->y.HALF.HI - tmpY) + gPlayerEntity.y.HALF.HI;
    }
}

void (*const Enemy64_Actions[])(Enemy64Entity*) = {
    Enemy64_Init, Enemy64_Action1, Enemy64_Action2, Enemy64_Action3, Enemy64_Action4,
};
void (*const Enemy64_Action1_SubActions[])(Enemy64Entity*) = {
    Enemy64_Action1_SubAction0,
    Enemy64_Action1_SubAction1,
};
void (*const Enemy64_Action2_SubActions[])(Enemy64Entity*) = {
    Enemy64_Action2_SubAction0, Enemy64_Action2_SubAction1, Enemy64_Action2_SubAction2,
    Enemy64_Action2_SubAction3, Enemy64_Action2_SubAction4, Enemy64_Action2_SubAction5,
};
void (*const Enemy64_Action3_SubActions[])(Enemy64Entity*) = {
    Enemy64_Action3_SubAction0,
    Enemy64_Action3_SubAction1,
    Enemy64_Action3_SubAction2,
    Enemy64_Action3_SubAction3,
};
const u8 gUnk_080D29B8[] = {
    144, 112, 56, 200, 0, 0, 0, 0,
};
void (*const Enemy64_Action4_SubActions[])(Enemy64Entity*) = {
    Enemy64_Action4_SubAction0, Enemy64_Action4_SubAction1, Enemy64_Action4_SubAction2, Enemy64_Action4_SubAction3,
    Enemy64_Action4_SubAction4, Enemy64_Action4_SubAction5, Enemy64_Action4_SubAction6, Enemy64_Action4_SubAction7,
};
