
/**
 * Behavior for bhvPiranhaPlantBubble and bhvPiranhaPlantWakingBubbles.
 *
 * This controls the bubble that grows and shrinks from a Piranha Plant's nose
 * as it sleeps, and the smaller bubbles that explode outwards when the Piranha
 * Plant is woken up.
 */

/**
 * Main loop for bhvPiranhaPlantWakingBubbles. Initialize a random 3D velocity
 * vector, then follow its path according to gravity.
 */
void bhv_piranha_plant_waking_bubbles_loop(void) {
    if (o->oTimer == 0) {
        o->oVelY = random_float() * 10.0f + 5.0f;
        o->oForwardVel = random_float() * 10.0f + 5.0f;
        o->oMoveAngleYaw = random_u16();
    }
    cur_obj_move_using_fvel_and_gravity();
}

/**
 * Main loop for bhvPiranhaPlantBubble. After it is initialized, the bubble
 * will grow and shrink continuously while the Piranha Plant sleeps. If the
 * Piranha Plant ever stops sleeping, the bubble will disappear and spawn many
 * bubbles to appear to burst. After bursting, the bubble is reinitialized
 * and the cycle can repeat.
 */
void bhv_piranha_plant_bubble_loop(void) {
    struct Object *parent = o->parentObj; // the Piranha Plant
    f32 scale = 0;
    s32 i;
    s32 animFrame = parent->header.gfx.animInfo.animFrame;
    // TODO: rename lastFrame if it is inaccurate
    s32 lastFrame = parent->header.gfx.animInfo.curAnim->loopEnd - 2;

    cur_obj_set_pos_relative(parent, 0, 72.0f, 180.0f);

    switch (o->oAction) {
        case PIRANHA_PLANT_BUBBLE_ACT_IDLE:
            cur_obj_disable_rendering();
            scale = 0;

            if (parent->oAction == PIRANHA_PLANT_ACT_SLEEPING) {
                o->oAction++; // move to PIRANHA_PLANT_BUBBLE_ACT_GROW_SHRINK_LOOP
            }
            break;

        case PIRANHA_PLANT_BUBBLE_ACT_GROW_SHRINK_LOOP:
            if (parent->oDistanceToMario < parent->oDrawingDistance) {
                cur_obj_enable_rendering();

                if (parent->oAction == PIRANHA_PLANT_ACT_SLEEPING) {
                    /**
                     * Set the frame after shrinking is done to be slightly before
                     * halfway through the animation, and the frame before growing
                     * slightly after halfway. This leaves about 8 frames during
                     * which the bubble is at its smallest, where its scale is 1.0f.
                     */
                    // the first frame after shrinking is done
                    f32 doneShrinkingFrame = lastFrame / 2.0f - 4.0f;
                    // the frame just before growing begins
                    f32 beginGrowingFrame = lastFrame / 2.0f + 4.0f;

                    // Note that the bubble always starts this loop at its largest.
                    if (animFrame < doneShrinkingFrame) {
                        // Shrink from 5.0f to 1.0f.
                        scale = coss(animFrame / doneShrinkingFrame * 0x4000) * 4.0f + 1.0f;
                    } else if (animFrame > beginGrowingFrame) {
                        // Grow from 1.0f to 5.0f.
                        scale = sins((
                                    // they should have used beginGrowingFrame here:
                                    (animFrame - (lastFrame / 2.0f + 4.0f)) / beginGrowingFrame
                                    ) * 0x4000) * 4.0f + 1.0f;
                    } else {
                        // Stay at 1.0f for a few frames.
                        scale = 1.0f;
                    }
                } else {
                    // Piranha Plant is no longer sleeping.
                    o->oAction++; // move to PIRANHA_PLANT_BUBBLE_ACT_BURST
                }
            } else {
                cur_obj_disable_rendering();
            }
            break;

        case PIRANHA_PLANT_BUBBLE_ACT_BURST:
            cur_obj_disable_rendering();
            scale = 0.0f;

            // Spawn 15 small bubbles to make it look like this bubble burst.
            for (i = 0; i < 15; i++) {
                try_to_spawn_object(0, 1.0f, o, MODEL_BUBBLE, bhvPiranhaPlantWakingBubbles);
            }

            o->oAction = PIRANHA_PLANT_BUBBLE_ACT_IDLE;
            scale = 1.0f; // this has no effect; it is set to 0 in the idle state
            break;
    }

    cur_obj_scale(scale);
}
