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

#define TORPEDO_MASS 0.5f
#define TORPEDO_RADIUS 0.1f

typedef struct Torpedo Torpedo;

struct Torpedo
{
    unsigned int ref_count;

    CLmatrix flux_frame;

    float flux_t;
    float offset_height;
    float roll;

    bool valid;
};

static ZfSmartPointer smart_pointer; /* interface for torpedos */
static ZfDynamicCollider dynamic_collider; /* interface for torpedos */

static double max_flux_t;

static bool
is_valid(const Torpedo* torpedo)
{
    return torpedo->valid;
}

static void
reference(Torpedo* torpedo)
{
    torpedo->ref_count++;
}

static void
release(Torpedo* torpedo)
{
    torpedo->ref_count--;

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

static void
animate(Torpedo* torpedo)
{
    zf_flux_update_frame(&torpedo->flux_frame, 
			 torpedo->flux_t);
    
    torpedo->flux_t = zf_flux_query_next_t_given_distance(torpedo->flux_t,
							  0.22f); /*HACK - MAGIC number - slighting faster than ship*/

    
    if(torpedo->flux_t >= max_flux_t)
	torpedo->valid = false;
}

static void
query_frame(Torpedo* torpedo, 
	    CLmatrix* frame)
{
    CLnormal offset;
    CLvertex origin;
    
    static const CLnormal axis = {0.0f, 0.0f, 1.0f};
    CLUquaternion quaternion;
    CLmatrix matrix;

    /* for the rotation */
    cluSetQuaternionAxisAngle(&quaternion, &axis, torpedo->roll);
    cluQuaternionNormalise(&quaternion);
    clDefaultMatrix(&matrix);
    cluSetMatrixOrientation(&matrix, &quaternion);

    cluMatrixTransform(&matrix, &torpedo->flux_frame);
    clCopyMatrix(frame, &matrix);
    
    /* for the offset */
    cluSetNormalMatrixAxisY(&offset, frame);
    cluNormalScale(&offset, torpedo->offset_height);

    cluSetVertexMatrixOrigin(&origin, frame);
    cluVertexAdd(&origin, &offset);

    cluSetMatrixPosition(frame, &origin);
}


static void
query_position(Torpedo* torpedo,
	       CLvertex* position)
{
    CLmatrix torpedo_frame;
    
    query_frame(torpedo, &torpedo_frame);

    cluSetVertexMatrixOrigin(position, &torpedo_frame);
}

static void
collision_response(Torpedo* torpedo,
		   const void* collider_data,
		   ZfType collider_type,
		   const CLvertex* collision_position,
		   const CLnormal* collision_force_perp,
		   const CLnormal* collision_force_tan)
{
    CLvertex torpedo_position;

    query_position(torpedo, &torpedo_position);
    if (collider_type & ZF_WEAPON)
    {
	clCopyVertex(&torpedo_position, collision_position);
	cluSetMatrixPosition(&torpedo->flux_frame, &torpedo_position);
    }
    else if (collider_type & ZF_SHIP)
    {
	/* ignore?*/
    }
    else
    {
	zf_explosion_new(&torpedo_position, 3.0f);

	torpedo->valid = false; 
    }
}

static void
render(Torpedo* torpedo)
{
    CLmatrix frame;
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    query_frame(torpedo, &frame); /* Need this to get the torpedo frame */

    glDisable(GL_LIGHTING);
    glPushMatrix();
    glMultMatrixf((GLfloat*)&frame);

    glColor3f(1.0f, 0.0f, 0.0f);
    glutSolidSphere(TORPEDO_RADIUS, 8, 8);

    glPopMatrix();

    glPopAttrib();
}

void
zf_torpedo_close(void)
{
/*    clDeleteContext(context);*/
}

void
zf_torpedo_init(void)
{
    smart_pointer.is_valid = (ZfIsValid*) is_valid;
    smart_pointer.reference = (ZfReference*) reference;
    smart_pointer.release = (ZfRelease*) release;

    dynamic_collider.query_position = 
	(ZfQueryPosition*) query_position;

    dynamic_collider.collision_response =
	(ZfCollisionResponse*)collision_response;

    max_flux_t = zf_flux_query_max_t();
}

/* later make this take a target (data, smart pointer, collider,
   type) */
void
zf_torpedo_new(const CLmatrix* flux_frame,
	       float flux_t,
	       float offset_height,
	       float roll)
{
    Torpedo* torpedo;

    torpedo = g_new(Torpedo, 1);
    
    torpedo->ref_count = 0;
    clCopyMatrix(&torpedo->flux_frame, flux_frame);
    torpedo->valid = true;
    torpedo->flux_t = flux_t;
    torpedo->offset_height = offset_height;
    torpedo->roll = roll;

    zf_animation_system_add(torpedo,
			    &smart_pointer,
			    (ZfAnimate*) animate);
    
    zf_collision_system_add_dynamic(torpedo,
				    &smart_pointer,
				    &dynamic_collider,
				    ZF_TORPEDO,
				    TORPEDO_MASS, /* mass */
				    TORPEDO_RADIUS); /* radius */
    
    zf_render_system_add_opaque(torpedo,
				&smart_pointer,
				(ZfRender*) render);
}
