/**
 * @file beetle.c
 * @ingroup Enemies
 *
 * @brief Beetle enemy
 */
#include "enemy.h"
#include "functions.h"

typedef struct {
    /*0x00*/ Entity base;
    /*0x68*/ u8 unused1[30];
    /*0x86*/ u8 unk_86;
    /*0x87*/ u8 unk_87;
} BeetleEntity;

u32 sub_08021D00(BeetleEntity*);
void sub_08021D44(BeetleEntity* this, u32 direction);

extern void (*const Beetle_Functions[])(BeetleEntity*);
extern void (*const gUnk_080CB5A8[])(BeetleEntity*);
extern void (*const gUnk_080CB5C8[])(BeetleEntity*);
extern void (*const gUnk_080CB5D4[])(BeetleEntity*);

extern const s8 gUnk_080CB5DC[];
extern const s8 gUnk_080CB5E4[];

void Beetle(BeetleEntity* this) {
    EnemyFunctionHandler(super, (EntityActionArray)Beetle_Functions);
}

void Beetle_OnTick(BeetleEntity* this) {
    gUnk_080CB5A8[super->action](this);
}

void Beetle_OnCollision(BeetleEntity* this) {
    switch (super->contactFlags) {
        case 0x80:
            if (gPlayerState.framestate == PL_STATE_CLIMB) {
                Beetle_OnTick(this);
            } else {
                super->action = 5;
                super->timer = 180;
                super->subtimer = 0;
                COLLISION_OFF(super);
                super->spritePriority.b0 = 3;
                CopyPositionAndSpriteOffset(&gPlayerEntity.base, super);
                super->z.HALF.HI = -1;
                this->unk_87 = 1;
                InitializeAnimation(super, 6);
            }
            break;
        case 0x93:
            Beetle_OnTick(this);
            break;
    }

    if (super->health == 0)
        super->knockbackDuration = 0;

    EnemyFunctionHandlerAfterCollision(super, Beetle_Functions);
}

void Beetle_OnDeath(BeetleEntity* this) {
    if (super->type == 0) {
        GenericDeath(super);
    } else {
        Entity* entity = super->parent;
        if (entity != NULL) {
            entity->subtimer--;
            super->parent = NULL;
        }
        EnemyCreateDeathFX((Enemy*)super, 0xf0, 0);
    }
}

void Beetle_OnGrabbed(BeetleEntity* this) {
    if (sub_0806F520(super)) {
        gUnk_080CB5C8[super->subAction](this);
    } else {
        super->action = 3;
        super->z.HALF.HI = 0;
        InitializeAnimation(super, 2);
    }
}

void sub_0802187C(BeetleEntity* this) {
    super->subAction = 2;
}

void nullsub_130(BeetleEntity* this) {
}

void sub_08021888(BeetleEntity* this) {
    if (sub_0806F3E4(super))
        Beetle_OnDeath(this);
}

void Beetle_Initialize(BeetleEntity* this) {
    sub_0804A720(super);
    super->action = 1;
    super->gustJarFlags = 1;
    InitializeAnimation(super, 3);
}

void sub_080218B4(BeetleEntity* this) {
    gUnk_080CB5D4[super->type2](this);
}

void sub_080218CC(BeetleEntity* this) {
    if (super->subAction == 0) {
        super->subAction = 1;
        super->spriteSettings.draw = 1;
        super->direction = ((sub_08049F84(super, 1) ^ 0x10) + gUnk_080CB5DC[Random() & 7]) &
                           (0x3 | DIR_DIAGONAL | DirectionNorth | DirectionEast | DirectionSouth | DirectionWest);
        super->speed = 0x100;
        super->zVelocity = Q_16_16(1.125);
    }

    GetNextFrame(super);
    if (super->frame & 1) {
        ProcessMovement2(super);
        if (BounceUpdate(super, Q_8_8(28.0)) == BOUNCE_DONE_ALL)
            super->frameDuration = 1;
    }

    if (super->frame & ANIM_DONE) {
        super->action = 2;
        super->timer = (Random() & 0x38) + 8;
        super->subtimer = 1;
        COLLISION_ON(super);
        super->speed = 0x180;
        InitializeAnimation(super, 0);
    }
}

void sub_08021984(BeetleEntity* this) {
    if (super->subAction == 0) {
        super->subAction = 1;
        COLLISION_ON(super);
        super->spriteSettings.draw = 3;
        super->z.HALF.HI = -0x80;
        super->spriteRendering.b3 = 1;
        super->spriteOrientation.flipY = 1;
        EnqueueSFX(SFX_12D);
    }

    if (GravityUpdate(super, Q_8_8(24.0)) == 0) {
        super->action = 2;
        super->timer = 16;
        super->subtimer = 1;
        super->spriteSettings.draw = 1;
        super->speed = 0x180;
        this->unk_86 = 60;
        InitializeAnimation(super, 0);
        UpdateSpriteForCollisionLayer(super);
    }
}

void sub_08021A10(BeetleEntity* this) {
    GetNextFrame(super);
    if (EntityInRectRadius(super, &gPlayerEntity.base, 120, 80) && sub_08021D00(this) == 0) {
        if (--super->timer == 0) {
            super->action = 3;
            super->timer = (Random() & 0x3f) + 30;
            sub_08021D44(this, super->direction);
            InitializeAnimation(super, 2);
        }
    }
}

void sub_08021A64(BeetleEntity* this) {
    if (!sub_08021D00(this)) {
        if (--super->timer == 0) {
            super->action = 2;
            super->timer = (Random() & 0x1F) + 30;
            InitializeAnimation(super, 0);
        }

        if (--super->subtimer == 0) {
            u32 tmp;

            super->subtimer = 8;
            tmp = sub_08049F84(super, 1);
            if (tmp == 0xff) {
                super->action = 7;
                InitializeAnimation(super, 1);
                return;
            }
            sub_08021D44(this, tmp);
        }
        ProcessMovement0(super);
        GetNextFrame(super);
    }
}

void sub_08021AD8(BeetleEntity* this) {
    GetNextFrame(super);
    if (super->frame & 1) {
        if (super->timer != 0) {
            u32 tmp;

            super->timer = 0;
            tmp = sub_08049F84(super, 1);
            if (tmp == 0xff) {
                super->action = 2;
                super->timer = 8;
                InitializeAnimation(super, 0);
                return;
            }
            super->direction = (u8)tmp;
            EnqueueSFX(SFX_PLY_JUMP);
        }
        ProcessMovement2(super);
        if (!GravityUpdate(super, Q_8_8(24.0)))
            super->frameDuration = 1;
    }

    if (super->frame & ANIM_DONE) {
        super->action = 2;
        super->timer = 20;
        this->unk_86 = 60;
        InitializeAnimation(super, 0);
    }
}

void sub_08021B64(BeetleEntity* this) {
    if (gPlayerState.flags & PL_DROWNING) {
        super->action = 3;
        super->z.WORD = 0;
        InitializeAnimation(super, 2);
    } else {
        s32 iVar4 = 1;

        if (gPlayerState.framestate != PL_STATE_JUMP && gPlayerState.framestate != PL_STATE_CAPE) {
            if (sub_0807953C())
                iVar4 = super->type * 3 + 8;

            iVar4 = super->timer - iVar4;
            if (iVar4 < 0)
                iVar4 = 0;

            super->timer = (u8)iVar4;
        }

        if (gPlayerState.flags & (PL_DISABLE_ITEMS | PL_CAPTURED))
            iVar4 = 0;

        if (iVar4 == 0) {
            super->action = 6;
            super->zVelocity = Q_16_16(1.0);
            this->unk_87 = 0;
            if (gPlayerEntity.base.direction != 0xff) {
                super->direction = DirectionSouth ^ gPlayerEntity.base.direction;
            } else {
                super->direction = (gPlayerEntity.base.animationState << 2) ^ DirectionSouth;
            }
            InitializeAnimation(super, 5);
        } else {
            gPlayerState.mobility |= 0x80;
            gPlayerState.speed_modifier -= 0x50;
            gPlayerState.attachedBeetleCount++;
            CopyPositionAndSpriteOffset(&gPlayerEntity.base, super);
            super->x.HALF.HI += gUnk_080CB5E4[(super->subtimer++ & 0xe) >> 1];
            super->z.HALF.HI--;
            GetNextFrame(super);
        }
    }
}

void sub_08021C58(BeetleEntity* this) {
    GetNextFrame(super);
    if (super->frame & 1) {
        ProcessMovement2(super);
        if (GravityUpdate(super, Q_8_8(24.0)) == 0)
            super->frameDuration = 1;
    }

    if (super->frame & ANIM_DONE) {
        super->action = 2;
        super->timer = 60;
        super->subtimer = 1;
        COLLISION_ON(super);
        super->spritePriority.b0 = 4;
        this->unk_86 = 60;
        InitializeAnimation(super, 0);
    }
}

void sub_08021CD0(BeetleEntity* this) {
    if ((super->frame & ANIM_DONE) == 0)
        GetNextFrame(super);

    if (sub_08049F84(super, 1) == 0xff)
        return;

    super->action = 3;
    InitializeAnimation(super, 2);
}

u32 sub_08021D00(BeetleEntity* this) {
    u32 ret;

    if (this->unk_86) {
        this->unk_86--;
        ret = 0;
    } else if (PlayerInRange(super, 1, 0x10) == 0) {
        ret = 0;
    } else {
        super->action = 4;
        super->timer = 1;
        super->zVelocity = Q_16_16(1.5);
        InitializeAnimation(super, 4);
        ret = 1;
    }

    return ret;
}

void sub_08021D44(BeetleEntity* this, u32 direction) {
    direction = DirectionRoundUp(direction);
    super->direction = direction;
    if (DirectionIsHorizontal(direction)) {
        super->speed = 0x180;
    } else {
        super->speed = 0xc0;
    }
}

// clang-format off
void (*const Beetle_Functions[])(BeetleEntity*) = {
    Beetle_OnTick,
    Beetle_OnCollision,
    (void (*)(BeetleEntity*))GenericKnockback,
    Beetle_OnDeath,
    (void (*)(BeetleEntity*))GenericConfused,
    Beetle_OnGrabbed,
};

void (*const gUnk_080CB5A8[])(BeetleEntity*) = {
    Beetle_Initialize,
    sub_080218B4,
    sub_08021A10,
    sub_08021A64,
    sub_08021AD8,
    sub_08021B64,
    sub_08021C58,
    sub_08021CD0,
};

void (*const gUnk_080CB5C8[])(BeetleEntity*) = {
    sub_0802187C,
    nullsub_130,
    sub_08021888,
};

void (*const gUnk_080CB5D4[])(BeetleEntity*) = {
    sub_080218CC,
    sub_08021984,
};

const s8 gUnk_080CB5DC[] = {
    -1, -2, 1, 2, -3, 3, -4, 4,
};

const s8 gUnk_080CB5E4[] = {
    -1, -2, -1, 0, 1, 2, 1, 0,
};
// clang-format on
