/*
* This code is released under the GNU General Public License.  See COPYING for 
* details.  Copyright 2003 John Spray: spray_john@users.sourceforge.net
*/


#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "Ufo.h"
#include "Game.h"
#include "SoundCore.h"
#include "Player.h"
#include "Particle.h"
#include "Visual.h"
#include "LList.h"
#include "Missile.h"


#ifndef M_2PI
#define M_2PI 6.28318
#endif

#ifndef M_PI
#define M_PI 3.14159
#endif

Ufo::Ufo()
{
	thrust=0.0f;
	bear=0.0f;
	bearv=0.0f;
	beara=0.0f;
	s.x=s.z=0;
	s.y=3.0;
	v=0.0f;
	a=0.0f;
	alive=0;	
	soundid=-1;
	collideradius=5.0f;
	collideradius2=25.0f;
	sniffradius=250.0f;
	spawneffect=0.0f;
	hp=1.0f;
	pilotstate=UFO_POTTER;
	thinkwait=0.0f;
}


void Ufo::Live()
{
	TriggerShield();
	alive=1;	
	soundid = game->sound->Play("ufo.wav",-1,&s,&v);
}

Ufo::~Ufo()
{
	if(alive){
		alive=0;	
		game->sound->StopEffect(soundid);
	}
}

void Ufo::TriggerShield()
{
	spawneffect=maxspawneffect=400.0f;
}

void Ufo::Physics()
{
	float afactor;
	float maxv=1.0f;
	float targetbear;

	if(!alive) return;

	if(s.y!=3.0f||a.y!=0.0f||v.y!=0.0f){
		printf("Ufo::Physics: My y is not static!  My a.y=%f,v.y=%f,s.y=%f\n",a.y,v.y,s.y);
	}

	if(spawneffect){
		spawneffect-=game->dtf;
		if(spawneffect<0.0f)
			spawneffect=0.0f;
	}

	//bear isn't really used properly:
	if(fabs(v.x)>0.001f||fabs(v.z)>0.001f){
		targetbear=atan(a.x/a.z);
		if(a.z<0.0f)
			targetbear+=M_PI;
	}
	else
		targetbear=0.0f;

	while(bear<targetbear-M_PI)
					bear+=M_PI*2;
	while(bear>targetbear+M_PI)
					bear-=M_PI*2;

	bearv=(targetbear-bear)*0.01f;

	bearv+=beara*game->dtf;
	bearv*=pow(0.999f,game->dtf);
	bear+=bearv*game->dtf;
	if(bear>M_2PI)
		bear-=M_2PI;

	/*a=0.0f;
	a.x=sin(bear);
	a.z=cos(bear);
	a*=0.001f;
	printf("bear=%f,a.x=%f,a.z=%f\n",bear,a.x,a.z);*/

	afactor=1.0-(v.Mag()/maxv);

	v+=(game->dtf*afactor)*a;
	v*=pow(0.99f,game->dtf);
	Collide();
	s+=v*game->dtf;


	//Collision detection may allow ufo to escape in concave corners
	//Cope with this by bringing it back.  Player should never see this
	//happen up close and personal.
	if(fabs(s.x)>game->arena->halfwidth){
		if(game->verbose) printf("Ufo::Physics: out of arena, s.x=%f\n",s.x);
		s.x*=0.98f;
	}
	else if(fabs(s.z)>game->arena->halfwidth){
		if(game->verbose) printf("Ufo::Physics: out of arena, s.z=%f\n",s.z);
		s.z*=0.98f;
	}

	//Smoke for damaged ufos
	if(hp<0.5f){
		Particle newpart;
		newpart.s=s;
		newpart.v=v*0.5f;
		newpart.a.y=0.0001f;
		newpart.diffuse=0.000001f;
		newpart.res=0.99f;
		newpart.life=1000.0f;
		newpart.rad=3.0f;
		newpart.blendmode=GL_ONE_MINUS_SRC_ALPHA;
		strcpy(newpart.texfile,"smoke1.png");
		game->visual->NewParticle(&newpart);
	}

}


void Ufo::Collide()
{
	Vector dist;
	dist=s-game->player->s;
	if(dist.Mag2()<collideradius2+game->player->collideradius2+game->player->collideradius*collideradius && game->player->alive){
		Die();
		game->player->Hurt(0.21f);
		return;
	}

	if(game->arena->Collision(s,s+v*game->dtf,collideradius)){
		v=game->arena->GetLastQuad()->SlideVector(v);
	}

	Vector h,d;
	LListItem<Missile>* item;
	item=game->missilelist->head;
	while(item){

		h=PerpLinePoint(item->data.s,item->data.s+item->data.v*game->dtf,s);
		d=h-s;
		if(d.Mag2()<collideradius2 && PointOnLine(item->data.s,item->data.s+item->data.v*game->dtf,h)){
			Hurt(1.1f);
			return;
		}

		d=item->data.s-s;
		if(d.Mag2()<collideradius2 && PointOnLine(item->data.s,item->data.s+item->data.v*game->dtf,h)){
			Hurt(1.1f);
			return;
		}

		d=item->data.s+item->data.v*game->dtf-s;
		if(d.Mag2()<collideradius2 && PointOnLine(item->data.s,item->data.s+item->data.v*game->dtf,h)){
			Hurt(1.1f);
			return;
		}

		item=item->next;
	}
}


void Ufo::Pilot()
{
	Vector temp;
	Vector d;

	temp=s-game->player->s;

	thinkwait-=game->dtf;
	if(thinkwait<=0){
		if(game->player->alive && temp.Mag2() < sniffradius*sniffradius 
		&& !game->arena->Collision(s,game->player->s)){
			if(pilotstate!=UFO_SEEKPLAYER)
				game->sound->Play("spotted.wav",0,s,v);
			pilotstate=UFO_SEEKPLAYER;
		}
		else if(game->player->alive){
			pilotstate=UFO_SEARCH;
		}
		else
			pilotstate=UFO_POTTER;
		thinkwait+=500.0f;
	}

	if(pilotstate==UFO_SEEKPLAYER){
		temp.y=0.0f;
		temp.Unitize();
		a=-temp;
	}
	else if(pilotstate==UFO_POTTER){
		temp.Randomize();
		a+=temp;
		a.Unitize();
		a*=0.01f;
	}
	else if(pilotstate==UFO_SEARCH){
		temp.Randomize();
		a+=temp;
		a.Unitize();
		a*=0.01f;
	}

	Ufo* nearest=Nearest();

	d=nearest->s-s;
	temp=d;
	temp.Unitize();
	if(d.Mag2()!=0.0f)
		temp*=(collideradius*2)/d.Mag(); //ie ufos avoiding each other has equal precedence to homing
															//on player where temp.Mag=collideradius*2, ie the ufos are collideradius*2 apart.
	a+=-temp;

	a+=(nearest->v-v)*10.0f; //a more orderly formation

/*	if(nearest->pilotstate==UFO_SEEKPLAYER)
	{
		temp=d;
		temp.Unitize();
		a=-temp;
	}*/

	//clear the respawn if the player's waiting to spawn
	if(!game->player->alive){
		d=s;//IE distance from center, assuming spawn at ccenter
		if(d.Mag2()<sniffradius*sniffradius*1.2f){
			temp=s;
			temp.Unitize();
			temp*=(sniffradius)/s.Mag();
			a+=temp;
		}
	}

	a.Unitize();
	a.y=0.0;
	a*=0.001+0.0005*(1.0f-game->spawndelay/5000.0f);

#ifdef RELEASE
	//FIXME: Do this to cover up an unfound bug
	s.y=3.0f;
#endif
}

//Find the closest Ufo in game->ufolist to this
//if game->ufolist->count==1 then return this
Ufo* Ufo::Nearest()
{
	Ufo* retval;
	LListItem<Ufo> *ufoitem;
	Vector d;
	Vector temp;

	ufoitem=game->ufolist->head;
	temp=ufoitem->data.s-s;
	d=temp;
	retval=&ufoitem->data;
	ufoitem=ufoitem->next;

	while(ufoitem){
		temp=ufoitem->data.s-s;
		if(temp.Mag2()<d.Mag2() && &ufoitem->data!=this){
			d=temp;
			retval=&ufoitem->data;
		}
		ufoitem=ufoitem->next;
	}

	return retval;
}

void Ufo::Die()
{
	if(!alive){//general paranoia checking
		printf("Ufo::Die: Warning, (!alive) is true!\n");
		return; 
	}
	game->IncScore(200);
	Explode();
	alive=0;	
	game->sound->StopEffect(soundid);
}

void Ufo::Explode()
{
	Particle newpart;
	newpart.s=s;
	newpart.rad=collideradius;
	newpart.diffuse=0.000003;
	newpart.blendmode=GL_ONE;
	strcpy(newpart.texfile,"explosion1.png");
	newpart.life=200;
	for(int i=0;i<3;i++)
		game->visual->NewParticle(&newpart);
	strcpy(newpart.texfile,"smoke1.png");
	newpart.life=1000;
	newpart.rad=collideradius*1.2;
	newpart.diffuse=0.0000005;
	newpart.res=0.999f;
	newpart.a=game->g*0.1f;
	newpart.blendmode=GL_ONE_MINUS_SRC_ALPHA;
	for(int i=0;i<8;i++)
		game->visual->NewParticle(&newpart);
	strcpy(newpart.texfile,"explosion1.png");
	newpart.life=1500.0f;
	newpart.bounce=PARTICLE_BOUNCE_ONE;
	newpart.drawmode=PARTICLE_DRAW_SIMPLE;
	newpart.rad=0.5;
	newpart.diffuse=0.000000;
	newpart.res=1.0f;
	newpart.a=game->g;
	newpart.collide=1;
	for(int i=0;i<8;i++){
		newpart.v.Randomize();
		newpart.v.y+=1.0f;
		newpart.v*=0.05f;
		game->visual->NewParticle(&newpart);
	}
	newpart.drawmode=PARTICLE_DRAW_MOTION;
	newpart.rad=0.25;
	newpart.life=400.0f;
	newpart.collide=1;
	for(int i=0;i<8;i++){
		newpart.v.Randomize();
		newpart.v*=1.0f;
		game->visual->NewParticle(&newpart);
	}


	game->sound->Play("ufodie.wav",0,s,v);
}

int Ufo::Hurt(float pain)
{
	if(hp<=0) return 1;

	hp-=pain;
	if(hp<=0){
		Die();
		return 1;
	}
	else
		return 0;
}
