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

#define MISSILE_MASS 0.5f
#define MISSILE_RADIUS 0.05f

typedef struct DroidMissile DroidMissile;

struct DroidMissile
{
    unsigned int ref_count;

    CLmatrix frame;
    int droid_type;  /* which tier to aim for tier 1/2/3  */

    bool valid;
};

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

static CLcontext* context;
static CLmodel* missile_model;

static CLvertex type_1_destination;
static CLvertex type_2_destination;
static CLvertex type_3_destination;

static bool
is_valid(const DroidMissile* droid_missile)
{
    return droid_missile->valid;
}

static void
reference(DroidMissile* droid_missile)
{
    droid_missile->ref_count++;
}

static void
release(DroidMissile* droid_missile)
{
    droid_missile->ref_count--;

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

static void
animate(DroidMissile* droid_missile)
{	

    CLmatrix destination_frame;
    float ship_flux_t = zf_ship_query_flux_t();
    CLvertex global_destination_position;
    CLnormal direction;
    CLvertex missile_position;
    CLnormal velocity;
    
    clDefaultMatrix(&destination_frame);
    zf_flux_update_frame(&destination_frame, ship_flux_t);
    
    switch(droid_missile->droid_type)
    {
    case 0:
	clCopyVertex(&global_destination_position, &type_1_destination);
	cluVertexTransform(&global_destination_position, &destination_frame);
	break;
    case 1:
	clCopyVertex(&global_destination_position, &type_2_destination);
	cluVertexTransform(&global_destination_position, &destination_frame);
	break;
    case 2:
	clCopyVertex(&global_destination_position, &type_3_destination);
	cluVertexTransform(&global_destination_position, &destination_frame);
	break;
    default:
	break;
    }

    cluSetVertexMatrixOrigin(&missile_position, &droid_missile->frame);
    
    cluNormalDifference(&velocity, &global_destination_position, &missile_position);
    cluNormalNormalise(&velocity);

    cluNormalScale(&velocity, 0.05f); /* speed hack */
    
    cluVertexAdd(&missile_position, &velocity);
    zf_align_frame_z_vertex_normal(&droid_missile->frame, &missile_position, &velocity);
}

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

static void
collision_response(DroidMissile* droid_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, &droid_missile->frame);

    
    if (droid_missile->droid_type==0 && (collider_type & ZF_TIER_1_RING))
    {
	droid_missile->valid = false;
	zf_ship_hit_tier(1);
        zf_explosion_new(&missile_position, 0.5f);
    }
    else if (droid_missile->droid_type==1 && (collider_type & ZF_TIER_2_RING))
    {
        droid_missile->valid = false;
	zf_ship_hit_tier(2);
        zf_explosion_new(&missile_position, 0.5f);
    }
    else if (droid_missile->droid_type==2 && (collider_type & ZF_TIER_3_RING))
    {
	droid_missile->valid = false;
	zf_ship_hit_tier(3);
        zf_explosion_new(&missile_position, 0.5f);
    }
    else if(collider_type & ZF_WEAPON || collider_type & ZF_EXPLOSIVE)
    {
	droid_missile->valid = false;
	zf_explosion_new(&missile_position, 0.5f);
    }

}

static void
render(DroidMissile* droid_missile)
{
    glPushAttrib(GL_ALL_ATTRIB_BITS);

    switch(droid_missile->droid_type)
    {
    case 0:
	glColor3f(1.0f, 0.0f, 0.0f);
	break;
    case 1:
	glColor3f(0.0f, 1.0f, 0.0f);
	break;
    case 2:
	glColor3f(0.0f, 0.0f, 1.0f);
	break;
    default:
	glColor3f(1.0f, 1.0f, 1.0f);
	break;
    }


    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_ALPHA_TEST);

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

    clRenderModel(missile_model);

    glPopMatrix();

    glPopAttrib();
}

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

void
zf_droid_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/missile3/missile3.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);
    }

    cluSetVertex(&type_1_destination, 0.5f, 0.0f, 0.0f);
    cluSetVertex(&type_2_destination, 0.0f, 1.0f, 0.0f);
    cluSetVertex(&type_3_destination, 1.5f, 0.0f, 0.0f);

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

/* later make this take a target (data, smart pointer, collider,
   type) */
void	
zf_droid_missile_new(const CLvertex* position, 
		     const int droid_type)
{
    DroidMissile* droid_missile;

    droid_missile = g_new(DroidMissile, 1);

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

    droid_missile->droid_type = droid_type;

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

    zf_collision_system_add_dynamic(droid_missile,
				    &smart_pointer,
				    &dynamic_collider,
				    ZF_DROID_MISSILE,
				    MISSILE_MASS, /* mass */
				    MISSILE_RADIUS); /* radius */

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