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


#include "Arena.h"
#include "Vector.h"

#include "Particle.h" //for debugging markers in collision
#include "Visual.h"
#include <string.h>

Arena::Arena(Game* newgame){
	halfwidth=300;
	wallheight=50;
	game=newgame;
	lastquad=NULL;
}

Arena::~Arena(){
}

int Arena::Blocked(Vector s,float radius)
{
	LListItem<Obstacle>* item;
	item=obstaclelist.head;
	while(item){
		if(s.x>item->data.s.x-radius&&s.x<item->data.s.x+item->data.w.x+radius)
			if(s.z>item->data.s.z-radius&&s.z<item->data.s.z+item->data.w.z+radius)
			  if(s.y>item->data.s.y-radius&&s.y<item->data.s.y+item->data.w.y+radius)
				return 1;
		item=item->next;
	}

	if(s.x>halfwidth-radius||s.x<-halfwidth+radius||s.z>halfwidth-radius||s.z<-halfwidth+radius)
		return 1;

	return 0;
}

int Arena::Collision(Vector s,float r)
{

	//return 1 for 'out of bounds'
	//this is necessary if something gets wanged over the walls
	//OutOfBounds sets lastquad to a big virtualquad off in -y
	if(OutOfBounds(s))
		return 1;

	Vector p1,p2;
	Vector temp;

	LListItem<Quad> *item;

	item=geomlist.head;
	while(item){
		p1=s-item->data.n*r;
		p2=s+item->data.n*r;
		if(item->data.PlaneIntersection2Way(p1,p2)){
			temp=item->data.PlaneIntersectionPoint(p1,p2);
			if(item->data.IsInside(temp)){
				lastquad=&item->data;
				return 1;
			}
		}
		item=item->next;
	}
	return 0;
}

int Arena::Collision(Vector p1,Vector p2)
{
	Vector temp;
	LListItem<Quad> *item;

	//return 1 for 'out of bounds'
	//this is necessary if something gets wanged over the walls
	//OutOfBounds sets lastquad to a big virtualquad off in -y
	if(OutOfBounds(p1) || OutOfBounds(p2))
		return 1;

	item=geomlist.head;
	while(item){
		if(item->data.PlaneIntersection(p1,p2)){
			temp=item->data.PlaneIntersectionPoint(p1,p2);
			if(item->data.IsInside(temp)){
				lastquad=&item->data;
				return 1;
			}
		}
		item=item->next;
	}
	return 0;
}

//surface collision (1 way ray p1->p2), returns closest collision point to p1
Vector Arena::CollisionNearest(Vector p1,Vector p2)
{
	Vector temp,temprel,retval,retvalrel;
	retval=999999.0f;//ie infinity
  retvalrel=999999.9f;
	LListItem<Quad> *item;

	item=geomlist.head;
	while(item){
		if(item->data.PlaneIntersection(p1,p2)){
			temp=item->data.PlaneIntersectionPoint(p1,p2);
			if(item->data.IsInside(temp)){
        temprel=temp-p1;
				if(temprel.Mag2()<retvalrel.Mag2()){
					retval=temp;
          retvalrel=temprel;
					lastquad=&item->data;
				}
			}
		}
		item=item->next;
	}

	return retval;

}

//collide a moving (p1->p2) sphere of radius r
int Arena::Collision(Vector p1,Vector p2,float r)
{
	//return 1 for 'out of bounds'
	//this is necessary if something gets wanged over the walls
	//OutOfBounds sets lastquad to a big virtualquad off in -y
	if(OutOfBounds(p1) || OutOfBounds(p2))
		return 1;

	Vector v; //direction of motion
	Vector temp;
	Vector c1,c2; //centres of spheres : p[12] will become ends of ray
	Vector r1,r2; //edges of spheres, along a diameter parallel to the quad's normal
	LListItem<Quad> *item;
	
	c1=p1;c2=p2;

	v=p2-p1;
	v.Unitize();
	p1-=v*r;
	p2+=v*r;

	item=geomlist.head;
	while(item){
		if(item->data.PlaneIntersection(p1,p2)){
			temp=item->data.PlaneIntersectionPoint(p1,p2);
			if(item->data.IsInside(temp)){
				lastquad=&item->data;
				return 1;
			}
		}

		
		r1=c1-item->data.n*r;
		r2=c1+item->data.n*r;
		if((item->data.n|v)<0 && item->data.PlaneIntersection2Way(r1,r2)){
			temp=item->data.PlaneIntersectionPoint(p1,p2);
			if(item->data.IsInside(temp)&&temp.InCube(r1,r2)){
				lastquad=&item->data;
				return 1;
			}
		}
		
		
		r1=c2-item->data.n*r;
		r2=c2+item->data.n*r;
		if((item->data.n|v)<0 && item->data.PlaneIntersection2Way(r1,r2)){
			temp=item->data.PlaneIntersectionPoint(p1,p2);
			if(item->data.IsInside(temp)&&temp.InCube(r1,r2)){
				lastquad=&item->data;
				return 1;
			}
		}
		
		item=item->next;
	}

	return 0;
}

//is it really far away from the origin?
int Arena::OutOfBounds(Vector s)
{
	//if(s.Mag2()>(halfwidth*4)*(halfwidth*4)){
	if(s.Mag2()>(halfwidth)*(halfwidth)*16.0f){
		virtualquad.n.Set3f(0.0f,1.0f,0.0f);
		virtualquad.v1.Set3f(-halfwidth*4,-halfwidth*4,-halfwidth*4);
		virtualquad.v2.Set3f(halfwidth*4,-halfwidth*4,-halfwidth*4);
		virtualquad.v3.Set3f(halfwidth*4,-halfwidth*4,halfwidth*4);
		virtualquad.v4.Set3f(-halfwidth*4,-halfwidth*4,halfwidth*4);
		lastquad=&virtualquad;
		return 1;
	}

	return 0;
}


void Arena::MakeObstacles()
{
	Vector place;
	Obstacle newobst;
//	int placed=0;
	float x;
	float y;

	place.y=0;
	newobst.w=50.0f;
  newobst.w.y=50.0f;
	
  for(x=-halfwidth;x<halfwidth;x+=100.0f){
  for(y=-halfwidth;y<halfwidth;y+=100.0f){
//		x=y=50.0f;
		place.x=x;
		place.z=y;
		place.x+=((float)rand()/RAND_MAX)*40.0f;
		place.z+=((float)rand()/RAND_MAX)*40.0f;
		newobst.s=place;
		obstaclelist.Add(&newobst);
	}
	}
}

void Arena::GenerateGeometry()
{
	geomlist.Flush();
	Quad newquad;

	//floor:
	newquad.v1.x=-halfwidth;
	newquad.v1.y=0.0f;
	newquad.v1.z=-halfwidth;
	newquad.v2.x=-halfwidth;
	newquad.v2.y=0.0f;
	newquad.v2.z=halfwidth;
	newquad.v3.x=halfwidth;
	newquad.v3.y=0.0f;
	newquad.v3.z=halfwidth;
	newquad.v4.x=halfwidth;
	newquad.v4.y=0.0f;
	newquad.v4.z=-halfwidth;
	newquad.n.x=0.0f;
	newquad.n.y=1.0f;
	newquad.n.z=0.0f;
	geomlist.Add(&newquad);

	//arena walls:

	newquad.v1.Set3f(halfwidth,0.0f,halfwidth);
	newquad.v2.Set3f(halfwidth,wallheight,halfwidth);
	newquad.v3.Set3f(halfwidth,wallheight,-halfwidth);
	newquad.v4.Set3f(halfwidth,0.0f,-halfwidth);
	newquad.n.Set3f(-1.0f,0.0f,0.0f);
	geomlist.Add(&newquad);

	newquad.v1.Set3f(-halfwidth,0.0f,halfwidth);
	newquad.v2.Set3f(-halfwidth,0.0f,-halfwidth);
	newquad.v3.Set3f(-halfwidth,wallheight,-halfwidth);
	newquad.v4.Set3f(-halfwidth,wallheight,halfwidth);
	newquad.n.Set3f(1.0f,0.0f,0.0f);
	geomlist.Add(&newquad);

	newquad.v1.Set3f(halfwidth,0.0f,-halfwidth);
	newquad.v2.Set3f(halfwidth,wallheight,-halfwidth);
	newquad.v3.Set3f(-halfwidth,wallheight,-halfwidth);
	newquad.v4.Set3f(-halfwidth,0.0f,-halfwidth);
	newquad.n.Set3f(0.0f,0.0f,1.0f);
	geomlist.Add(&newquad);

	newquad.v1.Set3f(halfwidth,0.0f,halfwidth);
	newquad.v2.Set3f(-halfwidth,0.0f,halfwidth);
	newquad.v3.Set3f(-halfwidth,wallheight,halfwidth);
	newquad.v4.Set3f(halfwidth,wallheight,halfwidth);
	newquad.n.Set3f(0.0f,0.0f,-1.0f);
	geomlist.Add(&newquad);

	//buildings:
	LListItem<Obstacle>* item;
	Obstacle* obstacle;
	item=obstaclelist.head;
	while(item){
		obstacle=&item->data; //just to make the rest not have to do item->data
  	newquad.v1.Set3f(obstacle->s.x,obstacle->s.y,obstacle->s.z);
  	newquad.v2.Set3f(obstacle->s.x,obstacle->s.y+obstacle->w.y,obstacle->s.z);
  	newquad.v3.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y+obstacle->w.y,obstacle->s.z);
  	newquad.v4.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y,obstacle->s.z);
		newquad.n.Set3f(0.0f,0.0f,-1.0f);
		geomlist.Add(&newquad);

  	newquad.v1.Set3f(obstacle->s.x,obstacle->s.y,obstacle->s.z+obstacle->w.z);
  	newquad.v2.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y,obstacle->s.z+obstacle->w.z);
  	newquad.v3.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y+obstacle->w.y,obstacle->s.z+obstacle->w.z);
  	newquad.v4.Set3f(obstacle->s.x,obstacle->s.y+obstacle->w.y,obstacle->s.z+obstacle->w.z);
		newquad.n.Set3f(0.0f,0.0f,1.0f);
		geomlist.Add(&newquad);

  	newquad.v1.Set3f(obstacle->s.x,obstacle->s.y,obstacle->s.z);
  	newquad.v2.Set3f(obstacle->s.x,obstacle->s.y,obstacle->s.z+obstacle->w.z);
  	newquad.v3.Set3f(obstacle->s.x,obstacle->s.y+obstacle->w.y,obstacle->s.z+obstacle->w.z);
  	newquad.v4.Set3f(obstacle->s.x,obstacle->s.y+obstacle->w.y,obstacle->s.z);
		newquad.n.Set3f(-1.0f,0.0f,0.0f);
		geomlist.Add(&newquad);

  	newquad.v1.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y,obstacle->s.z);
  	newquad.v2.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y+obstacle->w.y,obstacle->s.z);
  	newquad.v3.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y+obstacle->w.y,obstacle->s.z+obstacle->w.z);
  	newquad.v4.Set3f(obstacle->s.x+obstacle->w.x,obstacle->s.y,obstacle->s.z+obstacle->w.z);
		newquad.n.Set3f(1.0f,0.0f,0.0f);
		geomlist.Add(&newquad);

		item=item->next;
	}
}

Quad* Arena::GetLastQuad()
{
	return lastquad;
}

