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

#include <math.h>

#include "Laser.hpp"
#include "../library/mathematics.hpp"

void Laser::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);
}

void Laser::updateReflectors(float aika)
{
	int i;

	for (i=0;i<reflectorcount;i++)
	{
		Vector3 rotation = reflectors[i].startrotation * (1-aika) + reflectors[i].endrotation*aika;
		Matrix rotate;
		rotate.makeRotation(rotation.x, rotation.y, rotation.z);

		reflectors[i].r1 = reflectors[i].position + (reflectors[i].v1 * rotate);
		reflectors[i].r2 = reflectors[i].position + (reflectors[i].v2 * rotate);
		reflectors[i].r3 = reflectors[i].position + (reflectors[i].v3 * rotate);

		reflectors[i].normal = (reflectors[i].r3 - reflectors[i].r2).crossProduct(
								reflectors[i].r3 - reflectors[i].r1).normalize();
	}
}

#define EPSILON 0.0001f

//#define TEST_CULL
bool Laser::intersectTriangle(Vector3 &origin, Vector3 &dir, Vector3 &vert0, Vector3 &vert1, Vector3 &vert2, 
							  float *t, float *u, float *v)
{
   Vector3 edge1, edge2, tvec, pvec, qvec;
   float det,inv_det;

   /* find Vector3s for two edges sharing vert0 */
//   SUB(edge1, vert1, vert0);
 //  SUB(edge2, vert2, vert0);
   edge1 = vert1-vert0;
   edge2 = vert2-vert0;

   /* begin calculating determinant - also used to calculate U parameter */
//   CROSS(pvec, dir, edge2);
   pvec = dir.crossProduct(edge2);

   /* if determinant is near zero, ray lies in plane of triangle */
//   det = DOT(edge1, pvec);
   det = edge1.dotProduct(pvec);

#ifdef TEST_CULL           /* define TEST_CULL if culling is desired */
   if (det < EPSILON)
      return 0;

   /* calculate distance from vert0 to ray origin */
//   SUB(tvec, orig, vert0);
   tvec = origin-vert0;

   /* calculate U parameter and test bounds */
//   *u = DOT(tvec, pvec);
   *u = tvec.dotProduct(pvec);
   if (*u < 0.0 || *u > det)
      return 0;

   /* prepare to test V parameter */
//   CROSS(qvec, tvec, edge1);
   qvec = tvec.crossProduct(edge1);

    /* calculate V parameter and test bounds */ 
//   *v = DOT(dir, qvec);
   *v = dir.dotProduct(qvec);
   if (*v < 0.0 || *u + *v > det)
      return 0;

   /* calculate t, scale parameters, ray intersects triangle */
//   *t = DOT(edge2, qvec);
   *t = edge2.dotProduct(qvec);
   inv_det = 1.0f / det;
   *t *= inv_det;
   *u *= inv_det;
   *v *= inv_det;
#else                    /* the non-culling branch */
   if (det > -EPSILON && det < EPSILON)
     return 0;
   inv_det = 1.0f / det;

   /* calculate distance from vert0 to ray origin */
//   SUB(tvec, orig, vert0);
   tvec = origin-vert0;

   /* calculate U parameter and test bounds */
//   *u = DOT(tvec, pvec) * inv_det;

   *u = tvec.dotProduct(pvec) * inv_det;

   if (*u < 0.0 || *u > 1.0)
     return 0;

   /* prepare to test V parameter */
//   CROSS(qvec, tvec, edge1);
   qvec = tvec.crossProduct(edge1);

   /* calculate V parameter and test bounds */
//   *v = DOT(dir, qvec) * inv_det;
   *v = dir.dotProduct(qvec) * inv_det;
   if (*v < 0.0 || *u + *v > 1.0)
     return 0;

   /* calculate t, ray intersects triangle */
//   *t = DOT(edge2, qvec) * inv_det;

	*t = edge2.dotProduct(qvec) * inv_det;

#endif

	dmsMsg("onnistunut trace, t = %f\n", *t);
   return 1;

}

void Laser::renderScene(float pos, float alpha)
{
	int i, j, k;
	glLoadIdentity();
	glTranslatef(0, 0, -14.0f);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	updateReflectors(pos);

	//piirretään linssit

	glColor4f(0.5f, 0.5f, 0.7f, alpha*0.8f);

	for (i=0;i<reflectorcount;i++)
	{
		glBegin(GL_TRIANGLES);
		glVertex3fv((float *)&reflectors[i].r1);
		glVertex3fv((float *)&reflectors[i].r2);
		glVertex3fv((float *)&reflectors[i].r3);
		glEnd();

		glBegin(GL_POINTS);
		glColor4f(1,0,0,1);
		glVertex3fv((float *)&reflectors[i].r1);
		glColor4f(0,1,0,1);
		glVertex3fv((float *)&reflectors[i].r2);
		glColor4f(0,0,1,1);
		glVertex3fv((float *)&reflectors[i].r3);

		glEnd();
	}


	glPointSize(10);
	glColor4f(1,1,1,1);
	glBegin(GL_POINTS);
	for (i=0;i<emittercount;i++)
	{
		glVertex3fv((float *)&(emitters[i].position));
	}
	glEnd();

    for (i=0;i<emittercount;i++)
    {
        const int depth = 3;

        Vector3 raystartpos = emitters[i].position;
        Vector3 raydirection = emitters[i].direction;

        for (j=0;j<depth;j++)
        {
            bool hit = false;
            int closestindex = 0;
            float closestdist = 100000.0f;
            
            for (k=0;k<reflectorcount;k++)
            {
                float t, u , v;
			    if (intersectTriangle(raystartpos, raydirection, 
								      (reflectors[j].r1),
								      (reflectors[j].r2),
								      (reflectors[j].r3),
								      &t, &u, &v))
                {
                    if (t < closestdist)
                    {
                        hit = true;
                        closestindex = k;
                        closestdist = t;
                    }
                }
            }

            //osuttiin
            if (hit == true)
            {
				Vector3 hitpos = raystartpos + raydirection * closestdist;
	
				Vector3 reflection = reflectors[closestindex].normal*(reflectors[closestindex].normal.dotProduct(emitters[i].direction))*2
					 - emitters[i].direction;

				Vector3 reflected = hitpos + reflection*15;
				glVertex3fv((float *)&hitpos);
				glVertex3fv((float *)&reflected);
                //lasketaan heijastukset ja uusi paikka


            }

        }

    }

/*

	/*
		for each emitter

			while (iterations left)

				for each reflector
					check for hits
					draw line to closest
					position = closest
				end for
			end while
		end for
//				

	glBegin(GL_LINES);
	float t, u, v;
	for (i=0;i<emittercount;i++)
	{
		for (j=0;j<reflectorcount;j++)
		{
			if (intersectTriangle(emitters[i].position, emitters[i].direction, 
								  (reflectors[j].r1),
								  (reflectors[j].r2),
								  (reflectors[j].r3),
								  &t, &u, &v))
			{
//R = 2(N•L)N - L				
				Vector3 hitpos = emitters[i].position + emitters[i].direction*t;
				glColor4f(emitters[i].color.x, emitters[i].color.y, emitters[i].color.z, alpha);
				glVertex3fv((float *)&emitters[i].position);
				glVertex3fv((float *)&hitpos);
	
				Vector3 reflection = reflectors[j].normal*(reflectors[j].normal.dotProduct(emitters[i].direction))*2
					 - emitters[i].direction;

				Vector3 reflected = hitpos + reflection*15;
				glVertex3fv((float *)&hitpos);
				glVertex3fv((float *)&reflected);

			}
		}
	}
	glEnd();
*/
}

Laser::Laser()
{	
	int i = 0;
	emittercount = 5;
	reflectorcount = 1;

	emitters = new Emitter[emittercount];
	reflectors = new Reflector[reflectorcount];

    srand(150691);
    for (i=0;i<emittercount;i++)
    {
        emitters[i].position = Mathematics::randVector();
        emitters[i].direction = Mathematics::randVectSphere().normalize();//Vector3(-1, 0, 0).normalize();
	    emitters[i].color = Vector3(0.9f, 0.5, 0.2f);
    }

	reflectors[0].position = Vector3(1, 0, 0);
	reflectors[0].v1 = Vector3(0, 1, 0);
	reflectors[0].v2 = Vector3(-1, -0.7f, 0);
	reflectors[0].v3 = Vector3(1, -0.7f, 0);
	reflectors[0].startrotation = Vector3(((rand()%10000)/10000.0f)-0.5f, 
										 ((rand()%10000)/10000.0f)-0.5f, 
										 ((rand()%10000)/10000.0f)-0.5f);
	const float minrotate = 9;
	const float maxrotate = 9;
	const float rotate = minrotate + (maxrotate-minrotate) * ((rand()%10000)/10000.0f);

	reflectors[0].endrotation = Vector3(((rand()%10000)/10000.0f)-0.5f, 
										 ((rand()%10000)/10000.0f)-0.5f, 
										 ((rand()%10000)/10000.0f)-0.5f) * rotate  * 2 * 3.141592f;
}

Laser::~Laser()
{
	delete [] emitters;
	delete [] reflectors;
}

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

