#include "../include/zf.h"

#define FADE_TIME 15;
#define LASER_LENGTH_LIMIT 200.0f

typedef struct Laser Laser;

struct Laser
{
    unsigned int ref_count;
    bool hit_valid;

    CLvertex origin;    
    CLvertex destination;
    
    float max_time;
    float time;
};

static ZfSmartPointer smart_pointer0; /* interface for laser animate */
static ZfSmartPointer smart_pointer1; /* interface for laser render*/
static ZfDynamicCollider dynamic_collider; /* interface for laser */

static bool
animate_is_valid(Laser* laser)
{
    return laser->hit_valid;
}

static bool
render_is_valid(Laser* laser)
{
    return laser->time < FADE_TIME;
}

static void
reference(Laser* laser)
{
    laser->ref_count++;
}

static void
release(Laser* laser)
{
    laser->ref_count--;

    if (laser->ref_count == 0)
	g_free(laser);
}

static void
query_position(Laser* laser,
	       CLvertex* position)
{
    clCopyVertex(position, &laser->destination);
}

static void
collision_response(Laser* laser,
		   const void* collider_data,
		   ZfType collider_type,
		   const CLvertex* collision_position,
		   const CLnormal* collision_force)
{
    /* This ensures it only does damage once */
    laser->hit_valid = false;

    /* zf_explosion_new(&laser->destination); */
}

static void
animate(Laser* laser)
{
    zf_ship_query_position(&laser->origin);

    laser->time++;
}

static void
draw_laser(Laser* laser)
{
    float half_size;
    CLmatrix modelview;
    CLnormal right;
    CLnormal up;
    CLnormal sright;
    CLnormal sup;
    CLvertex centre0;
    CLvertex centre1;
    CLnormal offset;
    CLvertex corner0;
    CLvertex corner1;
    CLvertex corner2;
    CLvertex corner3;

    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview);
  
    cluSetNormal(&right, modelview.m00, modelview.m10, modelview.m20);
    cluSetNormal(&up, modelview.m01, modelview.m11, modelview.m21);

    glEnable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    /* clLoadTexture(texture); */

    glBegin(GL_QUADS);

    half_size = 0.1f;
    
    clCopyNormal(&sright, &right);
    cluNormalScale(&sright, half_size);
    clCopyNormal(&sup, &up);
    cluNormalScale(&sup, half_size);

    clCopyVertex(&centre0, &laser->origin);
    clCopyVertex(&centre1, &laser->destination);

    cluSetNormal(&offset, 0.0f, half_size, 0.0f);
    cluVertexAdd(&centre0, &offset);
    cluVertexAdd(&centre1, &offset);

    clCopyVertex(&corner0, &centre0);
    clCopyVertex(&corner1, &centre0);
    clCopyVertex(&corner2, &centre1);
    clCopyVertex(&corner3, &centre1);
    
    /* bottom left */
    cluVertexSubtract(&corner0, &sright);
    cluVertexSubtract(&corner0, &sup);

    /* bottom right */
    cluVertexAdd(&corner1, &sright);
    cluVertexSubtract(&corner1, &sup);

    /* top right */
    cluVertexAdd(&corner2, &sright);
    cluVertexAdd(&corner2, &sup);

    /* top left */
    cluVertexSubtract(&corner3, &sright);
    cluVertexAdd(&corner3, &sup);

    glColor4f(0.7f, 0.8f, 1.0f,
	      0.8f * (1.0f - (laser->time / laser->max_time)));
    
    glTexCoord2f( 0.0f, 0.0f );
    glVertex3fv((GLfloat*)&corner0);
    
    glTexCoord2f( 1.0f, 0.0f );
    glVertex3fv((GLfloat*)&corner1);
    
    glTexCoord2f( 1.0f, 1.0f);
    glVertex3fv((GLfloat*)&corner2);
    
    glTexCoord2f( 0.0f, 1.0f );
    glVertex3fv((GLfloat*)&corner3);
  
    glEnd();
}

static void
render(Laser* laser)
{
    float t;

    /* glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_CURRENT_BIT); */
    glPushAttrib(GL_ALL_ATTRIB_BITS);

#if 1
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);

    t = 1.0f - (float) laser->time/FADE_TIME;

    glColor4f(0.2f, 
	      0.8f - 0.2f * t,
	      0.3f,
	      t);

    glEnable(GL_LINE_SMOOTH);
    glLineWidth(6.0f);

    glBegin(GL_LINES);
    
    /*
    glVertex3f(t * laser->origin.x + (1.0f - t) * laser->destination.x,
	       t * laser->origin.y + (1.0f - t) * laser->destination.y,
	       t * laser->origin.z + (1.0f - t) * laser->destination.z);
    */
    glVertex3f(laser->origin.x,
	       laser->origin.y,
	       laser->origin.z);

    glVertex3f(laser->destination.x,
	       laser->destination.y,
	       laser->destination.z);
    glEnd();
#else
    draw_laser(laser);
#endif

    glPopAttrib();
}

void
zf_laser_init(void)
{
    smart_pointer0.is_valid = (ZfIsValid*) animate_is_valid;
    smart_pointer0.reference = (ZfReference*) reference;
    smart_pointer0.release = (ZfRelease*) release;
    
    smart_pointer1.is_valid = (ZfIsValid*) render_is_valid;
    smart_pointer1.reference = (ZfReference*) reference;
    smart_pointer1.release = (ZfRelease*) release;


    dynamic_collider.query_position = (ZfQueryPosition*) query_position;
    dynamic_collider.collision_response = (ZfCollisionResponse*) collision_response;
}

void
zf_laser_close(void)
{
    /* nothing atm - texture later? */
}

void
zf_laser_new(const CLvertex* origin,
	     const CLvertex* destination)
{
    Laser* laser;
    CLnormal distance;

    laser = g_new(Laser, 1);
    
    clCopyVertex(&laser->origin, origin);    
    clCopyVertex(&laser->destination, destination);
    
    laser->ref_count = 0;
    
    laser->time = 0;
    laser->hit_valid = true;
    
    /* maybe wrap-up all functions below in zf_add_object ? */

    zf_animation_system_add(laser,
			    &smart_pointer1,
			    (ZfAnimate*) animate);

    zf_render_system_add_translucent(laser,
				     &smart_pointer1,
				     (ZfRender*)render);

    /* It is is not within range, render but don't add collider. Stops it
       making colliders at inifinity */

    cluNormalDifference(&distance, origin, destination);
    
    if(cluNormalMagnitude(&distance) < LASER_LENGTH_LIMIT)
    {
#if 1
	zf_collision_system_add_dynamic(laser,
					&smart_pointer0,
					&dynamic_collider,
					ZF_LASER,
					1.0f, /* mass */
					0.4f); /* radius */
#endif

    }
}


void
zf_beam_new(const CLvertex* origin,
	     const CLvertex* destination)
{
    Laser* laser;
    CLnormal distance;

    laser = g_new(Laser, 1);
    
    clCopyVertex(&laser->origin, origin);    
    clCopyVertex(&laser->destination, destination);
    
    laser->ref_count = 0;
    
    laser->time = 0;
    laser->hit_valid = true;
    
    /* maybe wrap-up all functions below in zf_add_object ? */

    zf_animation_system_add(laser,
			    &smart_pointer1,
			    (ZfAnimate*) animate);

    zf_render_system_add_translucent(laser,
				     &smart_pointer1,
				     (ZfRender*)render);

    /* It is is not within range, render but don't add collider. Stops it
       making colliders at inifinity */

    cluNormalDifference(&distance, origin, destination);
    
    if(cluNormalMagnitude(&distance) < LASER_LENGTH_LIMIT)
    {
#if 1
	zf_collision_system_add_dynamic(laser,
					&smart_pointer0,
					&dynamic_collider,
					ZF_BEAM,
					1.0f, /* mass */
					0.4f); /* radius */
#endif

    }
}
