/**
 * @file chuchu.c
 * @ingroup Enemies
 *
 * @brief Chuchu enemy
 */
#include "asm.h"
#include "enemy.h"
#include "functions.h"

typedef struct {
    /*0x00*/ Entity base;
    /*0x68*/ Entity* unk_68;
    /*0x6c*/ u8 unused1[6];
    /*0x72*/ u8 unk_72;
    /*0x73*/ u8 unused2[13];
    /*0x80*/ u8 unk_80;
    /*0x81*/ u8 unk_81;
    /*0x82*/ u8 unk_82;
    /*0x83*/ u8 unk_83;
} ChuchuEntity;

extern Entity* gUnk_020000B0;

void sub_0801F328(ChuchuEntity* this);
void sub_0801F340(ChuchuEntity* this);
void sub_0801F360(ChuchuEntity* this);
void sub_0801F3AC(ChuchuEntity* this);
void sub_0801F730(ChuchuEntity* this);
void sub_0801F748(ChuchuEntity* this);
void sub_0801F764(ChuchuEntity* this);
void sub_0801FAE0(ChuchuEntity* this);
void sub_0801FAF8(ChuchuEntity* this);
void sub_0801FB14(ChuchuEntity* this);
void sub_0801FB34(ChuchuEntity* this);
void sub_0801FB68(ChuchuEntity* this);
u32 sub_0801FBD0(ChuchuEntity* this);
void Chuchu_JumpAtPlayer(ChuchuEntity* this);

extern void (*const Chuchu_Functions[])(ChuchuEntity*);
extern void (*const gUnk_080CA234[])(ChuchuEntity*);
extern void (*const gUnk_080CA25C[])(ChuchuEntity*);
extern void (*const gUnk_080CA288[])(ChuchuEntity*);

extern const s8 gUnk_080CA2B4[];

void Chuchu(ChuchuEntity* this) {
    s32 index;

    index = sub_080012DC(super);
    switch (index) {
        default:
            gUnk_080012C8[index](super);
            return;
        case 0:
            /* ... */
            break;
        case 2:
            super->gustJarState &= 0xfe;
            if (index != this->unk_81) {
                switch (super->type) {
                    case 0:
                        if (super->flags & ENT_COLLIDE) {
                            super->action = 6;
                            COLLISION_OFF(super);
                            super->speed = 0x20;
                            super->hitType = 0x5c;
                            InitializeAnimation(super, 5);
                        }
                        break;
                    case 1:
                        /* ... */
                        break;
                    case 2:
                        sub_080043A8(super);
                        return;
                }
            }
            break;
    }
    this->unk_81 = index;
    Chuchu_Functions[GetNextFunction(super)](this);
    if (this->unk_68->type == 0x1c) {
        EnemySetFXOffset(super, 0, 1, -0x10);
    } else if (super->type == 2) {
        sub_0801FB34(this);
    }
}

void Chuchu_OnTick(ChuchuEntity* this) {
    switch (super->type) {
        case 0:
            sub_0801F3AC(this);
            gUnk_080CA234[super->action](this);
            break;
        case 1:
            sub_0801F764(this);
            gUnk_080CA25C[super->action](this);
            break;
        case 2:
            gUnk_080CA288[super->action](this);
            break;
    }
}

void Chuchu_OnCollision(ChuchuEntity* this) {
    u8 health;

    if (super->type == 2) {
        if (super->contactFlags == 0x8e || super->contactFlags == 0x95) {
            COLLISION_OFF(super);
            super->health = 0;
        }
    }

    health = super->health;
    if (health) {
        if (super->contactFlags == 0x94) {
            sub_0801FB68(this);
            EnemyCreateFX(super, FX_STARS);
            InitializeAnimation(super, 6);
        } else if (this->unk_80 != health) {
            sub_0801FB68(this);
            InitializeAnimation(super, 6);
        }
    } else {
        EnemyDetachFX(super);
        super->zVelocity = 0;
        InitializeAnimation(super, 9);
    }
    this->unk_80 = super->health;
    EnemyFunctionHandlerAfterCollision(super, Chuchu_Functions);
}

void Chuchu_OnGrabbed(ChuchuEntity* this) {
    if (!sub_0806F520(super) && super->confusedTime) {
        EnemyCreateFX(super, FX_STARS);
        InitializeAnimation(super, 6);
    } else {
        if (super->animIndex != 8) {
            sub_0801FB68(this);
            InitializeAnimation(super, 8);
        }
        GravityUpdate(super, Q_8_8(24.0));
        GetNextFrame(super);
    }
}

void Chuchu_OnKnockback(ChuchuEntity* this) {
    if (super->animIndex == 6)
        GetNextFrame(super);
    sub_08001318(super);
}

void Chuchu_OnDeath(ChuchuEntity* this) {
    GravityUpdate(super, Q_8_8(24.0));
    GetNextFrame(super);
    if (super->type == 0) {
        GenericDeath(super);
    } else if (super->type == 1) {
        EnemyCreateDeathFX((Enemy*)super, 0xf2, 0);
    } else {
        EnemyCreateDeathFX((Enemy*)super, 0xf1, 0);
    }
}

void Chuchu_OnConfused(ChuchuEntity* this) {
    if ((super->frame & ANIM_DONE) == 0)
        GetNextFrame(super);
    GenericConfused(super);
}

void sub_0801F0A4(ChuchuEntity* this) {
    sub_0804A720(super);
    super->action = 1;
    this->unk_80 = super->health;
    this->unk_82 = 0;
}

void nullsub_4(ChuchuEntity* this) {
    (void)super;
}

void sub_0801F0C8(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 3;
        super->timer = (Random() & 3) + 12;
        super->subtimer = Random();
        super->direction = sub_08049F84(super, 1);
        COLLISION_ON(super);
        super->spritePriority.b0 = 4;
        super->spritePriority.b1 = 3;
        InitializeAnimation(super, 2);
    }
}

void sub_0801F12C(ChuchuEntity* this) {
    if (sub_0801FBD0(this)) {
        sub_0801F328(this);
    } else {
        if ((super->subtimer++ & 7) == 0) {
            super->direction = sub_08049F84(super, 1);
        }
        ProcessMovement0(super);
        GetNextFrame(super);
        if (--super->timer == 0) {
            if (PlayerInRange(super, 1, 0x38)) {
                super->action = 4;
                Chuchu_JumpAtPlayer(this);
            } else if (PlayerInRange(super, 1, 0x48)) {
                super->timer = (Random() & 3) + 12;
            } else {
                sub_0801F328(this);
            }
        }
    }
}

void sub_0801F1B0(ChuchuEntity* this) {
    if (super->frame & 0x10) {
        if (super->frame & 1) {
            super->frame ^= 1;
            super->hitType = 90;
            EnqueueSFX(SFX_12B);
        }
        ProcessMovement2(super);
        if (GravityUpdate(super, Q_8_8(64.0)) == 0)
            GetNextFrame(super);
    } else {
        GetNextFrame(super);
    }

    if (super->frame & ANIM_DONE) {
        if (sub_0801FBD0(this)) {
            sub_0801F328(this);
        } else {
            sub_0801F340(this);
        }
    }
}

void sub_0801F228(ChuchuEntity* this) {
    if (--super->timer == 0) {
        super->action = 3;
        super->direction = sub_08049F84(super, 1);
    }
    GetNextFrame(super);
}

void sub_0801F250(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE)
        sub_0801F360(this);
}

void sub_0801F270(ChuchuEntity* this) {
    if ((super->subtimer++ & 7) == 0) {
        super->direction = sub_08049F84(super, 1);
    }

    ProcessMovement5(super);
    GetNextFrame(super);
    if (sub_0801FBD0(this))
        return;

    if (--super->timer != 0)
        return;

    if (this->unk_81 == 0) {
        super->action = 2;
        InitializeAnimation(super, 4);
    } else {
        super->timer = 8;
    }
}

void sub_0801F2CC(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 1;
        super->spriteSettings.draw = 0;
        InitializeAnimation(super, 4);
    }
}

void sub_0801F2F8(ChuchuEntity* this) {
    GravityUpdate(super, Q_8_8(24.0));
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        sub_0801F340(this);
        EnemyDetachFX(super);
    }
}

void sub_0801F328(ChuchuEntity* this) {
    super->action = 6;
    COLLISION_OFF(super);
    InitializeAnimation(super, 5);
}

void sub_0801F340(ChuchuEntity* this) {
    super->action = 5;
    super->timer = 60;
    super->speed = 0x20;
    super->hitType = 92;
    InitializeAnimation(super, 2);
}

void sub_0801F360(ChuchuEntity* this) {
    super->action = 7;
    super->timer = (Random() & 0x38) + 180;
    super->subtimer = Random();
    super->direction = sub_08049F84(super, 1);
    super->spritePriority.b1 = 2;
    super->spritePriority.b0 = 6;
    InitializeAnimation(super, 1);
}

void sub_0801F3AC(ChuchuEntity* this) {
    if (super->action == 9 || super->action == 0)
        return;

    if (sub_08049FDC(super, 1)) {
        if (this->unk_82 || PlayerInRange(super, 1, 0x48)) {
            if (super->action == 1) {
                super->action = 2;
                super->spriteSettings.draw = 1;
                this->unk_82 = 1;
                InitializeAnimation(super, 0);
            }
        } else if (super->action == 3) {
            sub_0801F328(this);
        }
    } else if (super->action == 3) {
        sub_0801F328(this);
    } else if (super->action == 7) {
        super->action = 8;
        InitializeAnimation(super, 7);
    }
}

void sub_0801F428(ChuchuEntity* this) {
    sub_0804A720(super);
    super->action = 1;
    super->timer = Random();
    this->unk_80 = super->health;
    this->unk_82 = 0;
    if (super->type2 == 0)
        return;

    super->action = 3;
    super->subtimer = 30;

#ifdef EU
    super->direction = sub_08049F84(super, 1);
#endif

    COLLISION_ON(super);
    super->spritePriority.b1 = 3;

#ifndef EU
    super->spriteSettings.draw = 1;
#endif

    InitializeAnimation(super, 2);
}

void sub_0801F48C(ChuchuEntity* this) {
    GetNextFrame(super);
}

void sub_0801F494(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 3;
        super->subtimer = 30;
        super->direction = sub_08049F84(super, 1);
        COLLISION_ON(super);
        super->spritePriority.b0 = 4;
        super->spritePriority.b1 = 3;
        InitializeAnimation(super, 2);
    }
}

void sub_0801F4EC(ChuchuEntity* this) {
    GetNextFrame(super);
    if (--super->subtimer == 0)
        super->action = 4;
}

void sub_0801F508(ChuchuEntity* this) {
    if (sub_0801FBD0(this)) {
        this->unk_83 = 0;
        sub_0801F730(this);
    } else {
        u8 tmp = ++super->timer & 7;
        if (tmp == 0 && PlayerInRange(super, 1, 0x38)) {
            if (Random() & 1) {
                super->action = 5;
                Chuchu_JumpAtPlayer(this);
            } else {
                this->unk_83 = 120;
                sub_0801F730(this);
            }
        } else {
            if (tmp == 4) {
                super->direction = sub_08049F84(super, 1);
            }
            ProcessMovement0(super);
            GetNextFrame(super);
        }
    }
}

void sub_0801F584(ChuchuEntity* this) {
    if (super->frame & 0x10) {
        if (super->frame & 0x1) {
            super->frame ^= 1;
            super->hitType = 91;
            EnqueueSFX(SFX_12B);
        }
        ProcessMovement2(super);
        if (GravityUpdate(super, Q_8_8(64.0)) == 0)
            GetNextFrame(super);

    } else {
        GetNextFrame(super);
    }

    if (super->frame & ANIM_DONE) {
        if (sub_0801FBD0(this)) {
            this->unk_83 = 0;
            sub_0801F730(this);
        } else {
            super->action = 6;
            super->subtimer = 60;
            super->speed = 0x20;
            super->hitType = 92;
            InitializeAnimation(super, 2);
        }
    }
}

void sub_0801F61C(ChuchuEntity* this) {
    if (--super->subtimer == 0)
        super->action = 4;
    GetNextFrame(super);
}

void sub_0801F638(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 8;
        super->subtimer = 30;
        super->direction = sub_08049F84(super, 1);
        super->spritePriority.b1 = 2;
        super->spritePriority.b0 = 6;
        InitializeAnimation(super, 1);
    }
}

void sub_0801F688(ChuchuEntity* this) {
    if (this->unk_83)
        this->unk_83--;

    if (sub_0801FBD0(this) || this->unk_83) {
        super->direction = sub_08049F84(super, 1);
        ProcessMovement5(super);
        GetNextFrame(super);
    } else {
        sub_0801F748(this);
    }
}

void sub_0801F6CC(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 1;
        super->spriteSettings.draw = 0;
        InitializeAnimation(super, 4);
    }
}

void sub_0801F6F8(ChuchuEntity* this) {
    GravityUpdate(super, Q_8_8(24.0));
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 4;
        super->speed = 0x20;
        EnemyDetachFX(super);
        InitializeAnimation(super, 2);
    }
}

void sub_0801F730(ChuchuEntity* this) {
    super->action = 7;
    COLLISION_OFF(super);
    InitializeAnimation(super, 5);
}

void sub_0801F748(ChuchuEntity* this) {
    super->action = 2;
    super->spriteSettings.draw = 1;
    InitializeAnimation(super, 4);
}

void sub_0801F764(ChuchuEntity* this) {
    if (super->action == 10 || super->action == 0)
        return;

    if (sub_08049FDC(super, 1)) {
        if (super->action == 1) {
            if (this->unk_82 || PlayerInRange(super, 1, 0x48)) {
                super->action = 2;
                super->spriteSettings.draw = 1;
                this->unk_82 = 1;
                InitializeAnimation(super, 0);
            }
        }
    } else if (super->action == 4) {
        sub_0801F730(this);
    } else if (super->action == 8) {
        super->action = 9;
        InitializeAnimation(super, 7);
    }
}

void sub_0801F7D8(ChuchuEntity* this) {
    sub_0804A720(super);
    super->action = 1;
    super->timer = Random();
    this->unk_80 = super->health;
    this->unk_82 = 0;
}

void sub_0801F7FC(ChuchuEntity* this) {
    if (sub_08049FDC(super, 1) == 0)
        return;

    if (this->unk_82 || PlayerInRange(super, 1, 0x48)) {
        super->action = 2;
        super->spriteSettings.draw = 1;
        this->unk_82 = 1;
        InitializeAnimation(super, 0);
    }
}

void sub_0801F840(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        sub_0801FB14(this);
        COLLISION_ON(super);
        super->spritePriority.b0 = 4;
        super->spritePriority.b1 = 3;
    }
}

void sub_0801F884(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->subtimer) {
        super->subtimer--;
    } else {
        Entity* entity = EnemyCreateFX(super, FX_LIGHTNING_STRIKE);
        if (entity != NULL) {
            entity->type2 = 64;
            super->action = 4;
            super->hitType = 165;
            EnqueueSFX(SFX_193);
        }
    }
}

void sub_0801F8C0(ChuchuEntity* this) {
    if (sub_0801FBD0(this)) {
        sub_0801FAE0(this);
    } else if (sub_08049FDC(super, 1) == 0) {
        sub_0801F730(this);
    } else {
        u8 tmp = ++super->timer & 7;
        if (tmp == 0 && sub_08049F1C(super, gUnk_020000B0, 0x38)) {
            super->action = 5;
            Chuchu_JumpAtPlayer(this);
        } else {
            if (tmp == 4) {
                super->direction = GetFacingDirection(super, gUnk_020000B0);
            }
            ProcessMovement0(super);
            GetNextFrame(super);
        }
    }
}

void sub_0801F940(ChuchuEntity* this) {
    if (super->frame & 0x10) {
        if (super->frame & 1) {
            super->frame ^= 1;
            EnqueueSFX(SFX_12B);
        }
        ProcessMovement2(super);
        if (GravityUpdate(super, Q_8_8(64.0)) == 0)
            GetNextFrame(super);
    } else {
        GetNextFrame(super);
    }

    if (super->frame & ANIM_DONE) {
        if (sub_0801FBD0(this)) {
            sub_0801FAE0(this);
        } else {
            super->action = 6;
            super->subtimer = 60;
            super->speed = 0x20;
            InitializeAnimation(super, 2);
        }
    }
}

void sub_0801F9C4(ChuchuEntity* this) {
    if (--super->subtimer == 0)
        super->action = 4;
    GetNextFrame(super);
}

void sub_0801F9E0(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 8;
        super->subtimer = 30;
        super->direction = sub_08049F84(super, 1);
        super->spritePriority.b1 = 2;
        super->spritePriority.b0 = 6;
        InitializeAnimation(super, 1);
    }
}

void sub_0801FA30(ChuchuEntity* this) {
    if (sub_0801FBD0(this)) {
        super->direction = sub_08049F84(super, 1);
        ProcessMovement5(super);
        GetNextFrame(super);
    } else if (sub_08049FDC(super, 1) == 0) {
        super->action = 9;
        InitializeAnimation(super, 7);
    } else {
        sub_0801FAF8(this);
    }
}

void sub_0801FA78(ChuchuEntity* this) {
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        super->action = 1;
        super->spriteSettings.draw = 0;
        InitializeAnimation(super, 4);
        EnemyDetachFX(super);
    }
}

void sub_0801FAAC(ChuchuEntity* this) {
    GravityUpdate(super, Q_8_8(24.0));
    GetNextFrame(super);
    if (super->frame & ANIM_DONE) {
        sub_0801FB14(this);
        super->speed = 0x20;
        EnemyDetachFX(super);
    }
}

void sub_0801FAE0(ChuchuEntity* this) {
    super->action = 7;
    COLLISION_OFF(super);
    InitializeAnimation(super, 5);
}

void sub_0801FAF8(ChuchuEntity* this) {
    super->action = 2;
    super->spriteSettings.draw = 1;
    InitializeAnimation(super, 4);
}

void sub_0801FB14(ChuchuEntity* this) {
    super->action = 3;
    super->subtimer = 30;
    super->direction = sub_08049F84(super, 1);
    InitializeAnimation(super, 2);
}

void sub_0801FB34(ChuchuEntity* this) {
    if (this->unk_68) {
        sub_0806FA90(super, this->unk_68, gUnk_080CA2B4[super->frame & 0xf], 1);
        (this->unk_68)->spriteOffsetY--;
    }
}

void sub_0801FB68(ChuchuEntity* this) {
    switch (super->type) {
        case 0:
            super->action = 9;
            break;
        case 1:
            super->action = 10;
            COLLISION_ON(super);
            super->spriteSettings.draw = 1;
            super->spritePriority.b1 = 3;
            break;
        case 2:
            super->action = 10;
            super->hitType = 92;
            EnemyDetachFX(super);
            break;
    }

    super->zVelocity = 0;
}

u32 sub_0801FBD0(ChuchuEntity* this) {
    if (GetActTile(super) == 0x10) {
        return 1;
    } else {
        return 0;
    }
}

void Chuchu_JumpAtPlayer(ChuchuEntity* this) {
    super->speed = 0x180;
    super->zVelocity = Q_16_16(2.0);
    super->direction = sub_08049F84(super, 1);
    InitializeAnimation(super, 3);
}

// clang-format off
void (*const Chuchu_Functions[])(ChuchuEntity*) = {
    Chuchu_OnTick,
    Chuchu_OnCollision,
    Chuchu_OnKnockback,
    Chuchu_OnDeath,
    Chuchu_OnConfused,
    Chuchu_OnGrabbed,
};

void (*const gUnk_080CA234[])(ChuchuEntity*) = {
    sub_0801F0A4,
    nullsub_4,
    sub_0801F0C8,
    sub_0801F12C,
    sub_0801F1B0,
    sub_0801F228,
    sub_0801F250,
    sub_0801F270,
    sub_0801F2CC,
    sub_0801F2F8,
};

void (*const gUnk_080CA25C[])(ChuchuEntity*) = {
    sub_0801F428,
    sub_0801F48C,
    sub_0801F494,
    sub_0801F4EC,
    sub_0801F508,
    sub_0801F584,
    sub_0801F61C,
    sub_0801F638,
    sub_0801F688,
    sub_0801F6CC,
    sub_0801F6F8,
};

void (*const gUnk_080CA288[])(ChuchuEntity*) = {
    sub_0801F7D8,
    sub_0801F7FC,
    sub_0801F840,
    sub_0801F884,
    sub_0801F8C0,
    sub_0801F940,
    sub_0801F9C4,
    sub_0801F9E0,
    sub_0801FA30,
    sub_0801FA78,
    sub_0801FAAC,
};
// clang-format on
