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

#define NODE_RADIUS 0.5f
#define HEX_FIELD_RADIUS 5.0f
#define HEX_MAIN_FIELD_SCORE 800
#define HEX_FIELD_SCORE 100

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

    CLvertex node_pos[6];
    float flux_t; /* only needed to write to file */
    float angle;  /* rotated by how much */

    /* true if on */
    bool top;
    bool top_right;
    bool bottom_right;
    bool bottom;
    bool bottom_left;
    bool top_left;
};

static ZfSmartPointer smart_pointer; /* interface for hexfluxfields */
static CLvertex top, top_right, bottom_right;
static CLvertex bottom, bottom_left, top_left;

static CLcontext* context;
static CLmodel* model;


static bool
is_valid(const ZfHexFluxField* hexfluxfield)
{
    return true;
}

static void
reference(ZfHexFluxField* hexfluxfield)
{
    hexfluxfield->ref_count++;
}

static void
release(ZfHexFluxField* hexfluxfield)
{
    hexfluxfield->ref_count--;

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

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


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

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

    fscanf(stream, "}\n");

    zf_hexfluxfield_new((double)flux_t);
}


static void
render_opaque(ZfHexFluxField* hexfluxfield)
{
    CLvertex position;

    cluSetVertexMatrixOrigin(&position, &hexfluxfield->frame);
    
    glPushAttrib(GL_ALL_ATTRIB_BITS);

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

    glColor3f(1.0f, 0.0f, 0.0f); 
    if(hexfluxfield->top)
    {
	glPushMatrix();
	glTranslatef(hexfluxfield->node_pos[0].x, hexfluxfield->node_pos[0].y, hexfluxfield->node_pos[0].z);
	cluRenderModel(model);
	glPopMatrix();
    }
 
    glColor3f(1.0f, 1.0f, 1.0f);
    if(hexfluxfield->top_right)
    {
	glPushMatrix();
	glTranslatef(hexfluxfield->node_pos[1].x, hexfluxfield->node_pos[1].y, hexfluxfield->node_pos[1].z);
	cluRenderModel(model);
	glPopMatrix();
    }
    if(hexfluxfield->bottom_right)
    {  
	glPushMatrix();
	glTranslatef(hexfluxfield->node_pos[2].x, hexfluxfield->node_pos[2].y, hexfluxfield->node_pos[2].z);
	cluRenderModel(model);
	glPopMatrix();
    }
    if(hexfluxfield->bottom)
    {
	glPushMatrix();
	glTranslatef(hexfluxfield->node_pos[3].x, hexfluxfield->node_pos[3].y, hexfluxfield->node_pos[3].z);
	cluRenderModel(model);
	glPopMatrix();
    }
    if(hexfluxfield->bottom_left)
    {
	glPushMatrix();
	glTranslatef(hexfluxfield->node_pos[4].x, hexfluxfield->node_pos[4].y, hexfluxfield->node_pos[4].z);
	cluRenderModel(model);
	glPopMatrix();
    }
    if(hexfluxfield->top_left)
    {
	glPushMatrix();
	glTranslatef(hexfluxfield->node_pos[5].x, hexfluxfield->node_pos[5].y, hexfluxfield->node_pos[5].z);
	cluRenderModel(model);
	glPopMatrix();
    }



    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
    /* rendering the lines */

    glColor3f(0.0f, 0.0f, 0.0f);
    glLineWidth(2.0f);
    glBegin(GL_LINES);
    if(hexfluxfield->top)
    {
	glVertex3f(hexfluxfield->node_pos[0].x, hexfluxfield->node_pos[0].y, hexfluxfield->node_pos[0].z);
	glVertex3f(position.x, position.y, position.z);
    }
    if(hexfluxfield->top_left)
    {
	glVertex3f(hexfluxfield->node_pos[5].x, hexfluxfield->node_pos[5].y, hexfluxfield->node_pos[5].z);
	glVertex3f(position.x, position.y, position.z);
    }
    if(hexfluxfield->top_right)
    {
	glVertex3f(hexfluxfield->node_pos[1].x, hexfluxfield->node_pos[1].y, hexfluxfield->node_pos[1].z);
	glVertex3f(position.x, position.y, position.z);
    }
    if(hexfluxfield->bottom)
    {
	glVertex3f(hexfluxfield->node_pos[3].x, hexfluxfield->node_pos[3].y, hexfluxfield->node_pos[3].z);
	glVertex3f(position.x, position.y, position.z);
    }
    if(hexfluxfield->bottom_left)
    {
	glVertex3f(hexfluxfield->node_pos[4].x, hexfluxfield->node_pos[4].y, hexfluxfield->node_pos[4].z);
	glVertex3f(position.x, position.y, position.z);
    }
    if(hexfluxfield->bottom_right)
    {
	glVertex3f(hexfluxfield->node_pos[2].x, hexfluxfield->node_pos[2].y, hexfluxfield->node_pos[2].z);
	glVertex3f(position.x, position.y, position.z);
    }
    glEnd();

    glPopAttrib();
}

static void
render_translucent(ZfHexFluxField* hexfluxfield)
{
    CLvertex position;

    cluSetVertexMatrixOrigin(&position, &hexfluxfield->frame);

    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glDisable(GL_LIGHTING);

    glColor4f(0.5f, 0.4f, 0.8f, 0.6f);

    glBegin(GL_TRIANGLES);
    if(hexfluxfield->top)
    {
	if(hexfluxfield->top_right)
	{
	    glVertex3f(hexfluxfield->node_pos[1].x,
		       hexfluxfield->node_pos[1].y,
		       hexfluxfield->node_pos[1].z);
	    glVertex3f(position.x, position.y, position.z);
	    glVertex3f(hexfluxfield->node_pos[0].x,
		       hexfluxfield->node_pos[0].y,
		       hexfluxfield->node_pos[0].z);
	}
	if(hexfluxfield->top_left)
	{
	    glVertex3f(hexfluxfield->node_pos[0].x,
		       hexfluxfield->node_pos[0].y,
		       hexfluxfield->node_pos[0].z);
	    glVertex3f(position.x, position.y, position.z);
	    glVertex3f(hexfluxfield->node_pos[5].x,
		       hexfluxfield->node_pos[5].y,
		       hexfluxfield->node_pos[5].z);
	}
    }

    if(hexfluxfield->bottom_left)
    {
	if(hexfluxfield->top_left)
	{
	    glVertex3f(hexfluxfield->node_pos[5].x,
		       hexfluxfield->node_pos[5].y,
		       hexfluxfield->node_pos[5].z);
	    glVertex3f(position.x, position.y, position.z);
	    glVertex3f(hexfluxfield->node_pos[4].x,
		       hexfluxfield->node_pos[4].y,
		       hexfluxfield->node_pos[4].z);
	}
	if(hexfluxfield->bottom)
	{ 

	    glVertex3f(hexfluxfield->node_pos[4].x,
		       hexfluxfield->node_pos[4].y,
		       hexfluxfield->node_pos[4].z);
	    glVertex3f(position.x, position.y, position.z);
	    glVertex3f(hexfluxfield->node_pos[3].x,
		       hexfluxfield->node_pos[3].y,
		       hexfluxfield->node_pos[3].z);
	}
    }

    if(hexfluxfield->bottom_right)
    {
	if(hexfluxfield->bottom)
	{
	    glVertex3f(hexfluxfield->node_pos[3].x,
		       hexfluxfield->node_pos[3].y,
		       hexfluxfield->node_pos[3].z);
	    glVertex3f(position.x, position.y, position.z);
	    glVertex3f(hexfluxfield->node_pos[2].x,
		       hexfluxfield->node_pos[2].y,
		       hexfluxfield->node_pos[2].z);
	}
	if(hexfluxfield->top_right)
	{
	    glVertex3f(hexfluxfield->node_pos[2].x,
		       hexfluxfield->node_pos[2].y,
		       hexfluxfield->node_pos[2].z);
	    glVertex3f(position.x, position.y, position.z);
	    glVertex3f(hexfluxfield->node_pos[1].x,
		       hexfluxfield->node_pos[1].y,
		       hexfluxfield->node_pos[1].z);
	}
    }
    glEnd();
    
    glPopAttrib();
}

static inline int
overlap_angle_range(float a0, float a1, float b0, float b1)
{
    while(b0 > 360.0f)
    {
	b0 -=360.0f;
    }
    while(b1 > 360.0f)
    {
	b1 -=360.0f;
    }

    printf("before in overlap %f %f %f %f\n", a0, a1, b0 ,b1);
    if(a1 < a0)
	a1 += 360.0f;

    if(b1 < b0)
    {
	if(a1 < b1)
	    a1 += 360.0f;

	if(a0 < b1)
	    a0 += 360.0f;

	if(a1 < a0)
	    a1 += 360.0f;
		
	b1 +=360.0f;
    }

    printf("after in overlap %f %f %f %f\n", a0, a1, b0 ,b1);

    if(a0 > b0 && a1 < b1)
	return 0;

    return 1;
    /*


    if(a0 < b1 && a1 > b1)
    return 1;
    else if(a1 > b0 && a0 < b0)
    return 1;
    else if(a0 > b0 && a1 < b1)
    return 1;

    return 0;*/
}

static bool
query_collision(ZfHexFluxField* hexfluxfield,
		const CLUsphere* sphere,
		const CLnormal* force,
		ZfType collider_type,
		CLvertex* collision_position,
		CLnormal* collision_force_perp,
		CLnormal* collision_force_tan)
{
    float dist1, dist2, dist3, dist4, dist5, dist6;
    float gap;
    CLvertex position;
    
    /* NODE_RADIUS *2 to make it easier to hit */
    gap = sphere->radius + NODE_RADIUS * 2;

    cluSetVertexMatrixOrigin(&position, &hexfluxfield->frame);

  
    if((collider_type & ZF_LASER) | (collider_type & ZF_MISSILE)  | (collider_type & ZF_SUPER_MISSILE))
    {
	dist1 = cluVertexDistance(&sphere->origin, &hexfluxfield->node_pos[0]);
	if(dist1 < gap && hexfluxfield->top)
	{
	    hexfluxfield->top = false;
	    zf_explosion_new(&hexfluxfield->node_pos[0], 3.0f);
	    zf_score_indicator_new(&hexfluxfield->node_pos[0], HEX_MAIN_FIELD_SCORE);
	    zf_hud_increase_score(HEX_MAIN_FIELD_SCORE);

	    if(hexfluxfield->top_right)
	    {
		hexfluxfield->top_right =  false;
		zf_explosion_new(&hexfluxfield->node_pos[1], 2.0f);
	    }
	    if(hexfluxfield->bottom_right)
	    {
		hexfluxfield->bottom_right =  false;
		zf_explosion_new(&hexfluxfield->node_pos[2], 2.0f);
	    }
	    if(hexfluxfield->bottom)
	    {
		hexfluxfield->bottom =  false;
		zf_explosion_new(&hexfluxfield->node_pos[3], 2.0f);
	    }
	    	
	    if(hexfluxfield->bottom_left)
	    {
		hexfluxfield->bottom_left =  false;
		zf_explosion_new(&hexfluxfield->node_pos[4], 2.0f);
	    }
	    
	    if(hexfluxfield->top_left)
	    { 
		hexfluxfield->top_left =  false;
		zf_explosion_new(&hexfluxfield->node_pos[5], 2.0f);
	    }

	    return true;
	}

	dist2 = cluVertexDistance(&sphere->origin, &hexfluxfield->node_pos[1]);
	if(dist2 < gap && hexfluxfield->top_right)
	{
	    hexfluxfield->top_right =  false;
	    zf_explosion_new(&hexfluxfield->node_pos[1], 2.0f);
	    zf_score_indicator_new(&hexfluxfield->node_pos[1], HEX_FIELD_SCORE);
	    zf_hud_increase_score(HEX_FIELD_SCORE);
	    return true;
	}

	dist3 = cluVertexDistance(&sphere->origin, &hexfluxfield->node_pos[2]);
	if(dist3 < gap && hexfluxfield->bottom_right)
	{
	    hexfluxfield->bottom_right =  false;
	    zf_explosion_new(&hexfluxfield->node_pos[2], 2.0f);
	    zf_score_indicator_new(&hexfluxfield->node_pos[2], HEX_FIELD_SCORE);
	    zf_hud_increase_score(HEX_FIELD_SCORE);
	    return true;
	}

	dist4 = cluVertexDistance(&sphere->origin, &hexfluxfield->node_pos[3]);
	if(dist4 < gap && hexfluxfield->bottom)
	{
	    hexfluxfield->bottom =  false;
	    zf_explosion_new(&hexfluxfield->node_pos[3], 2.0f);
	    zf_score_indicator_new(&hexfluxfield->node_pos[3], HEX_FIELD_SCORE);
	    zf_hud_increase_score(HEX_FIELD_SCORE);
	    return true;
	}

	dist5 = cluVertexDistance(&sphere->origin, &hexfluxfield->node_pos[4]);
	if(dist5 < gap && hexfluxfield->bottom_left)
	{
	    hexfluxfield->bottom_left =  false;
	    zf_explosion_new(&hexfluxfield->node_pos[4], 2.0f);
	    zf_score_indicator_new(&hexfluxfield->node_pos[4], HEX_FIELD_SCORE);
	    zf_hud_increase_score(HEX_FIELD_SCORE);
	    return true;
	}

	dist6 = cluVertexDistance(&sphere->origin, &hexfluxfield->node_pos[5]);
	if(dist6 < gap && hexfluxfield->top_left)
	{
	    hexfluxfield->top_left =  false;
	    zf_explosion_new(&hexfluxfield->node_pos[5], 2.0f);
	    zf_score_indicator_new(&hexfluxfield->node_pos[5], HEX_FIELD_SCORE);
	    zf_hud_increase_score(HEX_FIELD_SCORE);
	    return true;
	}
    }
    else if(collider_type & ZF_EXPLOSIVE)  /* why not work? because bomb and HFF are static colliders!*/
    {
	hexfluxfield->top = false;
	hexfluxfield->top_right = false;
	hexfluxfield->top_left = false;	
	hexfluxfield->bottom = false;
	hexfluxfield->bottom_right = false;
	hexfluxfield->bottom_left = false;

	zf_explosion_new(&hexfluxfield->node_pos[0], 3.0f);
	zf_explosion_new(&hexfluxfield->node_pos[1], 2.0f);
	zf_explosion_new(&hexfluxfield->node_pos[2], 2.0f);
	zf_explosion_new(&hexfluxfield->node_pos[3], 2.0f);
	zf_explosion_new(&hexfluxfield->node_pos[4], 2.0f);
	zf_explosion_new(&hexfluxfield->node_pos[5], 2.0f);
	return true;
    }
    else 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 + HEX_FIELD_RADIUS)
	{
	    if(fabs(cluPlaneDistance(&hexfluxfield->plane, &sphere->origin)) < sphere->radius)
	    {
		float roll1,roll2, angle_range, ang0, ang1;
		CLnormal n2, n1, n0, axis;
		CLvertex pos;
		CLmatrix frame;
		CLUquaternion quat;
		float cos_angle;
		float sin_angle;
		bool ship_hit = false;
		
		cluSetNormalMatrixAxisY(&n0, &hexfluxfield->frame);
		cluSetNormalMatrixAxisZ(&axis, &hexfluxfield->frame);
		//cluSetVertexMatrixOrigin(&pos, &hexfluxfield->frame);
		cluNormalNormalise(&n0);
		cluNormalNormalise(&axis);

		zf_ship_query_frame(&frame);
	       	zf_ship_query_position(&pos);
		zf_align_frame_z_vertex_normal(&frame, &pos, &axis);

		cluSetNormalMatrixAxisY(&n1, &frame);
		//n1.k = 0.0f;  /* project the ship's axis onto the z-plane.approximate only */
		cluNormalNormalise(&n1);	

		/* since cos doesn't know which hemicircle it's in....*/
		cluSetNormal(&axis, 0.0f, 0.0f, -1.0f);
		cluSetQuaternionAxisAngle(&quat, &axis, 2.0f); /* degrees right*/
		cluSetMatrixOrientation(&frame, &quat);
		clCopyNormal(&n2, &n1);
		cluNormalTransform(&n2, &frame);

		cos_angle = cluNormalDotProduct(&n0, &n1);
		roll1 = CL_RAD2DEG(acos(cos_angle));

		cos_angle = cluNormalDotProduct(&n0, &n2);
		roll2 = CL_RAD2DEG(acos(cos_angle));

		if(roll2 < roll1)
		    roll1 = 360.0f - roll1;

		angle_range = 20.0f;  /* hack size that seems to work well with ship size */
		
		ang0 = roll1 - angle_range;
		ang1 = roll1 + angle_range;

		if(ang0 < 0.0f)
		    ang0 += 360.0f;
		else if(ang0 > 360.0f)
		    ang0 -= 360.0f;
		
		if(ang1 < 0.0f)
		    ang1 += 360.0f;
		else if(ang1 > 360.0f)
		    ang1 -= 360.0f;

		if(0.0f < ang0 && ang0 <= 60.0f)
		{
		    if(hexfluxfield->top && hexfluxfield->top_right)
			ship_hit = true;
		}
		else if(60.0f < ang0 && ang0 <= 120.0f)
		{
		    if(hexfluxfield->top_right && hexfluxfield->bottom_right)
			ship_hit = true;
		}
		else if(120.0f < ang0 && ang0 <= 180.0f)
		{
		    if(hexfluxfield->bottom_right && hexfluxfield->bottom)
			ship_hit = true;
		}
		else if(180.0f < ang0 && ang0 <= 240.0f)
		{
		    if(hexfluxfield->bottom && hexfluxfield->bottom_left)
			ship_hit = true;
		}
		else if(240.0f < ang0 && ang0 <= 300.0f)
		{
		    if(hexfluxfield->bottom_left && hexfluxfield->top_left)
			ship_hit = true;
		}
		else
		{
		    if(hexfluxfield->top_left && hexfluxfield->top)
			ship_hit = true;
		}


		if(0.0f < ang1 && ang1 <= 60.0f)
		{
		    if(hexfluxfield->top && hexfluxfield->top_right)
			ship_hit = true;
		}
		else if(60.0f < ang1 && ang1 <= 120.0f)
		{
		    if(hexfluxfield->top_right && hexfluxfield->bottom_right)
			ship_hit = true;
		}
		else if(120.0f < ang1 && ang1 <= 180.0f)
		{
		    if(hexfluxfield->bottom_right && hexfluxfield->bottom)
			ship_hit = true;
		}
		else if(180.0f < ang1 && ang1 <= 240.0f)
		{
		    if(hexfluxfield->bottom && hexfluxfield->bottom_left)
			ship_hit = true;
		}
		else if(240.0f < ang1 && ang1 <= 300.0f)
		{
		    if(hexfluxfield->bottom_left && hexfluxfield->top_left)
			ship_hit = true;
		}
		else
		{
		    if(hexfluxfield->top_left && hexfluxfield->top)
			ship_hit = true;
		}
	
		return ship_hit;
	    }
	}
    }
    return false;
}

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

    cluSetVertex(&top, 0.0f, 5.0f, 0.0f);
    cluSetVertex(&top_right, 4.33f, 2.5f, 0.0f);
    cluSetVertex(&bottom_right, 4.33f, -2.5f, 0.0f);
    cluSetVertex(&bottom, 0.0f, -5.0f, 0.0f);
    cluSetVertex(&bottom_left, -4.33f, -2.5f, 0.0f);
    cluSetVertex(&top_left, -4.33f, 2.5f, 0.0f);

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

	stream = fopen(filename, "r");

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

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

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

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

    context = clDefaultContext(clNewContext());
    model = clioLoadModel(context, "../data/models/mine3/mine3.3DS");
    
    /* not clean to just exit, but use this until we have a good error
       system */
    if (!model)
    {
	printf("could not load hex flux field model\n");
	exit(1);
    }

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


}


void
zf_hexfluxfield_write(char* filename, GList* hexfluxfield_list)
{
    FILE* stream; 
    GList*  li;
    ZfHexFluxField* hexfluxfield;
    
    printf("%s() : save hex flux field file %s\n", __FUNCTION__, filename);
    
    stream = fopen(filename, "w");

    fprintf(stream, "ZfHexFluxFields\n{\n");
    fprintf(stream, "num_hexfluxfields = %d\n",  g_list_length(hexfluxfield_list));
    fprintf(stream, "hexfluxfields =\n[\n");

    for (li = hexfluxfield_list; li; li = li->next)
    {
	hexfluxfield = (ZfHexFluxField*)li->data;

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

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

    fclose(stream);
}


void
zf_hexfluxfield_close(void)
{
    clDeleteContext(context);
}

ZfHexFluxField*
zf_hexfluxfield_new(const double flux_t)
{
    CLvertex position;
    CLnormal axis, yAxis, zAxis;
    CLUquaternion rotate;
    ZfHexFluxField* hexfluxfield;

    hexfluxfield = g_new(ZfHexFluxField, 1);
    
    hexfluxfield->ref_count = 0;
    hexfluxfield->flux_t = flux_t;

    clDefaultMatrix(&hexfluxfield->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(&hexfluxfield->frame, &position);
    zf_flux_update_frame(&hexfluxfield->frame, flux_t - 0.1f);
    cluSetNormalMatrixAxisZ(&zAxis, &hexfluxfield->frame);
    cluNormalScale(&zAxis, -1.0f);

    zf_flux_update_frame(&hexfluxfield->frame,flux_t);
    cluSetVertexMatrixOrigin(&position,&hexfluxfield->frame); 

    hexfluxfield->angle = 360.0f * rand() / RAND_MAX;

    /*
      cluSetNormal(&axis, 0.0f, 0.0f, 1.0f);
      cluSetQuaternionAxisAngle(&rotate, &axis, hexfluxfield->angle);
      cluSetMatrixOrientation(&hexfluxfield->frame, &rotate);
      
      cluSetNormalMatrixAxisY(&yAxis, &hexfluxfield->frame);
      
      
      zf_align_frame_z_vertex_normal(&hexfluxfield->frame, 
      &position,
      &zAxis);
      
      zf_align_frame_y_vertex_normal(&hexfluxfield->frame, 
      &position,
      &yAxis);
    */
    cluSetNormal(&axis, 0.0f, 1.0f, 0.0f);
    zf_align_frame_y_vertex_normal(&hexfluxfield->frame, 
				   &position,
				   &axis);
    

    clCopyVertex(&hexfluxfield->node_pos[0], &top);
    cluVertexTransform(&hexfluxfield->node_pos[0], &hexfluxfield->frame);

    clCopyVertex(&hexfluxfield->node_pos[1], &top_right);
    cluVertexTransform(&hexfluxfield->node_pos[1], &hexfluxfield->frame);

    clCopyVertex(&hexfluxfield->node_pos[2], &bottom_right);
    cluVertexTransform(&hexfluxfield->node_pos[2], &hexfluxfield->frame);

    clCopyVertex(&hexfluxfield->node_pos[3], &bottom);
    cluVertexTransform(&hexfluxfield->node_pos[3], &hexfluxfield->frame);

    clCopyVertex(&hexfluxfield->node_pos[4], &bottom_left);
    cluVertexTransform(&hexfluxfield->node_pos[4], &hexfluxfield->frame);

    clCopyVertex(&hexfluxfield->node_pos[5], &top_left);
    cluVertexTransform(&hexfluxfield->node_pos[5], &hexfluxfield->frame);

    cluSetPlaneTriangle(&hexfluxfield->plane,
			&hexfluxfield->node_pos[0],
			&position,
			&hexfluxfield->node_pos[1]);

    hexfluxfield->top = true;
    hexfluxfield->top_right = true;
    hexfluxfield->bottom_right = true;
    hexfluxfield->bottom = true;
    hexfluxfield->bottom_left = true;
    hexfluxfield->top_left = true;

    zf_render_system_add_opaque(hexfluxfield,
				&smart_pointer,
				(ZfRender*) render_opaque);

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

    zf_collision_system_add_static(hexfluxfield,
				   &smart_pointer,
				   (ZfQueryCollision*)query_collision,
				   ZF_HEXFIELD);

    return hexfluxfield;
}

bool
zf_hexfluxfield_query_ray_intercept(const CLUray* ray, 
				    void* data,
				    CLvertex** position)
{
    CLUsphere sphere;
    float t;             /* intersection time along ray */
    unsigned int i;

    ZfHexFluxField *hexfluxfield;
    hexfluxfield = data;


    sphere.radius = NODE_RADIUS * 2; /* make it easier to hit */

    for(i = 0; i < 6; i++)
    {
	clCopyVertex(&sphere.origin, &hexfluxfield->node_pos[i]);
	
	if(cluRayIntersectSphere(&t, ray, &sphere))
	{
	    *position = &hexfluxfield->node_pos[i];
	    return true;
	}
    }

    return false;

}

