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

/* types of landscape object, to determine what model to use */
#define TREE 0
#define DOME 1
#define GOMM 2


struct ZfLandscapeObject
{
    unsigned int ref_count;
    
    CLmatrix frame; /* used to randomised each object orientation*/

    CLvertex position;
    float size;  /* between 0.8 - 1.8f */
    int type;

};

static ZfSmartPointer smart_pointer; 
static CLcontext* context_tree;
static CLcontext* context_dome;
static CLcontext* context_gomm;
static CLmodel* model_gomm;
static CLmodel* model_tree;
static CLmodel* model_dome;

static GList* landscape_object_list;

/*!
  \todo Incomplete?
*/
static bool
is_valid(const ZfLandscapeObject* object)
{
    return true;
}

static void
reference(ZfLandscapeObject* object)
{
    object->ref_count++;
}

static void
release(ZfLandscapeObject* object)
{
    object->ref_count--;

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


static void
object_read(FILE* stream)
{
    CLvertex position;
    int type;            /* type of object */

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

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

    zf_landscape_object_new(&position, type);
}

static void
render(ZfLandscapeObject* object)
{
    CLnormal distance;
    CLvertex ship_pos;
    
    zf_ship_query_position(&ship_pos);

    cluNormalDifference(&distance, &object->position, &ship_pos);

    if(cluNormalMagnitude(&distance) < 200.0f || zf_render_system_get_tool_mode()) 
    {
	glPushAttrib(GL_ALL_ATTRIB_BITS);

	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	glColor3f(1.0f, 1.0f, 1.0f);

	glPushMatrix();

	glMultMatrixf((GLfloat *) &object->frame);

	switch(object->type)
	{
	case TREE:
	    glScalef(object->size, object->size, object->size);
	    cluRenderModel(model_tree);
	    break;
	case DOME:
	    glScalef(object->size, object->size, object->size);
	    cluRenderModel(model_dome);
	    break;
	case GOMM:
	    cluRenderModel(model_gomm);
	    break;
	default:
	    printf("%s() : unknown landscape object type\n", __FUNCTION__);
	    exit(1);
	}
	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;
  }
*/


/*!
  \todo Move read code to dedicated read() function.
*/
void
zf_landscape_object_init(char* filename)
{
    //printf("%s()\n", __FUNCTION__);

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

    //printf("%s() : loading tree1\n", __FUNCTION__);

    context_tree = clDefaultContext(clNewContext());
    model_tree = clioLoadModel(context_tree, "../data/models/tree1/tree1.3ds");

    //printf("%s() : loading broken_dome2\n", __FUNCTION__);

    context_dome = clDefaultContext(clNewContext());
    model_dome = clioLoadModel(context_dome, "../data/models/broken_dome/broken_dome.3ds");

    //printf("%s() : loading gomm\n", __FUNCTION__);
    
    context_gomm = clDefaultContext(clNewContext());
    model_gomm = clioLoadModel(context_gomm, "../data/models/gomm/gomm_statue.3ds");

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

    cluModelScaleUnitCube(model_tree);
    cluModelScale(model_tree, 10.0f);
    clUpdateContext(model_tree->context);

    cluModelScaleUnitCube(model_dome);
    cluModelScale(model_dome, 10.0f);
    clUpdateContext(model_dome->context);

    cluModelScaleUnitCube(model_gomm);
    cluModelScale(model_gomm, 50.0f);
    clUpdateContext(model_gomm->context);

    //printf("%s() : done\n", __FUNCTION__);

    /* haxor! */
    {
	unsigned int i;
	int num_objects;
    
	FILE* stream;

	stream = fopen(filename, "r");

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

	/* read */
	fscanf(stream, "ZfLandscape\n{\n");
    
	fscanf(stream, "num_objects = %d\n", &num_objects);

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

	for (i = 0; i < num_objects; i++)
	{
	    object_read(stream);   
	}

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

	fclose(stream);
    }
}

void
zf_landscape_object_close(void)
{
    clDeleteContext(context_tree);
    clDeleteContext(context_dome);
    clDeleteContext(context_gomm);
}

/*!
  \todo landscape objects are not added to collision system

  \todo type = 0, what does that mean?
*/
ZfLandscapeObject*
zf_landscape_object_new(const CLvertex* position, const int type)
{
  
    ZfLandscapeObject* object;

    object = g_new(ZfLandscapeObject, 1);
    
    object->ref_count = 0;
  
    clCopyVertex(&object->position, position);

    object->type = type;
    object->size = (float)rand() / RAND_MAX + 0.8f;

    if(object->type != GOMM) 
    {
	/* randomise orientation a little */
	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 *) &object->frame);
	glPopMatrix();
    }
    else
    {
	clDefaultMatrix(&object->frame);
	cluSetMatrixPosition(&object->frame, position);
    }


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

    landscape_object_list = g_list_append(landscape_object_list, object);

    return object;
}

void
zf_landscape_object_write(char* filename)
{
    FILE*   stream;
    GList*  li;
    ZfLandscapeObject* landscape_object;

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

    fprintf(stream, "ZfLandscape\n{\n");
    
    fprintf(stream, "num_objects = %d\n", g_list_length(landscape_object_list));

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

    for (li = landscape_object_list; li; li = li->next)
    {
	landscape_object = (ZfLandscapeObject*)li->data;

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

	fprintf(stream, "type = %d\n", landscape_object->type);
	fprintf(stream, "}\n");
    }

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