#ifdef _DEBUG
	#include <stdlib.h>
	#include "../mmgr.h"
#endif

#include <math.h>

#include "Tuli.hpp"
#include "../mathematics.hpp"
#include "../primitives.hpp"

void Tuli::draw()
{
	const float pos = (time - startTime) / (endTime - startTime);
	float alpha = 1.0f;

	const float fadeinstart = 0.0f;
	const float fadeinstop = 0.1f;
	const float fadeoutstart = 0.90f;
	const float fadeoutstop = 1.0f;

	if (pos >= fadeinstart && pos <= fadeinstop)
		alpha *= (pos-fadeinstart) / (fadeinstop-fadeinstart);
	if (pos >= fadeoutstart && pos <= fadeoutstop)
		alpha *= 1-(pos-fadeoutstart) / (fadeoutstop-fadeoutstart);

	renderScene(pos, alpha);
}

Vector Tuli::turbulenceFunc1(float t,Vector &pos)
{
    const float m = 1.8f;

    const float x = 0.7f*cosf(pos.x*m*t*12)+1.1f*sinf(pos.y*m*t*8)+0.52f*cosf(pos.z*m*t*17); 
    const float y = 0.9f*cosf(pos.x*m*t*19)+1.6f*sinf((pos.y-0.6f*pos.x)*m*t*18)+1.22f*cosf(pos.z*m*t*6); 
    const float z = 1.4f*cosf(pos.x*m*t*9)+0.98f*sinf(pos.y*m*t*12)+1.42f*cosf((pos.z-0.6f*pos.x)*m*t*6); 

    return Vector(x, y, z);

}
Vector Tuli::turbulenceFunc2(float t,Vector &pos)
{
    const float m = 2.4f;

    const float x = 1.9f*cosf(pos.x*m*t*11)+1.1f*sinf(pos.y*m*t*9)+0.32f*cosf(pos.z*m*t*11); 
    const float y = 1.3f*cosf(pos.x*m*t*5)+1.6f*sinf((pos.y-0.9f*pos.x)*m*t*18)+0.82f*cosf(pos.z*m*t*8); 
    const float z = 0.3f*cosf(pos.x*m*t*32)+0.38f*sinf(pos.y*m*t*13)+0.82f*cosf((pos.z-0.6f*pos.x)*m*t*15); 

    return Vector(x, y, z);

}
Vector Tuli::turbulenceFunc3(float t,Vector &pos)
{
    const float m = 0.3f;

    const float x = 1.5f*cosf(pos.x*m*t*13)+1.1f*sinf(pos.y*m*t*5)+0.52f*cosf(pos.z*m*t*21); 
    const float y = 1.1f*cosf(pos.x*m*t*15)+1.6f*sinf((pos.y-0.151f*pos.x)*m*t*19)+1.22f*cosf(pos.z*m*t*11); 
    const float z = 0.6f*cosf(pos.x*m*t*16)+0.98f*sinf(pos.y*m*t*9)+1.42f*cosf((pos.z-0.4f*pos.x)*m*t*9); 

    return Vector(x, y, z);

}


void Tulispiraali::draw(float pos, float alpha)
{
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glDisable(GL_TEXTURE_2D);
    glColor4f(this->color.x, this->color.y, this->color.z, alpha);//*0.56f);
    glDisable(GL_DEPTH_TEST);

    int c = (int)(Mathematics::calcPosFloat(pos, this->st, this->et)*this->count);

    Vector p = this->movedelta * pos;
    glBegin(GL_LINE_STRIP);
    for (int i = 0; i < c; i++)
    {
        glVertex3fv((float *)&(p + this->vertices[i]));
    }
    glEnd();
}



void Tuli::renderScene(float pos, float alpha)
{
    int i;
    cam->useCamera(0);
//	glScalef(1.25f,1.25f,1.0f);
/*
    static float prevpos = 0.0f;
    static float addcumultime = 0.0f;
    static float updatecumultime = 0.0f;
    float dt = pos - prevpos;
    prevpos = pos;

    addcumultime += dt;
    updatecumultime += dt;
    const float addlimit = 0.0005f;
    const float updatelimit = 0.0004f;

*/
    static int particleindex = 0;

    const int addamount = 30;
    //const int addlimit = 1000/50;
	const int addlimit = 1000/70;
    //const int updatelimit = 1000/100; //100 fps = 2-3 krt/frame
	const int updatelimit = 1000/130; //100 fps = 2-3 krt/frame

    static int prevtime = 0;
    static int addcumultime = 0;
    static int updatecumultime = 0;

    int currenttime = dmsGetModulePosition();
    int dt = currenttime - prevtime;
    prevtime = currenttime;

    addcumultime += dt;
    updatecumultime += dt;

    if (addcumultime > 20*addlimit)
        addcumultime = 20*addlimit;
    if (updatecumultime > 20*updatelimit)
        updatecumultime = 20*updatelimit;


    const float particles_stop = 0.93f;

    if (pos < particles_stop)
    {
        while (addcumultime > addlimit)
        {
            //add particles
            for (i = 0; i < addamount; i++)
            {
                if (parts[particleindex].state == PARTICLE_STATE_NONE)
                {
                    const float maxradius = 0.4f+pos*0.7f+2.0f*powf(pos, 2.0f);//1.3f;
                    const float maxenergy = 0.3f;
                    const float minenergy = 0.03f;
                
                    parts[particleindex].state = PARTICLE_STATE_IGNITE;
                    const float r = powf(Mathematics::randFloat() * maxradius, 1.2f);
                    const float a = Mathematics::randFloat() * 2*3.141592f;
                    const float e = minenergy + Mathematics::randFloat() * (maxenergy - minenergy);
                    Vector p = Vector(cosf(a), 0, sinf(a))*r;
                    parts[particleindex].p = p;
                    parts[particleindex].maxenergy = e;
                    parts[particleindex].energy = 0.0f;
                    parts[particleindex].upspeed = 0.003f + Mathematics::randFloat()*0.007f;
                    parts[particleindex].a = 0.2f+0.6f*Mathematics::randFloat();
                    parts[particleindex].s = (0.01f + Mathematics::randFloat()*0.06f);
                }

                particleindex++;
                particleindex %= maxparticles;
            }
            addcumultime -= addlimit;
        }
    }
    while (updatecumultime > updatelimit)
    {
        //update particles


        Matrix turbrot;

        for (i = 0; i < maxparticles; i++)
        {
            const float gatherconstant = 0.0003f;
            const float burnconstant = 0.0003f;
            const float turbulence = 0.0007f;

            if (parts[i].state == PARTICLE_STATE_IGNITE)
            {
                parts[i].energy += gatherconstant;
                if (parts[i].energy > parts[i].maxenergy)
                {
                    parts[i].state = PARTICLE_STATE_BURN;
                }
            }
            else if (parts[i].state == PARTICLE_STATE_BURN)
            {
                parts[i].energy -= burnconstant;

                if (parts[i].energy > 0.0f)
                {
                    const float r = 4.7f;
                    parts[i].p.y += parts[i].upspeed;
                    turbrot.makeRotation(parts[i].p.x*r+pos*11, parts[i].p.y*r+pos*5,-parts[i].p.z*r+pos*17);
                    switch(i%3)
                    {
                        case 0: parts[i].p += (turbulenceFunc1(pos, parts[i].p)*turbrot)*turbulence; break;
                        case 1: parts[i].p += (turbulenceFunc2(pos, parts[i].p)*turbrot)*turbulence; break;
                        case 2: parts[i].p += (turbulenceFunc3(pos, parts[i].p)*turbrot)*turbulence; break;
                    }
                }
                else
                {
                    parts[i].state = PARTICLE_STATE_NONE;
                }
            }
        }
        updatecumultime -= updatelimit;
    }

    float multiplier = 68.0f;

	Vector p1 = Vector(-2, 0,-2)*multiplier;
	Vector p2 = Vector( 2, 0,-2)*multiplier;
	Vector p3 = Vector( 2, 0, 2)*multiplier;
	Vector p4 = Vector(-2, 0, 2)*multiplier;

    glDepthMask(0);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBindTexture(GL_TEXTURE_2D, dmsGetTexture("dirt5.jpg")->getID());
//    Primitives::renderTexturedPlaneWithFadeOut(p1,p2,p3,p4, 25, 60, Vector(0.541f*alpha,0.541f*alpha,0.541f*alpha), Vector(), 5.0f, 12.0f, alpha*0.7f);
	Primitives::renderTexturedPlaneWithFadeOut(p1,p2,p3,p4, 25, 60, Vector(0.8541f*alpha,0.8541f*alpha,0.8541f*alpha ), Vector(), 5.0f, 12.0f, alpha*0.7f);

    glDepthMask(1);

    filter.init(true);
    glLineWidth(1.0f);  
    glEnable(GL_LINE_SMOOTH);
    for (i = 0; i < spiraalicount; i++)
    {
        spiraalit[i]->draw(pos, alpha);
    }
    glDisable(GL_LINE_SMOOTH);
    glLineWidth(1.0f);  


	glEnable(GL_BLEND);
    glEnable(GL_LINE_SMOOTH);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    glLineWidth(1.0f);
/*
    glColor4f(1,1,1,alpha*0.1f);
    glBegin(GL_LINES);
    const int slices = 50;
    const float planesize = 4.0f;
    for (i = 0; i < slices; i++)
    {
        const float t = i / (float)slices;
        float x = -planesize*(1-t) + planesize*t;

        Vector v1 = Vector(x, 0, -planesize);
        Vector v2 = Vector(x, 0, planesize);
        glVertex3fv((float *)&v1);
        glVertex3fv((float *)&v2);
    }
    for (i = 0; i < slices; i++)
    {
        const float t = i / (float)slices;
        float x = -planesize*(1-t) + planesize*t;

        Vector v1 = Vector(-planesize, 0, x);
        Vector v2 = Vector(planesize, 0, x);
        glVertex3fv((float *)&v1);
        glVertex3fv((float *)&v2);
    }
    

    glEnd();
*/

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, dmsGetTexture("fireparticle.jpg")->getID());
	Vector x, y, z;
	Mathematics::antiRotate(&x, &y, &z);

	glBegin(GL_QUADS);

    for (i = 0; i < maxparticles; i++)
    {
        if (parts[i].state != PARTICLE_STATE_NONE)
        {
            float t = sinf((parts[i].energy / parts[i].maxenergy)*3.141592f*0.5f);
            float a = t*parts[i].a;
            const float size = t*parts[i].s;
            Vector p = parts[i].p;

            if (parts[i].state == PARTICLE_STATE_IGNITE)
            {
                glColor4f(0.2f*t,0,0,alpha*a);
            }
            else
            {
                glColor4f(t,t,t,alpha*a);
            }

	        Vector v1 = p - x*size - y*size;
	        Vector v2 = p + x*size - y*size;
	        Vector v3 = p + x*size + y*size;
	        Vector v4 = p - x*size + y*size;

            glTexCoord2f(0, 0);
            glVertex3fv((float *)&v1);
            glTexCoord2f(1, 0);
            glVertex3fv((float *)&v2);
            glTexCoord2f(1, 1);
            glVertex3fv((float *)&v3);
            glTexCoord2f(0, 1);
            glVertex3fv((float *)&v4);

        }

    }



    glEnd();


    glDisable(GL_LINE_SMOOTH);


    filter.glow(4, 0.005f, 0.005f, 0.92f, -1.0f, 1.0f);

}




Tuli::Tuli()
{	
    int i;
    maxparticles = 9000;
    parts = new TuliParticle[maxparticles];

    for (i = 0; i < maxparticles; i++)
    {
        parts[i].state = 0;
    }

    srand(1619186);


    spiraalicount = 90;
    spiraalit = new Tulispiraali*[spiraalicount];
    
    for (i = 0; i < spiraalicount; i++)
    {
        spiraalit[i] = new Tulispiraali();
        spiraalit[i]->count = 100;
        spiraalit[i]->vertices = new Vector[spiraalit[i]->count];
        spiraalit[i]->st = Mathematics::randFloat();
        spiraalit[i]->et = spiraalit[i]->st + 0.2f+0.1f*Mathematics::randFloat();

        float splinetime = spiraalit[i]->st + 0.05f;
        if (splinetime > 1.0f)
            splinetime = 1.0f;

        float ga = Mathematics::randFloat()*2*3.141592f;
        float gr = powf(Mathematics::randFloat(), 0.4f)*4.0f;

        Vector p = Vector(cosf(ga), 0, sinf(ga))*gr;

        Vector c1, c2;
        switch(rand()%4)
        {
            case 0:
            {
                //vihreä
    //            c1 = Vector(0.15f, 0.5f, 0.25f);
    //            c2 = Vector(0.14f, 0.8f, 0.24f);
                c1 = Vector(0.15f, 0.7f, 0.9f);
                c2 = Vector(0.14f, 0.9f, 1.0f);
            } break;
            case 1:
            {
                //keltainen
                c1 = Vector(0.9f, 0.7f, 0.3f);
                c2 = Vector(1.0f, 0.8f, 0.3f);

            } break;
            case 2:
            {
                //oranssi
                c1 = Vector(0.8f, 0.4f, 0.1f);
                c2 = Vector(1.0f, 0.5f, 0.1f);
            } break;

            case 3:
            {
                //punainen
                c1 = Vector(0.8f, 0.2f, 0.1f);
                c2 = Vector(1.0f, 0.3f, 0.1f);
            } break;
        }

        float coloralpha = 0.5f;
        float colort = Mathematics::randFloat();
        spiraalit[i]->color = (c1 * (1-colort) + c2*colort)*coloralpha;

        float movea = Mathematics::randFloat()*2*3.141592f;
        spiraalit[i]->movedelta = Vector(cosf(movea), 0, sinf(movea))*(0.3f+Mathematics::randFloat());
/*

        switch(rand()%2)
        {
            case 0: 
                spiraalit[i]->color = Vector(1 - Mathematics::randFloat()*0.2f, 0.2f, 0.2f);
                break;

            case 1: 
                spiraalit[i]->color = Vector(0.5f, 0.5f, 1.0f - Mathematics::randFloat()*0.15f);
                break;
        }
*/

		bool suora = false;//(rand()%3) == 0;

		if (suora)
		{
			Vector p1 = p;

			float a = Mathematics::randFloat()*2*3.141592f;
			float r = Mathematics::randBetween(0.7f, 1.5f);
			Vector p2 = p1 + Vector(cosf(a), 0, sinf(a))*r;

			for (int j = 0; j < spiraalit[i]->count; j++)
			{
				float t = j / (float)(spiraalit[i]->count-1);

				spiraalit[i]->vertices[j] = p1 * t + p2 * (1-t);//p + (Vector(cosf(a), 0, sinf(a))*radius);
			}


		}
		else
		{
			float koko = 1.03f + powf(Mathematics::randFloat(), 5.5f)*2.26f;//Mathematics::randBetween(0.08f, 0.15f);
			float kiertyma = Mathematics::randBetween(3, 5)*2*3.141592f;

			for (int j = 0; j < spiraalit[i]->count; j++)
			{
				float t = j / (float)(spiraalit[i]->count-1);

				float radius = koko*0.3f;// * powf(t, 2.5f);
				float a = t*2*3.141529f;//kiertyma * t;

				spiraalit[i]->vertices[j] = p + (Vector(cosf(a), 0, sinf(a))*radius);
			}
		}
    }




}

Tuli::~Tuli()
{
	delete [] parts;
}


bool Tuli::init(unsigned long s, unsigned long e)
{
	startTime = s;
	endTime = e;
	return true;
}

