//Returns the hill of a specific bounce, working backwards and excluding the final half-bounce.
//0 = the last bounce, excluding the final half-bounce.
//1 = the second-to-last bounce.
float priv_Bounce_GetBounceWidth(unsigned bounce)
{
    //Each bounce is half the width of the previous bounce.
    //The first bounce (excluding the half-bounce) is 1.0f,
    //The second bounce is 0.5f, the third is 0.25f, and so on.
    return std::pow(0.5f, float(bounce));
}

//Returns the height of a specific bounce, working backwards and excluding the final half-bounce.
//0 = the last bounce, excluding the final half-bounce.
//1 = the second-to-last bounce.
//
//'bounciness' is a value from 0.0 to 1.0, describing how (from the half-bounce on down)
//small the size of each bounce becomes, relative to the previous.
float priv_Bounce_GetBounceHeight(unsigned bounce, float bounciness)
{
    return std::pow(bounciness, float(bounce+1));
}

float priv_Bounce_GetBouncePosition(unsigned bounce)
{
    float position = 1.0f; //The final half-bounce.
    for(unsigned i = 0; i < bounce; i++)
    {
        //Add up the width of all the bounces before this one.
        position += priv_Bounce_GetBounceWidth(i);
    }

    return position;
}

float priv_Bounce_GetBounceHalfwayPoint(unsigned bounce)
{
    return (priv_Bounce_GetBouncePosition(bounce) + (priv_Bounce_GetBounceWidth(bounce) * 0.5f));
}

//Gets the amount we need to offset the bounces by, to make them be at the 'bottom' of the ease.
float priv_Bounce_GetVerticalBounceOffset(unsigned bounce, float bounciness)
{
    const float inverseBounciness = (1.0f - bounciness);
    float offset = 0.0f;
    
    for(unsigned i = 0; i <= bounce; i++)
    {
        //For each bounce, add 0.75f (or whatever (1.0f - bounciness) is) of the remaining height. (Since Each bounce is 0.25f of the previous).
        offset += (inverseBounciness * (1.0f - offset));
    }

    return offset;
}

//Returns the total width of all the bounces, plus the final half-bounce.
float priv_Bounce_TotalWidth(unsigned bounces)
{
    float totalWidth = 1.0f; //The final half-bounce.
    for(unsigned bounce = 0; bounce < bounces; bounce++)
    {
        //The First bounce added, adds '1.0f'. Second bounce adds '0.5f', the next adds '0.25f', and so on.
        totalWidth += priv_Bounce_GetBounceWidth(bounce);
    }

    return totalWidth;
}

float CustomBounceEase(float position, int bounces, float bounciness)
{
    //Optimization for end points, which are the most common spots.
    if(position == 0.0f || position == 1.0f)
    {
        return position;
    }
    
    //I don't know the purpose this constant serves in the original ease equation.
    const float UnknownVar = 7.5625f;
    
    //Calculate the total width needed for all the bounces.
    const float TotalWidth = priv_Bounce_TotalWidth(bounces);
    
    for(unsigned int bounce = bounces; bounce-- > 0; )
    {
        float bouncePos = priv_Bounce_GetBouncePosition(bounce);
        
        if(position > (bouncePos / TotalWidth))
        {
            position -= (priv_Bounce_GetBounceHalfwayPoint(bounce) / TotalWidth);
            float verticalOffset = priv_Bounce_GetVerticalBounceOffset(bounce, bounciness);
            
            return ((UnknownVar * position * position) + verticalOffset);
        }
    }
    
    //The final half-bounce:
    return (UnknownVar * position * position);
}