#version 150

//
// Explanation of different particle types.
//
// Firework Launcher - launches a PT_SHELL every so many seconds.
#define PT_LAUNCHER 0 
// Unexploded shell - flies from the origin and explodes into many PT_EMBERX's.
#define PT_SHELL    1 
// Basic particle - after it's emitted from the shell, it dies.
#define PT_EMBER1   2 
// After it's emitted, it explodes again into many PT_EMBER1's.
#define PT_EMBER2   3 
// Just a different colored ember1.
#define PT_EMBER3   4
#define P_SHELLLIFE 3.0
#define P_EMBER1LIFE 2.5
#define P_EMBER2LIFE 1.5
#define P_EMBER3LIFE 2.0

// These two were originally shader params, but they caused runtime errors.
#define NUM_EMBER_1S 30
#define NUM_EMBER_2S 15
#define NUM_EMBER_3S 10
// This one was originally a variant, but also causes runtime errors.
//#define MAX_EMBER_2S 15.0

layout(points) in;
layout(points, max_vertices = 60) out;

in block {
    vec3 Pos;
    float Timer;
    float Type;
    vec3 Vel;
} FireworkData[];

// out vec3 outputPos;
// out float outputTimer;
// out float outputType;
// out vec3 outputVel;

// out vec3 gl_Position;
out vec3 oPos;
out float oUv0; // Timer
out float oUv1; // Type
out vec3 oUv2;  // Velocity

uniform sampler1D randomTexture;
uniform vec3 frameGravity;
uniform float globalTime;
uniform float elapsedTime;
uniform float secondsPerFirework;

//
// Generic particle motion handler
//
void GSGenericHandler( vec3 Pos, vec3 Vel, float Timer, float Type,
                       float elapsedTime,
                       vec3 frameGravity)
{
    Vel += frameGravity;
    Timer -= elapsedTime;

    if (Pos.y > -100)
    {
        oPos = Pos + (Vel * elapsedTime);
        oUv0 = Timer;
        oUv1 = Type;
        oUv2 = Vel;
        EmitVertex();
    }
}

//
// Sample a random direction from our random texture
//
vec3 RandomDir(float fOffset, float globalTime, sampler1D randomTex)
{
    float tCoord = (globalTime + fOffset) / 300.0;
    return texture(randomTex, tCoord).rgb;
}

//
// Launcher type particle handler
//
void GSLauncherHandler( vec3 Pos, vec3 Vel, float Timer, float Type,
                        float elapsedTime,
                        float globalTime,
                        sampler1D randomTex,
                        float secondsPerFirework)
{
    if (Timer <= 0)
    {
        vec3 vRandom = normalize(RandomDir(Type, globalTime, randomTex));
        // Give it more of an up bias.
        vRandom = normalize(vRandom + vec3(0, 2.5, 0));

        // Time to emit a new SHELL.
        oPos = Pos + Vel * elapsedTime;
        oUv2 = Vel + vRandom * 35.0;
        oUv0 = P_SHELLLIFE + vRandom.y * 0.5;
        oUv1 = PT_SHELL;
        EmitVertex();

        // Reset our timer.
        Timer = secondsPerFirework + vRandom.x * 0.4;
    }
    else
    {
        Timer -= elapsedTime;
    }

    // Emit ourselves to keep us alive.
    oPos = Pos;
    oUv2 = Vel;
    oUv0 = Timer;
    oUv1 = Type;
    EmitVertex();
}

//
// Shell type particle handler
//
void GSShellHandler( vec3 Pos, vec3 Vel, float Timer, float Type,
                     float elapsedTime,
                     float globalTime,
                     sampler1D randomTex,
                     vec3 frameGravity)
{
    if (Timer <= 0)
    {
        vec3 vRandom = vec3(0,0,0);

        // Time to emit a series of new Ember1s.
        for (int i = 0; i < NUM_EMBER_1S; i++)
        {
            vRandom = normalize(RandomDir(Type + i, globalTime, randomTex));
            oPos = Pos + Vel * elapsedTime;
            oUv2 = Vel + vRandom * 15.0;
            oUv0 = P_EMBER1LIFE;
            oUv1 = PT_EMBER1;
            EmitVertex();
        }

        // Find out how many Ember2s to emit.
        // Not doing this because it causes a runtime error.
        //int numEmber2s = abs(vRandom.x)*MAX_EMBER_2S;
        for (int i = 0; i < NUM_EMBER_2S; i++)
        {
            vRandom = normalize(RandomDir(Type, globalTime, randomTex));
            oPos = Pos + Vel * elapsedTime;
            oUv2 = Vel + vRandom * 10.0;
            oUv0 = P_EMBER2LIFE + 0.4 * vRandom.x;
            oUv1 = PT_EMBER2;
            EmitVertex();
        }

    }
    else
    {
        GSGenericHandler(Pos, Vel, Timer, Type, elapsedTime, frameGravity);
    }
}

//
// Ember1 and Ember3 type particle handler.
//
void GSEmber1Handler( vec3 Pos, vec3 Vel, float Timer, float Type,
                      float elapsedTime,
                      vec3 frameGravity)
{
    if (Timer > 0)
    {
        GSGenericHandler(Pos, Vel, Timer, Type, elapsedTime, frameGravity);
    }
}

//
// Ember2 type particle handler.
//
void GSEmber2Handler( vec3 Pos, vec3 Vel, float Timer, float Type,
                      float elapsedTime,
                      float globalTime,
                      sampler1D randomTex,
                      vec3 frameGravity)
{ 
    if (Timer <= 0)
    {
        // Time to emit a series of new Ember3's.
        for (int i = 0; i < NUM_EMBER_3S; i++)
        {
            oPos = Pos + Vel * elapsedTime;
            oUv2 = Vel + normalize(RandomDir(Type + i, globalTime, randomTex)) * 10.0;
            oUv0 = P_EMBER3LIFE;
            oUv1 = PT_EMBER3;
            EmitVertex();
        }
    }
    else
    {
        GSGenericHandler(Pos, Vel, Timer, Type, elapsedTime, frameGravity);
    }
}

void main()
{
    if (FireworkData[0].Type == PT_LAUNCHER)
        GSLauncherHandler(FireworkData[0].Pos.xyz, FireworkData[0].Vel, FireworkData[0].Timer, FireworkData[0].Type,
                           elapsedTime, globalTime, randomTexture, secondsPerFirework);
    else if (FireworkData[0].Type == PT_SHELL)
        GSShellHandler(FireworkData[0].Pos.xyz, FireworkData[0].Vel, FireworkData[0].Timer, FireworkData[0].Type,
                        elapsedTime, globalTime, randomTexture, frameGravity);
    else if (FireworkData[0].Type == PT_EMBER1 ||
             FireworkData[0].Type == PT_EMBER3)
        GSEmber1Handler(FireworkData[0].Pos.xyz, FireworkData[0].Vel, FireworkData[0].Timer, FireworkData[0].Type,
                         elapsedTime, frameGravity);
    else if (FireworkData[0].Type == PT_EMBER2)
        GSEmber2Handler(FireworkData[0].Pos.xyz, FireworkData[0].Vel, FireworkData[0].Timer, FireworkData[0].Type,
                         elapsedTime, globalTime, randomTexture, frameGravity);
    EndPrimitive();

    // // gl_Position.xyz = FireworkData[0].Pos;// + FireworkData[0].Vel * elapsedTime;
    // // gl_Position.w = 1;
    // gl_Position = FireworkData[0].Pos + 1;// + FireworkData[0].Vel * elapsedTime;
    // // gl_Position = Pos[0];// + FireworkData[0].Vel * elapsedTime;
    // //gl_Position = vec4(10, 10, 10, 1);
    // // oUv0 = P_SHELLLIFE + 0.5;
    // oUv0 = FireworkData[0].Timer;
    // // oUv0 = Timer[0];
    // oUv1 = FireworkData[0].Type;
    // // oUv1 = Type[0];
    // oUv2 = FireworkData[0].Vel;
    // // oUv2 = Vel[0];
    // //oUv2 = vec3(4, 5, 35.0);
    // EmitVertex();
    // EndPrimitive();
    // // // gl_Position = vec4(10, 20, 30, 1);
    // gl_Position = vec3(10, 20, 30);
    // oUv0 = P_EMBER3LIFE;
    // oUv1 = PT_SHELL;
    // oUv2 = vec3(40, 50, 60);
    // EmitVertex();
    // EndPrimitive();
}
