/**
 * @file bombPeahat.c
 * @ingroup Enemies
 *
 * @brief Bomb Peahat enemy
 */
#include "asm.h"
#include "enemy.h"
#include "entity.h"
#include "functions.h"
#include "object.h"
#include "player.h"

typedef struct {
    /*0x00*/ Entity base;
    /*0x68*/ u8 unused1[16];
    /*0x78*/ u8 unk_78;
    /*0x79*/ u8 unk_79;
    /*0x7a*/ u8 unk_7a;
    /*0x7b*/ u8 unk_7b;
    /*0x7c*/ u8 unused2[4];
    /*0x80*/ u8 unk_80;
    /*0x81*/ u8 unk_81;
    /*0x82*/ u16 unk_82;
} BombPeahatEntity;

void sub_0802AD1C(BombPeahatEntity*, u32);
void sub_0802AD54(BombPeahatEntity*);
void sub_0802B048(BombPeahatEntity*);
void sub_0802ACDC(BombPeahatEntity*, u32);
void sub_0802ADDC(BombPeahatEntity*);
void sub_0802AC40(BombPeahatEntity*);
bool32 sub_0802B234(BombPeahatEntity*);
BombPeahatEntity* sub_0802B250(BombPeahatEntity*);
void sub_0802B264(BombPeahatEntity*);

extern void (*const BombPeahat_Functions[])(BombPeahatEntity*);
extern void (*const gUnk_080CD108[])(BombPeahatEntity*);
extern void (*const gUnk_080CD120[])(BombPeahatEntity*);
extern void (*const gUnk_080CD134[])(BombPeahatEntity*);
extern void (*const gUnk_080CD140[])(BombPeahatEntity*);
extern void (*const gUnk_080CD158[])(BombPeahatEntity*);

extern const Hitbox gUnk_080CD16C;
extern const Hitbox gUnk_080CD174;
extern const Hitbox gUnk_080CD17C;

void BombPeahat(BombPeahatEntity* this) {
    s32 iVar1;

    if ((super->type == 2) && (iVar1 = sub_080012DC(super), iVar1)) {
        gUnk_080012C8[iVar1](super);
    } else {
        BombPeahat_Functions[GetNextFunction(super)](this);
    }
}

void BombPeahat_OnTick(BombPeahatEntity* this) {
    switch (super->type) {
        default:
            gUnk_080CD108[super->action](this);
            sub_0802AD54(this);
            break;
        case 2:
            gUnk_080CD120[super->action](this);
            sub_0802B048(this);
            break;
        case 3:
            gUnk_080CD134[super->action](this);
            break;
    }
}

void BombPeahat_OnCollision(BombPeahatEntity* this) {
    this->unk_81 = 0;
    if (super->type < 2) {
        InitializeAnimation(super, 0);
    }
}

void BombPeahat_OnGrabbed(BombPeahatEntity* this) {
    gUnk_080CD140[super->subAction](this);
}

void sub_0802A8E0(BombPeahatEntity* this) {
    super->subAction = 1;
    super->gustJarTolerance = 60;
}

void sub_0802A8EC(BombPeahatEntity* this) {
    sub_0806F4E8(super);
}

void sub_0802A8F4(BombPeahatEntity* this) {
    sub_0806F3E4(super);
}

void sub_0802A8FC(BombPeahatEntity* this) {
    if ((gPlayerState.field_0x1c & 0xf) == 0) {
        super->health = gPlayerState.field_0x1c & 0xf;
    }
}

void nullsub_143(BombPeahatEntity* this) {
}

void sub_0802A91C(BombPeahatEntity* this) {
    super->health = 0;
}

void sub_0802A924(BombPeahatEntity* this) {
    super->action = 1;
    super->subAction = 0;
    super->timer = 0;
    super->subtimer = 0;
    super->hitbox = (Hitbox*)&gUnk_080CD16C;
#ifdef EU
    super->collisionFlags |= 0x10;
#endif
    super->z.HALF.HI = -0x30;
    this->unk_80 = Random() & 1;
    this->unk_82 = 0;
    this->unk_7b = 0;
    this->unk_81 = 0;
    this->unk_7a = 0;
#ifndef EU
    this->unk_78 = 0;
    this->unk_79 = 0;
#endif
    super->collisionLayer = 3;
    super->spriteOrientation.flipY = 1;
    super->spriteRendering.b3 = 1;
    super->spritePriority.b0 = 1;
    InitializeAnimation(super, super->type + 1);
}

void sub_0802A9A8(BombPeahatEntity* this) {
    if (super->subtimer) {
        super->subtimer--;
    } else {
        if (super->timer != 0) {
            if (--super->timer == 0) {
                super->action = 2;
                super->subAction = 0;
                super->timer = 64;
                sub_0802ACDC(this, 8);
            }
        } else {
            if (gRoomControls.reload_flags != 0) {
                return;
            }
            super->spriteSettings.draw = 1;
            this->unk_82 = 1;
            if (this->unk_80) {
                super->x.HALF.HI = gRoomControls.scroll_x - 0x10;
                super->direction = DirectionEast;
            } else {
                super->x.HALF.HI = gRoomControls.scroll_x + 0x100;
                super->direction = DirectionWest;
            }
            super->y.HALF.HI = gRoomControls.scroll_y + 0x40;
            super->timer = 128;
            sub_0802ADDC(this);
        }
        sub_0802AC40(this);
    }
}

void sub_0802AA40(BombPeahatEntity* this) {
    sub_0802AC40(this);
    if (--super->subtimer == 0) {
        sub_0802ACDC(this, 8);
        this->unk_7b++;
    }
    if (super->timer != 0) {
        super->timer--;
    } else {
        BombPeahatEntity* entity = (BombPeahatEntity*)super->child;
        if (entity != NULL) {
            if (entity->base.next == NULL) {
                super->child = NULL;
            } else {
                u32 direction = DirectionRound(this->unk_80 * 0x10 + 0x18);

                if (0x20 < this->unk_7b) {
                    super->direction = direction;
                }
                if (super->direction == direction) {
                    super->action = 3;
                    super->speed = 0x180;
                    this->unk_7b = 0;
                    entity->unk_80 = 1;
                    entity->base.timer = 150;
                }
            }
        }
    }
}

void sub_0802AAC0(BombPeahatEntity* this) {
    sub_0802AC40(this);
    if (--super->subtimer == 0) {
        sub_0802AD1C(this, 4);
    }

    if (this->unk_81) {
        Entity* entity = super->child;
        if (entity == NULL) {
            this->unk_81 = 0;
        } else if (entity->next == NULL) {
            this->unk_81 = 0;
            super->child = NULL;
#ifdef EU
        } else if (entity->timer == 0) {
            if (sub_080B1B44(COORD_TO_TILE(super), 1) == 0) {
                if (EntityInRectRadius(super, &gPlayerEntity.base, 0x10, 0x10) && entity->subtimer <= 0x50) {
                    this->unk_81 = 0;
                }
            } else if (entity->subtimer <= 0x13) {
                this->unk_81 = 0;
            }
        }
#else
        } else if ((entity->timer == 0) && (entity->subtimer <= 0x50)) {
            this->unk_81 = 0;
        }
#endif
    } else {
        super->action = 4;
        super->timer = 192;
        super->subtimer = 4;
        this->unk_80 ^= 1;
#ifndef EU
        this->unk_78 = 0;
        this->unk_79 = 4;
        super->direction = DIR_NONE;
#endif
        InitializeAnimation(super, 0);
    }
}

#ifdef EU
void sub_0802AB40(BombPeahatEntity* this) {
    sub_0802AC40(this);
    if (--super->timer == 0) {
        if (this->unk_7a <= 4) {
            super->action = 5;
            super->speed = 0;
            InitializeAnimation(super, super->type + 1);
        } else {
            super->timer = 192;
            super->subtimer = 4;
            this->unk_80 ^= 1;
        }
    } else if (--super->subtimer == 0) {
        sub_0802ACDC(this, 4);
    }
}
#else
void sub_0802AB40(BombPeahatEntity* this) {
    sub_0802AC40(this);
    switch (this->unk_78) {
        case 0:
            if (super->z.HALF.HI) {
                if (--this->unk_79 == 0) {
                    this->unk_79 = 4;
                    super->z.HALF.HI++;
                }
            } else {
                this->unk_78 = 1;
                this->unk_79 = 0x30;
                super->direction = sub_08049F84(super, 0);
            }
            break;
        case 1:
            if (--this->unk_79 == 0) {
                this->unk_78 = 2;
                this->unk_79 = 4;
            } else if ((this->unk_79 & 7) == 0) {
                super->direction = sub_08049F84(super, 0);
            }
            break;
        case 2:
            if (-0x30 < super->z.HALF.HI) {
                if (--this->unk_79 == 0) {
                    this->unk_79 = 4;
                    super->z.HALF.HI--;
                    sub_0802ACDC(this, 4);
                }
            } else {
                super->action = 5;
                super->speed = 0;
                InitializeAnimation(super, super->type + 1);
            }
            break;
    }
}
#endif

void sub_0802AC08(BombPeahatEntity* this) {
    if (super->frame & ANIM_DONE) {
        super->action = 2;
        super->subAction = 0;
        super->timer = 64;
        super->speed = 0xc0;
        sub_0802ACDC(this, 8);
        sub_0802ADDC(this);
    }
    GetNextFrame(super);
}

void sub_0802AC40(BombPeahatEntity* this) {
    GetNextFrame(super);
    LinearMoveUpdate(super);
    if (this->unk_7a) {
        if (!sub_0802B234(this)) {
            this->unk_7a = 0;
            super->spritePriority.b1 = 0;
#ifndef EU
        } else {
            if (super->z.HALF.HI == 0) {
                if (super->spritePriority.b1 != 1) {
                    super->spritePriority.b1 = 1;
                }
            } else {
                if (super->spritePriority.b1 != 3) {
                    super->spritePriority.b1 = 3;
                }
            }
#endif
        }
    } else {
        if (sub_0802B234(this)) {
            this->unk_7a = 1;
#ifndef EU
            if (super->z.HALF.HI == 0) {
                super->spritePriority.b1 = 1;
            } else {
                super->spritePriority.b1 = 3;
            }
#else
            super->spritePriority.b1 = 3;
#endif
        }
    }
}

void sub_0802ACDC(BombPeahatEntity* this, u32 param_2) {
    u32 x;
    if (this->unk_80) {
        x = sub_080045B4(super, gRoomControls.scroll_x + 0x20, gRoomControls.scroll_y + 0x60);
    } else {
        x = sub_080045B4(super, gRoomControls.scroll_x + 0xd0, gRoomControls.scroll_y + 0x60);
    }
    sub_08004596(super, x);
    super->subtimer = param_2;
}

void sub_0802AD1C(BombPeahatEntity* this, u32 param_2) {
    Entity* entity = sub_08049DF4(1);
    if (entity != NULL) {
        u32 y = entity->y.HALF.HI - 0x18;
        u32 x = entity->x.HALF.HI;
        sub_08004596(super, sub_080045B4(super, x, y));
        super->subtimer = param_2;
    } else {
        sub_0802ACDC(this, param_2);
    }
}

void sub_0802AD54(BombPeahatEntity* this) {
    if (this->unk_82 != 0) {
        if (gRoomControls.reload_flags != 0) {
            super->spriteSettings.draw = 0;
            DeleteEntity(super);
            return;
        }
        if (gRoomControls.origin_y > super->y.HALF.HI ||
            (gRoomControls.origin_y + gRoomControls.height + 0x20) < super->y.HALF.HI) {
            super->spriteSettings.draw = 0;
        } else {
            super->spriteSettings.draw = 1;
        }
    }
    if (this->unk_81) {
        if (super->child == NULL || super->child->next == NULL) {
            super->action = 4;
            super->timer = 192;
            super->subtimer = 4;
            this->unk_80 ^= 1;
        }
    }
}

void sub_0802ADDC(BombPeahatEntity* this) {
    Entity* entity = CreateEnemy(BOMB_PEAHAT, super->type + 2);
    if (entity != NULL) {
        entity->type2 = !!sub_0802B234(this);
        entity->parent = super;
        super->child = entity;
        CopyPosition(super, entity);
#ifdef EU
        entity->z.HALF.HI += 8;
#endif
        this->unk_81 = 1;
        if (super->type == 0) {
            this->unk_7a++;
        }
    }
}

void sub_0802AE24(BombPeahatEntity* this) {
    super->action = 1;
    super->timer = 240;
    super->zVelocity = Q_16_16(0.5);
    super->hitbox = (Hitbox*)&gUnk_080CD174;
    super->collisionFlags = 3;
    super->carryFlags = 0;
    super->gustJarFlags = 2;
    this->unk_81 = 0;
    this->unk_7b = 1;
    InitializeAnimation(super, 3);
}

#ifdef EU
void sub_0802AE68(BombPeahatEntity* this) {
    Entity* entity = super->parent;
    if (entity == NULL) {
        super->action = 3;
        super->spriteSettings.draw = 1;
        this->unk_80 = 1;
    }

    if (((BombPeahatEntity*)entity)->unk_81) {
        CopyPosition(entity, super);
        super->z.HALF.HI += 8;
        super->spriteSettings.draw = 0;
    } else {
        super->action = 3;
        this->unk_80 = 1;
        super->spriteSettings.draw = 1;
        if (sub_0802B234(this)) {
            super->spritePriority.b1 = 3;
        } else {
            super->spritePriority.b1 = 0;
        }
        GetNextFrame(super);
    }
}
#else
void sub_0802AE68(BombPeahatEntity* this) {
    BombPeahatEntity* entity = sub_0802B250(this);
    if (entity == NULL) {
        super->action = 3;
        this->unk_80 = 1;
        sub_0802B264(this);
    } else {
        if (entity->unk_81) {
            CopyPosition(&entity->base, super);
            super->spriteSettings.draw = 0;
        } else {
            super->action = 3;
            this->unk_80 = 1;
            sub_0802B264(this);
        }
    }
}
#endif

void sub_0802AEBC(BombPeahatEntity* this) {
    gUnk_080CD158[super->subAction](this);
}

void sub_0802AED4(BombPeahatEntity* this) {
    GetNextFrame(super);
    if (this->unk_81 < 2) {
        if (BounceUpdate(super, Q_8_8(40.0)) == BOUNCE_INIT_NEXT) {
            this->unk_81++;
        }
    } else {
        if (!sub_0800442E(super)) {
            RegisterCarryEntity(super);
        }
    }
    if (super->type2 != 0 && this->unk_81) {
        super->timer = 0;
        super->subtimer = 1;
    }
}

void sub_0802AF28(BombPeahatEntity* this) {
    super->subAction = 1;
    sub_08079BD8(super);
    super->spritePriority.b1 = 2;
    GetNextFrame(super);
}

void sub_0802AF50(BombPeahatEntity* this) {
    GetNextFrame(super);
}

void sub_0802AF58(BombPeahatEntity* this) {
    GetNextFrame(super);
    super->spritePriority.b1 = 3;
}

void sub_0802AF74(BombPeahatEntity* this) {
    super->action = 3;
    super->subAction = 0;
    if (super->timer > 60) {
        super->timer = 60;
    }
    GetNextFrame(super);
}

void sub_0802AF94(BombPeahatEntity* this) {
    DeleteEntity(super);
}

void sub_0802AF9C(BombPeahatEntity* this) {
    GetNextFrame(super);
    if (--super->timer == 0) {
        if (((BombPeahatEntity*)super->parent)->unk_7a) {
            ((BombPeahatEntity*)super->parent)->unk_7a--;
        }
        DeleteEntity(super);
    }
}

void sub_0802AFC8(BombPeahatEntity* this) {
    u32 subtimer = super->subtimer;
    u32 flag = 8;
    if (subtimer < 0x29) {
        u32 tmp, tmp2;
        flag = 4;
        if (this->unk_82 & 0x8000) {
            this->unk_82 -= 0x10;
        } else {
            this->unk_82 += 0x10;
        }
        tmp = this->unk_82 & 0xf0;
        if (tmp == 0 || tmp > 0x80) {
            this->unk_82 ^= 0x8000;
        }
        tmp = this->unk_82 & 0xf0;
        SetAffineInfo(super, 0x130 - tmp, 0x130 - tmp, 0);
    }
    if (super->subtimer & flag) {
        super->palette.b.b0 = super->palette.b.b4;
    } else {
        super->palette.b.b0 = 0;
    }
}

void sub_0802B048(BombPeahatEntity* this) {
    Entity* entity;
    u32 action;

    if (this->unk_7b) {
        if (gRoomControls.origin_y > super->y.HALF.HI ||
            (gRoomControls.origin_y + gRoomControls.height + 0x20) < super->y.HALF.HI) {
            COLLISION_OFF(super);
        } else {
            COLLISION_ON(super);
        }
    }

    entity = super->parent;
    if ((entity != NULL) && super->action == 1) {
        super->spriteRendering.b3 = entity->spriteRendering.b3;
        super->spritePriority.b0 = entity->spritePriority.b0;
        super->spriteOrientation.flipY = entity->spriteOrientation.flipY;
    }

    action = super->action;
    if (action != 4 && this->unk_80) {
        if (super->timer != 0) {
            if (--super->timer == 0) {
                super->subtimer = 80;
                this->unk_82 = 0;
                super->spriteRendering.b0 = 3;
                SetAffineInfo(super, 0x100, 0x100, 0);
            }
        } else {
            if (super->subtimer) {
                if (--super->subtimer == 0) {
                    if (action == 2 && super->subAction == 1) {
                        gPlayerState.heldObject = 0;
                    }
                    sub_0805EC60(super);
                    super->action = 4;
                    super->hitbox = (Hitbox*)&gUnk_080CD17C;
                    super->timer = 15;
                    super->spriteSettings.draw = 0;
                    COLLISION_ON(super);
                    this->unk_7b = 0;
                    FreeCarryEntity(super);
                    if (super->parent->next != NULL) {
                        ((BombPeahatEntity*)super->parent)->unk_81 = 0;
                    }
                    entity = CreateObjectWithParent(super, SMOKE_PARTICLE, 0, 0);
                    if (entity != NULL) {
                        super->collisionLayer = 1;
                    } else {
                        DeleteEntity(super);
                    }
                } else {
                    sub_0802AFC8(this);
                }
            }
        }
    }
}

void sub_0802B1A0(BombPeahatEntity* this) {
    super->action = 1;
    super->timer = 0;
    super->subtimer = 79;
    this->unk_81 = 1;
    InitializeAnimation(super, 0);
}

#ifdef EU
void sub_0802B1BC(BombPeahatEntity* this) {
    Entity* entity;

    if (super->timer != 0) {
        super->timer--;
    }

    entity = super->parent;
    if (entity == NULL) {
        super->action = 2;
        super->spriteSettings.draw = 1;
    }

    if (((BombPeahatEntity*)entity)->unk_81) {
        CopyPosition(entity, super);
        super->z.HALF.HI += 8;
    } else {
        super->action = 2;
        super->spriteSettings.draw = 1;
        if (sub_0802B234(this)) {
            super->spritePriority.b1 = 3;
        } else {
            super->spritePriority.b1 = 0;
        }

        GetNextFrame(super);
    }
}
#else
void sub_0802B1BC(BombPeahatEntity* this) {
    BombPeahatEntity* entity;

    if (super->timer != 0) {
        super->timer--;
    }

    entity = sub_0802B250(this);
    if (entity == NULL) {
        super->action = 2;
        sub_0802B264(this);
    } else {
        if (entity->unk_81) {
            CopyPosition(&entity->base, super);
        } else {
            super->action = 2;
            sub_0802B264(this);
        }
    }
}
#endif

void sub_0802B204(BombPeahatEntity* this) {
    if (BounceUpdate(super, Q_8_8(40.0)) == BOUNCE_INIT_NEXT) {
        Entity* entity = CreateEnemy(BOBOMB, 1);
        if (entity != NULL) {
            CopyPosition(super, entity);
            entity->parent = super->parent;
        }
        DeleteThisEntity();
    }
}

bool32 sub_0802B234(BombPeahatEntity* this) {
    bool32 ret = FALSE;
    if (GetTileTypeByEntity(super) - 0x1c4 > 1)
        ret = TRUE;
    return ret;
}

#ifndef EU
BombPeahatEntity* sub_0802B250(BombPeahatEntity* this) {
    BombPeahatEntity* parent = (BombPeahatEntity*)super->parent;
    if ((parent != NULL) && (parent->base.next == NULL)) {
        parent = NULL;
    }
    return parent;
}

void sub_0802B264(BombPeahatEntity* this) {
    super->spriteSettings.draw = 1;
    if (sub_0802B234(this)) {
        super->spritePriority.b1 = 3;
    } else {

        super->spritePriority.b1 = 0;
    }
    GetNextFrame(super);
}
#endif

// clang-format off
void (*const BombPeahat_Functions[])(BombPeahatEntity*) = {
    BombPeahat_OnTick,
    BombPeahat_OnCollision,
    (void (*)(BombPeahatEntity*))GenericKnockback,
    (void (*)(BombPeahatEntity*))GenericDeath,
    (void (*)(BombPeahatEntity*))GenericConfused,
    BombPeahat_OnGrabbed,
};

void (*const gUnk_080CD108[])(BombPeahatEntity*) = {
    sub_0802A924,
    sub_0802A9A8,
    sub_0802AA40,
    sub_0802AAC0,
    sub_0802AB40,
    sub_0802AC08,
};

void (*const gUnk_080CD120[])(BombPeahatEntity*) = {
    sub_0802AE24,
    sub_0802AE68,
    sub_0802AEBC,
    sub_0802AED4,
    sub_0802AF9C,
};

void (*const gUnk_080CD134[])(BombPeahatEntity*) = {
    sub_0802B1A0,
    sub_0802B1BC,
    sub_0802B204,
};

void (*const gUnk_080CD140[])(BombPeahatEntity*) = {
    sub_0802A8E0,
    sub_0802A8EC,
    sub_0802A8F4,
    sub_0802A8FC,
    nullsub_143,
    sub_0802A91C,
};

void (*const gUnk_080CD158[])(BombPeahatEntity*) = {
    sub_0802AF28,
    sub_0802AF50,
    sub_0802AF58,
    sub_0802AF74,
    sub_0802AF94,
};

const Hitbox gUnk_080CD16C = {
#ifdef EU
    0x00, 0xFD, 0x05, 0x03, 0x03, 0x05, 0x06, 0x06,
#else
    0x00, 0xFB, 0x05, 0x03, 0x03, 0x05, 0x06, 0x06,
#endif
};

#ifdef EU
const u8 unk_data[] = { // TODO
    0x0c, 0x0, 0x0, 0x0
};
#endif

const Hitbox gUnk_080CD174 = {
    0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x04, 0x04,
};


const Hitbox gUnk_080CD17C = {
    0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x16, 0x16,
};

// clang-format on
