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

#include <math.h>

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

void Sivuttain::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.99f;
	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);
}

void SivuttainRunkoSpline::draw(float pos, float alpha, float sync)
{
    float t = 0.0f;
    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.5f);
    glDisable(GL_DEPTH_TEST);

    float poshere = pos + 0.04f*sinf(fmodf(pos*this->speed + this->phase, 1)*3.141592f*2.0f) + 
					sinf(sync*3.141592f)*0.18f;
    if (poshere < 0)
        poshere = 0.0f;
    if (poshere > 1.0f)
        poshere = 1.0f;
    
    const int splinepoints = 2000;
    glBegin(GL_LINE_STRIP);
    for (t = 0.0f; t < pos; t += 1.0f / splinepoints)
    {
        glVertex3fv((float *)&this->curve->getValue(t));
    }
    glEnd();
}

void Spiraali::draw(float pos, float alpha, float sync)
{
    float t = Mathematics::calcSaturate(pos, this->st, this->et, 2);

    if (t > 0.0001f)
    {
        int c = (int)(this->count * t);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glDisable(GL_TEXTURE_2D);
		const float colmod = 1+(sync);
        glColor4f(min(1,this->color.x*colmod), min(1,this->color.y*colmod), min(1,this->color.z*colmod), alpha*0.9f);
        glDisable(GL_DEPTH_TEST);

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


    }
}
void Sivuttain::renderScene(float pos, float alpha)
{
    int i;
    Vector s = masterspline->getValue(pos);
	const float f_ = (pos+0.1f);
	Vector s2 = masterspline->getValue( f_ > 1.0f ? 1.0f : f_ );
    Matrix rot2;
    rot2.makeRotation(pos*5.0f, 0, -pos*2.5f*powf(sinf(pos*6.0f), 2));

//	cam *= rot2;

	Vector p = s + (s2 - s)*0.5f;

    Vector displace = (Vector(0.1f*cosf(pos*11), 0.1f*cosf(pos*15)+0.02f*cosf(pos*6), 2.7f+1.1f*cosf(pos*19))*-1)*rot2;

    Vector cam = p - displace;
    cam.z -= powf(sinf(Mathematics::calcPosFloat(pos, 0.8f, 1.0f)*3.141592f*0.5f), 1.6f)*0.9f;

    Vector tgt = s;
    Vector upw = Vector(0, 1, 0);

    Matrix rot;
    rot.makeRotation(0, 0, -pos*5*cosf(pos*9));
    upw *= rot;

    glLoadIdentity();
    gluLookAt(cam.x, cam.y, cam.z, 
			  tgt.x, tgt.y, tgt.z, 
			  upw.x, upw.y, upw.z);

    float analval = 0.1f*anal->get();

    glPushMatrix();
/*
    float scalefactor = 1-powf(sinf(Mathematics::calcPosFloat(pos, 0.85f, 0.999f)*3.141592f*0.5f), 3);
    scalefactor *= powf(sinf(Mathematics::calcPosFloat(pos, 0.0f, 0.07f)*3.141592f*0.5f), 3);
    glScalef(scalefactor, scalefactor, scalefactor);
 */

    //tausta
    glPushMatrix();
    glRotatef(130, 1, 0.6f, 0.9f );//
    int facecount = sky->getFaceCount();
    Vector *vertices = sky->getVertices();
    Vector *normals = sky->getNormals();

    Face *faces = sky->getFaces();
    TexCoord *uv = sky->getUV();
    
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);
    glColor4f(1,1,1,1);
    
    //texture0 = texture
	glActiveTextureARB(GL_TEXTURE0_ARB);
    glBindTexture(GL_TEXTURE_2D, dmsGetTexture("texture-moon.jpg")->getID());

    //texture1 = displace
    glActiveTextureARB(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, dmsGetTexture("flash.png")->getID()); //flash.png!!

	// ------------------
	// Caustic
	// ------------------

	// Caustic numero
	int n = int(pos*32*25)%32 + 1;
	
	// Haetaan vastaava tekstuuri
	char buf[75];
	sprintf(buf, "cau_0%02d.jpg", n);
	Texture *t = dmsGetTexture(buf);

    glActiveTextureARB(GL_TEXTURE2_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, dmsGetTexture(buf)->getID()); //flash.png!!
/*
	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);	
*/
	shaders.sivuttaintausta->bind();
	shaders.sivuttaintausta->setUniform1i("texunit0", 0);  
	shaders.sivuttaintausta->setUniform1i("texunit1", 1);  
	shaders.sivuttaintausta->setUniform1i("texunit2", 2);  
    shaders.sivuttaintausta->setUniform1f("displace_amount", 0.4f+0.2f*cosf(pos*18));//+analval*4);
    shaders.sivuttaintausta->setUniform1f("alpha", 0.5f);
	shaders.sivuttaintausta->setUniform1f("fadepaske", Mathematics::calcPosFloat(pos, 0.0f, 0.05f));

    int count = (int)(facecount);//*alpha);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glBegin(GL_TRIANGLES);

    const float dispX = pos*3;
    const float dispY = 0;//pos*2;

    for (i=0;i<count;i++)
    {
        Face &f = faces[i];
        Vector &v1 = vertices[faces[i].v1];
        Vector &v2 = vertices[faces[i].v2];
        Vector &v3 = vertices[faces[i].v3];

        TexCoord uv1 = uv[faces[i].v1];
        TexCoord uv2 = uv[faces[i].v2];
        TexCoord uv3 = uv[faces[i].v3];

        TexCoord duv1 = uv[faces[i].v1];
        TexCoord duv2 = uv[faces[i].v2];
        TexCoord duv3 = uv[faces[i].v3];

        TexCoord luv1 = uv[faces[i].v1];
        TexCoord luv2 = uv[faces[i].v2];
        TexCoord luv3 = uv[faces[i].v3];

        const float basetexscale = 5.0f;
        const float displacetexscale = 2.0f;
        const float lighttexscale = 3.0f;
        uv1.u *= basetexscale;
        uv1.v *= basetexscale;
        uv2.u *= basetexscale;
        uv2.v *= basetexscale;
        uv3.u *= basetexscale;
        uv3.v *= basetexscale;

        duv1.u *= displacetexscale;
        duv1.v *= displacetexscale;
        duv2.u *= displacetexscale;
        duv2.v *= displacetexscale;
        duv3.u *= displacetexscale;
        duv3.v *= displacetexscale;

        luv1.u *= lighttexscale;
        luv1.v *= lighttexscale*0.5f;
        luv2.u *= lighttexscale;
        luv2.v *= lighttexscale*0.5f;
        luv3.u *= lighttexscale;
        luv3.v *= lighttexscale*0.5f;

        Vector &n1 = normals[faces[i].v1];
        Vector &n2 = normals[faces[i].v2];
        Vector &n3 = normals[faces[i].v3];

        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, uv1.u, uv1.v);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, duv1.u + dispX, duv1.v + dispY);
        glMultiTexCoord2fARB(GL_TEXTURE2_ARB, luv1.u, luv1.v);
        glNormal3fv((float *)&n1);
        glVertex3fv((float *)&v1);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, uv2.u, uv2.v);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, duv2.u + dispX, duv2.v + dispY);
        glMultiTexCoord2fARB(GL_TEXTURE2_ARB, luv2.u, luv2.v);
        glNormal3fv((float *)&n2);
        glVertex3fv((float *)&v2);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, uv3.u, uv3.v);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, duv3.u + dispX, duv3.v + dispY);
        glMultiTexCoord2fARB(GL_TEXTURE2_ARB, luv3.u, luv3.v);
        glNormal3fv((float *)&n3);
        glVertex3fv((float *)&v3);
    }
    glEnd();

    shaders.unbind();

	glActiveTextureARB(GL_TEXTURE2_ARB);
    glDisable(GL_TEXTURE_2D);

	glActiveTextureARB(GL_TEXTURE1_ARB);
    glDisable(GL_TEXTURE_2D);

    glActiveTextureARB(GL_TEXTURE0_ARB);
    glDisable(GL_TEXTURE_2D);
    glPopMatrix();




    filter.init(true);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glDisable(GL_TEXTURE_2D);
    glColor4f(1,0.2f,0.2f,1);
    glDisable(GL_DEPTH_TEST);

    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
/*
    glBegin(GL_LINE_STRIP);

    for (float t = 0.0f; t < 1.0f; t+=0.001f)
    {
        glVertex3fv((float *)&masterspline->getValue(t));

    }
    glEnd();
*/

	float sync = triggers->get(dmsGetModulePosition());

    for (i = 0; i < runkosplinecount; i++)
    {
        rsplinet[i]->draw(pos, alpha, sync);
    }
    glLineWidth(3.0f);  
    for (i = 0; i < tilpecount; i++)
    {
        tilpet[i]->draw(pos, alpha, analval);
    }


    glLineWidth(1.0f);  
    for (i = 0; i < spiraalicount; i++)
    {
        spiraalit[i]->draw(pos, alpha, sync);
    }

    Vector x, y, z;
	Mathematics::antiRotate(&x, &y, &z);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glDisable(GL_DEPTH_TEST);
    glBindTexture(GL_TEXTURE_2D, dmsGetTexture("particle3.jpg")->getID());
    
    const float particlealpha = 1.0f;
    glBegin(GL_QUADS);
    for (int j = 0; j < particlecount / 2; j++)
    {
        float t = Mathematics::calcPosFloat(pos, particlet[j].st, particlet[j].et);
        if (t > 0.0001f)
        {
            Vector p = particlet[j].loc;
            const float size = particlet[j].size*t*(1+sync*0.15f);
            glColor4f(1,1,1,alpha*t*particlealpha);
	        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();
    glBindTexture(GL_TEXTURE_2D, dmsGetTexture("purpleparticle.jpg")->getID());
    glBegin(GL_QUADS);
    for (j = particlecount / 2; j < particlecount; j++)
    {
        float t = Mathematics::calcPosFloat(pos, particlet[j].st, particlet[j].et);
        if (t > 0.0001f)
        {
            Vector p = particlet[j].loc;
            const float size = particlet[j].size*t*(1+sync*0.075f);
            glColor4f(1,1,1,alpha*t*particlealpha);
	        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();


    glPopMatrix();


    glDisable(GL_LINE_SMOOTH);
    filter.glow(8, 0.005f, 0.005f, 0.92f, -1.0f, 1.0f);
    /*
    glPointSize(3);
    glBegin(GL_POINTS);
    glColor4f(1,1,1,1);
    for (int i = 0; i < pointcount; i++)
    {
        glVertex3fv((float*)&points[i]);
    }
    glEnd();
*/
/*
    glBegin(GL_LINE_STRIP);
    glColor4f(0,0,1,1);
    for (t = 0.0f; t < pos; t+=0.001f)
    {
        glVertex3fv((float *)&spline1->getValue(t));

    }
    glEnd();
*/
}

void SivuttainTilpe::draw(float pos, float alpha, float analval)
{
    float t = Mathematics::calcSaturate(pos, this->st, this->et, 1.15f);
    if (t > 0.0001f)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_DEPTH_TEST);

        glBegin(GL_LINE_STRIP);
        for (float u = 0.0f; u < t; u += 0.025f)
        {
            
            float a = alpha*0.7f*(Mathematics::calcPosFloat(analval, 0, u));//(0.5f+0.5f*(1-u));
            glColor4f(this->color.x, this->color.y, this->color.z, a);
            glVertex3fv((float *)&this->curve->getValue(u));
        }
        glEnd();



    }

}



Sivuttain::Sivuttain()
{	
    int i;

    anal = new Analyzer(2);

    const int masterpoints = 90;
    masterspline = new CatmullRom(masterpoints);
//    srand(160191);
    srand(696969);
    pointcount = 60;
    runkosplinecount = 9;
    tilpecount = 2850;
    particlecount = 2000;

    Vector startpoint = Vector(0, 0, 0);
    float angle = 0.7f;
    for (i = 0; i < masterpoints; i++)
    {
        float it = i / (float)masterpoints;
        float z = (cosf(it*17)+0.6f*sinf(it*11+2)+1.1f*cosf(it*25+1))*2 ;
        masterspline->addPoint(startpoint * (1 - powf(it, 10)) + Vector(0, 0, powf(it, 2)*z));

        startpoint += Vector(cosf(angle), sinf(angle), -0.1f)*0.5f;

        float angledelta = 0.0f;
        while (fabsf(angledelta) < 0.3f)
        {
            angledelta = Mathematics::randBetween(-0.4f, 0.8f);
        }
        angle += angledelta;
    }

    points = new Vector[pointcount];

    for (i = 0; i < pointcount; i++)
    {
        float it = i / (float)pointcount;
        Vector p = masterspline->getValue(it);

        float r = Mathematics::randBetween(0.03f, 0.1f);
        float a = Mathematics::randBetween(0, 2*3.141592f);

        points[i] = p + Vector(cosf(a), sinf(a), 0) * r;
    }

    rsplinet = new SivuttainRunkoSpline*[runkosplinecount];

    for (i = 0; i < runkosplinecount; i++)
    {
        int rpoints = 200;
        rsplinet[i] = new SivuttainRunkoSpline();
        rsplinet[i]->curve = new CatmullRom(rpoints);
        rsplinet[i]->phase = Mathematics::randFloat();
        rsplinet[i]->speed = Mathematics::randBetween(2, 4);

//        Vector c1 = Vector(0.1f, 0.6f, 0.2f);
//        Vector c2 = Vector(0.3f, 0.75f, 0.3f);


        Vector c1 = Vector(0.1f, 0.6f, 0.2f);
        Vector c2 = Vector(0.763f, 0.3f, 0.75f);
        float ct = Mathematics::randFloat();
        rsplinet[i]->color = c1*ct + c2*(1-ct);

        float freq = Mathematics::randBetween(6, 14);
        float maxr = Mathematics::randBetween(0.1f, 0.4f);

        for (int j = 0; j < rpoints; j++)
        {
            float jt = j / (float)rpoints;
            Vector p = masterspline->getValue(jt);

            float amp = cosf(jt*freq*2*3.141592f);
            
            rsplinet[i]->curve->addPoint(p + Mathematics::randVectSphere()*maxr*amp);
        }
    }


    tilpet = new SivuttainTilpe*[tilpecount];
    for (i = 0; i < tilpecount; i++)
    {
        int tilpevert = 8;
        tilpet[i] = new SivuttainTilpe();
        tilpet[i]->curve = new CatmullRom(tilpevert);

        float master_t = Mathematics::randBetween(0.01f, 0.98f);
        float master_t_delta = 0.002f;

        tilpet[i]->st = master_t;
        tilpet[i]->et = tilpet[i]->st + Mathematics::randBetween(0.05f, 0.10f);;

//        tilpet[i]->color = Mathematics::randVectSphere();

        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();
        tilpet[i]->color = (c1 * (1-colort) + c2*colort)*coloralpha;

        if (rand()%80 == 0)
        {
            tilpet[i]->color = Vector(1,1,1);
        }



        const int steps_along_the_spline = 3;
        Vector startpos = masterspline->getValue(master_t);

        for (int j = 0; j < steps_along_the_spline; j++)
        {
            tilpet[i]->curve->addPoint(masterspline->getValue(master_t));
            master_t += master_t_delta;
        }

        Vector curpos = masterspline->getValue(master_t);
        Vector direction = (curpos - startpos).normalize();

        float rotaatio = Mathematics::randBetween(2.4f, 3.6f)*4;

        Vector rotationdelta = Mathematics::randVectSphere();
//        float speed = Mathematics::randBetween(0.25f, 0.82f);
        float speed = 0.25f + (0.8f*powf(Mathematics::randFloat(), 1.7f));

       
        Vector r = Mathematics::randVectSphere()*rotaatio*(1+speed);

        r.x *= master_t;
        r.y *= master_t;


        for (j = steps_along_the_spline; j < tilpevert; j++)
        {
            Matrix rotation;
            rotation.makeRotation(r.x, r.y, r.z);
            tilpet[i]->curve->addPoint(curpos);
            curpos += direction*speed;
            direction *= rotation;
            r += rotationdelta*1.3f; 
        }
    }


    particlet = new SivuttainParticle[particlecount];
    for (i = 0; i < particlecount; i++)
    {
        float t = Mathematics::randFloat();
        particlet[i].st = t;
        particlet[i].et = particlet[i].st + Mathematics::randBetween(0.005f, 0.013f);
        particlet[i].loc = masterspline->getValue(t) + Mathematics::randVectSphere()*0.24f*Mathematics::randFloat();
        particlet[i].size = Mathematics::randBetween(0.01f, 0.022f);
    }

    sky = new Mesh();
    sky->buildSphere(100, 64, 64, 1.0f);

    spiraalicount = 620;
    spiraalit = new Spiraali*[spiraalicount];
    
    for (i = 0; i < spiraalicount; i++)
    {
        spiraalit[i] = new Spiraali();
        spiraalit[i]->st = Mathematics::randBetween(0.25f, 1.0f);
        spiraalit[i]->et = spiraalit[i]->st + Mathematics::randBetween(0.066f, 0.24f);
        spiraalit[i]->count = 100;
        spiraalit[i]->vertices = new Vector[spiraalit[i]->count];

        float splinetime = spiraalit[i]->st + 0.05f;
        if (splinetime > 1.0f)
            splinetime = 1.0f;
        Vector p = masterspline->getValue(splinetime) + Mathematics::randVectSphere()*Mathematics::randBetween(0.2f, 0.5f);
        Vector r = Mathematics::randVectSphere()*2*3.141592f;

        switch(rand()%2)
        {
            case 0: 
                spiraalit[i]->color = Vector(0.7f - 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;
        }
        float koko = 0.03f + powf(Mathematics::randFloat(), 5.5f)*0.26f;//Mathematics::randBetween(0.08f, 0.15f);
        float kiertyma = Mathematics::randBetween(3, 5)*2*3.141592f;

        Matrix rot;
        rot.makeRotation(r.x, r.y, r.z);
        for (int j = 0; j < spiraalit[i]->count; j++)
        {
            float t = j / (float)spiraalit[i]->count;

            float radius = koko * powf(t, 2.5f);
            float a = kiertyma * t;

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


	triggers = new TriggerSystem();
	triggers->add(220552, 221602);
	triggers->add(227442, 228492);
	triggers->add(226567, 227617);
	triggers->add(226985, 228035);
	triggers->add(234375, 235425);

}

Sivuttain::~Sivuttain()
{
}


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

