
#include <math.h>

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

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

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

	const float fadeinstart = 0.0f;
	const float fadeinstop = 0.001f;
	const float fadeoutstart = 0.9999f;
	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);

//    filter.init(true);
	renderScene(pos, alpha);
//    filter.glow(8, 0.005f, 0.005f, 0.92f, -1.0f, 1.0f);
}

void Keiju::renderScene(float pos, float alpha)
{
    cam->useCamera(0);
    vector<Fairy *>::iterator keijuiterator;

    const int updatelimit = 1000/100; //100 fps = 2-3 krt/frame
    const int particleupdatelimit = 1000/100; //100 fps = 2-3 krt/frame

    static int prevtime = 0;
    static int updatecumultime = 0;
    static int particlecumultime = 0;
    static int particleindex = 0;

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

    if (dt < 0)
        dt = -dt; 
    updatecumultime += dt;
    particlecumultime += dt;
    const int maxcycles = 20;

    //******************** PARTICLEUPDATELIMIT puuttuu, tää kusee tää implementaatio
    if (updatecumultime > maxcycles*updatelimit)
        updatecumultime = maxcycles*updatelimit;
    if (particlecumultime> maxcycles*updatelimit)
        particlecumultime = maxcycles*updatelimit;

    while (updatecumultime > updatelimit)
    {
        for (keijuiterator = keijulista.begin(); keijuiterator < keijulista.end(); keijuiterator++)
        {
            Fairy *f = *keijuiterator;
            f->update();
        }

        updatecumultime -= updatelimit;
    }
    while (particlecumultime > particleupdatelimit)
    {
        //lisätään particlet
        for (keijuiterator = keijulista.begin(); keijuiterator < keijulista.end(); keijuiterator++)
        {
            Fairy *f = *keijuiterator;

            for (int j = 0; j < 5; j++)
            {
                if (!particles[particleindex].active)
                {
                    particles[particleindex].active = true;
                    particles[particleindex].pos = f->currentPosition + Mathematics::randVectSphere()*0.05f;
                    particles[particleindex].energy = Mathematics::randBetween(0.1f, 0.2f);
                    particles[particleindex].maxenergy = particles[particleindex].energy;
                }
                particleindex++;
                particleindex %= pcount;

            }
        }

        //päivitetään particleja
        for (int j = 0; j < pcount; j++)
        {
            if (particles[j].active)
            {
                particles[j].energy -= 0.001f;
                particles[j].pos.y -= (j%2)?0.001f:0.0007f;
                if (particles[j].energy < 0.0f)
                {
                    particles[j].active = false;
                }
            }
        }
        particlecumultime -= particleupdatelimit;
    }

    
    
    
    for (keijuiterator = keijulista.begin(); keijuiterator < keijulista.end(); keijuiterator++)
    {
        Fairy *f = *keijuiterator;
        f->draw(alpha);
    }

    glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glDisable(GL_DEPTH_TEST);
    glBindTexture(GL_TEXTURE_2D, dmsGetTexture("blueparticle.jpg")->getID());

	Vector x, y, z;
	Mathematics::antiRotate(&x, &y, &z);

    glColor4f(1,1,1,alpha);
    glBegin(GL_QUADS);
    for (int j = 0; j < pcount; j++)
    {
        if (particles[j].active)
        {
            const float maxsize = 0.03f;
            float size = maxsize*(particles[j].energy / particles[j].maxenergy);
	        Vector v1 = particles[j].pos - x*size - y*size;
	        Vector v2 = particles[j].pos + x*size - y*size;
	        Vector v3 = particles[j].pos + x*size + y*size;
	        Vector v4 = particles[j].pos - 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();


    glColor4f(0,1,1,1);
    Primitives::wireCube(2);
}

Fairy::Fairy()
{
    this->currentPosition = Vector(0, 0, 0);//Mathematics::randVectSphere()*Mathematics::randBetween(0.5f, 2.0f);
    if (this->currentPosition.y < 0)
    {
        this->currentPosition.y *= -1.0;

    }
    this->state = FAIRY_STATE_BIRTH;
    this->initNewDirection();
    this->currentSize = 0.0f;

    this->canMove = true;
    this->flare = dmsGetTexture("blueparticle.jpg"); //TODO
}

void Fairy::initNewDirection()
{
    this->direction_a = Mathematics::randBetween(0, 3.141592f);
    this->direction_b = Mathematics::randBetween(0, 2*3.141592f);
}

void Fairy::draw(float alpha)
{
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glDisable(GL_DEPTH_TEST);
    glColor4f(1,1,1,alpha);

    glBindTexture(GL_TEXTURE_2D, this->flare->getID());
    Primitives::drawBillboard(this->currentPosition, this->currentSize);

/*
    glDisable(GL_TEXTURE_2D);
    if (this->state == FAIRY_STATE_SEEK)
    {
        glColor4f(1,0,0,1);
    }
    if (this->state == FAIRY_STATE_WANDER)
    {
        glColor4f(0,1,0,1);
    }
    if (this->state == FAIRY_STATE_BIRTH)
    {
        glColor4f(0,0,1,1);
    }
    glPushMatrix();
    glTranslatef(this->currentPosition.x, this->currentPosition.y, this->currentPosition.z);
    Primitives::wireCube(0.1f);
    Vector direction = Mathematics::sphereToCartesian(0.7f, this->direction_a, this->direction_b);
    glBegin(GL_LINES);
    glVertex3fv((float *)&Vector(0, 0, 0));
    glVertex3fv((float *)&direction);
    glEnd();
    glPopMatrix();

*/



}

void Fairy::update()
{
    const float speed = 0.01f;
    if (this->state == FAIRY_STATE_BIRTH)
    {
        this->currentSize += 0.001f;

        if (this->currentSize > 0.3f)
        {
            this->state = FAIRY_STATE_WANDER;
        }

    }

    else if (this->state == FAIRY_STATE_WANDER)
    {
        //move
        this->currentPosition += Mathematics::sphereToCartesian(speed, this->direction_a, this->direction_b);

        //check for bounds
        const float max_x = 2;
        const float min_x = -2;
        const float max_y = 2;
        const float min_y = -2;
        const float max_z = 2;
        const float min_z = -2;

        if ((this->currentPosition.x > max_x) || (this->currentPosition.x < min_x) ||
            (this->currentPosition.y > max_y) || (this->currentPosition.y < min_y) ||
            (this->currentPosition.z > max_z) || (this->currentPosition.z < min_z))
        {
            this->destination = Mathematics::randVectSphere()*Mathematics::randBetween(0.0f, 0.25f);//Vector(0, 0, 0); //TODO TODO
            if (this->destination.y < 0.0f)
            {
                this->destination.y *= -1.5f;

            }

//            dmsMsg("delta_a = %f, delta_b = %f\n", this->direction_delta_a, this->direction_delta_b);

            this->state = FAIRY_STATE_SEEK;
        }
    }
    else if (this->state == FAIRY_STATE_SEEK)
    {
        //move
        if (this->canMove)
        {
            this->currentPosition += Mathematics::sphereToCartesian(speed, this->direction_a, this->direction_b);
        }

        //adjust turning
        Vector direction = this->destination - this->currentPosition;
        Vector temp = Mathematics::cartesianToSphere(direction.x, direction.y, direction.z);
        this->dest_direction_a = temp.y;
        this->dest_direction_b = temp.z;

        this->direction_delta_a = (this->dest_direction_a - this->direction_a)*0.01f;
        this->direction_delta_b = (this->dest_direction_b - this->direction_b)*0.01f;
        
        const float epsilon = 0.01f;
        //turn
        this->canMove = true;
        if (fabsf(this->direction_a - this->dest_direction_a) > epsilon)
        {
            this->direction_a += this->direction_delta_a;
            this->canMove = false;
        }
        if (fabsf(this->direction_b - this->dest_direction_b) > epsilon)
        {
            this->direction_b += this->direction_delta_b;
            this->canMove = false;
        }

        //reached destination
        if ((this->destination - this->currentPosition).length() < 0.1f)
        {
            this->state = FAIRY_STATE_WANDER;
            this->initNewDirection();
        }
    }
}


Keiju::Keiju()
{	
    int i = 0;
    for (i = 0; i < 15; i++)
    {
        keijulista.push_back(new Fairy());
    }

    pcount = 1000;
    particles = new FairyParticle[pcount];

    for (i = 0; i < pcount; i++)
    {
        particles[i].active = false;
    }
}

Keiju::~Keiju()
{
    delete [] particles;
}


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

