/**
 * @file beanstalk.c
 * @ingroup Objects
 *
 * @brief Beanstalk object
 */

#define NENT_DEPRECATED
#include "global.h"
#include "object.h"
#include "functions.h"

typedef struct {
    /*0x00*/ Entity base;
    /*0x68*/ u8 unk_68[4];
    /*0x6c*/ u32 unk_6c;
    /*0x70*/ u16 unk_70;
    /*0x72*/ u16 unk_72;
} BeanstalkEntity;

typedef struct {
    u8 type;
    s8 x;
    s8 y;
    u8 flipX;
} struct_08120DFC;

typedef struct {
    u8 unk_0;
    u8 unk_1;
} struct_08120E3C;

void Beanstalk_Action1(BeanstalkEntity*);
void Beanstalk_Init(BeanstalkEntity*);
void Beanstalk_Action1Type0(BeanstalkEntity*);
void Beanstalk_Action1Type1(BeanstalkEntity*);
void Beanstalk_Action1Type2(BeanstalkEntity*);
void Beanstalk_Action1Type4(BeanstalkEntity*);
void Beanstalk_Action1Type7(BeanstalkEntity*);
void Beanstalk_Action1Type8(BeanstalkEntity*);
void Beanstalk_Action1Type9(BeanstalkEntity*);
void Beanstalk_Action1Type0SubAction0(BeanstalkEntity*);
void Beanstalk_Action1Type0SubAction1(BeanstalkEntity*);
void Beanstalk_Action1Type0SubAction2(BeanstalkEntity*);
void Beanstalk_Action1Type8SubAction0(BeanstalkEntity*);
void Beanstalk_Action1Type8SubAction1(BeanstalkEntity*);

void Beanstalk(BeanstalkEntity* this) {
    static void (*const Beanstalk_Actions[])(BeanstalkEntity*) = {
        Beanstalk_Init,
        Beanstalk_Action1,
    };
    Beanstalk_Actions[super->action](this);
}

const struct_08120DFC gUnk_08120DFC[] = {
    { 4, -2, 8, 0 }, { 6, 10, 16, 1 }, { 5, -4, 33, 0 }, { 6, -7, 7, 0 }, { 4, 5, 33, 1 }, { 5, 2, 37, 1 },
};
void Beanstalk_Init(BeanstalkEntity* this) {
    static const struct_08120E3C gUnk_08120E14[] = {
        { 0, 3 }, { 1, 3 }, { 0, 3 }, { 2, 3 }, { 3, 2 }, { 6, 2 }, { 9, 4 }, { 0, 3 }, { 0, 4 }, { 12, 6 },
    };
    static const struct_08120E3C gUnk_08120E3C[] = {
        { 1, 6 }, { 2, 6 }, { 4, 3 }, { 7, 5 }, { 10, 7 },
    };

    Entity* obj;
    const struct_08120DFC* ptr;

    super->spriteRendering.b3 = 2;
    super->spriteOrientation.flipY = 1;
    SetDefaultPriority(super, 4);
    if (super->type == 7) {
#ifndef EU
        if ((super->flags & ENT_DID_INIT) == 0) {
            return;
        }
#endif
        if (super->type2 == 0 && super->actionDelay != 0) {
            if (CheckLocalFlag(super->actionDelay) == 0) {
                return;
            }
            super->spriteOrientation.flipY = 2;
        }
        InitializeAnimation(super, gUnk_08120E3C[super->type2].unk_0);
        super->spritePriority.b0 = gUnk_08120E3C[super->type2].unk_1;
    } else {
        if (super->type != 0) {
            InitializeAnimation(super, gUnk_08120E14[super->type].unk_0);
        } else {
            LoadSwapGFX(super, 1, 2);
            InitAnimationForceUpdate(super, gUnk_08120E14[super->type].unk_0);
        }
        super->spritePriority.b0 = gUnk_08120E14[super->type].unk_1;
    }
    super->action = 1;
    super->spriteSettings.draw = 1;

    switch (super->type) {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
            super->spriteVramOffset = 0x1c0;
            super->actionDelay = 0;
            break;
        case 7:
            switch (super->type2) {
                case 0:
                    this->unk_6c = 0x10;
                    super->actionDelay = 0;
                    do {
                        obj = CreateObject(BEANSTALK, 7, 1);
                        super->child = obj;
                        if (obj != NULL) {
                            obj->x.HALF.HI = super->x.HALF.HI;
                            (super->child)->y.HALF.HI = super->y.HALF.HI - (s16)this->unk_6c;
                            super->field_0xf = 4;
                            while (--super->field_0xf != 0) {
                                ptr = &gUnk_08120DFC[super->actionDelay];
                                obj = CreateObject(BEANSTALK, 7, ptr->type - 2);
                                super->child = obj;
                                if (obj != NULL) {
                                    obj->x.HALF.HI = super->x.HALF.HI + ptr->x;
                                    (super->child)->y.HALF.HI = (super->y.HALF.HI - (s16)this->unk_6c) - ptr->y;
                                    super->child->spriteSettings.flipX = ptr->flipX;
                                }
                                super->actionDelay++;
                            }
                            if (super->actionDelay > 5) {
                                super->actionDelay = 0;
                            }
                        }
                        this->unk_6c += 0x38;
                    } while (((s16)(super->y.HALF_U.HI - this->unk_6c)) >= gRoomControls.origin_y);
                    super->actionDelay = 1;
                    break;
                case 2:
                case 3:
                    super->spriteRendering.b3 = 1;
                    // fallthrough
                case 4:
                    super->collisionLayer = 3;
            }
            break;
    }
    Beanstalk_Action1(this);
}

void Beanstalk_Action1(BeanstalkEntity* this) {
    static void (*const Beanstalk_Action1Types[])(BeanstalkEntity*) = {
        Beanstalk_Action1Type0, Beanstalk_Action1Type1, Beanstalk_Action1Type2, Beanstalk_Action1Type1,
        Beanstalk_Action1Type4, Beanstalk_Action1Type4, Beanstalk_Action1Type4, Beanstalk_Action1Type7,
        Beanstalk_Action1Type8, Beanstalk_Action1Type9,
    };
    Beanstalk_Action1Types[super->type](this);
}

void Beanstalk_Action1Type0(BeanstalkEntity* this) {
    static void (*const Beanstalk_Action1Type0SubActions[])(BeanstalkEntity*) = {
        Beanstalk_Action1Type0SubAction0,
        Beanstalk_Action1Type0SubAction1,
        Beanstalk_Action1Type0SubAction2,
    };
    Beanstalk_Action1Type0SubActions[super->subAction](this);
}

void Beanstalk_Action1Type0SubAction0(BeanstalkEntity* this) {
    UpdateAnimationSingleFrame(super);
    if ((super->frame & 0x80) != 0) {
        if (super->actionDelay < 2) {
            super->actionDelay++;
        } else {
            InitAnimationForceUpdate(super, 1);
            super->actionDelay = 0x10;
            super->subAction++;
        }
    }
}

void Beanstalk_Action1Type0SubAction1(BeanstalkEntity* this) {
    if (super->actionDelay-- == 0) {
        super->actionDelay = 0x10;
        SoundReq(SFX_198);
    }
    UpdateAnimationSingleFrame(super);
    if ((super->frame & 0x80) != 0) {
        Entity* entity = CreateObject(BEANSTALK, 1, 0);
        super->child = entity;
        if (entity != NULL) {
            entity->x.HALF.HI = super->x.HALF.HI;
            (super->child)->y.HALF.HI = super->y.HALF.HI;
        }
        entity = CreateObject(BEANSTALK, 2, 0);
        super->child = entity;
        if (entity != NULL) {
            entity->x.HALF.HI = super->x.HALF.HI;
            (super->child)->y.HALF.HI = super->y.HALF.HI + -0x10;
        }
        super->spriteSettings.draw = 0;
        super->subAction++;
    }
}

void Beanstalk_Action1Type0SubAction2(BeanstalkEntity* this) {
    if (super->actionDelay-- == 0) {
        super->actionDelay = 0x10;
        SoundReq(SFX_198);
    }
}

void Beanstalk_Action1Type2(BeanstalkEntity* this) {
    s32 tmp;
    const struct_08120DFC* ptr;

    GetNextFrame(super);
    if ((super->frame & 1) != 0) {
        ptr = &gUnk_08120DFC[super->actionDelay];
        super->child = CreateObject(BEANSTALK, ptr->type, 0);
        if (super->child != NULL) {
            super->child->x.HALF.HI = super->x.HALF.HI + ptr->x;
            super->child->y.HALF.HI = super->y.HALF.HI - ptr->y;
            super->child->spriteSettings.flipX = ptr->flipX;
        }
        if (++super->actionDelay > 5) {
            super->actionDelay = 0;
        }
    }
    if ((super->frame & 0x80) != 0) {
        super->child = CreateObject(BEANSTALK, 3, 0);
        if (super->child != NULL) {
            super->child->x.HALF.HI = super->x.HALF.HI;
            super->child->y.HALF.HI = super->y.HALF.HI;
        }
        tmp = super->y.HALF.HI -= 0x38;
        if (tmp * 0x10000 < 0) {
            DeleteEntity(super);
        }
    }
}

void Beanstalk_Action1Type4(BeanstalkEntity* this) {
    if ((super->frame & 0x80) == 0) {
        GetNextFrame(super);
    }
}

void Beanstalk_Action1Type1(BeanstalkEntity* this) {
}

void Beanstalk_Action1Type7(BeanstalkEntity* this) {
    switch (super->type2) {
        case 0:
            if (super->actionDelay == 0) {
                return;
            }
            this->unk_6c = gRoomControls.origin_y;
            this->unk_70 = TILE(super->x.HALF.HI, this->unk_6c);
            while (TRUE) {
                this->unk_70 = TILE(super->x.HALF.HI, this->unk_6c);
                SetTile(0x4017, this->unk_70, 2);
                SetTile(0x4017, this->unk_70, 1);
                this->unk_6c += 0x10;
                if (this->unk_6c >= gRoomControls.origin_y + gRoomControls.height)
                    break;
                if (this->unk_6c > super->y.HALF.HI) {
                    break;
                }
            }
            super->actionDelay = 0;
            return;
            break;
        case 1:
            return;
        case 2:
        case 3:
        case 4:
            if (gPlayerState.floor_type == 0x1e) {
                if (EntityInRectRadius(super, &gPlayerEntity, 0, 8)) {
                    if ((super->animIndex == (super->type2 - 1) * 3 + 1) && (super->actionDelay == 0)) {
                        super->actionDelay = 1;
                        InitializeAnimation(super, super->animIndex + 1);
                    }
                } else {
                    super->actionDelay = 0;
                }
            } else {
                super->actionDelay = 0;
            }
            GetNextFrame(super);
            if ((super->frame & 0x80) != 0) {
                InitializeAnimation(super, (super->type2 - 1) * 3 + 1);
            }
            break;
    }
}

void Beanstalk_Action1Type8(BeanstalkEntity* this) {
    static void (*const Beanstalk_Action1Type8SubActions[])(BeanstalkEntity*) = {
        Beanstalk_Action1Type8SubAction0,
        Beanstalk_Action1Type8SubAction1,
    };
    Beanstalk_Action1Type8SubActions[super->subAction](this);
}

void Beanstalk_Action1Type8SubAction0(BeanstalkEntity* this) {
    Entity* obj;

    super->spriteOrientation.flipY = 2;
    this->unk_70 = COORD_TO_TILE(super);
    SetTile(0x4017, this->unk_70, 1);
    super->subAction = 1;
    obj = CreateObjectWithParent(super, OBJECT_15, 0, 0);
    if (obj != NULL) {
        obj->y.HALF.HI = super->y.HALF.HI + 0x38;
        obj->spriteIndex = super->spriteIndex;
        obj->spriteVramOffset = super->spriteVramOffset;
        obj->palette.b.b0 = super->palette.b.b0;
        obj->animIndex = 2;
        obj->actionDelay = 0x80;
        obj->type = 1;
        obj->spriteRendering.b3 = 2;
        obj->spritePriority.b0 = 6;
        obj->spriteOrientation.flipY = 2;
    }
    CreateObjectWithParent(super, BEANSTALK, 9, 0);
}

void Beanstalk_Action1Type8SubAction1(BeanstalkEntity* this) {
}

void Beanstalk_Action1Type9(BeanstalkEntity* this) {
    if (super->subAction == 0) {
        super->subAction = 1;
        super->y.HALF.HI += 0x28;
        this->unk_72 = sub_080B1A0C(super, 0, -0x18);
    }
    super->spriteOrientation.flipY = gPlayerEntity.spriteOrientation.flipY;
    super->spriteRendering.b3 = gPlayerEntity.spriteRendering.b3;
    if (gPlayerState.floor_type == 0x1e) {
        super->spritePriority.b0 = 0;
        if (sub_080B1A0C(super, 0, -0x18) != 0x4014) {
            SetTile(0x4014, COORD_TO_TILE_OFFSET(super, 0, 0x18), 1);
        }
    } else {
        super->spritePriority.b0 = 5;
        if (this->unk_72 != sub_080B1A0C(super, 0, -0x18)) {
            SetTile(this->unk_72, COORD_TO_TILE_OFFSET(super, 0, 0x18), 1);
        }
    }
}
