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

/*!
  \todo Make the two values match by storing one value in a global
 space, maybe in zf.h
*/

/*These MUST match the heights of the ship tier hover heights in zf_ship.c */
#define TIER_HEIGHT_1 0.5f
#define TIER_HEIGHT_2 1.0f
#define TIER_HEIGHT_3 1.5f

#define RING_RADIUS 3.5f

#define TIER_RING_SCORE 100

#define TIER_RED   0
#define TIER_GREEN 1
#define TIER_BLUE  2


struct ZfTierRing
{
    unsigned int ref_count;
    int tier_level;   /*specifies which tier level the ring is at 0 - 2 */
    float flux_t;  /* only needed to write to file */
    CLmatrix frame;
    CLUplane plane;

    bool ship_score;
};

static ZfSmartPointer smart_pointer; 

/* for testing only - Should remove this once the model is in*/
static CLtexture* temp_texture;

static CLcontext* context;
static CLmodel* model;

static bool
is_valid(const ZfTierRing* tier_ring)
{
   return true;
}

static void
reference(ZfTierRing* tier_ring)
{
    tier_ring->ref_count++;
}

static void
release(ZfTierRing* tier_ring)
{
    tier_ring->ref_count--;

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


static void
animate(ZfTierRing* tier_ring)
{
    /* make texture ripple perhaps? */
}


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


static void
tier_ring_read(FILE* stream)
{
    float flux_t;
    int tier_level;

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

    zf_tier_ring_new((double)flux_t, tier_level);
}



void
zf_tier_ring_write(char* filename, GList* tier_ring_list)
{
    FILE* stream; 
    GList*  li;
    ZfTierRing* tierring;
    
    printf("%s() : save tier ring file %s\n", __FUNCTION__, filename);
    
    stream = fopen(filename, "w");
    
    fprintf(stream, "ZfTierRings\n{\n");
    fprintf(stream, "num_tierrings = %d\n",  g_list_length(tier_ring_list));
    fprintf(stream, "tierrings =\n[\n");

    for (li = tier_ring_list; li; li = li->next)
    {
	tierring = (ZfTierRing*)li->data;

	fprintf(stream, "ZfTierRing\n{\n");
	fprintf(stream, "flux_t = %f\n",tierring->flux_t);
	fprintf(stream, "tier_level = %d\n",tierring->tier_level);
	fprintf(stream, "}\n");
    }

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

    fclose(stream);
}



/*!
  \todo Render function does a bunch of state changes but doesnt render anything!
 */
static void
render_opaque(ZfTierRing* tier_ring)
{
    CLvertex position;
    cluSetVertexMatrixOrigin(&position, &tier_ring->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*)&tier_ring->frame);


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


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

   /* glColor3f(0.0f, 0.0f, 1.0f);*/
   

   /*
    switch(tier_ring->tier_level)
    {
    case 1:
	glColor3f(1.0f, 0.0f, 0.0f);
	glutWireTorus(0.1f, 0.5f, 10, 16);
	break;
    case 2:
	glColor3f(0.0f, 1.0f, 0.0f);
	glutWireTorus(0.1f, 1.0f, 10, 16);
	break;
    case 3:
	glColor3f(0.0f, 0.0f, 1.0f);
	glutWireTorus(0.1f, 1.5f, 10, 16);
	break;
    default:
	glColor3f(1.0f, 1.0f, 1.0f);
	glutWireTorus(0.2f, 1.7f, 10, 16);
	break;
    }
*/
    /*    cluRenderModel(model);*/

    glPopMatrix();


    glPopAttrib();
}

static void
render_translucent(ZfTierRing* tier_ring)
{
    /* the clear stuff */
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glPushMatrix();
    glMultMatrixf((GLfloat*)&tier_ring->frame);

    glDisable(GL_LIGHTING);
    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);

    switch(tier_ring->tier_level)
    {
    case TIER_RED:
	glColor4f(1.0f, 0.0f, 0.0f, 0.8f);
	glutSolidTorus(0.1f, 0.5f, 10, 16);
	break;
    case TIER_GREEN:
	glColor4f(0.0f, 1.0f, 0.0f, 0.8f);
	glutSolidTorus(0.1f, 1.0f, 10, 16);
	break;
    case TIER_BLUE:
	glColor4f(0.0f, 0.0f, 1.0f, 0.8f);
	glutSolidTorus(0.1f, 1.5f, 10, 16);
	break;
    default:
	glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
	glutSolidTorus(0.2f, 1.7f, 10, 16);
	break;
    }
    glPopAttrib();
    
    glPopMatrix();
}

static bool
query_collision(ZfTierRing* tier_ring,
		const CLUsphere* sphere,
		const CLnormal* force,
		ZfType collider_type,
		CLvertex* collision_position,
		CLnormal* collision_force_perp,
		CLnormal* collision_force_tan)
{
    CLvertex position;

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

    /*tierlevel -> 0-2, whereas ship tier -> 1-3..... SHRUG*/
    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(&tier_ring->plane, &sphere->origin)) < (sphere->radius*1.5f))
	    {
		if((zf_ship_query_tier_level() == tier_ring->tier_level+1))
		{
		    return true;
		}
		else if(!tier_ring->ship_score)
		{
		    zf_hud_increase_score(TIER_RING_SCORE);
		    tier_ring->ship_score = true;
		}
	    }
	}
    }

    return false;
}

/*!
  \todo Move read code to dedicated read() function.
*/
void
zf_tier_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_tier_rings;
	
	FILE* stream;

	stream = fopen(filename, "r");

	if(!stream)
	{
	    printf("%s() : ERROR : load tier rings file %s\n", __FUNCTION__, filename);
	    exit(1);
	}
	
	/* read */
	fscanf(stream, "ZfTierRings\n{\n");
	fscanf(stream, "num_tierrings = %d\n", &num_tier_rings);
	
	fscanf(stream, "tierrings =\n[\n");
	
	for (i = 0; i < num_tier_rings; i++)
	{
	    tier_ring_read(stream);   
	}

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

void
zf_tier_ring_close(void)
{
    clDeleteTexture(temp_texture);
    /* model later maybe ?*/
    clDeleteContext(context);
}

ZfTierRing*
zf_tier_ring_new(const double flux_t, const int tier_level)
{
    CLvertex position;
    CLnormal zAxis;
    ZfTierRing* tier_ring;

    tier_ring = g_new(ZfTierRing, 1);
    
    tier_ring->ref_count = 0;
    tier_ring->ship_score = false;

    clDefaultMatrix(&tier_ring->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(&tier_ring->frame, &position);

    tier_ring->tier_level = tier_level;

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

    tier_ring->flux_t = flux_t;

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

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

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

    zf_collision_system_add_static(tier_ring,
				   &smart_pointer,
				   (ZfQueryCollision*)query_collision,
				   ZF_TIER_RING);

    return tier_ring;
}

