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

#define MISSILE_MASS 0.5f
#define MISSILE_RADIUS 0.25f

typedef struct Missile Missile;

struct Missile
{
    unsigned int ref_count;

    CLmatrix frame;

    bool valid;
    bool super_type;

    float speed;

    void* target_data;
    ZfSmartPointer* target_smart_pointer;
    ZfDynamicCollider* target_collider;
};

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

static CLcontext* context;
static CLmodel* missile_model;
/*static CLmodel* super_missile_model;*/

static bool
is_valid(const Missile* missile)
{
    return missile->valid;
}

static void
reference(Missile* missile)
{
    missile->ref_count++;
}

static void
release(Missile* missile)
{
    missile->ref_count--;

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

static void
animate(Missile* missile)
{
    if(missile->valid)
    {
   
	CLvertex missile_position;
    
	cluSetVertexMatrixOrigin(&missile_position, &missile->frame);

	/* If the locked target is a static object. The target_data becomes the position */
	if(missile->target_collider == 0) 
	{
	    CLvertex position;
	    CLnormal velocity;
	
	    clCopyVertex(&position, (CLvertex *)missile->target_data);
	
	
	    cluNormalDifference(&velocity, &position, &missile_position);
	    /* missile reached position, and if missile still not hit anything, target must have been destroyed*/
	    if(cluNormalMagnitude(&velocity) < 0.5f)  /*theshold value...*/
	    {
		missile->valid = false;
		zf_explosion_new(&missile_position, 2.0f);
	    }
	    else
	    {
		cluNormalNormalise(&velocity);
	    
		if(missile->super_type == true)
		    cluNormalScale(&velocity, missile->speed); /* speed hack */
		else
		    cluNormalScale(&velocity, missile->speed); /* speed hack */
	    
		cluVertexAdd(&missile_position, &velocity);
		zf_align_frame_z_vertex_normal(&missile->frame, &missile_position, &velocity);
	    }
	}
	else /* locked target is dynamic */
	{
	    /* if dynamic target is valid */
	    if (missile->target_smart_pointer->is_valid(missile->target_data))
	    {
		CLvertex position;
		CLnormal velocity;
	
		missile->target_collider->query_position(missile->target_data,
							 &position);
	    
		cluNormalDifference(&velocity, &position, &missile_position);
		cluNormalNormalise(&velocity);
	
		if(missile->super_type == true)
		    cluNormalScale(&velocity, missile->speed); /* speed hack */
		else
		    cluNormalScale(&velocity, missile->speed); /* speed hack */
	    
		cluVertexAdd(&missile_position, &velocity);
		zf_align_frame_z_vertex_normal(&missile->frame, &missile_position, &velocity);
	    }
	    else /* dynamic target already destroyed? - explode*/
	    {
		missile->target_smart_pointer->release(missile->target_data);
		zf_explosion_new(&missile_position, 2.0f);
		missile->valid = false; 
		/*
		  if (zf_collision_system_nearest_target(&missile_position,
		  ZF_LEECH,
		  &missile->target_data,
		  &missile->target_smart_pointer,
		  &missile->target_collider))
		  missile->target_smart_pointer->reference(missile->target_data);
		*/
	    }
	}
	/* add particle trail to missile */

	{
	    CLvertex particle_position;
	    CLnormal particle_velocity;
	    CLcolour particle_colour;

	    /* set position, velocity, colour */
	    clCopyVertex(&particle_position, &missile_position);
	    cluSetNormal(&particle_velocity, 0.0f, 0.0f, 0.0f);
	    cluSetColour(&particle_colour, 1.0f, 1.0f, 1.0f, 1.0f);
	
	    /* spawn particle */
	    zf_particle_system_add_particle(&particle_position,
					    &particle_velocity, 
					    0.0f,
					    &particle_colour,
					    100,
					    4.0f);
	
	}
    }
}

static void
query_position(Missile* missile,
	       CLvertex* position)
{
    cluSetVertexMatrixOrigin(position, &missile->frame);
    
}

static void
collision_response(Missile* missile,
		   const void* collider_data,
		   ZfType collider_type,
		   const CLvertex* collision_position,
		   const CLnormal* collision_force_perp,
		   const CLnormal* collision_force_tan)
{
    CLvertex missile_position;

    cluSetVertexMatrixOrigin(&missile_position, &missile->frame);
    if (collider_type & ZF_WEAPON)
	clCopyVertex(&missile_position, collision_position); 
    else if (collider_type & ZF_SHIP)
	clCopyVertex(&missile_position, collision_position);
    else if (collider_type & ZF_TIER_1_RING)
	clCopyVertex(&missile_position, collision_position);
    else if (collider_type & ZF_TIER_2_RING)
	clCopyVertex(&missile_position, collision_position);
    else if (collider_type & ZF_TIER_3_RING)
	clCopyVertex(&missile_position, collision_position);
    else if (collider_type & ZF_DEBRIS)
	clCopyVertex(&missile_position, collision_position);
    else if (collider_type & ZF_DROID_MISSILE)
	clCopyVertex(&missile_position, collision_position);
    else
    {
	
 	if(missile->super_type == true)
	    zf_explosion_new(&missile_position, 4.0f);    
 	else
 	    zf_explosion_new(&missile_position, 2.0f);
	
	if(missile->valid)
	{
	    if(missile->target_collider != 0)
		missile->target_smart_pointer->release(missile->target_data);
	    
	    missile->valid = false; 
	}

    }
}

static void
render(Missile* missile)
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glEnable(GL_LIGHTING);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_TEXTURE_2D);

    glPushMatrix();
    glMultMatrixf((GLfloat*)&missile->frame);

    /*
    if(missile->super_type == false)
	glColor3f(1.0f, 0.0f, 0.0f);
    else
	glColor3f(1.0f, 0.0f, 1.0f);
    */
    glColor3f(1.0f, 1.0f, 1.0f);

    /* render the missile */
    glEnable(GL_TEXTURE_2D);
    clRenderModel(missile_model);

    glPopMatrix();

    glPopAttrib();
}

void
zf_missile_close(void)
{
    clDeleteContext(context);
}

void
zf_missile_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;

    context = clDefaultContext(clNewContext());
    missile_model = clioLoadModel(context, "../data/models/missile4/missile4.3DS");

    /* not clean to just exit, but use this until we have a good error
       system */
    if (!missile_model)
    {
	printf("could not load missile model\n");
	exit(1);
    }

    cluModelCentre(missile_model); 
    cluModelScaleUnitCube(missile_model);
    cluModelScale(missile_model, 0.3f); /*hack */
    clUpdateContext(context);
}

/*!
  \param speed Constant speed at which the missile will fly. 1.0f equals 100%
  of default speed. 0.6f equals 60% of default speed.

  \todo later make this take a target (data, smart pointer, collider,
  type)

  \todo A real-life missile's speed would start at zero and accelerate rather
  than stay constant.
 */
void	
zf_missile_new(const CLvertex* position,
	       float speed,
	       void* target_data,
	       ZfSmartPointer* target_smart_pointer,
	       ZfDynamicCollider* target_collider)
{
    Missile* missile;

    missile = g_new(Missile, 1);

    missile->ref_count = 0;
    clDefaultMatrix(&missile->frame);
    cluSetMatrixPosition(&missile->frame, position);
    missile->valid = true;
    missile->super_type = false;

    missile->speed = speed;
    missile->target_data = target_data;
    missile->target_smart_pointer = target_smart_pointer;
    missile->target_collider = target_collider;


    /* This means the locked is a static object */
    if(target_collider == 0)
    {
	zf_animation_system_add(missile,
				&smart_pointer,
				(ZfAnimate*) animate);

	zf_collision_system_add_dynamic(missile,
					&smart_pointer,
					&dynamic_collider,
					ZF_MISSILE,
					MISSILE_MASS, /* mass */
					MISSILE_RADIUS); /* radius */

	zf_render_system_add_opaque(missile,
				    &smart_pointer,
				    (ZfRender*) render);
    }
    else if(target_smart_pointer->is_valid(target_data))
    {
	missile->target_smart_pointer->reference(missile->target_data);

	zf_animation_system_add(missile,
				&smart_pointer,
				(ZfAnimate*) animate);

	zf_collision_system_add_dynamic(missile,
					&smart_pointer,
					&dynamic_collider,
					ZF_MISSILE,
					MISSILE_MASS, /* mass */
					MISSILE_RADIUS); /* radius */

	zf_render_system_add_opaque(missile,
				    &smart_pointer,
				    (ZfRender*) render);
    }
}


void	
zf_super_missile_new(const CLvertex* position,
		     float speed,
		     void* target_data,
		     ZfSmartPointer* target_smart_pointer,
		     ZfDynamicCollider* target_collider)
{
    Missile* missile;

    missile = g_new(Missile, 1);

    missile->ref_count = 0;
    clDefaultMatrix(&missile->frame);
    cluSetMatrixPosition(&missile->frame, position);
    missile->valid = true;
    missile->super_type = true;

    missile->speed = speed;
    missile->target_data = target_data;
    missile->target_smart_pointer = target_smart_pointer;
    missile->target_collider = target_collider;

    /* This means the locked is a static object */
    if(target_collider == 0)
    {

	zf_animation_system_add(missile,
				&smart_pointer,
				(ZfAnimate*) animate);

	zf_collision_system_add_dynamic(missile,
					&smart_pointer,
					&dynamic_collider,
					ZF_MISSILE,
					MISSILE_MASS, /* mass */
					MISSILE_RADIUS); /* radius */

	zf_render_system_add_opaque(missile,
				    &smart_pointer,
				    (ZfRender*) render);
    }
    else if(target_smart_pointer->is_valid(target_data))
    {
	missile->target_smart_pointer->reference(missile->target_data);

	zf_animation_system_add(missile,
				&smart_pointer,
				(ZfAnimate*) animate);

	zf_collision_system_add_dynamic(missile,
					&smart_pointer,
					&dynamic_collider,
					ZF_SUPER_MISSILE,
					MISSILE_MASS, /* mass */
					MISSILE_RADIUS); /* radius */

	zf_render_system_add_opaque(missile,
				    &smart_pointer,
				    (ZfRender*) render);
    }
}
