// renderparticles.cpp

#include "cube.h"

const int MAXPARTICLES = 10500;
const int NUMPARTCUTOFF = 20;
struct particle { vec o, d; int fade, type; int millis; particle *next; int tex; }; // EDIT: AH
particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL;
bool parinit = false;

VAR(maxparticles, 100, 2000, MAXPARTICLES-500);

void newparticle(vec &o, vec &d, int fade, int type, int tex = -1)
{
    if(!parinit)
    {
        loopi(MAXPARTICLES)
        {
            particles[i].next = parempty;
            parempty = &particles[i];
        };
        parinit = true;
    };
    if(parempty)
    {
        particle *p = parempty;
        parempty = p->next;
        p->o = o;
        p->d = d;
        p->fade = fade;
        p->type = type;
        p->millis = lastmillis;
        p->next = parlist;
        p->tex = tex;
        parlist = p;
    };
};

VAR(demotracking, 0, 0, 1);
VAR(particlesize, 20, 100, 500); 

void render_particles(int time)
{
	if(demoplayback && demotracking)
	{
		vec nom(0, 0, 0);
		newparticle(player1->o, nom, 100000000, 4);
	};

    if(!parlist) return;

    glDepthMask(GL_FALSE);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glDisable(GL_FOG);

    static Texture *parttex[2] = {NULL, NULL};
    if(!parttex[0]) parttex[0] = textureload("packages/misc/base.png");
    if(!parttex[1]) parttex[1] = textureload("packages/misc/smoke.png");
    static struct parttype { float r, g, b; int gr, tex; float sz; } parttypes[] =
    {
        { 0.2f, 0.2f, 0.2f, 2,  0, 0.06f }, // yellow: sparks 
        { 0.5f, 0.5f, 0.5f, 20, 1, 0.15f }, // grey:   small smoke
        { 0.2f, 0.2f, 1.0f, 20, 0, 0.08f }, // blue:   edit mode entities
        { 1.0f, 0.1f, 0.1f, 1,  1, 0.06f }, // red:    blood spats
        { 1.0f, 0.1f, 0.1f, 0,  1, 0.2f  }, // red:    demotrack

//        { 1.0f, 0.8f, 0.8f, 20, 6, 1.2f  }, // yellow: fireball1
//        { 0.5f, 0.5f, 0.5f, 20, 1, 0.6f  }, // grey:   big smoke   
//        { 1.0f, 1.0f, 1.0f, 20, 8, 1.2f  }, // blue:   fireball2
//        { 1.0f, 1.0f, 1.0f, 20, 9, 1.2f  }, // green:  fireball3
//        { 1.0f, 1.0f, 1.0f, 2,  3, 0.06f }, // 
    };
    
    int numrender = 0;
    
    parttype *lastpt = NULL;
    for(particle *p, **pp = &parlist; (p = *pp);)
    {       
//        if(p->type==9 && p->tex!=-1) parttypes[9].tex = p->tex; // hack, AH
        parttype &pt = parttypes[p->type];

        if(&pt!=lastpt)
        {
            if(!lastpt || pt.tex!=lastpt->tex)
            {
                if(lastpt) glEnd();
                glBindTexture(GL_TEXTURE_2D, parttex[pt.tex]->id);
                glBegin(GL_QUADS);
            };
            if(!lastpt || pt.r!=lastpt->r || pt.g!=lastpt->g || pt.b!=lastpt->b)
                glColor3f(pt.r, pt.g, pt.b);
            lastpt = &pt;
        };
        
        float sz = pt.sz*particlesize/100.0f; 
        // perf varray?
        glTexCoord2i(0, 1); glVertex3f(p->o.x+(-camright.x+camup.x)*sz, p->o.z+(-camright.y+camup.y)*sz, p->o.y+(-camright.z+camup.z)*sz);
        glTexCoord2i(1, 1); glVertex3f(p->o.x+( camright.x+camup.x)*sz, p->o.z+( camright.y+camup.y)*sz, p->o.y+( camright.z+camup.z)*sz);
        glTexCoord2i(1, 0); glVertex3f(p->o.x+( camright.x-camup.x)*sz, p->o.z+( camright.y-camup.y)*sz, p->o.y+( camright.z-camup.z)*sz);
        glTexCoord2i(0, 0); glVertex3f(p->o.x+(-camright.x-camup.x)*sz, p->o.z+(-camright.y-camup.y)*sz, p->o.y+(-camright.z-camup.z)*sz);
        xtraverts += 4;

        if(numrender++>maxparticles || (p->fade -= time)<0)
        {
            *pp = p->next;
            p->next = parempty;
            parempty = p;
        }
        else
        {
			if(pt.gr) p->o.z -= ((lastmillis-p->millis)/3.0f)*curtime/(pt.gr*10000);
            vec a = p->d;
            a.mul(time/20000.0f);
            p->o.add(a);
            pp = &p->next;
        };
    };

    if(lastpt) glEnd();

    glEnable(GL_FOG);
    glDisable(GL_BLEND);
    glDepthMask(GL_TRUE);
};

void particle_splash(int type, int num, int fade, vec &p)
{
    loopi(num)
    {
        const int radius = type==5 ? 50 : 150;
        int x, y, z;
        do
        {
            x = rnd(radius*2)-radius;
            y = rnd(radius*2)-radius;
            z = rnd(radius*2)-radius;
        }
        while(x*x+y*y+z*z>radius*radius);
        vec d((float)x, (float)y, (float)z);
        newparticle(p, d, rnd(fade*3), type);
    };
};

void particle_trail(int type, int fade, vec &s, vec &e)
{
    vec v;
    float d = e.dist(s, v); 
    v.div(d*2+0.1f);
    vec p = s;
    loopi((int)d*2)
    {
        p.add(v);
        vec d((float)(rnd(11)-5), (float)(rnd(11)-5), (float)(rnd(11)-5));
        newparticle(p, d, rnd(fade)+fade, type);
    };
};
