#define NENT_DEPRECATED
#include "global.h"
#include "entity.h"
#include "object.h"
#include "area.h"
#include "room.h"
#include "screen.h"
#include "functions.h"
#include "enemy/gyorg.h"

extern const ScreenTransitionData gUnk_0813ABD0;

extern u32 sub_08079F8C(void);
struct GyorgChildSpawns {
    s16 offsetX;
    s16 offsetY;
    s16 unk2;
} FORCE_WORD_ALIGNED;
extern struct GyorgChildSpawns* const gUnk_08124FF0[];

extern void sub_080A1DCC(GyorgBossObjectEntity*);
extern void sub_080A1C9C(GyorgBossObjectEntity*);
void sub_080A1E54(GyorgBossObjectEntity*);

void sub_080A1D8C(GyorgBossObjectEntity*, s32);

void sub_080A1FF0(GyorgBossObjectEntity*);
u32 sub_080A20B8(GyorgBossObjectEntity*, GyorgMaleEntity*);
void sub_080A1D70(GyorgBossObjectEntity*, u32);

void GyorgBossObject_SetupStart(GyorgBossObjectEntity* this);
void GyorgBossObject_Setup(GyorgBossObjectEntity* this);
void GyorgBossObject_FemalePhase1(GyorgBossObjectEntity* this);
void GyorgBossObject_MalePhase1(GyorgBossObjectEntity* this);
void GyorgBossObject_FemalePhase2(GyorgBossObjectEntity* this);
void GyorgBossObject_MalePhase2(GyorgBossObjectEntity* this);
void GyorgBossObject_FemalePhase3(GyorgBossObjectEntity* this);
void GyorgBossObject_MalePhase3(GyorgBossObjectEntity* this);
void GyorgBossObject_FemalePhase4(GyorgBossObjectEntity* this);
void GyorgBossObject_FightEnd(GyorgBossObjectEntity* this);

void GyorgBossObject(Entity* this) {
    static void (*const gUnk_08124ED0[])(GyorgBossObjectEntity*) = {
        GyorgBossObject_SetupStart,   GyorgBossObject_Setup,        GyorgBossObject_FemalePhase1,
        GyorgBossObject_MalePhase1,   GyorgBossObject_FemalePhase2, GyorgBossObject_MalePhase2,
        GyorgBossObject_FemalePhase3, GyorgBossObject_MalePhase3,   GyorgBossObject_FemalePhase4,
        GyorgBossObject_FightEnd,
    };
    gUnk_08124ED0[this->action]((GyorgBossObjectEntity*)this);
    sub_080A1DCC((GyorgBossObjectEntity*)this);
    sub_080A1C9C((GyorgBossObjectEntity*)this);
}

void GyorgBossObject_SetupStart(GyorgBossObjectEntity* this) {
    GyorgHeap* heap;
    Entity* tmp;
    if (CheckFlags(0x7B)) {
        DeleteThisEntity();
    }
    if (gEntCount > 0x45)
        return;
    heap = zMalloc(sizeof(GyorgHeap));
    if (!heap)
        return;
    super->action = 1;
    super->myHeap = heap;
    this->timer = 0x258;
    this->unk_6c = 0;
    heap->boss = this;
    tmp = CreateEnemy(GYORG_FEMALE, 0);
    tmp->myHeap = heap;
    heap->female = (GyorgFemaleEntity*)tmp;
    tmp = CreateEnemy(GYORG_MALE, 0);
    tmp->x.HALF.HI = gRoomControls.origin_x + 0x200;
#ifdef EU
    tmp->y.HALF.HI = gRoomControls.origin_y + 0x380;
#else
    tmp->y.HALF.HI = gRoomControls.origin_y + 0x330;
#endif
    tmp->myHeap = heap;
    heap->male1 = (GyorgMaleEntity*)tmp;
    tmp = CreateEnemy(GYORG_MALE, 1);
    tmp->x.HALF.HI = gRoomControls.origin_x + 0x260;
#ifdef EU
    tmp->y.HALF.HI = gRoomControls.origin_y + 0x360;
#else
    tmp->y.HALF.HI = gRoomControls.origin_y + 0x310;
#endif
    tmp->myHeap = heap;
    heap->male2 = (GyorgMaleEntity*)tmp;
    gScreen.bg3.control = 0x1E07;
    gScreen.lcd.displayControl |= 0x800;
    this->unk_70 = 0;
    this->unk_72 = 0;
    this->unk_74 = 0;
    this->unk_7a = 0;
    this->unk_76 = 0xc0;
    this->unk_78 = 0xc0;
    gRoomTransition.field_0x39 = 1;
    gPlayerState.flags |= PL_FLAGS20000;
    gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
    gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
#ifndef EU
    SoundReq(SONG_STOP_BGM);
    gArea.bgm = gArea.queued_bgm;
#endif
}

void GyorgBossObject_Setup(GyorgBossObjectEntity* this) {
    if (--this->timer == 0) {
        // start female phase 1
        super->action = 2;
        this->unk_6c = 8;
        ((GyorgHeap*)super->myHeap)->female->base.health = 8;
    }
    gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
    gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
}

void GyorgBossObject_FemalePhase1(GyorgBossObjectEntity* this) {
    if (((GyorgHeap*)super->myHeap)->female->base.health == 0) {
        // start male phase 1
        super->action = 3;
        super->actionDelay = 0x23;
        this->unk_6c = 1;
        this->unk_78 = 0x400;
        this->unk_7b = 1;
        ((GyorgHeap*)super->myHeap)->male1->base.health = 12;
        SoundReq(SFX_BOSS_DIE);
        InitScreenShake(0x96, 1);
    }
    gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
    gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
}

void GyorgBossObject_MalePhase1(GyorgBossObjectEntity* this) {
    sub_080A1FF0(this);
    if (((GyorgHeap*)super->myHeap)->male1->base.health == 0) {
        if (sub_080A20B8(this, ((GyorgHeap*)super->myHeap)->male1)) {
            // start female phase 2
            super->action = 4;
            this->unk_6c = 0x10;
            this->unk_78 = 0xC0;
            sub_080A1D70(this, ((GyorgHeap*)super->myHeap)->female->base.animationState);
            ((GyorgHeap*)super->myHeap)->female->base.health = 24;
        }
        gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
        gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
    } else {
        gPlayerState.startPosX = ((GyorgHeap*)super->myHeap)->male1->base.x.HALF.HI;
        gPlayerState.startPosY = ((GyorgHeap*)super->myHeap)->male1->base.y.HALF.HI;
    }
}

void GyorgBossObject_FemalePhase2(GyorgBossObjectEntity* this) {
    if (((GyorgHeap*)super->myHeap)->female->base.health == 0) {
        // start male phase 2
        ((GyorgHeap*)super->myHeap)->male1->base.health = 12;
        super->action = 5;
        super->actionDelay = 0x23;
        this->unk_6c = 2;
        this->unk_78 = 0x400;
        this->unk_7b = 1;
        SoundReq(SFX_BOSS_DIE);
        InitScreenShake(0x96, 1);
    }
    gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
    gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
}

void GyorgBossObject_MalePhase2(GyorgBossObjectEntity* this) {
    sub_080A1FF0(this);
    if (((GyorgHeap*)super->myHeap)->male2->base.health == 0) {
        if (sub_080A20B8(this, ((GyorgHeap*)super->myHeap)->male2)) {
            // start female phase 3
            super->action = 6;
            this->unk_6c = 0x20;
            this->unk_78 = 0xc0;
            sub_080A1D70(this, ((GyorgHeap*)super->myHeap)->female->base.animationState);
            ((GyorgHeap*)super->myHeap)->female->base.health = 24;
        }
        gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
        gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
    } else {
        gPlayerState.startPosX = ((GyorgHeap*)super->myHeap)->male2->base.x.HALF.HI;
        gPlayerState.startPosY = ((GyorgHeap*)super->myHeap)->male2->base.y.HALF.HI;
    }
}

void GyorgBossObject_FemalePhase3(GyorgBossObjectEntity* this) {
    if (((GyorgHeap*)super->myHeap)->female->base.health == 0) {
        // start male phase 3
        ((GyorgHeap*)super->myHeap)->male2->base.health = 12;
        super->action = 7;
        super->actionDelay = 0x23;
        this->unk_6c = 0x104;
        this->unk_78 = 0x400;
        SoundReq(SFX_BOSS_DIE);
        InitScreenShake(0x96, 1);
    }
    gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
    gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
}

void GyorgBossObject_MalePhase3(GyorgBossObjectEntity* this) {
    sub_080A1FF0(this);
    if (((GyorgHeap*)super->myHeap)->male2->base.health == 0) {
        if (sub_080A20B8(this, ((GyorgHeap*)super->myHeap)->male2)) {
            // start female phase 4
            super->action = 8;
            this->unk_6c = 0x40;
            this->unk_78 = 0xC0;
            ((GyorgHeap*)super->myHeap)->female->base.health = 12;
        }
    }
    gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
    gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
}

void GyorgBossObject_FemalePhase4(GyorgBossObjectEntity* this) {
    if (((GyorgHeap*)super->myHeap)->female->base.health == 0) {
        if (this->unk_6c != 0) {
            InitScreenShake(0x2d, 1);
            SoundReq(SFX_BOSS_DIE);
            this->unk_78 = 0x600;
        }
        this->unk_6c = 0;
        gRoomTransition.field_0x39 = 0;
        if (sub_08079F8C() && gPlayerEntity.z.HALF.HI == 0) {
            super->action = 9;
            super->actionDelay = 0;
            super->field_0xf = 0xF0;
            this->timer = 0x1a4;
            super->direction = 0;
            super->speed = 0x60;
            gPlayerState.flags &= ~PL_FLAGS20000;
            CopyPosition(&gPlayerEntity, super);
            gRoomControls.camera_target = super;
            SetPlayerControl(2);
        }
    }
    gPlayerState.startPosX = gRoomControls.origin_x + 0x200;
    gPlayerState.startPosY = gRoomControls.origin_y + 0x210;
}

void GyorgBossObject_FightEnd(GyorgBossObjectEntity* this) {
    if (this->timer == 0) {
        LinearMoveUpdate(super);
        sub_080A1E54(this);
        if (--super->field_0xf == 0) {
            SetFlag(0x7B);
            sub_0808091C(&gUnk_0813ABD0, 8);
            return;
        }
        if (super->field_0xf == 0x3C) {
            SetFade(7, 4);
        }
        return;
    }
    if (--this->timer < 0xb4) {
        sub_080A1E54(this);
        return;
    }
    switch (this->timer) {
        case 0xb4:
            SoundReq(SFX_BOSS_DIE);
            InitScreenShake(0x2d0, 2);
            break;
        case 0x12C:
            SoundReq(SFX_BOSS_DIE);
            InitScreenShake(0x4b, 1);
            break;
    }
}

void sub_080A1C9C(GyorgBossObjectEntity* this) {
    if (this->unk_7a) {
        if (this->unk_74 != this->unk_75) {
            this->unk_78 = 0;
            sub_080A1D8C(this, 0xC);
            if (this->unk_76 == 0) {
                this->unk_74 = this->unk_75;
                this->unk_78 = this->unk_7c;
            }
        } else {
            sub_080A1D8C(this, 0xC);
            if (this->unk_78 == this->unk_76) {
                this->unk_7a = 0;
            }
        }
    } else {
        sub_080A1D8C(this, 8);
    }
    this->unk_70 += gSineTable[this->unk_74] * this->unk_76 / 0x100;
    this->unk_72 -= gSineTable[this->unk_74 + 0x40] * this->unk_76 / 0x100;
    gScreen.bg3.xOffset = this->unk_70 >> 0x8;
    gScreen.bg3.yOffset = this->unk_72 >> 0x8;
}

void sub_080A1D70(GyorgBossObjectEntity* this, u32 unk1) {
    this->unk_75 = unk1;
    this->unk_7a = 1;
    this->unk_7c = this->unk_78;
}

void sub_080A1D8C(GyorgBossObjectEntity* this, s32 unk1) {
    if (this->unk_78 != this->unk_76) {
        if (this->unk_78 > this->unk_76) {
            if (unk1 < this->unk_78 - this->unk_76) {
                this->unk_76 += unk1;
            } else {
                this->unk_76 = this->unk_78;
            }
        } else {
            if (unk1 < this->unk_76 - this->unk_78) {
                this->unk_76 -= unk1;
            } else {
                this->unk_76 = this->unk_78;
            }
        }
    }
}

void sub_080A1DCC(GyorgBossObjectEntity* this) {
    GenericEntity* tmp;
    if ((tmp = (GenericEntity*)((GyorgHeap*)super->myHeap)->male1) != NULL ||
        (tmp = (GenericEntity*)((GyorgHeap*)super->myHeap)->male2) != NULL) {
        if (tmp->field_0x7c.BYTES.byte0 && tmp->base.spriteRendering.b3 == 2) {
            ((GyorgHeap*)super->myHeap)->mouth->base.flags &= ~0x80;
            tmp = ((GyorgHeap*)super->myHeap)->tail;
            tmp->base.flags &= ~0x80;
            tmp = (GenericEntity*)tmp->base.child;
            tmp->base.flags &= ~0x80;
            tmp = (GenericEntity*)tmp->base.child;
            tmp->base.flags &= ~0x80;
            tmp = (GenericEntity*)tmp->base.child;
            tmp->base.flags &= ~0x80;
            return;
        }
    }
    ((GyorgHeap*)super->myHeap)->mouth->base.flags |= 0x80;
    tmp = ((GyorgHeap*)super->myHeap)->tail;
    tmp->base.flags |= 0x80;
    tmp = (GenericEntity*)tmp->base.child;
    tmp->base.flags |= 0x80;
    tmp = (GenericEntity*)tmp->base.child;
    tmp->base.flags |= 0x80;
    tmp = (GenericEntity*)tmp->base.child;
    tmp->base.flags |= 0x80;
}

void sub_080A1E54(GyorgBossObjectEntity* this) {
    Entity* fx;
    if ((++super->actionDelay & 0x1F) == 0) {
        fx = CreateFx(super, FX_GIANT_EXPLOSION4, 0);
        if (fx) {
            u32 r = Random();
            fx->x.HALF.HI = gRoomControls.origin_x + 0x200 + (r & 0xf0) - 0x78;
            fx->y.HALF.HI = gRoomControls.origin_y + 0x210 + ((r >> 8) & 0x70) - 0x38;
            fx->spritePriority.b0 = 5;
            fx->collisionLayer = 2;
            UpdateSpriteForCollisionLayer(fx);
        }
    }
}

void GyorgBossObject_SpawnChildren(u32 unk0, bool32 fromBlue, u32 animationState) {
    static const u16 gUnk_08124EF8[] = { 0x200, 0x1C0, 0x250, 0x210, 0x200, 0x260, 0x1B0, 0x210 };
    static const s16 gUnk_08124F08[] = {
        -80, 48, 320, -40, 32, 320, 0, 16, 320, 40, 32, 320, 80, 48, 320, 1,
    };
    static const s16 gUnk_08124F28[] = {
        -80, 16, 448, -40, 48, 448, 0, 16, 448, 40, 48, 448, 80, 16, 448, 1,
    };
    static const s16 gUnk_08124F48[] = {
        -80, 16, 320, -40, 48, 576, 0, 16, 320, 40, 48, 576, 80, 16, 320, 1,
    };
    static const s16 gUnk_08124F68[] = {
        -80, 16, 320, -40, 32, 448, 0, 48, 576, 40, 32, 448, 80, 16, 320, 1,
    };
    static const s16 gUnk_08124F88[] = {
        -80, 32, 320, -40, 48, 320, 0, 32, 320, 40, 16, 320, 80, 32, 320, 1,
    };
    static const s16 gUnk_08124FA8[] = {
        -80, 32, 576, -40, 32, 448, 0, 32, 320, 40, 32, 448, 80, 32, 576, 1,
    };
    static const s16 gUnk_08124FC8[] = {
        -80, 32, 320, 0, 16, 320, 80, 32, 320, 1,
    };
    static const s16 gUnk_08124FDC[] = {
        -80, 16, 448, 0, 48, 448, 80, 16, 448, 1,
    };
    static const s16* const gUnk_08124FF0[] = {
        gUnk_08124F08, gUnk_08124F28, gUnk_08124F48, gUnk_08124F68,
        gUnk_08124F88, gUnk_08124FA8, gUnk_08124FC8, gUnk_08124FDC,
    };

    u32 i = 0;
    struct GyorgChildSpawns* p;
    u32 x, y;
    x = gUnk_08124EF8[animationState * 2] + gRoomControls.origin_x;
    y = gUnk_08124EF8[animationState * 2 + 1] + gRoomControls.origin_y;
    p = (struct GyorgChildSpawns*)gUnk_08124FF0[unk0];
    while (p->offsetX != 1) {
        GyorgChildEntity* tmp = (GyorgChildEntity*)CreateEnemy(GYORG_CHILD, fromBlue);
        if (tmp) {
            tmp->base.type2 = i++;
            tmp->base.x.HALF.HI = x;
            tmp->base.y.HALF.HI = y;
            tmp->base.direction = DirectionFromAnimationState(animationState);
            tmp->attackDirection = DirectionTurnAround(DirectionFromAnimationState(animationState));
            tmp->attackSpeed = p->unk2;
            switch (animationState) {
                case 0:
                    tmp->attackOffsetX = p->offsetX;
                    tmp->attackOffsetY = -p->offsetY;
                    break;
                case 1:
                    tmp->attackOffsetX = p->offsetY + 0xF0;
                    tmp->attackOffsetY = p->offsetX;
                    break;
                case 2:
                    tmp->attackOffsetX = p->offsetX;
                    tmp->attackOffsetY = p->offsetY + 0xA0;
                    break;
                case 3:
                    tmp->attackOffsetX = -p->offsetY;
                    tmp->attackOffsetY = p->offsetX;
                    break;
            }
        }
        p++;
    }
    if (fromBlue == FALSE) {
        Entity* tmp;
        tmp = CreateObject(SPECIAL_FX, 2, 0);
        if (tmp) {
            tmp->x.HALF.HI = x;
            tmp->y.HALF.HI = y;
            tmp->spriteOrientation.flipY = 3;
            tmp->spriteRendering.b3 = 3;
            tmp->collisionLayer = 1;
        }
    }
}

void sub_080A1FF0(GyorgBossObjectEntity* this) {
    if (super->actionDelay != 0) {
        super->actionDelay--;
        if (super->actionDelay <= 0x20 && (super->actionDelay & 0xF) == 0) {
            Entity* fx;
            fx = CreateFx(super, FX_GIANT_EXPLOSION4, 0);
            if (fx) {
                u32 r = Random();
                fx->x.HALF.HI = gRoomControls.origin_x + 0x200 + (r & 0x78) - 0x3C;
                fx->y.HALF.HI = gRoomControls.origin_y + 0x210 + (r & 0x78) - 0x3C;
                fx->spritePriority.b0 = 6;
                fx->collisionLayer = 1;
                UpdateSpriteForCollisionLayer(fx);
            }
        }
    }
    if (this->unk_7b) {
        if (EntityWithinDistance(&gPlayerEntity, gRoomControls.origin_x + 0x200, gRoomControls.origin_y + 0x210,
                                 0x100)) {
            if (super->actionDelay == 0) {
                super->actionDelay = 0x78;
            } else {
                if (super->actionDelay == 0x23) {
                    InitScreenShake(0x1e, 0);
                }
            }
        } else {
            this->unk_7b = 0;
        }
    }
}

u32 sub_080A20B8(GyorgBossObjectEntity* this, GyorgMaleEntity* other) {
    if (other == NULL) {
        return 1;
    }
    if (sub_08079F8C() && gPlayerEntity.z.HALF.HI == 0) {
        return other->unk_7c == 0 && gPlayerState.field_0x14 != 0;
    }
    return 0;
}
