#include "functions.h"
#include "item.h"
#include "playeritem.h"
#include "sound.h"

u32 sub_08077F64(ItemBehavior* arg0, u32 index);
u32 sub_080789A8(void);
void sub_080762C4(ItemBehavior*, Entity*, u8, u32);
void sub_08076088(ItemBehavior*, Entity*, u32);
void ItemPickupCheck(ItemBehavior*, u32);
void sub_080762D8(ItemBehavior*, u32);
void sub_08076488(ItemBehavior*, u32);
void sub_08076518(ItemBehavior*, u32);
void sub_080765E0(ItemBehavior*, u32);
void sub_0807660C(ItemBehavior*, u32);

extern s32 sub_0800875A(Entity*, u32, ItemBehavior*);

extern const u16 gUnk_0811BE38[];
extern const u16 gUnk_0811BE40[];

void sub_08076088(ItemBehavior* this, Entity* param_2, u32 param_3) {
    if (param_2 != NULL) {
        if ((param_2->carryFlags & 1) != 0) {
            return;
        }
        param_2->action = 2;
        param_2->subAction = param_2->carryFlags & 1;
    }

    this->field_0x18 = param_2;
    if ((gPlayerState.flags & PL_NO_CAP)) {
        SetItemAnim(this, ANIM_PICKUP_NOCAP);
    } else {
        SetItemAnim(this, ANIM_PICKUP);
    }
    gPlayerState.heldObject = 3;
    gPlayerState.framestate = 4;
    this->stateID = 2;
    this->animPriority = 0xf;
    if ((gPlayerEntity.unk_79 & 0x80)) {
        gPlayerEntity.unk_79 = 0;
        COLLISION_ON(&gPlayerEntity.base);
        gPlayerState.heldObject = 4;
        gPlayerState.keepFacing = ~(8 >> param_3) & gPlayerState.keepFacing;
        gPlayerState.field_0xa = ~(8 >> param_3) & gPlayerState.field_0xa;
        this->stateID = 3;
        this->animPriority = 0;
    } else {
        gPlayerState.field_0xa |= (8 >> param_3);
        gPlayerState.keepFacing |= (8 >> param_3);
    }

    param_2 = CreatePlayerItemWithParent(this, PLAYER_ITEM_HELD_OBJECT);
    if (param_2 == NULL) {
        PlayerCancelHoldItem(this, param_3);
    } else {
        PlayerEntity* playerEnt = &gPlayerEntity;
        playerEnt->unk_74 = param_2;
        playerEnt->base.subtimer = 0;
        param_2->child = this->field_0x18;
        param_2->carryFlags = playerEnt->base.carryFlags;
        param_2->parent = (Entity*)this;
        this->field_0x18 = param_2;
        param_2->type2 = this->field_0x2[1];
        param_2->timer = this->timer;
        param_2->subtimer = this->subtimer;
        if ((this->field_0x18->carryFlags & 0xf0) == 0x10) {
            this->timer = 8;
        } else {
            this->timer = 0;
        }
        SoundReq(SFX_PLY_LIFT);
    }
}

void ItemTryPickupObject(ItemBehavior* this, u32 index) {
    static void (*const stateFuncs[])(ItemBehavior*, u32) = {
        ItemPickupCheck, sub_080762D8, sub_08076488, sub_08076518, sub_080765E0, sub_0807660C,
    };
    stateFuncs[this->stateID](this, index);
}

void ItemPickupCheck(ItemBehavior* this, u32 index) {
    Entity* carried;

    if (gPlayerState.attack_status == 0 &&
        (gPlayerState.jump_status == 0 || (gPlayerState.flags & (PL_BUSY | PL_FALLING | PL_IN_MINECART)) != 0) &&
        (gPlayerState.grab_status = gPlayerEntity.base.animationState | 0x80, gPlayerEntity.base.iframes <= 8)) {

        switch (sub_080789A8()) {
            case 2:
                if (((gCarriedEntity.unk_8)->carryFlags & 0xf) == 0) {
                    sub_08076088(this, gCarriedEntity.unk_8, index);
                    return;
                }
                carried = gCarriedEntity.unk_8;
                this->field_0x18 = carried;
                carried->action = 2;
                carried->subAction = 5;
                gPlayerState.framestate = PL_STATE_THROW;
                sub_080762C4(this, carried, 2, index);
                gUnk_0200AF00.rActionGrabbing = R_ACTION_GRAB;
                SoundReq(SFX_102);
                break;
            case 1:
                sub_080762C4(this, 0, 1, index);
                SoundReq(SFX_102);
                break;
            case 0:
                this->stateID = 5;
                this->timer = 0x0f;
                this->animPriority = 6;
                gPlayerState.field_0xa = (8 >> index) | gPlayerState.field_0xa;
                gPlayerState.keepFacing = (8 >> index) | gPlayerState.keepFacing;
                if (!(gPlayerState.flags & PL_NO_CAP)) {
                    SetItemAnim(this, ANIM_GRAB);
                } else {
                    SetItemAnim(this, ANIM_GRAB_NOCAP);
                }
                SoundReq(SFX_GRAB);
                break;
            default:
                break;
        }
    } else {
        PlayerCancelHoldItem(this, index);
    }
}

void sub_080762C4(ItemBehavior* this, Entity* arg1, u8 arg2, u32 arg3) {
    this->field_0x18 = arg1;
    gPlayerState.heldObject = arg2;
    sub_08077D38(this, arg3);
}

void sub_080762D8(ItemBehavior* this, u32 index) {
    u32 animIndex;

    gPlayerState.heldObject &= 0xcf;
    if (!PlayerTryDropObject(this, index)) {
        return;
    }

    if (gPlayerEntity.base.iframes < 9 && gPlayerEntity.base.knockbackDuration == 0) {
        if (this->field_0x18 != NULL) {
            if (this->field_0x18->action == 2 && this->field_0x18->subAction == 5) {
                if (!(gPlayerState.playerInput.heldInput & INPUT_ACTION)) { // Pressing R
                    this->field_0x18->subAction = 6;
                    PlayerCancelHoldItem(this, index);
                    return;
                }
            } else {
                PlayerCancelHoldItem(this, index);
                return;
            }
        }

        gPlayerState.framestate = PL_STATE_THROW;
        gUnk_0200AF00.rActionGrabbing = 8;
    } else {
        if (this->field_0x18 != NULL) {
            this->field_0x18->subAction = 6;
        }
        PlayerCancelHoldItem(this, index);
        return;
    }

    if (!gPlayerState.jump_status) {

        if (gPlayerState.heldObject == 1 && sub_0800875A(&gPlayerEntity.base, 6, this) != 0) {
            sub_08076088(this, NULL, index);
            return;
        } else if (gUnk_0811BE38[gPlayerEntity.base.animationState >> 1] & gPlayerState.playerInput.heldInput) {
            UpdateItemAnim(this);

            if (!(gPlayerState.flags & PL_NO_CAP)) {
                animIndex = ANIM_PULL;
            } else {
                animIndex = ANIM_PULL_START_NOCAP;
            }

            if (animIndex != this->animIndex) {
                SetItemAnim(this, animIndex);
            }

            gPlayerState.heldObject |= 0x10;
            gPlayerState.framestate = PL_STATE_PULL;

            if (gPlayerState.heldObject == 1) {
                return;
            }

            sub_08076088(this, this->field_0x18, index);
        } else {
            if (gPlayerState.playerInput.heldInput & gUnk_0811BE40[gPlayerEntity.base.animationState >> 1]) {
                if (gPlayerEntity.base.subtimer < 6) {
                    gPlayerEntity.base.subtimer++;
                    return;
                }

                gPlayerState.field_0x35 = this->playerAnimationState;
                gPlayerState.pushedObject |= 0x80;
                gPlayerState.heldObject |= 0x20;
                gPlayerState.framestate = PL_STATE_PUSH;

                if (!(gPlayerState.flags & PL_NO_CAP)) {
                    animIndex = ANIM_PUSH;
                } else {
                    animIndex = ANIM_PUSH_NOCAP;
                }

                if (animIndex == this->animIndex) {
                    UpdateItemAnim(this);
                } else {
                    SetItemAnim(this, animIndex);
                }
            } else {
                gPlayerEntity.base.subtimer = 0;

                if (!(gPlayerState.flags & PL_NO_CAP)) {
                    SetItemAnim(this, ANIM_PULL);
                } else {
                    SetItemAnim(this, ANIM_PULL_START_NOCAP);
                }
            }
        }
    } else {
        PlayerCancelHoldItem(this, index);
    }
}

void sub_08076488(ItemBehavior* this, u32 index) {
    u32 bVar1;
    s32 iVar2;

    if (this->timer == 0) {
        if (PlayerTryDropObject(this, index) != 0) {
            if ((((this->field_0x18->carryFlags) & 0xf0) == 0x10) && ((gRoomTransition.frameCount & 1U) != 0)) {
                return;
            }
            UpdateItemAnim(this);
            if ((this->playerFrame & 0x80) != 0) {
                gPlayerEntity.base.flags |= ENT_COLLIDE;
                gPlayerState.heldObject = 4;
                bVar1 = ~(8 >> index);
                gPlayerState.keepFacing = bVar1 & gPlayerState.keepFacing;
                gPlayerState.field_0xa = bVar1 & gPlayerState.field_0xa;
                this->stateID++;
                this->animPriority = 0;
            }
        }
    } else {
        this->timer--;
    }
    gPlayerState.framestate = PL_STATE_HOLD;
}

void sub_08076518(ItemBehavior* this, u32 index) {
    if (PlayerTryDropObject(this, index)) {
        gPlayerState.framestate = PL_STATE_HOLD;
        if ((gPlayerState.jump_status & 0x80) == 0 && gPlayerState.field_0x1f[0] == 0) {
            if (gPlayerEntity.base.knockbackDuration != 0) {
                PlayerCancelHoldItem(this, index);
            } else {
                if ((gPlayerState.playerInput.newInput & (INPUT_LIFT_THROW | INPUT_CANCEL | INPUT_INTERACT)) != 0) {
                    sub_0806F948(&gPlayerEntity.base);
                    gPlayerState.heldObject = 5;
                    this->field_0x18->subAction = 2;
                    this->field_0x18->direction = (gPlayerEntity.base.animationState & 0xe) << 2;
                    this->field_0x18 = NULL;
                    this->stateID++;
                    this->animPriority = 0x0f;
                    if (gPlayerState.flags & PL_NO_CAP) {
                        SetItemAnim(this, ANIM_THROW_NOCAP);
                    } else {
                        SetItemAnim(this, ANIM_THROW);
                    }
                    gPlayerState.field_0xa |= 8 >> index;
                    gPlayerState.keepFacing |= 8 >> index;
                }
            }
        }
    }
}

void sub_080765E0(ItemBehavior* this, u32 index) {
    if (PlayerTryDropObject(this, index) != 0) {
        if ((this->playerFrame & 0x80) != 0) {
            PlayerCancelHoldItem(this, index);
        } else {
            UpdateItemAnim(this);
        }
    }
}

void sub_0807660C(ItemBehavior* this, u32 index) {
    UpdateItemAnim(this);
    if ((this->timer-- == 0) || (gPlayerState.grab_status == 0)) {
        gPlayerState.grab_status = 0;
        DeleteItemBehavior(this, index);
    }
}
