// BanjoDecomp: nipper.c
#include <ultra64.h>
#include "functions.h"
#include "variables.h"

#include "port/Patches/Patches.h"

static void __chNipper_updateFunc(Actor *this);
static Actor *__chNipper_animFunc(ActorMarker *marker, Gfx **gfx, Mtx **mtx, Vtx **vtx);

enum ch_nipper_states_e {
    CH_NIPPER_STATE_1_UNKNOWN = 1, // L80388578
    CH_NIPPER_STATE_2_UNKNOWN,     // L8038872C
    CH_NIPPER_STATE_3_UNKNOWN,     // L803887B4
    CH_NIPPER_STATE_4_DIEING,      // L80388910
    CH_NIPPER_STATE_5_SPAWNED,     // L80388938
    CH_NIPPER_STATE_6_DEAD,        // L803889A8
    CH_NIPPER_STATE_7_UNKNOWN      // already dead? L80388A20
};

static s32 __chNipper_sharedHits(void) {
    s32 bits = port_puzzleStep_get(ANCHOR_PUZZLE_TTC_NIPPER) & 0x7;
    return (bits & 1) + ((bits >> 1) & 1) + ((bits >> 2) & 1);
}

static s32 __chNipper_localHits(Actor *this) {
    if (this->state == CH_NIPPER_STATE_6_DEAD || this->state == CH_NIPPER_STATE_7_UNKNOWN) {
        return 3;
    }
    if (this->lifetime_value == 80.0f) {
        return 1;
    }
    if (this->lifetime_value == 40.0f) {
        return 2;
    }
    return 0;
}

/* .data */
ActorAnimationInfo gChNipperAnimations[8] = {
    {0, 0.0},
    {ASSET_C0_ANIM_NIPPER_IDLE, 2.0f},
    {ASSET_BD_ANIM_NIPPER_VULNERABLE, 1.5f},
    {ASSET_BF_ANIM_NIPPER_ATTACK, 1.2f},
    {ASSET_BE_ANIM_NIPPER_OW, 1.3f},
    {ASSET_C0_ANIM_NIPPER_IDLE, 2.0f},
    {ASSET_133_ANIM_NIPPER_DIE, 3.0f},
    {0, 0.0}
};

ActorInfo gChNipper = { 
    MARKER_A5_NIPPER, ACTOR_117_NIPPER, ASSET_3D5_MODEL_NIPPER,
    0x1, gChNipperAnimations,
    __chNipper_updateFunc, actor_update_func_80326224, __chNipper_animFunc,
    0, 0x299, 10.0f, 0
};

/* .code */
static void __chNipper_drawParticlesAtPosition(f32 *position, s32 count) {
    static s32 D_8038C5A4[3] = {180, 180, 180};
    static ParticleScaleAndLifetimeRanges D_8038C5B0 = {
        {0.1f, 0.5f},
        {1.4f, 2.8f},
        {0.0f, 0.01f},
        {0.5f, 1.4f},
        0.0f, 0.01f
    };
    ParticleEmitter *pCtrl;

    pCtrl = partEmitMgr_newEmitter(count);
    pCtrl = pCtrl;
    particleEmitter_setSprite(pCtrl, ASSET_700_SPRITE_DUST);
    particleEmitter_setRGB(pCtrl, D_8038C5A4);
    particleEmitter_setStartingFrameRange(pCtrl, 0, 7);
    particleEmitter_setPosition(pCtrl, position);
    particleEmitter_setSpawnPositionRange(pCtrl,
        -10.0f, -5.0f, -10.0f,
         10.0f, 10.0f,  10.0f
    );
    particleEmitter_setParticleVelocityRange(pCtrl,
        -40.0f, 10.0f, -40.0f,
         40.0f, 40.0f,  40.0f
    );
    particleEmitter_setScaleAndLifetimeRanges(pCtrl, &D_8038C5B0);
    particleEmitter_emitN(pCtrl, count);
}

static Actor *__chNipper_animFunc(ActorMarker *marker, Gfx **gfx, Mtx **mtx, Vtx **vtx) {
    Actor *this;
    
    this = marker_getActor(marker);
    modelRender_setAppendageVisibility(3, (this->state == 7)? 0 : 1);
    return actor_draw(marker, gfx, mtx, vtx);
}

static void __chNipper_setAnimationDuration(Actor *this) {
    subaddie_set_state_looped(this, CH_NIPPER_STATE_3_UNKNOWN);
    this->unk1C[0] = 0.0f;
    switch((s32)this->lifetime_value){
        case 120:
            anctrl_setDuration(this->anctrl, 1.2f);
            break;

        case 80:
            anctrl_setDuration(this->anctrl, 1.05f);
            break;

        case 40:
            anctrl_setDuration(this->anctrl, 0.9f);
            break;
    }
}

static void __chNipper_spawnedShowTextCallback(ActorMarker *caller, enum asset_e text_id, s32 arg2){
    Actor *this;
    this = marker_getActor(caller);
    __chNipper_setAnimationDuration(this);
    ncStaticCamera_exit();
    comusic_8025AB44(COMUSIC_12_TTC_NIPPER, -1, 300);
}

static void __chNipper_playDeathAnimation(Actor *this) {
    s32 i;

    subaddie_set_state_with_direction(this, CH_NIPPER_STATE_4_DIEING, 0.01f, 1);
    actor_playAnimationOnce(this);
    for(i = 0; i < 3; i ++){
        sfx_playFadeShorthandDefault(SFX_79_TICKER_DEATH, 0.5f, 17000, this->position, 1500, 3000);
    };
}

// if player too far away -> false
// otherwise return whether sp2C is within -35 and 35?
static bool __func_80388088(Actor *this){
    f32 sp2C;
    f32 sp20[3];
    bool out;

    sp2C = this->yaw - subaddie_getYawToPlayer(this);
    player_getPosition(sp20);
    if(sp20[0] < -5680.0f){
        return false;
    }

    return BOOL(-35.0f < sp2C && sp2C < 35.0f);
}

static bool __chNipper_shouldShowActor(Actor *this){
    return BOOL(subaddie_playerIsWithinSphereAndActive(this, 1300) && __func_80388088(this));
}

static void __chNipper_dieFunc(ActorMarker *this_marker, ActorMarker *other_marker) {
    Actor *this;
    s32 i;

    this = marker_getActor(this_marker);
    func_8032B4DC(this, other_marker, 7);

    if (this->lifetime_value == 40.0f) {
        port_puzzleStep_orBits(ANCHOR_PUZZLE_TTC_NIPPER, 0x7);
        subaddie_set_state_with_direction(this, CH_NIPPER_STATE_6_DEAD, 0.01f, 1);
        actor_playAnimationOnce(this);
        for(i = 0; i < 3; i++){
            sfx_playFadeShorthandDefault(SFX_78_EAGLECRY, 0.7f, 20000, this->position, 1500, 3000);
        };
        comusic_8025AB44(COMUSIC_12_TTC_NIPPER, 0, 300);
        func_8025AABC(COMUSIC_12_TTC_NIPPER);
        func_8032BB88(this, -1, 300);
        gcStaticCamera_activate(0x1C);
        return;
    }

    if (this->lifetime_value == 80.0f) {
        port_puzzleStep_orBits(ANCHOR_PUZZLE_TTC_NIPPER, 0x3);
        __chNipper_playDeathAnimation(this);
        this->lifetime_value = 40.0f;
        return;
    }

    port_puzzleStep_orBits(ANCHOR_PUZZLE_TTC_NIPPER, 0x1);
    __chNipper_playDeathAnimation(this);
    this->lifetime_value = 80.0f;
    gcdialog_showDialog(VER_SELECT(ASSET_A10_DIALOG_NIPPER_HURT, 0x910, 0, 0), 4, NULL, NULL, NULL, NULL);
    return;
}

static s32 __chNipper_determineMarkerId(ActorMarker * this_marker, ActorMarker * other_marker){
    if(this_marker->unk40_31 == 1){
        this_marker->id = MARKER_16C_NIPPER;
    }
    else{
        this_marker->id = MARKER_A5_NIPPER;
    }
    return true;
}

static void __chNipper_ow2Func(ActorMarker * this_marker, ActorMarker *other_marker){
    Actor *this;

    if(other_marker->id == 1){
        this = marker_getActor(this_marker);
        if( !mapSpecificFlags_get(TTC_SPECIFIC_FLAG_7_NIPPER_FIRST_MEET_TEXT_SHOWN)
            && this->has_met_before
            && gcdialog_showDialog(VER_SELECT(ASSET_A0F_DIALOG_NIPPER_HIT_BY_EGG, 0x90F, 0, 0), 0, NULL, NULL, NULL, NULL)
        ){
            mapSpecificFlags_set(TTC_SPECIFIC_FLAG_7_NIPPER_FIRST_MEET_TEXT_SHOWN, true);
        }
    }
}

static void __chNipper_owFunc(ActorMarker * this_marker, ActorMarker *other_marker){
    Actor *this = marker_getActor(this_marker);
    if( !this->unk138_23
        && this->has_met_before
        && gcdialog_showDialog(VER_SELECT(ASSET_A11_DIALOG_NIPPER_ATTACK, 0x911, 0, 0), 0, NULL, NULL, NULL, NULL)
    ){
        this->unk138_23 = true;
    }
}

static void __chNipper_updateFunc(Actor *this){
    f32 particlePosition[3];
    s32 sp48;
    s32 xVelocity;
    f32 playerPosition[3];
    enum bsgroup_e player_movement_group;
    s32 sharedHits;

    player_getPosition(playerPosition);
    xVelocity = func_80309D58(playerPosition, 1);
    if(!this->volatile_initialized){
        this->volatile_initialized = true;
        this->velocity_x = xVelocity;
        func_8032BC18(this);
    }

    if(this->state != CH_NIPPER_STATE_7_UNKNOWN){
        if(0.0f == this->velocity_x && xVelocity){
            comusic_8025AB44(COMUSIC_12_TTC_NIPPER, -1, 5000);
            func_8032BB88(this, 0, 4000);
            midichannel_incOrDecCounter(false);
        }
        else if(!xVelocity && 0.0f != this->velocity_x){
            comusic_8025AB44(COMUSIC_12_TTC_NIPPER, 0, 300);
            func_8025AABC(COMUSIC_12_TTC_NIPPER);
            func_8032BB88(this, -1, 300);
            midichannel_incOrDecCounter(true);
        }
        this->velocity_x = xVelocity;
    }

    if (this->initialized && this->state != CH_NIPPER_STATE_4_DIEING && this->state != CH_NIPPER_STATE_6_DEAD &&
        this->state != CH_NIPPER_STATE_7_UNKNOWN) {
        sharedHits = __chNipper_sharedHits();
        if (__chNipper_localHits(this) < sharedHits) {
            if (sharedHits >= 3) {
                this->lifetime_value = 40.0f;
                subaddie_set_state_with_direction(this, CH_NIPPER_STATE_6_DEAD, 0.01f, 1);
                actor_playAnimationOnce(this);
                sfx_playFadeShorthandDefault(SFX_78_EAGLECRY, 0.7f, 20000, this->position, 1500, 3000);
                comusic_8025AB44(COMUSIC_12_TTC_NIPPER, 0, 300);
                func_8025AABC(COMUSIC_12_TTC_NIPPER);
                func_8032BB88(this, -1, 300);
            } else {
                this->lifetime_value = (sharedHits == 1) ? 80.0f : 40.0f;
            }
        }
    }

    switch(this->state){
        case CH_NIPPER_STATE_1_UNKNOWN:
            if(!this->initialized){
                anctrl_setTransitionDuration(this->anctrl, 0.35f);
                subaddie_set_state_with_direction(this, CH_NIPPER_STATE_1_UNKNOWN, 0.01f, 1);
                this->lifetime_value = 120.0f;
                this->marker->propPtr->unk8_3 = true;
                marker_setCollisionScripts(this->marker, __chNipper_owFunc, __chNipper_ow2Func, __chNipper_dieFunc);
                func_803300C0(this->marker, __chNipper_determineMarkerId);
                this->initialized = true;
            }

            if(__chNipper_shouldShowActor(this)){
                player_movement_group = player_movementGroup();
                if( !this->has_met_before
                    && player_movement_group != BSGROUP_1_INTR
                    && player_movement_group != BSGROUP_A_FLYING
                ){
                    subaddie_set_state_with_direction(this, CH_NIPPER_STATE_5_SPAWNED, 0.01f, 1);
                    if(gcdialog_showDialog(VER_SELECT(ASSET_A0E_DIALOG_NIPPER_SPAWNED, 0x90E, 0, 0), 0xf, this->position, this->marker, __chNipper_spawnedShowTextCallback, NULL)){
                        this->has_met_before = TRUE;
                    }
                    comusic_8025AB44(COMUSIC_12_TTC_NIPPER, 5000, 300);
                    ncStaticCamera_setToNode(11);
                }
                else{
                    __chNipper_setAnimationDuration(this);
                }
                break;
            }
            
            if( actor_animationIsAt(this, 0.2f)
                || actor_animationIsAt(this, 0.2f)
                || actor_animationIsAt(this, 0.6f)
                || actor_animationIsAt(this, 0.7f)
            ){
                sfx_playFadeShorthandDefault(SFX_3D_TICKER_WALKING, 0.75f, 12000, this->position, 0x5dc, 0xbb8);
            }
            break;

        case CH_NIPPER_STATE_2_UNKNOWN:
            if(!__chNipper_shouldShowActor(this)){
                subaddie_set_state_looped(this, CH_NIPPER_STATE_1_UNKNOWN);
                break;
            }

            if(this->lifetime_value <= this->unk38_31){
                __chNipper_setAnimationDuration(this);
                break;
            }
            this->unk38_31++;
            break;

        case CH_NIPPER_STATE_3_UNKNOWN:
            if(actor_animationIsAt(this, 0.5f) && this->marker->unk14_21){
                vec3fArray_get_vec3f(this->marker->unk44, 6, particlePosition);
                __chNipper_drawParticlesAtPosition(particlePosition, 2);
            }
            else if(actor_animationIsAt(this, 0.95f) && this->marker->unk14_21){//L80388800
                vec3fArray_get_vec3f(this->marker->unk44, 5, particlePosition);
                __chNipper_drawParticlesAtPosition(particlePosition, 2);
            }

            if(actor_animationIsAt(this, 0.99f)){
                this->unk1C[0] += 1.0f;
            }

            if(2.0f <= this->unk1C[0]){
                subaddie_set_state_looped(this, CH_NIPPER_STATE_2_UNKNOWN);
                this->unk38_31 = 0;
                break;
            }

            if( actor_animationIsAt(this, 0.5f)
                || actor_animationIsAt(this, 0.95f) 
            ){
                for(sp48 = 0; sp48 < 3; sp48++){
                    sfx_playFadeShorthandDefault(SFX_3D_TICKER_WALKING, 0.75f, 12000, this->position, 1500, 3000);
                }
            }
            break;

        case CH_NIPPER_STATE_4_DIEING:
            if(actor_animationIsAt(this, 0.99f)){
                __chNipper_setAnimationDuration(this);
            }
            break;

        case CH_NIPPER_STATE_5_SPAWNED:
            if( actor_animationIsAt(this, 0.2f)
                || actor_animationIsAt(this, 0.2f)
                || actor_animationIsAt(this, 0.6f)
                || actor_animationIsAt(this, 0.7f)
            ){
                sfx_playFadeShorthandDefault(SFX_3D_TICKER_WALKING, 0.75f, 12000, this->position, 0x5dc, 0xbb8);
            }
            break;

        case CH_NIPPER_STATE_6_DEAD:
            this->marker->collidable = false;
            if(actor_animationIsAt(this, 0.6f)){
                sfx_playFadeShorthandDefault(SFX_7C_CHEBOOF, 0.9f, 20000, this->position, 1500, 3000);
                break;
            }

            if(actor_animationIsAt(this, 0.99f)){
                subaddie_set_state_with_direction(this, CH_NIPPER_STATE_7_UNKNOWN, 0.01f, 1);
            }
            break;

        case CH_NIPPER_STATE_7_UNKNOWN:
            this->marker->collidable = false;
            break;
    }
}

bool chNipper_isInState7(s16 arg0[3]){
    f32 sp1C[3];
    Actor *nipper;

    sp1C[0] = (f32) arg0[0];
    sp1C[1] = (f32) arg0[1];
    sp1C[2] = (f32) arg0[2];

    nipper = actorArray_findClosestActorFromActorId(sp1C, ACTOR_117_NIPPER, -1, NULL);
    return nipper->state == CH_NIPPER_STATE_7_UNKNOWN;
}