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

#define TURRET_MASS 40.0f
#define TURRET_RADIUS 2.0f

#define FRAMES_BETWEEN_FIRING 300
#define SHIP_RANGE 100.0f

typedef struct 
{
    unsigned int ref_count;

    CLvertex position;

    CLmatrix cannon_frame;
    
    int frames_since_last_fire;
    
    int health;
} ZfTurret;

static ZfSmartPointer smart_pointer; 
static ZfDynamicCollider dynamic_collider; 

static CLcontext* context_top;
static CLcontext* context_base;
static CLmodel* model_top;
static CLmodel* model_base;

static bool
is_valid(const ZfTurret* turret)
{
    return turret->health >= 0;
}

static void
reference(ZfTurret* turret)
{
    turret->ref_count++;
}

static void
release(ZfTurret* turret)
{
    turret->ref_count--;

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

static void
query_position(ZfTurret* turret,
	       CLvertex* position)
{
    clCopyVertex(position, &turret->position);
}

static void
turret_read(FILE* stream)
{
    CLvertex position;

    fscanf(stream, "ZfTurret\n{\n");

    fscanf(stream, "position = %f %f %f\n",
	   &position.x, &position.y, &position.z);

    fscanf(stream, "}\n");

    //zf_turret_new(&position);
}

/* z axis is aligned to normal, normal is normalised in this function */
static void
align_frame_vertex_normal(CLmatrix* frame,
			  CLvertex* vertex,
			  CLnormal* normal)
{
    float angle;
    CLnormal n;
    CLnormal x, y, z;
    CLnormal axis;
    CLUquaternion quaternion;
    CLmatrix rot_matrix;
	
    clCopyNormal(&n, normal);

	/* orientation not determinable, just set position */
    if (!cluNormalNormalise(&n))
    {
    	cluSetMatrixPosition(frame, vertex);
	    return;
    }

    cluSetNormalMatrixAxisX(&x, frame);
    cluSetNormalMatrixAxisY(&y, frame);
    cluSetNormalMatrixAxisZ(&z, frame);

    angle = CL_RAD2DEG(acos(cluNormalDotProduct(&n, &z)));
    cluNormalCrossProduct(&axis, &n, &z);
    cluNormalNormalise(&axis);

    cluSetQuaternionAxisAngle(&quaternion, &axis, angle);
    clDefaultMatrix(&rot_matrix);
    cluSetMatrixOrientation(&rot_matrix, &quaternion);

    /*
      glPushMatrix();
      glLoadIdentity();
      glRotatef(angle, axis.i, axis.j, axis.k);
      glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&rot_matrix);
      glPopMatrix();
    */

    if (angle && !(isnan((double)angle)) && (axis.i || axis.j || axis.k))
    {
	    cluNormalTransform(&y, &rot_matrix);
	    cluNormalTransform(&x, &rot_matrix);
	
	    cluSetMatrixAxesOrigin(frame, &x, &y, &n, vertex);
    }
    else
	    cluSetMatrixPosition(frame, vertex);
}

static void
animate(ZfTurret* turret)
{
    CLnormal direction;
    CLvertex point;

    /* hax0r - align svivelling stand towards ship around the y axis only */
/*    zf_ship_query_position(&point);

    point.y = turret->position.y;

    cluNormalDifference(&direction, &point, &turret->position);

	clDefaultMatrix(&turret->swivel_frame);

    align_frame_vertex_normal(&turret->swivel_frame, &turret->position, &direction);
*/

    /* align cannon on all axes */
    zf_ship_query_position(&point);

    cluNormalDifference(&direction, &point, &turret->position);

	clDefaultMatrix(&turret->cannon_frame);

    zf_align_frame_z_vertex_normal(&turret->cannon_frame, &turret->position, &direction);

    /* fire missile at ship */
    if(++turret->frames_since_last_fire == FRAMES_BETWEEN_FIRING)
    {
        CLvertex firing_position;
        clCopyVertex(&firing_position, &turret->position);

        firing_position.y += 3.0f;
	/*
	  if(cluNormalMagnitude(&direction) < SHIP_RANGE)
	  zf_enemy_missile_new(&firing_position);
	*/
        
        turret->frames_since_last_fire = 0;
    }
}

static void
render(ZfTurret* turret)
{
    if (zf_render_system_get_tool_mode())
    {
	glDisable(GL_COLOR_MATERIAL);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	
	glColor3f(0.0f, 1.0f, 1.0f);
	
	glPushMatrix();
	glTranslatef(turret->position.x, turret->position.y, turret->position.z);

	/* draw a wire sphere around turret */
	glutWireSphere(16.0f, 16, 16);
	
	glPopMatrix();
    }
    else
    {
        glEnable(GL_COLOR_MATERIAL);
        glEnable(GL_LIGHTING);
        glEnable(GL_TEXTURE_2D);

        glColor3f(1.0f, 1.0f, 1.0f);

        glPushMatrix();
        
        glTranslatef(turret->position.x, turret->position.y, turret->position.z);
        
        //glTranslatef(0.0f, -1.0f, 0.0f);
        //clRenderModel(model_base);
        glPopMatrix();
        
        glPushAttrib(GL_ALL_ATTRIB_BITS);
        glPushMatrix();
        
        glMultMatrixf((GLfloat*)&turret->cannon_frame);
        
        /* HACK */
        /*glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
        glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
        */
        
        clRenderModel(model_top);
        /*glutSolidCone(2.0, 5.0, 3, 3);*/
        
        glPopMatrix();
        glPopAttrib();
    }
}

static void
collision_response(ZfTurret* turret,
		   const void* collider_data,
		   ZfType collider_type,
		   const CLvertex* collision_position,
		   const CLnormal* collision_force_perp,
		   const CLnormal* collision_force_tan)
{
    switch(collider_type)
    {
        case ZF_LASER:
        	turret->health -= 25;
        	break;
        case ZF_MISSILE:
        	turret->health -= 50;
        	break;
        case ZF_TRITOR:
	        turret->health -= 100;
        	break;
        case ZF_DEBRIS:
        	turret->health -= 10;
        	break;
        case ZF_EXPLOSIVE:
        	turret->health -= 100;
        	zf_explosion_new(&turret->position, 2.0f);
        	break;
    }


    if (!is_valid(turret))
	zf_explosion_new(&turret->position, 2.0f);
    
}

void
zf_turret_close()
{
    clDeleteContext(context_base);
    clDeleteContext(context_top);
}

void
zf_turret_init(char* filename)
{
    unsigned int i;
    int num_turrets;
    
    FILE* stream;
    
    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;
	
    printf("%s() : load flux file %s\n", __FUNCTION__, filename);

    stream = fopen(filename, "r");

    fscanf(stream, "ZfTurrets\n{\n");
    
    fscanf(stream, "num_turrets = %d\n", &num_turrets);

    fscanf(stream, "turrets =\n[\n");

    for (i = 0; i < num_turrets; i++)
    {
	    turret_read(stream);   
    }

    fscanf(stream, "]\n");
    fscanf(stream, "}\n");
	
    fclose(stream);

    context_top = clDefaultContext(clNewContext());
    model_top = clioLoadModel(context_top, "../data/models/laser_turret/laser_turret_top.3DS");
    //cluModelCentre(model_top); 
    //cluModelScaleUnitCube(model_top);
    //cluModelScale(model_top, 2.0f);
    clUpdateContext(model_top->context);

    context_base = clDefaultContext(clNewContext());
    model_base = clioLoadModel(context_base, "../data/models/laser_turret/laser_turret_base.3DS");
    //cluModelCentre(model_base); 
    //cluModelScaleUnitCube(model_base);
    //cluModelScale(model_base, 2.0f);
    clUpdateContext(model_base->context);

    CLvertex position;
    zf_ship_query_position(&position);

    position.z += 10.0f;
    //position.z += 30.0f;
    //position.y = zf_heightmap_query_height(position.x, position.z);
    //position.y += 1.0f;

    //zf_turret_new(&position);
}

void
zf_turret_new(const CLvertex* position)
{
    ZfTurret* turret;

    turret = g_new(ZfTurret, 1);

    turret->ref_count = 0;
    turret->frames_since_last_fire = 0;

    clCopyVertex(&turret->position, position);

    turret->health = 100;

    /* maybe wrap-up all functions below in zf_add_object ? */

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

    zf_collision_system_add_dynamic(turret,
				    &smart_pointer,
				    &dynamic_collider,
				    ZF_TURRET,
				    TURRET_MASS, /*mass */
				    TURRET_RADIUS); /* radius */

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