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

#define BOSS_TARGET_MASS 0.01f
#define BOSS_TARGET_RADIUS 1.0f
#define COLOUR_CHANGE_TIME 100
#define BOSS_TARGET_DAMAGE_RECOVER 100
#define BOSS_TARGET_SCORE 1000

/* should roughly BOSS_RADIUS*2 + 5.0f */
#define BOSS_TARGET_MODEL_DIAMETER 55.0f

#define ROTATE_INCREMENT 1.0f;

struct ZfBossTarget
{
    unsigned int ref_count;
    CLvertex position;
    CLmatrix frame;
     
    int health;
    bool active;
    bool valid;
    
    ZfBoss* boss; /* pointer back to boss, so can decrement valid target*/
    ZfSmartPointer* boss_smart_pointer;

    int target_num;
    float rotate;
    int recovery;
};

static ZfSmartPointer smart_pointer; 
static ZfDynamicCollider dynamic_collider; 

static CLcontext* context;
static CLmodel* model;


/* target->valid can only be set to false by boss.*/
static bool
is_valid(const ZfBossTarget* target)
{
   return target->valid;
}

static void
reference(ZfBossTarget* target)
{
    target->ref_count++;
}

static void
release(ZfBossTarget* target)
{
    target->ref_count--;

    if (target->ref_count == 0)
    {
	//	printf("deleting target %p + releaseing boss\n", target);
	target->boss_smart_pointer->release(target->boss);
	g_free(target);
    }

}

static void
animate(ZfBossTarget* target)
{
    if(target->active)
    {
	if(target->boss_smart_pointer->is_valid(target->boss))
	{
	    if(target->recovery > 0)
		target->recovery--;

	    glPushMatrix();
	    glLoadIdentity();
	    glMultMatrixf((GLfloat*)  zf_boss_query_frame(target->boss));
	    /*
	      glTranslatef(target->position.x,
	      target->position.y,
	      target->position.z);
	    */
	    glRotatef(target->rotate, 0.0f, 0.0f, 1.0f);

	    glGetFloatv(GL_MODELVIEW_MATRIX,  (GLfloat *) &target->frame);
	    glPopMatrix();
	
	    target->rotate += ROTATE_INCREMENT;
	}
	else
	{
	    target->active = false;
	    //printf("deactivating target %p\n", target);
	    target->health = 0;
	}
    }
    
}


static void
render_opaque(ZfBossTarget* target)
{
    if(target->active)
    {
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glEnable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);

	glPushMatrix();

	glMultMatrixf((GLfloat*) &target->frame);
	clRenderModel(model);

	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	if(target->recovery > 0)
	    glColor3f(1.0f, 0.0f, 0.0f);
	  else
	      glColor3f(0.0f, 1.0f, 0.0f);
	glTranslatef(target->position.x,
		     target->position.y,
		     target->position.z);
	glutWireSphere(BOSS_TARGET_RADIUS, 8, 8);		     
	

	glPopMatrix();

	glPopAttrib();
    }
}

static void
render_translucent(ZfBossTarget* target)
{
}

static void
query_position(ZfBossTarget* target,
	       CLvertex* position)
{
    CLnormal temp;
 
    clCopyVertex(position,  &target->position);
    cluVertexTransform(position, &target->frame);

}

static void
collision_response(ZfBossTarget* target,
		   const void* collider_data,
		   ZfType collider_type,
		   const CLvertex* collision_position,
		   const CLnormal* collision_force_perp,
		   const CLnormal* collision_force_tan)
{
    if((collider_type & ZF_WEAPON))
    {
	if(target->active && target->recovery <= 0 && zf_boss_query_battle(target->boss))
	{
	    CLvertex target_pos;

	    clCopyVertex(&target_pos,  &target->position);
	    cluVertexTransform(&target_pos, &target->frame);
	    zf_explosion_new(&target_pos, 2.0f);
	    
	    target->health -= 25;
	    target->recovery = BOSS_TARGET_DAMAGE_RECOVER;
	    if(target->health <= 0)
	    {
		CLnormal velocity;
		CLnormal axis;
		float angular_velocity;

		target->active = false;
		// printf("deactivating target %p\n", target);
		zf_boss_decrement_target(target->boss);
		zf_explosion_new(&target_pos, 5.0f);

		cluSetNormal(&velocity, 0.0f, 0.0f, 0.0f);
		cluNormalCrossProduct(&axis, 
				      collision_force_tan, 
				      collision_force_perp);

		cluNormalNormalise(&axis);
		
		angular_velocity = 
		    cluNormalMagnitude(collision_force_tan);
		
		zf_debris_new(&target->frame,
			      &velocity,
			      &axis,
			      angular_velocity,
			      BOSS_TARGET_MASS,
			      BOSS_TARGET_RADIUS,
			      500,
			      model);

		zf_score_indicator_new(&target_pos, BOSS_TARGET_SCORE);
		zf_hud_increase_score(BOSS_TARGET_SCORE);
	    }
	    
	}
    }
}

void
zf_boss_target_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());
    model = clioLoadModel(context, "../data/models/boss/boss_visor.3ds");

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

    //    cluModelCentre(model); 
    cluModelScaleUnitCube(model);
    cluModelScale(model, BOSS_TARGET_MODEL_DIAMETER); 
    clUpdateContext(context);
}

/*!
  \todo free model later maybe?
*/
void
zf_boss_target_close(void)
{
    clDeleteContext(context);
}

void
zf_boss_target_destroy(ZfBossTarget* target)
{
    target->valid = false;
}

void
zf_boss_target_activate(ZfBossTarget* target)
{
    //printf("activating target %p\n", target);
    target->active = true;
}


void
zf_boss_target_reset(ZfBossTarget* target)
{
    // printf("resetting target %p\n", target);
    target->active = false;
    target->valid = true;
    target->health = 100;
    target->recovery = 0;

    target->rotate = (float)target->target_num * 60.0f;

}


/*assumes local coord for position */
ZfBossTarget*
zf_boss_target_new(const CLvertex* position, 
		   const float flux_t, 
		   const int index, 
		   ZfBoss* boss,
		   ZfSmartPointer* boss_smart_pointer)
{
    ZfBossTarget* target;
    
    target = g_new(ZfBossTarget, 1);

    target->ref_count = 0;
    target->active = false;
    target->valid = true;
    target->health = 100;
    target->rotate = (float)index * 60.0f;
    target->target_num = index;
    target->recovery = 0;

    target->boss = boss;
    target->boss_smart_pointer = boss_smart_pointer;

    //printf("creating target %p\n", target);    
    target->boss_smart_pointer->reference(target->boss);
    
    clDefaultMatrix(&target->frame);
    cluSetVertex(&target->position, -0.5f, 0.0f, 0.866025403784439f); /* for the first one at 30deg from x-plane*/
    cluNormalScale((CLnormal*)&target->position, (float)BOSS_TARGET_MODEL_DIAMETER / 2.0f);


    zf_animation_system_add(target,
			    &smart_pointer,
			    (ZfAnimate*) animate);
    
    zf_render_system_add_opaque(target,
				&smart_pointer,
				(ZfRender*) render_opaque);
    /*
      zf_render_system_add_translucent(target,
      &smart_pointer,
      (ZfRender*) render_translucent);
    */

    /*
      zf_collision_system_add_static(target,
      &smart_pointer,
      (ZfQueryCollision*)query_collision,
      ZF_BOSS_TARGET);
    */
    zf_collision_system_add_dynamic(target,
				    &smart_pointer,
				    &dynamic_collider,
				    ZF_BOSS_TARGET,
				    BOSS_TARGET_MASS, /* mass */
				    BOSS_TARGET_RADIUS); /* radius */

    return target;
}
