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

#define RING_RADIUS 3.5f

//typedef struct ZfBatteryRing ZfBatteryRing;

struct ZfBatteryRing
{
    unsigned int ref_count;
    CLmatrix frame;
    CLUplane plane;

    bool ship_pass;
    float flux_t; /* for writing to file only really.... */
};

static ZfSmartPointer smart_pointer; 

static CLcontext* context;
static CLmodel* model;

static bool
is_valid(const ZfBatteryRing* batteryring)
{
   return true;
}

static void
reference(ZfBatteryRing* batteryring)
{
    batteryring->ref_count++;
}

static void
release(ZfBatteryRing* batteryring)
{
    batteryring->ref_count--;

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


static void
animate(ZfBatteryRing* batteryring)
{
    /* make texture ripple perhaps? */
}


static void
query_position(const ZfBatteryRing* batteryring, 
	       CLvertex* position)
{
    cluSetVertexMatrixOrigin(position, &batteryring->frame);
}



static void
batteryring_read(FILE* stream)
{
    float flux_t;

    fscanf(stream, "ZfBatteryRing\n{\n");
    fscanf(stream, "flux_t = %f\n",&flux_t);
    fscanf(stream, "}\n");

    zf_battery_ring_new((double)flux_t);
}


static void
render_opaque(ZfBatteryRing* batteryring)
{
    CLvertex position;
    cluSetVertexMatrixOrigin(&position, &batteryring->frame);
        
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    /*
      glDisable(GL_LIGHTING);
      
      glColor3f(0.9f, 0.3f, 0.3f);
      glPushMatrix();
      glTranslatef(position.x, position.y, position.z);
      glutWireSphere(RING_RADIUS, 8, 8);
      glPopMatrix();
    */

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


    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);


    /* temporariarly to see the model better 
       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
       glDisable(GL_CULL_FACE);*/

    glColor3f(1.0f, 1.0f, 1.0f);
    
    cluRenderModel(model);
    /*
      glColor3f(1.0f, 1.0f, 1.0f);
      glEnable(GL_TEXTURE_2D);
      glBegin(GL_QUADS);
      glTexCoord2f(0.0f, 0.0f); 
      glVertex3f(-4.0f, -4.0f, 0.0f);
      glTexCoord2f(800.0f/1024.0f, 0.0f); 
      glVertex3f(4.0f, -4.0f, 0.0f);
      glTexCoord2f(800.0f/1024.0f, 600.0f/1024.0f); 
      glVertex3f(4.0f, 4.0f, 0.0f);
      glTexCoord2f(0.0f, 600.0f/1024.0f);
      glVertex3f(-4.0f, 4.0f, 0.0f);
      glEnd();
    */
    glPopMatrix();


    glPopAttrib();
}

static void
render_translucent(ZfBatteryRing* batteryring)
{
    /* energy plane? */
}

static bool
query_collision(ZfBatteryRing* batteryring,
		const CLUsphere* sphere,
		const CLnormal* force,
		ZfType collider_type,
		CLvertex* collision_position,
		CLnormal* collision_force_perp,
		CLnormal* collision_force_tan)
{
    CLvertex position;
    
    cluSetVertexMatrixOrigin(&position, &batteryring->frame);
    if((collider_type & ZF_SHIP)) /* |(collider_type & ZF_ENEMY))*/
    {
	float dist;
	/* First check if in sphere */
	dist = cluVertexDistance(&sphere->origin, &position);
	if(dist < sphere->radius + RING_RADIUS)
	{
	    if(fabs(cluPlaneDistance(&batteryring->plane, &sphere->origin)) < (sphere->radius*1.5f))
	    {
		if(!batteryring->ship_pass)
		{
		    CLvertex pos;
		    cluSetVertex(&pos, -6.0f, 0.0f, -20.0f);
		    zf_hud_new_message(0.0f, 1, &pos, "checkpoint!");
		}   
		return true;
	    }
	}
    }

    return false;
}

/*!
  \todo Move read code to dedicated read() function.
*/
void
zf_battery_ring_init(char* filename)
{
    smart_pointer.is_valid = (ZfIsValid*) is_valid;
    smart_pointer.reference = (ZfReference*) reference;
    smart_pointer.release = (ZfRelease*) release;

    {
	unsigned int i;
	int num_batteryrings;
	
	FILE* stream;

	stream = fopen(filename, "r");

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

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

	//printf("%s() : num_batteryrings = %u\n", __FUNCTION__, num_batteryrings);
	
	fscanf(stream, "batteryrings =\n[\n");
	
	for (i = 0; i < num_batteryrings; i++)
	{
	    batteryring_read(stream);   
	}

	fscanf(stream, "]\n");
	fscanf(stream, "}\n");
	
	fclose(stream);
    }
    
     
    context = clDefaultContext(clNewContext());
    model = clioLoadModel(context, "../data/models/checkpoint/checkpoint.3DS");
    cluModelCentre(model); 
    cluModelScaleUnitCube(model);
    cluModelScale(model, 15.0f);
    clUpdateContext(model->context);
}

void
zf_battery_ring_write(char* filename, GList* battery_ring_list)
{
    FILE* stream; 
    GList*  li;
    ZfBatteryRing* batteryring;
    
    printf("%s() : save battery ring file %s\n", __FUNCTION__, filename);
    
    stream = fopen(filename, "w");
    
    fprintf(stream, "ZfBatteryRings\n{\n");
    fprintf(stream, "num_batteryrings = %d\n",  g_list_length(battery_ring_list));
    fprintf(stream, "batteryrings =\n[\n");

    for (li = battery_ring_list; li; li = li->next)
    {
	batteryring = (ZfBatteryRing*)li->data;

	fprintf(stream, "ZfBatteryRing\n{\n");
	fprintf(stream, "flux_t = %f\n",batteryring->flux_t);
	fprintf(stream, "}\n");
    }

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

    fclose(stream);
}

void
zf_battery_ring_close(void)
{
     /* model later maybe ?*/
    clDeleteContext(context);
}

ZfBatteryRing*
zf_battery_ring_new(const double flux_t)
{
    CLvertex position;
    CLnormal zAxis;
    ZfBatteryRing* batteryring;

    batteryring = g_new(ZfBatteryRing, 1);
    
    batteryring->ref_count = 0;
    batteryring->flux_t = flux_t;
    batteryring->ship_pass = false;
	
    clDefaultMatrix(&batteryring->frame);

    /* These two lines must be done to get a close frame. Otherwise,
       difference too large to get accurate alignment with flux */
    zf_flux_query_position(&position, flux_t - 0.1);
    cluSetMatrixPosition(&batteryring->frame, &position);

    zf_flux_update_frame(&batteryring->frame, flux_t);
    cluSetVertexMatrixOrigin(&position, &batteryring->frame); 
    cluSetNormalMatrixAxisZ(&zAxis, &batteryring->frame);

    cluSetPlaneVertexNormal(&batteryring->plane,
			    &position,
			    &zAxis);

    zf_animation_system_add(batteryring,
			    &smart_pointer,
			    (ZfAnimate*) animate);
    
    zf_render_system_add_opaque(batteryring,
				&smart_pointer,
				(ZfRender*) render_opaque);

    zf_render_system_add_translucent(batteryring,
				     &smart_pointer,
				     (ZfRender*) render_translucent);

    zf_collision_system_add_static(batteryring,
				   &smart_pointer,
				   (ZfQueryCollision*)query_collision,
				   ZF_BATTERY_RING);

    return batteryring;
}

