/**
 * @file vaatiBall.c
 * @ingroup Enemies
 *
 * @brief Vaati Ball enemy
 */

#include "entity.h"
#include "enemy.h"
#include "player.h"
#include "object.h"
#include "functions.h"

typedef struct {
    s8 h, v;
} PACKED PosOffset;

extern void (*const gUnk_080D1604[])(Entity*);
extern PosOffset gUnk_080D1620[4];

extern void sub_08044E74(Entity*, u32);

void VaatiBall(Entity* this) {
    Entity* parent;

    parent = this->parent;
    if (this->action && this->action != 3) {
        this->x.WORD += parent->x.WORD - *(int*)&parent->field_0x78;
        this->y.WORD += parent->y.WORD - parent->field_0x7c.WORD;
    }

    gUnk_080D1604[this->action](this);

    if (this->cutsceneBeh.HALF.LO) {
        this->health = -1;
    }

    if (this->bitfield & 0x80) {
        if ((this->bitfield & 0x3f) == 0 && this->action == 6) {
            ModHealth(-2);
        }

#ifdef EU
        if (this->health < 0xfa) {
#else
        if (this->health < 0xfd) {
#endif
            this->spriteSettings.draw = 0;
            COLLISION_OFF(this);
            this->health = -1;
            parent->field_0x80.HALF.LO--;
            CreateDust(this);
            SoundReq(SFX_1C3);
        }
    }
}

void sub_0804468C(Entity* this) {
    PosOffset* off;

    this->collisionLayer = 3;
    this->spriteRendering.b3 = 2;
    this->spritePriority.b0 = 5;
    this->cutsceneBeh.HALF.LO = 0;
    switch (this->type) {
        case 0:
            this->action = 1;
            this->actionDelay = 1;
            this->direction = (this->field_0x78.HALF.HI * 8) & 0x1f;
            this->field_0x78.HALF.LO = 0;
            this->field_0x82.HALF.HI = 0;
            this->spriteSettings.draw = 0;
            off = &gUnk_080D1620[this->field_0x78.HALF.HI & 3];
            PositionRelative(this->parent, this, Q_16_16(off->h), Q_16_16(off->v - 0x10));
            this->z.HALF.HI = this->parent->z.HALF.HI;
            InitAnimationForceUpdate(this, 0);
            break;
        case 1:
            this->action = 3;
            this->field_0x74.HALF.LO = 0;
            this->field_0x82.HALF.HI = 1;
            this->spriteSettings.draw = 1;
            InitAnimationForceUpdate(this, 1);
            break;
    }
}

void sub_0804474C(Entity* this) {
    switch (this->parent->action) {
        case 3:
            this->action = 3;
            this->field_0x74.HALF.LO = 0;
            this->actionDelay = 0;
            break;
        case 5:
            this->action = 5;
            this->field_0x74.HALF.LO = 0;
            this->field_0xf = 1;
            break;
        case 6:
            this->action = 6;
            this->field_0x74.HALF.LO = 0;
            this->field_0xf = 32;
            break;
        case 2:
            this->action = 2;
            this->field_0x74.HALF.LO = 0;
            this->hitType = 0;
            break;
    }

    if (this->action != 1) {
        UpdateAnimationSingleFrame(this);
    } else {
        if (--this->actionDelay == 0) {
            this->actionDelay = 2;
            this->direction++;
            this->direction &= 0x1f;
        }
        LinearMoveUpdate(this);
        UpdateAnimationSingleFrame(this);
    }
}

void sub_080447E0(Entity* this) {
    Entity* vaati = this->parent;

    if (vaati->action == 1) {
        this->action = 1;
        this->hitType = 43;
        sub_08044E74(this, 0);
        if (this->flags & ENT_COLLIDE)
            this->spriteSettings.draw = 1;
    } else {
        this->field_0x76.HALF.HI++;
        this->field_0x76.HALF.HI &= 7;
        if (this->flags & ENT_COLLIDE) {
            if (this->field_0x76.HALF.HI & 1) {
                this->spriteSettings.draw = 1;
            } else {
                this->spriteSettings.draw = 0;
            }
        }

        if (vaati->field_0x74.HALF.LO == 2) {
            sub_08044E74(this, 2);
        }
    }
}

extern void sub_08044DEC(Entity*);

void sub_08044868(Entity* this) {
    Entity* vaati = this->parent;

    switch (vaati->field_0x74.HALF.LO) {
        case 0xfe:
            if (this->field_0x82.HALF.HI && this->field_0x74.HALF.LO == 2)
                DeleteThisEntity();
            break;
        case 0xff:
            if (this->field_0x82.HALF.HI) {
                switch (this->field_0x74.HALF.LO) {
                    case 0:
                        if (this->frame & 0x80) {
                            this->field_0x74.HALF.LO = 1;
                            this->direction = sub_080045B4(this, vaati->x.HALF.HI, vaati->y.HALF.HI - 0x10);
                            this->speed = 0x180;
                        }
                        break;
                    case 1:
                        LinearMoveUpdate(this);
                        if (EntityWithinDistance(this, vaati->x.HALF.HI, vaati->y.HALF.HI - 0x10, 0xc)) {
                            this->field_0x74.HALF.LO++;
                            this->x.HALF.HI = vaati->x.HALF.HI;
                            this->y.HALF.HI = vaati->y.HALF.HI - 0x10;
                            vaati->actionDelay++;
                        } else {
                            this->direction = sub_080045B4(this, vaati->x.HALF.HI, vaati->y.HALF.HI - 0x10);
                        }
                        break;
                }
            }
            break;
        case 1:
            if (this->field_0x82.HALF.HI == 0) {
                this->field_0x82.HALF.HI++;
                this->spriteSettings.draw = 1;
            }
            sub_08044DEC(this);
            if (*(u8*)&vaati->field_0x86 > 1) {
                u8 draw = this->spriteSettings.draw;
                if (draw == 1 && this->cutsceneBeh.HALF.LO == 0) {
                    vaati = CreateProjectileWithParent(this, V1_DARK_MAGIC_PROJECTILE, 0);
                    if (vaati) {
                        vaati->type2 = 1;
                        vaati->parent = this;
                        this->cutsceneBeh.HALF.LO = 1;
                        this->hitType = 0;
                    }
                }
            }
            this->actionDelay = 32;
            break;
        case 2:
            if (this->actionDelay)
                if (--this->actionDelay < 0x11)
                    LinearMoveUpdate(this);
            break;
        case 3: {
            u8 draw;

            sub_08044E74(this, 1);
            draw = this->spriteSettings.draw;
            if (draw == 1) {
                COLLISION_ON(this);
            } else {
                COLLISION_OFF(this);
            }
            break;
        }
    }
    UpdateAnimationSingleFrame(this);
}

void sub_080449F8(Entity* this) {
    Entity* vaati = this->parent;

    UpdateAnimationSingleFrame(this);
    if (vaati->action == 1) {
        sub_08044E74(this, 0);
        return;
    }

    switch (vaati->field_0x74.HALF.LO) {
        case 0:
            LinearMoveUpdate(this);
            if (--this->actionDelay)
                break;

            if (this->field_0x78.HALF.LO == 0) {
                this->field_0x78.HALF.LO++;
                this->speed = 640;
            }

            this->actionDelay = 4;
            this->direction++;
            this->direction &= 0x1f;
            if (vaati->field_0x80.HALF.LO == 0)
                vaati->field_0x74.HALF.LO = 1;
            break;
        case 1:
            switch (this->actionDelay) {
                case 2:
                    if (vaati->field_0x80.HALF.LO == 0) {
                        this->field_0xf = 0;
                        sub_08044E74(this, 0);
                    }
                    break;
                case 1:
                    this->direction = (this->direction + 0x10) & 0x1f;
                    LinearMoveUpdate(this);
                    this->direction = (this->direction + 0x10) & 0x1f;
                    this->actionDelay = 2;
                    break;
                case 3:
                    LinearMoveUpdate(this);
                    this->actionDelay = 2;
                    break;
                case 4:
                    LinearMoveUpdate(this);
                    LinearMoveUpdate(this);
                    this->actionDelay = 2;
                    break;
            }
            break;
        case 2:
            if (this->field_0x74.HALF.LO == 0) {
                u8 draw;

                this->field_0x74.HALF.LO++;
                draw = this->spriteSettings.draw;
                if (draw) {
                    vaati = CreateProjectileWithParent(this, V1_EYE_LASER, 0);
                    if (vaati) {
                        vaati->y.HALF.HI += 4;
                        vaati->parent = this;
                        this->child = vaati;
                    }
                }
            }
            break;
        case 3:
            if (this->field_0xf) {
                this->field_0xf = 0;
            }
            break;
    }
}

void sub_08044B04(Entity* this) {
    Entity* vaati = this->parent;

    UpdateAnimationSingleFrame(this);
    if (vaati->action == 1) {
        sub_08044E74(this, 0);
        this->actionDelay = 32;
        return;
    }

    switch (vaati->field_0x74.HALF.LO) {
        case 0:
            LinearMoveUpdate(this);
            switch (this->field_0x74.HALF.LO) {
                case 0:
                    if (--this->actionDelay == 0) {
                        this->actionDelay = this->field_0x78.HALF.LO ? 4 : 2;
                        this->direction++;
                        this->direction &= 0x1f;
                    }
                    if (--this->field_0xf == 0) {
                        if (this->actionDelay != 2) {
                            this->field_0xf = 1;
                        } else {
                            if (++this->field_0x78.HALF.LO > 2) {
                                this->field_0x74.HALF.LO++;
                                this->field_0x78.HALF.LO = 1;
                                this->actionDelay = 4;
                                this->field_0xf = 0;
                            } else {
                                this->field_0xf = 32;
                            }

                            this->speed = this->field_0x78.HALF.LO ? 640 : 1280;
                        }
                    }
                    break;
                case 1:
                    if (--this->actionDelay == 0) {
                        this->actionDelay = 6;
                        this->direction = (this->direction + 1) & 0x1f;
                        if (++this->field_0xf == 0x30) {
                            u32 direction = sub_080045B4(this, vaati->x.HALF.HI, vaati->y.HALF.HI - 0x10);
                            this->speed = 0;
                            this->direction = (direction + 16) & 0x1f;
                            this->actionDelay = 16;
                            this->field_0xf = 16;
                            this->field_0x74.HALF.LO++;
                        }
                    }
                    break;
                case 2:
                    if (this->field_0xf == 0) {
                        switch (--this->actionDelay) {
                            case 12:
                                this->speed = 1280;
                                break;
                            case 0:
                                this->field_0x74.HALF.LO++;
                                this->direction = sub_080045B4(this, vaati->x.HALF.HI, vaati->y.HALF.HI - 0x10);
                                this->speed = 0;
                                this->actionDelay = 4;
                                this->field_0xf = 16;
                                break;
                            case 4:
                                this->speed = 640;
                                break;
                        }
                    } else {
                        if (--this->field_0xf == 0)
                            this->speed = 640;
                    }
                    break;
                case 3:
                    if (this->field_0xf) {
                        if (--this->field_0xf == 0)
                            this->speed = 640;
                    } else {
                        if (this->actionDelay) {
                            if (--this->actionDelay == 0) {
                                this->speed = 1280;
                                SoundReq(SFX_14F);
                            }
                        }
                        if (this->field_0x78.HALF.HI == 3)
                            if (EntityWithinDistance(this, vaati->x.HALF.HI, vaati->y.HALF.HI - 0x10, 0xc))
                                vaati->actionDelay++;
                        this->direction = sub_080045B4(this, vaati->x.HALF.HI, vaati->y.HALF.HI - 0x10);
                        return;
                    }
                    break;
            }
            break;
        case 1:
            switch (this->field_0x74.HALF.LO) {
                case 3:
                    this->field_0x74.HALF.LO = 1;
                    this->actionDelay = 80;
                    COLLISION_OFF(this);
                    PositionRelative(vaati, this, 0, Q_16_16(-16.0));
                    if (this->field_0xf)
                        this->spriteSettings.draw = 0;
                    break;
                case 1:
                    if (--this->actionDelay == 0) {
                        this->field_0x74.HALF.LO = 0;
                        this->actionDelay = 32;
                        this->field_0xf = 4;
                    }
                    break;
            }
            break;
        case 2:
            /* ... */
            break;
        case 3:
            switch (this->field_0x74.HALF.LO) {
                case 0:
                    if (this->field_0xf) {
                        if (--this->field_0xf == 0) {
                            sub_08044DEC(this);
                            this->field_0x74.HALF.LO = 1;
                            this->actionDelay = 16;
                        }
                    }
                    break;
                case 1:
                    LinearMoveUpdate(this);
                    if (--this->actionDelay == 0)
                        this->field_0x74.HALF.LO++;
                    break;
                case 2: {
                    u8 draw;

                    sub_08044E74(this, 1);
                    draw = this->spriteSettings.draw;
                    if (draw == 1) {
                        COLLISION_ON(this);
                    } else {
                        COLLISION_OFF(this);
                    }
                    vaati->actionDelay++;
                    break;
                }
            }
            break;
    }
}

extern u8 gUnk_080D1628[4][4];

void sub_08044DEC(Entity* this) {
    u32 off;

    if (this->parent->field_0x80.HALF.LO > this->field_0x78.HALF.HI) {
        this->spriteSettings.draw = 1;
        this->health = -1;
    } else {
        this->spriteSettings.draw = 0;
    }
    COLLISION_OFF(this);
    this->field_0x78.HALF.LO = 0;
    this->speed = 0x300;
    off = this->parent->field_0x80.HALF.LO - 1;
    this->direction = gUnk_080D1628[off][this->field_0x78.HALF.HI];
    PositionRelative(this->parent, this, 0, Q_16_16(-16.0));
}

void sub_08044E74(Entity* this, u32 state) {
    this->action = 1;
    this->field_0x74.HALF.LO = 0;

    switch (state) {
        case 2:
            this->action = 2;
            this->direction += 0x18;
            this->direction &= 0x1f;
            PositionRelative(this->parent, this, 0, Q_16_16(-16.0));
            this->speed = 12288;
            LinearMoveUpdate(this);
            this->direction += 0x8;
            this->direction &= 0x1f;
            this->speed = 1280;
            LinearMoveUpdate(this);
            break;
        case 1:
            this->direction = (this->direction + 8) & 0x1f;
            break;
        case 0:
            if (this->field_0x78.HALF.LO) {
                switch (this->actionDelay) {
                    case 3 ... 4:
                        do {
                            LinearMoveUpdate(this);
                        } while (this->actionDelay-- != 3);
                        break;
                    case 1:
                        this->direction = (this->direction + 0x10) & 0x1f;
                        LinearMoveUpdate(this);
                        this->direction = (this->direction + 0x10) & 0x1f;
                        break;
                }
            } else {
                if (this->actionDelay == 2) {
                    LinearMoveUpdate(this);
                }
            }
            break;
    }
    this->actionDelay = 1;
    this->field_0x78.HALF.LO = 0;
    this->speed = Q_8_8(5);
}
