/**
 * @file spearMoblin.c
 * @ingroup Enemies
 *
 * @brief Spear Moblin enemy
 */
#include "enemy.h"
#include "functions.h"

typedef struct {
    /*0x00*/ Entity base;
    /*0x68*/ u8 unused1[18];
    /*0x7a*/ u8 unk_7a;
    /*0x7b*/ u8 unk_7b;
    /*0x7c*/ u8 unused2[4];
    /*0x80*/ u8 unk_80;
    /*0x81*/ u8 unk_81;
    /*0x82*/ u8 unk_82;
    /*0x83*/ u8 unk_83;
} SpearMoblinEntity;

void sub_08028604(SpearMoblinEntity*);
void sub_08028754(SpearMoblinEntity*);
void sub_08028784(SpearMoblinEntity*);
void sub_080287E0(SpearMoblinEntity*);
void sub_08028728(SpearMoblinEntity*);
bool32 sub_080286CC(SpearMoblinEntity*);
bool32 sub_080288A4(SpearMoblinEntity*);
void sub_08028858(SpearMoblinEntity*);
void sub_080288C0(SpearMoblinEntity*);
bool32 sub_08028828(u32, u32);
void sub_080287B4(SpearMoblinEntity*);

extern void (*const SpearMoblin_Functions[])(SpearMoblinEntity*);
extern void (*const gUnk_080CC7A8[])(SpearMoblinEntity*);
extern const u8 gUnk_080CC7BC[];
extern const s8 gUnk_080CC7C0[];
extern const s8 gUnk_080CC7D0[];
extern const u16 gUnk_080CC7D8[];

extern const Hitbox* const gUnk_080CC944[];

void SpearMoblin(SpearMoblinEntity* this) {
    EnemyFunctionHandler(super, (EntityActionArray)SpearMoblin_Functions);
    EnemySetFXOffset(super, 0, 1, -0x20);
    if (super->child && super->child->next) {
        CopyPosition(super, super->child);
    }
}

void SpearMoblin_OnTick(SpearMoblinEntity* this) {
    gUnk_080CC7A8[super->action](this);
}

void SpearMoblin_OnCollision(SpearMoblinEntity* this) {
    if (super->confusedTime != 0)
        EnemyCreateFX(super, FX_STARS);

    EnemyFunctionHandlerAfterCollision(super, SpearMoblin_Functions);
    if (super->contactFlags & 0x80) {
        if (super->action != 4) {
            sub_08028754(this);
        } else {
            if ((super->contactFlags & 0x3f) == 0) {
                this->unk_7a++;
                this->unk_81 = 0x16;
                sub_08028784(this);
            }
        }
    }

    super->child->iframes = super->iframes;
    if (super->health == 0) {
        super->speed = 0;
        this->unk_82 = 0;
        sub_080287E0(this);
        DeleteEntity(super->child);
        super->child = NULL;
    }
}

void SpearMoblin_OnGrabbed(SpearMoblinEntity* this) {
    /* ... */
}

void sub_08028314(SpearMoblinEntity* this) {
    Entity* pEVar2;

    sub_0804A720(super);
    super->action = 1;
    super->animationState = 0;
    this->unk_7b = 0;
    this->unk_80 = 0;
    this->unk_83 = 0;
    this->unk_81 = 0;
    this->unk_7a = 0;
    this->unk_82 = 1;

    if (super->timer) {
        super->animationState = super->type2 << 1;
        super->timer = 30;
        super->speed = 0x80;
        super->direction = super->animationState << 2;
        sub_080287E0(this);
    } else {
        super->animationState = 0x10;
        sub_08028604(this);
    }

    pEVar2 = CreateProjectile(MOBLIN_SPEAR);
    if (pEVar2 != NULL) {
        pEVar2->parent = super;
        super->child = pEVar2;
    }
}

void sub_08028378(SpearMoblinEntity* this) {
    if (this->unk_81 != 0) {
        this->unk_81--;
    }

    if (--super->timer == 0) {
        if (++this->unk_80 >= 0x10) {
            sub_08028728(this);
        } else {
            sub_08028604(this);
        }
    } else {
        if (sub_080286CC(this)) {
            this->unk_7b |= 1;
        }
        if (sub_080288A4(this)) {
            sub_0800417E(super, super->collisions);
            super->animationState = DirectionRoundUp(super->direction) >> 2;
            this->unk_83++;
            sub_080287E0(this);
        }
    }

    if (super->subtimer >= 0xc) {
        if (this->unk_7b != 0) {
            sub_08028754(this);
        }
    } else {
        super->subtimer++;
    }

    sub_08028858(this);
}

void sub_08028420(SpearMoblinEntity* this) {
    if (--super->timer == 0) {
        super->action = 3;
        this->unk_82 = 0;
        this->unk_80 = 0;
        super->animationState = 0x10;
        sub_08028604(this);
    } else {
        if (sub_080286CC(this)) {
            this->unk_7b |= 1;
        }
    }

    if (super->subtimer >= 0xc) {
        if (this->unk_7b != 0) {
            sub_08028754(this);
        }
    } else {
        super->subtimer++;
    }

    sub_08028858(this);
}

void sub_08028488(SpearMoblinEntity* this) {
    if (--super->timer == 0) {
        switch (this->unk_82) {
            case 3:
                super->action = 4;
                super->speed = 0x180;
                super->direction = sub_08049F84(super, 1);
                EnqueueSFX(SFX_EM_MOBLIN_SPEAR);
                break;
            case 2:
                super->action = 2;
                super->speed = 0;
                super->timer = (Random() & 7) * 3 + 64;
                break;
            case 4:
                super->direction = (super->direction + 0x10) & DirectionWest;
                /* fallthrough */
            case 1:
            default:
                super->action = 1;
                this->unk_82 = 1;
                super->speed = 0x80;
                super->timer = (Random() & 7) * 3 + 0x22;
                break;
        }

        super->subtimer = 0;
        super->animationState = DirectionRoundUp(super->direction) >> 2;
        sub_080287E0(this);
    }

    sub_08028858(this);
}

void sub_08028528(SpearMoblinEntity* this) {
    Entity* entity = sub_08049DF4(1);
    const Hitbox* box;

    if (entity == NULL) {
        super->subtimer = 0;
        sub_08028728(this);
    } else {
        sub_080288C0(this);
        box = gUnk_080CC944[super->animationState >> 1];
        super->hitbox->offset_x = box->offset_x;
        super->hitbox->offset_y = box->offset_y;
        super->hitbox->width = box->width;
        super->hitbox->height = box->height;

        if (this->unk_7a != 0) {
            this->unk_7a = 0;
            this->unk_81 = 0x16;
            sub_08028784(this);
        } else {
            if (++super->timer == 0x20) {
                u32 direction;
                super->timer = 0;
                direction = GetFacingDirection(super, entity);
                if (sub_08028828(super->direction, direction)) {
                    super->direction = direction;
                }
            }

            if ((super->timer & 7) == 0) {
                EnqueueSFX(SFX_F0);
                CreateFx(super, FX_DEATH, 0x40);
            }

            if (++this->unk_7b == 0x80) {
                this->unk_7a++;
            }
        }

        if (0x20 < this->unk_7b) {
            ProcessMovement0(super);
        }

        GetNextFrame(super);
    }
}

void sub_08028604(SpearMoblinEntity* this) {
    u32 iVar3;
    super->subtimer = 0;
    if (this->unk_82 == 1) {
        super->timer = gUnk_080CC7BC[Random() & 3];
        super->speed = 0x80;
        if (sub_08049FA0(super) != 0) {
            u32 rand = Random();
            u32 tmp;
            tmp = gUnk_080CC7D0[rand & 7] + 0x18;
            super->direction = DirectionRound(tmp + super->direction);
            iVar3 = Direction8ToAnimationState(super->direction);
        } else {
            iVar3 = sub_08049EE4(super);
            if (this->unk_83 == 0) {
                u32 rand = Random();
                iVar3 += gUnk_080CC7C0[rand & 0xf];
            } else {
                u32 rand = Random();
                iVar3 += gUnk_080CC7C0[rand & 7];
                super->timer = super->timer + 0x10;
                this->unk_83--;
            }
            super->direction = (iVar3 + 4U) & DirectionWest;
            iVar3 = Direction8ToAnimationState(super->direction);
        }
    } else {
        super->timer = 12;
        super->speed = 0;
        iVar3 = Direction8ToAnimationState(super->direction);
    }

    if (iVar3 != super->animationState) {
        super->animationState = iVar3;
        sub_080287E0(this);
    }
}

bool32 sub_080286CC(SpearMoblinEntity* this) {
    if (this->unk_81 == 0) {
        Entity* entity = sub_08049DF4(1);
        if (entity != NULL) {
            if (this->unk_82 == 2 && sub_0806FC80(super, entity, 0x30))
                return TRUE;

            if (sub_0806FC80(super, entity, 0x40) &&
                DirectionRoundUp(GetFacingDirection(super, entity)) >> 2 == super->animationState)
                return TRUE;
        }
    }

    return FALSE;
}

void sub_08028728(SpearMoblinEntity* this) {
    super->animationState = DirectionRoundUp(super->direction) >> 2;
    super->direction = super->animationState << 2;
    this->unk_83 >>= 1;
    sub_080287B4(this);
    this->unk_82 = 2;
}

void sub_08028754(SpearMoblinEntity* this) {
    super->direction = sub_08049F84(super, 1);
    super->animationState = DirectionRoundUp(super->direction) >> 2;
    this->unk_83 = 0;
    sub_080287B4(this);
    this->unk_82 = 3;
}

void sub_08028784(SpearMoblinEntity* this) {
    super->animationState = DirectionRoundUp(super->direction) >> 2;
    super->direction = super->animationState << 2;
    this->unk_83 = 0;
    sub_080287B4(this);
    super->timer <<= 1;
    this->unk_82 = 4;
}

void sub_080287B4(SpearMoblinEntity* this) {
    super->action = 3;
    super->timer = 32;
    super->subtimer = 0;
    this->unk_80 = 0;
    this->unk_7b = 0;
    super->speed = 0;
    this->unk_82 = 0;
    sub_080287E0(this);
}

void sub_080287E0(SpearMoblinEntity* this) {
    u32 sprite = super->animationState >> 1;
    switch (this->unk_82) {
        case 0:
            /* ... */
            break;
        case 1:
        case 4:
            sprite += 4;
            break;
        case 3:
            sprite += 8;
            break;
        case 2:
            sprite += 0xc;
            break;
    }
    InitializeAnimation(super, sprite);
}

bool32 sub_08028828(u32 arg0, u32 arg1) {
    if (DirectionRoundUp(arg1) == DirectionRoundUp(arg0)) {
        u32 angle1 = (arg1 + 5) & 7;
        u32 angle0 = (arg0 + 5) & 7;
        if (angle1 < 3 && angle0 < 3)
            return TRUE;
    }

    return FALSE;
}

void sub_08028858(SpearMoblinEntity* this) {
    const Hitbox* box;

    sub_080288C0(this);
    box = gUnk_080CC944[super->animationState >> 1];
    super->hitbox->offset_x = box->offset_x;
    super->hitbox->offset_y = box->offset_y;
    super->hitbox->width = box->width;
    super->hitbox->height = box->height;

    if (this->unk_82 == 0) {
        super->speed = 0;
    }

    ProcessMovement0(super);
    GetNextFrame(super);
}

bool32 sub_080288A4(SpearMoblinEntity* this) {
    return (super->collisions & gUnk_080CC7D8[super->animationState >> 1]) != 0;
}

void sub_080288C0(SpearMoblinEntity* this) {
    Entity* entity = super->child;
    if ((entity != NULL) && (entity->contactFlags & 0x80)) {
        super->knockbackDirection = entity->knockbackDirection;
        super->iframes = -entity->iframes;
        super->knockbackSpeed = entity->knockbackSpeed;
        super->knockbackDuration = entity->knockbackDuration;
        entity->knockbackDuration = 0;
    }
}

// clang-format off
void (*const SpearMoblin_Functions[])(SpearMoblinEntity*) = {
    SpearMoblin_OnTick,
    SpearMoblin_OnCollision,
    (void (*)(SpearMoblinEntity*))GenericKnockback,
    (void (*)(SpearMoblinEntity*))GenericDeath,
    (void (*)(SpearMoblinEntity*))GenericConfused,
    SpearMoblin_OnGrabbed,
};

void (*const gUnk_080CC7A8[])(SpearMoblinEntity*) = {
    sub_08028314,
    sub_08028378,
    sub_08028420,
    sub_08028488,
    sub_08028528,
};

const u8 gUnk_080CC7BC[] = {
    0x30, 0x60, 0x6C, 0x91,
};

const s8 gUnk_080CC7C0[] = {
    0xF8, 0x0C,
    0xF4, 0x08,
    0x04, 0xFA,
    0x06, 0xFC,
    0x00, 0xFD,
    0x01, 0x03,
    0xFF, 0x02,
    0x00, 0xFE,
};

const s8 gUnk_080CC7D0[] = {
    0x08, 0x10,
    0x08, 0x00,
    0x10, 0x08,
    0x10, 0x00,
};

const u16 gUnk_080CC7D8[] = {
    0xE, 0xE000, 0xE0, 0xE00,
};
// clang-format on
