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

struct ZfHive
{
    unsigned int ref_count;

    CLvertex position;
    CLmatrix frame;

    int health;
    int num_leeches;
    int num_wasps;
    float time_elapsed; /*since last spawn */
    float time_between; /*time between spawn */

    float size; /* between 1.0f - 2.0f */
};

static ZfSmartPointer smart_pointer; /* interface for hivees */
static CLcontext* context;
static CLmodel* model;

static GList* hive_list; /* need this to quickly extract the closest hive*/ 

static bool
is_valid(const ZfHive* hive)
{
    return hive->health > 0;
}

static void
reference(ZfHive* hive)
{
    hive->ref_count++;
}

static void
release(ZfHive* hive)
{
    hive->ref_count--;

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

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

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

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

    fscanf(stream, "}\n");

    zf_hive_new(&position);
}


static void
animate(ZfHive* hive)
{
    hive->time_elapsed += ZF_TIME_STEP_SECS;
    
    if(hive->time_elapsed > hive->time_between)
    {
	if(hive->num_leeches < 1)/* < 5)*/
	{
	    zf_leech_new(&hive->position);
	    hive->num_leeches++;	
	}
	hive->time_elapsed = 0.0f;
    }
}


static void
render(ZfHive* hive)
{   
    CLnormal distance;
    CLvertex ship_pos;
    
    zf_ship_query_position(&ship_pos);
    
    cluNormalDifference(&distance, &hive->position, &ship_pos);

    if(cluNormalMagnitude(&distance) < 150.0f || zf_render_system_get_tool_mode()) 
    {
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	
	glEnable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	
	glColor3f(1.0f, 1.0f, 1.0f);

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

	glScalef(hive->size,hive->size,hive->size);
	cluRenderModel(model);
	
	glPopMatrix();
	
	glPopAttrib();
    }
}
/*
  static bool
  query_collision(void* there_is_no_spoon,
  const CLUsphere* sphere,
  const CLnormal* force,
  CLvertex* collision_position,
  CLnormal* collision_force)
  {
    
  if()
  {
  }
  return true;
  }

  return false;
  }
*/

void
zf_hive_init(char* filename)
{

    context = clDefaultContext(clNewContext());
    model = clioLoadModel(context, "../data/models/hive/hive.3ds");

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

    /* cluModelCentre(model); */
    cluModelScaleUnitCube(model);
    cluModelScale(model, 10.0f);
    clUpdateContext(model->context);

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

    hive_list = 0;

    /* haxor! */
    {
	unsigned int i;
	int num_hives;
    
	FILE* stream;
	
	stream = fopen(filename, "r");

	if(!stream)
	{
	    printf("%s() : ERROR : cant load hives file %s\n", __FUNCTION__, filename);
	    exit(1);
	}

	fscanf(stream, "ZfHives\n{\n");
    
	fscanf(stream, "num_hives = %d\n", &num_hives);

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

	for (i = 0; i < num_hives; i++)
	{
	    hive_read(stream);   
	}

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

void
zf_hive_close(void)
{
    /*Does the systems clean this up for hive?*/
    /*g_list_foreach(hive_list, (GFunc)g_free, NULL);*/
    g_list_free(hive_list);
    clDeleteContext(context);
}

void
zf_hive_new(const CLvertex* position)
{
  
    ZfHive* hive;

    hive = g_new(ZfHive, 1);
    
    hive->ref_count = 0;
    
    hive->health = 100;
    clCopyVertex(&hive->position, position);

    hive->num_leeches = 0;
    hive->num_wasps = 0;
    hive->time_elapsed = 0.0f;
    hive->time_between = 1.0f;

    hive->size = (float) rand() / RAND_MAX + 1.0f;

    glPushMatrix();
    glLoadIdentity();
    glTranslatef(position->x, position->y, position->z);
    glRotatef(180.0f * (float) rand()/RAND_MAX, 
	      0.2f *(float) rand() / RAND_MAX - 0.1f,
	      1.0f,
	      0.2f *(float) rand() / RAND_MAX - 0.1f);
    glGetFloatv(GL_MODELVIEW_MATRIX , (GLfloat *) &hive->frame);
    glPopMatrix();


#if 0
    /* Hive doesn't actually animate anything... but uses it to step. */
    zf_animation_system_add(hive,
			    &smart_pointer,
			    (ZfAnimate*) animate);
#endif

#if 0
    /* Needs a static collider instead */
    zf_collision_system_add_dynamic(hive,
				    &smart_pointer,
				    &dynamic_collider,
				    ZF_HIVE,
				    HIVE_MASS, /*mass */
				    HIVE_RADIUS); /* radius */

#endif

    zf_render_system_add_opaque(hive,
				&smart_pointer,
				(ZfRender*) render);


    hive_list = g_list_append(hive_list, hive);
}


bool
zf_hive_nearest_hive(const CLvertex* vertex,
		     void** hive_data,
		     ZfSmartPointer** hive_smart_pointer)
{
    GList* li;
    ZfHive* min_hive;
    float min_distance;
    
    
    min_hive = 0;
    min_distance = MAXFLOAT;

    for (li = hive_list; li; li = li->next)
    {
	ZfHive* hive;
	CLnormal difference;
	float distance;

	hive = (ZfHive*)li->data;

	cluNormalDifference(&difference, &hive->position, vertex);

	distance = cluNormalMagnitude(&difference);
	if (distance < min_distance)
	{
	    min_hive = hive;
	    min_distance = distance;
	}
    }
    
    if (min_hive)
    {
	*hive_data = min_hive;
	*hive_smart_pointer = &smart_pointer;
	
	return true;
    }

    return false;
}

void
zf_hive_query_position(const ZfHive* hive, 
		       CLvertex* position)
{
    clCopyVertex(position, &hive->position);
}

void
zf_hive_write(char* filename)
{
    FILE*   stream;
    GList*  li;
    ZfHive* hive;

    printf("%s() : START\n", __FUNCTION__);
    
    stream = fopen(filename, "w");

    fprintf(stream, "ZfHives\n{\n");
    
    fprintf(stream, "num_hives = %d\n", g_list_length(hive_list));

    fprintf(stream, "hives =\n[\n");

    for (li = hive_list; li; li = li->next)
    {
	hive = (ZfHive*)li->data;

	fprintf(stream, "ZfHive\n{\n");
	
	fprintf(stream, "position = %f %f %f\n",
		hive->position.x,
		hive->position.y,
		hive->position.z);
	
	fprintf(stream, "}\n");
    }

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