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

#define LOCK_LIMIT 6
#define MAX_MISSILE_STOCK 100
#define MAX_BOMB_STOCK 10
#define MAX_DECOY_STOCK 10
#define MAX_BOMB_TRITOR_STOCK 5
#define LOCK_RENDER_SIZE 0.1f /* ratio of screen width... or height...? */

/* In terms on energy of the ship */
#define LASER_COST 0.005f
#define SUPER_MISSILE_COST 0.05f 
#define TRITOR_COST 0.1f
#define DECOY_COST 0.3f
#define BOMB_COST 1.0f

#define TRITOR_LAUNCH_DELAY 200
#define BOMB_LAUNCH_DELAY 300

#define MISSILE_SPEED 0.6f
 
typedef struct WeaponTarget WeaponTarget;

/*!
  \todo Add lock time to weapon target struct to log time when weapon lock
  was achieved, then we can do some time-based animation of the lock reticule.
*/
struct WeaponTarget
{
    void* target_data;
    ZfSmartPointer* target_smart_pointer;
    ZfDynamicCollider* target_collider;

};


static WeaponTarget lock_list[LOCK_LIMIT];

static bool beam_mode;
static bool lock_mode;
static bool super_lock_mode;
static int lock_num;

static ZfType selected_weapon; /* which weapon the ship has currently selected */

static CLvertex cross_hair_position;

static int missile_stock;
static int decoy_stock;
static int bomb_stock;

static int tritor_countdown;
static int bomb_countdown;
static int bomb_tritor_stock;

/* sounds */
static ZfAudioSample* laser_sound;
static ZfAudioSample* rocket_sound;

/* textures */
static CLtexture* reticule_texture;

void
zf_weapon_control_init(void)
{
    beam_mode = false;
    lock_mode = false;
    super_lock_mode = false;
    lock_num = 0;

    selected_weapon = ZF_LASER;
    cluSetVertex(&cross_hair_position, 0.0f, 0.0f, 0.0f);

    missile_stock = 0;   
    decoy_stock = 0;
    bomb_stock = 0;
    
    tritor_countdown = 0;
    bomb_countdown = 0;
    bomb_tritor_stock = MAX_BOMB_TRITOR_STOCK - 2;

    /* load the sound */
    laser_sound = zf_audio_sample_load("../data/sound_effects/laser01.wav");

    rocket_sound = zf_audio_sample_load("../data/sound_effects/rocket_whoosh.mp3");

    /* load the texture */
    reticule_texture = clioLoadTexture("../data/textures/reticule/reticule.png");
}

void
zf_weapon_control_close(void)
{
    zf_audio_sample_destroy(laser_sound);
    clDeleteTexture(reticule_texture);
}

void
zf_weapon_control_set_weapon(ZfType state)
{
    /* Cannot change weapons when in either mode */
    if(!lock_mode && !beam_mode && !super_lock_mode)
    {
	printf("changing weapon\n");
	selected_weapon = state;
    }
}

ZfType
zf_weapon_control_query_weapon(void)
{
    return selected_weapon;
}


void
zf_weapon_control_start_lock_mode(void)
{
    lock_mode = true;
}

void
zf_weapon_control_stop_lock_mode(void)
{
    lock_mode = false;
}

void
zf_weapon_control_start_super_lock_mode(void)
{
    super_lock_mode = true;
}

void
zf_weapon_control_stop_super_lock_mode(void)
{
    super_lock_mode = false;
}
/*
  bool
  zf_weapon_control_query_lock_mode(void)
  {
  return lock_mode;
  }
*/

void
zf_weapon_control_start_beam_mode(void)
{
    beam_mode = true;
}

void
zf_weapon_control_stop_beam_mode(void)
{
    beam_mode = false;
}
/*
  bool
  zf_weapon_control_query_beam_mode(void)
  {
  return beam_mode;
  }
*/
int
zf_weapon_control_query_missile_stock(void)
{
    return missile_stock;
}

int
zf_weapon_control_query_decoy_stock(void)
{
    return decoy_stock;
}

int
zf_weapon_control_query_bomb_stock(void)
{
    return bomb_stock;
}

void
zf_weapon_control_add_to_missile_stock(int number)
{
    missile_stock += number;
    if(missile_stock > MAX_MISSILE_STOCK)
	missile_stock = MAX_MISSILE_STOCK;
}

void
zf_weapon_control_add_to_decoy_stock(int number)
{
    decoy_stock += number;
    if(decoy_stock > MAX_DECOY_STOCK)
	decoy_stock = MAX_DECOY_STOCK;
}

void
zf_weapon_control_add_to_bomb_tritor_stock(int number)
{
    bomb_tritor_stock += number;
    if(bomb_tritor_stock > MAX_BOMB_TRITOR_STOCK)
	bomb_tritor_stock = MAX_BOMB_TRITOR_STOCK;
}

void
zf_weapon_control_render_locked(void)
{
    GLdouble projection[16];
    GLdouble modelview[16];
    GLint viewport[4];
    GLdouble winX, winY, winZ;
    CLvertex position;
    double aspect;
    unsigned int i;

    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetIntegerv(GL_VIEWPORT, viewport);

    aspect = (double)viewport[2] / (double)viewport[3];

    glPushAttrib(GL_ALL_ATTRIB_BITS);    
    /*
      glDisable(GL_LIGHTING);
      glEnable(GL_BLEND);
      glBlendFunc(GL_ONE, GL_ONE);
      glEnable(GL_LINE_STIPPLE);
      glLineStipple(2, (GLushort)0xFF00FF00);
    
      glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
    */

    glPushMatrix();
    glLoadIdentity();

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    for(i = 0; i < lock_num; i++) 
    {
	if(lock_list[i].target_collider != 0)
	    lock_list[i].target_collider->query_position(lock_list[i].target_data,
							 &position);
	else 
	    clCopyVertex(&position, (CLvertex*)lock_list[i].target_data);
	
	gluProject((GLdouble)position.x,
		   (GLdouble)position.y,
		   (GLdouble)position.z,
		   modelview,
		   projection,
		   viewport,
		   &winX, 
		   &winY,
		   &winZ);

	/* screen coords to identity projection matrix */
	winX = winX / (double)viewport[2];
	winY = winY / (double)viewport[3];

	winX -= 0.5f;
	winX *= 2.0f;
	winY -= 0.5f;
	winY *= 2.0f;

#if 0
	glBegin(GL_LINE_LOOP);
	glVertex2f(winX - LOCK_RENDER_SIZE / aspect, winY - LOCK_RENDER_SIZE);
	glVertex2f(winX - LOCK_RENDER_SIZE / aspect, winY + LOCK_RENDER_SIZE);
	glVertex2f(winX + LOCK_RENDER_SIZE / aspect, winY + LOCK_RENDER_SIZE);
	glVertex2f(winX + LOCK_RENDER_SIZE / aspect, winY - LOCK_RENDER_SIZE);
	glEnd();
#else
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	clLoadTexture(reticule_texture);

	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 0.0f);
	glVertex2f(winX - LOCK_RENDER_SIZE / aspect, winY - LOCK_RENDER_SIZE);
	glTexCoord2f(0.0f, 1.0f);
	glVertex2f(winX - LOCK_RENDER_SIZE / aspect, winY + LOCK_RENDER_SIZE);
	glTexCoord2f(1.0f, 1.0f);
	glVertex2f(winX + LOCK_RENDER_SIZE / aspect, winY + LOCK_RENDER_SIZE);
	glTexCoord2f(1.0f, 0.0f);
	glVertex2f(winX + LOCK_RENDER_SIZE / aspect, winY - LOCK_RENDER_SIZE);
	glEnd();
#endif
    }
    
    glPopMatrix();

    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glPopAttrib();
    
}

void
zf_weapon_control_step(void)
{
    zf_cross_hair_query_position_distance(&cross_hair_position);
    
    if(lock_mode || super_lock_mode)
    {
	CLvertex ship_pos;
	CLvertex* position;
	CLUray ray;
	int num_missiles_needed;
#if 0
	if(lock_mode)
	    num_missiles_needed = lock_num;
	else
	    num_missiles_needed = lock_num * 2; /*super missiles take 2 missiles */
	
	if(lock_num < LOCK_LIMIT && missile_stock > num_missiles_needed)
	{
	}
#endif
	if(lock_num < LOCK_LIMIT)
	{
	    zf_ship_query_position(&ship_pos);
		
	    clCopyVertex(&ray.origin, &ship_pos);
	    cluNormalDifference(&ray.direction, &cross_hair_position, &ship_pos);
	    
	    cluNormalNormalise(&ray.direction);

	    /* Lock on to enemy targets */
	    if(zf_collision_system_query_dynamic_target(&ray, 
							ZF_ENEMY,
							&lock_list[lock_num].target_data,
							&lock_list[lock_num].target_smart_pointer,
							&lock_list[lock_num].target_collider))
	    {
		if(lock_list[lock_num].target_smart_pointer->is_valid(lock_list[lock_num].target_data))
		{
		    GLboolean found;
		    unsigned int i;
		
		    found = GL_FALSE;

		    /* Check if already lock this one */
		    for (i = 0; i < lock_num; i++)
		    {
			if (lock_list[i].target_data == lock_list[lock_num].target_data)
			{
			    found = GL_TRUE;
			    break;
			}
		    }

		    if (!found)
		    {
			lock_list[lock_num].target_smart_pointer->reference(lock_list[i].target_data);
			lock_num++;
		    }
		}
	    }

	    /* Lock on to static object. Just need position NO NEED TO REFERENCE, since position is enough*/
#if 1	    
	    else if(zf_collision_system_query_static_target(&ray, &position))
	    {
		GLboolean found;
		unsigned int i;
		
		found = GL_FALSE;

		lock_list[lock_num].target_data = position;
		lock_list[lock_num].target_smart_pointer = 0;
		lock_list[lock_num].target_collider = 0;

		/* Check if already lock this one */
		for (i = 0; i < lock_num; i++)
		{
		    if (lock_list[i].target_collider == 0)
		    {
			CLvertex temp;
			
			clCopyVertex(&temp, lock_list[i].target_data);

			if(cluVertexDistance(&temp, position) < CL_EPSILON)
			{
			    found = GL_TRUE;
			    break;
			}
		    }
		
		}
		if (!found)
		{
		    lock_num++;
		}
	    }
#endif
	}
    }
    else if(beam_mode)
	zf_weapon_control_shoot_beam();
    
    if(tritor_countdown > 0)
	tritor_countdown--;
    if(bomb_countdown > 0)
	bomb_countdown--;
}


void
zf_weapon_control_shoot_laser(void)
{
    CLvertex ship_pos;
	
    if(zf_ship_decrease_power(LASER_COST))
    {
	zf_ship_query_position(&ship_pos);
	zf_laser_new(&ship_pos, &cross_hair_position);
	zf_audio_sample_play(laser_sound);
    }
}

void
zf_weapon_control_shoot_beam(void)
{
    CLvertex ship_pos;
    zf_ship_query_position(&ship_pos);
    zf_beam_new(&ship_pos, &cross_hair_position);

#if 0
    if(zf_ship_decrease_power(LASER_COST))
    {
	zf_ship_query_position(&ship_pos);
	zf_beam_new(&ship_pos, &cross_hair_position);
	//printf("%s() : play laser sound\n", __FUNCTION__);
	zf_audio_sample_play(laser_sound);
    }
#endif
}


/* Will not lock unless there are missiles left. Therefore no need to check here */
void
zf_weapon_control_shoot_missile(void)
{
    CLvertex ship_pos;
    unsigned int i; /* for for loops heehee*/
    
    zf_ship_query_position(&ship_pos);
    
    for (i = 0; i < lock_num; i++)
    { 
	
	zf_missile_new(&ship_pos,
		       MISSILE_SPEED,
		       lock_list[i].target_data,
		       lock_list[i].target_smart_pointer,
		       lock_list[i].target_collider);


	///	printf("%s() : play rocket sound\n", __FUNCTION__);
	zf_audio_sample_play(rocket_sound);

	/*	missile_stock--;*/
	if(lock_list[i].target_collider != 0)
	    lock_list[i].target_smart_pointer->release(lock_list[i].target_data);
    }

    lock_num = 0;

}

void
zf_weapon_control_shoot_super_missile(void)
{
    CLvertex ship_pos;
    unsigned int i; /* for for loops heehee*/
    
    zf_ship_query_position(&ship_pos);
    
    for (i = 0; i < lock_num; i++)
    { 
	if(zf_ship_decrease_power(SUPER_MISSILE_COST))
	{
	    zf_super_missile_new(&ship_pos,
				 MISSILE_SPEED,
				 lock_list[i].target_data,
				 lock_list[i].target_smart_pointer,
				 lock_list[i].target_collider);
	    missile_stock -= 2;
	}
	
	/*	lock_list[i].target_smart_pointer->release(lock_list[i].target_data);*/
    }
    lock_num = 0;

}

void
zf_weapon_control_shoot_tritor(void)
{
    CLmatrix ship_flux_frame;
    float t, roll;    

    if(tritor_countdown <= 0 && bomb_tritor_stock > 0)
    {
	zf_ship_query_flux_frame(&ship_flux_frame);
	t = zf_ship_query_flux_t();
	roll = zf_ship_query_current_roll();
	
	zf_tritor_new(&ship_flux_frame,
		      t,
		      roll);
	tritor_countdown = TRITOR_LAUNCH_DELAY;
	bomb_tritor_stock--;
    }
}

void
zf_weapon_control_launchdecoy(void)
{
    CLvertex ship_pos;
    CLnormal direction;

    if(zf_ship_decrease_power(DECOY_COST))
    {
	zf_ship_query_position(&ship_pos);
	
	cluNormalDifference(&direction, &cross_hair_position, &ship_pos);
	cluNormalNormalise(&direction);
    
	zf_decoy_new(&ship_pos,
		     &direction);
	decoy_stock--;
    }
}



void
zf_weapon_control_launch_bomb(void)
{
    CLvertex ship_pos;

    if(bomb_countdown <= 0 && bomb_tritor_stock > 0)
    {
	bomb_countdown = BOMB_LAUNCH_DELAY;
	zf_ship_query_position(&ship_pos);
	
	zf_bomb_new(&ship_pos);
	bomb_tritor_stock--;
    }
}


float
zf_weapon_control_query_tritor_countdown(void)
{
    return (float) tritor_countdown / (float) TRITOR_LAUNCH_DELAY;
}

float
zf_weapon_control_query_bomb_countdown(void)
{
    return (float) bomb_countdown / (float) BOMB_LAUNCH_DELAY;
}

int
zf_weapon_control_query_bomb_tritor_stock(void)
{
    return bomb_tritor_stock;
}

void
zf_weapon_control_reset_bomb_tritor_stock(void)
{
   bomb_tritor_stock = MAX_BOMB_TRITOR_STOCK - 2;
   tritor_countdown = 0;
   bomb_countdown =0;
}
