/**
 * @file moldorm.c
 * @ingroup Enemies
 *
 * @brief Moldorm enemy
 */

#include "enemy.h"
#include "functions.h"

void sub_08022EAC(Entity*);
void sub_08022F14(Entity*);

extern void (*const gUnk_080CBB90[])(Entity*);
extern void (*const Moldorm_Functions[])(Entity*);
extern void (*const gUnk_080CBBB4[])(Entity*);
extern void (*const gUnk_080CBBBC[])(Entity*);

extern const s8 gUnk_080CBBC4[];

void Moldorm(Entity* this) {
    gUnk_080CBB90[this->type](this);
}

void sub_08022BA0(Entity* this) {
    this->field_0x78.HALF.HI++;
    this->field_0x74.HWORD = this->x.HALF.HI;
    this->field_0x76.HWORD = this->y.HALF.HI;
    EnemyFunctionHandler(this, Moldorm_Functions);
    sub_08022EAC(this);
}

void Moldorm_OnTick(Entity* this) {
    gUnk_080CBBB4[this->action](this);
}

void Moldorm_OnCollision(Entity* this) {
    if (this->field_0x7a.HALF.LO != this->health)
        this->field_0x7a.HALF.HI = 30;

    this->field_0x7a.HALF.LO = this->health;
    this->actionDelay = 1;
    this->direction = this->knockbackDirection;

    this->animationState = ((this->direction + 2) & 0x1c) >> 2;
    this->frameIndex = this->animationState;

    this->child->iframes = this->iframes;
    (*(Entity**)&this->field_0x7c)->iframes = this->iframes;
    (*(Entity**)&this->field_0x80)->iframes = this->iframes;
    EnemyFunctionHandlerAfterCollision(this, Moldorm_Functions);
}

void sub_08022C58(Entity* this) {
    Entity *tail0, *tail1, *tail2;

    COLLISION_OFF(this);

    if (gEntCount >= 0x45)
        return;

    tail0 = CreateEnemy(MOLDORM, 1);
    this->child = tail0;
    tail0->spritePriority.b0 = 5;
    tail0->parent = this;
    CopyPosition(this, tail0);

    tail1 = CreateEnemy(MOLDORM, 2);
    tail0->child = tail1;
    tail1->spritePriority.b0 = 5;
    tail1->parent = this;
    CopyPosition(this, tail1);

    tail2 = CreateEnemy(MOLDORM, 3);
    tail1->child = tail2;
    tail2->spritePriority.b0 = 5;
    tail2->parent = this;
    CopyPosition(this, tail2);

    sub_0804A720(this);
    this->action = 1;
    this->actionDelay = 1;
    this->field_0xf = 1;
    COLLISION_ON(this);
    this->parent = this;
    this->child = tail0;
    *(Entity**)&this->field_0x7c = tail1;
    *(Entity**)&this->field_0x80 = tail2;

    this->direction = Random() & 0x1f;
    this->animationState = ((this->direction + 2) & 0x1c) >> 2;
    this->frameIndex = this->animationState;
    this->field_0x7a.HALF.LO = this->health;
}

void sub_08022D40(Entity* this) {
    if (this->field_0x7a.HALF.HI) {
        if (this->field_0x7a.HALF.HI-- & 1) {
            this->animationState = (this->animationState + 1) & 7;
            this->frameIndex = this->animationState;
        }
    } else {
        sub_08022F14(this);
        ProcessMovement0(this);

        if (this->collisions) {
            sub_0800417E(this, this->collisions);
            this->animationState = ((this->direction + 2) & 0x1c) >> 2;
            this->frameIndex = this->animationState;
        }
    }
}

void sub_08022D90(Entity* this) {
    if (this->parent->next) {
        Entity* parent;

        gUnk_080CBBBC[this->action](this);
        parent = this->parent;
        this->spriteOrientation.flipY = parent->spriteOrientation.flipY;
        this->spriteRendering.b3 = parent->spriteRendering.b3;
        this->collisionLayer = parent->collisionLayer;
    } else {
        DeleteEntity(this);
    }
}

void sub_08022DE8(Entity* this) {
    this->action = 1;
    this->field_0x7c.BYTES.byte0 = 0x88;
    this->field_0x7c.BYTES.byte1 = 0x88;
    this->field_0x7c.BYTES.byte2 = 0x88;
    this->field_0x7c.BYTES.byte3 = 0x88;
    this->field_0x80.HALF.LO = 0x88;
    this->field_0x80.HALF.HI = 0x88;
    this->field_0x82.HALF.LO = 0x88;
    this->field_0x82.HALF.HI = 0x88;
    this->animationState = this->parent->animationState;
    *(u32*)&this->cutsceneBeh = this->animationState * 0x11111111;

    if (this->type != 3) {
        this->frameIndex = this->type + 7;
    } else {
        this->frameIndex = this->animationState + 10;
    }
}

// this definition is only here, so clang-tidy does not mess gUnk_080CBBBC up.
extern void sub_08022E40(Entity* this);

ASM_FUNC("asm/non_matching/moldorm/sub_08022E40.inc", void sub_08022E40(Entity* this))

ASM_FUNC("asm/non_matching/moldorm/sub_08022EAC.inc", void sub_08022EAC(Entity* this))

void sub_08022F14(Entity* this) {
    if (sub_08049FA0(this) == 0) {
        u32 bVar6 = (sub_08049EE4(this) - this->direction) & 0x1f;
        if (8 < ((bVar6 + 4) & 0x1f)) {
            this->field_0xf = 8;
            if (bVar6 < 0x10) {
                this->field_0x78.HALF.LO = 1;
            } else {
                this->field_0x78.HALF.LO = -1;
            }
        }
    }

    if (--this->actionDelay == 0) {
        this->actionDelay = 4;

        if (--this->field_0xf == 0) {
            this->field_0xf = 8;
            this->field_0x78.HALF.LO = gUnk_080CBBC4[Random() & 1];
        }

        this->direction += this->field_0x78.HALF.LO;
        this->direction &= 0x1f;

        this->animationState = ((this->direction + 2) & 0x1c) >> 2;
        this->frameIndex = this->animationState;
    }
}

// clang-format off
void (*const gUnk_080CBB90[])(Entity*) = {
    sub_08022BA0,
    sub_08022D90,
    sub_08022D90,
    sub_08022D90,
};

void (*const Moldorm_Functions[])(Entity*) = {
    Moldorm_OnTick,
    Moldorm_OnCollision,
    GenericKnockback,
    GenericDeath,
    GenericConfused,
};

void (*const gUnk_080CBBB4[])(Entity*) = {
    sub_08022C58,
    sub_08022D40,
};

void (*const gUnk_080CBBBC[])(Entity*) = {
    sub_08022DE8,
    sub_08022E40,
};

const s8 gUnk_080CBBC4[] = {
    1, -1, 0, 0,
};
// clang-format off
