package com.terry.engine {
	import flash.display.*;
	import flash.geom.*;
  import flash.events.*;
  import flash.net.*;
	import flash.utils.Dictionary;
	import com.terry.engine.util.*;
	import config.*;
	import objs.*;
	
	
	public class obj {		
		public static function init():void {		
			nentity = 0; nparticles = 0;
			nblocks = 0;
			temprect = new Rectangle();
			temprect2 = new Rectangle();
			colpoint1 = new Point; colpoint2 = new Point;	
			activedoor = "null";
			
			for (var z:Number = 0; z < 500; z++) {
				var entity:entclass = new entclass;
  			entities.push(entity);
				
				var initentity:initentclass = new initentclass;
				initentities.push(initentity);
				
				var part:particleclass = new particleclass
  			particles.push(part);
				
				var block:blockclass = new blockclass;
  			blocks.push(block);
			}
		}
		
		public static function loadtemplates():void {
			numtemplate = 0;
			for (var i:int = 0; i < templates.length; i++) {
				addtemplate(templates[i].name);
			}
		}
		
		public static function addtemplate(t:String):void {
			entindex[t] = numtemplate;
			numtemplate++;
		}
		
		public static function getperson(t:String):int {
			//Given person's name t, find the entity the corresponds. Super hacky!
			//Just reverses the tile numbers
			
			if (t == "ex") {
				if (entities[1].tile == 820) return 1;
				if (entities[2].tile == 820) return 2;
			}else if (t == "newgirl") {
				if (entities[1].tile == 940) return 1;
				if (entities[2].tile == 940) return 2;
			}else if (t == "mum") {
				if (entities[1].tile == 760) return 1;
				if (entities[2].tile == 760) return 2;
			}else if (t == "coworker") {
				if (entities[1].tile == 700) return 1;
				if (entities[2].tile == 700) return 2;
			}else if (t == "girl") {
				if (entities[1].tile == 540) return 1;
				if (entities[2].tile == 540) return 2;
			}
			
			return 0;
		}
		
		public static function getgridpoint(t:int):int { //This function often needs to be adjusted
			t = (t - (t % def.TILESIZE)) / def.TILESIZE;
			return t;
		}
		
		public static function createblock(t:int, xp:int = 0, yp:int = 0, w:int = 0, h:int = 0, trig:String="null", 
		                            destx:int = 0, desty:int = 0, doorname:String=""):void {
			var i:int, z:int;
			if(nblocks == 0) {
				//If there are no active blocks, Z=0;
				z = 0; 
			}else {
				i = 0; z = -1;
				while (i < nblocks) {
					if (!blocks[i].active) {
						z = i; i = nblocks;
					}
					i++;
				}
				if (z == -1) {
					z = nblocks;
					nblocks++;
				}
			}
			
			blocks[z].clear();
			blocks[z].active = true;
			switch(t) {
				case def.BLOCK: //Block
					 blocks[z].type = def.BLOCK;
					 blocks[z].xp = xp;
					 blocks[z].yp = yp;
					 blocks[z].wp = w;
					 blocks[z].hp = h;
					 blocks[z].rectset(xp, yp, w, h);
					 
					 nblocks++;
				break;
				case def.TRIGGER: //Trigger
					 blocks[z].type = def.TRIGGER;
					 blocks[z].xp = xp;
					 blocks[z].yp = yp;
					 blocks[z].wp = w;
					 blocks[z].hp = h;
					 blocks[z].rectset(xp, yp, w, h);
					 blocks[z].trigger = trig;
					 
					 nblocks++;
				break;
				case def.DAMAGE: //Damage
					 blocks[z].type = def.DAMAGE;
					 blocks[z].xp = xp;
					 blocks[z].yp = yp;
					 blocks[z].wp = w;
					 blocks[z].hp = h;
					 blocks[z].rectset(xp, yp, w, h);
					 
					 nblocks++;
				break;
				case def.DOOR: //Trigger
					 blocks[z].type = def.DOOR;
					 blocks[z].xp = xp;
					 blocks[z].yp = yp;
					 blocks[z].wp = w;
					 blocks[z].hp = h;
					 blocks[z].rectset(xp, yp, w, h);
					 blocks[z].trigger = trig;
					 blocks[z].destx = destx;
					 blocks[z].desty = desty;
					 blocks[z].doorname = doorname;
					 
					 nblocks++;
				break;
			}
		}		

		public static function createtrigger(xp:int, yp:int, trig:String = "null"):void {
			createblock(1, xp * def.TILESIZE, yp * def.TILESIZE, def.TILESIZE, def.TILESIZE, trig); 
		}

		public static function createdoor(xp:int, yp:int, trigs:String, destx:int, desty:int, dname:String=""):void {
			createblock(3, xp * def.TILESIZE, yp * def.TILESIZE, def.TILESIZE, def.TILESIZE, trigs, destx, desty, dname); 
		}

		public static function removeallblocks():void{
			for(var i:int=0; i<nblocks; i++) blocks[i].clear();
			nblocks=0;
		}
		
		public static function removeblock(t:int):void{
			blocks[t].clear();
			var i:int = nblocks-1; while(i>=0 && !blocks[i].active) { nblocks--; i--; }
		}
		
		public static function removeblockat(x:int, y:int):void{
			for(var i:int=0; i<nblocks; i++){
				if(blocks[i].xp == x && blocks[i].yp == y) removeblock(i);
			}
		}
		
		public static function activeblocks():int {
			var t:int = 0;
			for (var i:int = 0; i < nblocks; i++)	if (blocks[i].active) t++;
			return t;
		}
		
		public static function removetrigger(t:String):void{
			for(var i:int=0; i<nblocks; i++){
				if(blocks[i].type == def.TRIGGER) {
					if(blocks[i].trigger == t) {
						removeblock(i);
					}
				}
			}
		}
		
		public static function updateentitylogic(t:int):void {
			entities[t].oldxp = entities[t].xp; entities[t].oldyp = entities[t].yp;
			
			entities[t].vx = entities[t].vx + entities[t].ax;
			entities[t].vy = entities[t].vy + entities[t].ay;
			entities[t].ax = 0;
			
			if (entities[t].jumping) {
				if (entities[t].ay < 0) entities[t].ay++;
				if (entities[t].ay > -1) entities[t].ay = 0;
				entities[t].jumpframe--;
				if(entities[t].jumpframe<=0){
					entities[t].jumping=false;
				}
			}else {
				if (entities[t].gravity) entities[t].ay = 3;
			}
			
			if (entities[t].gravity) applyfriction(t, 0, 0.5);
			
			entities[t].newxp = entities[t].xp + entities[t].vx;
			entities[t].newyp = entities[t].yp + entities[t].vy;
		}

		public static function entitymapcollision(t:int):void {
			if (testwallsx(t, entities[t].newxp, entities[t].yp)) {
				entities[t].xp = entities[t].newxp;
			}else {
				if (entities[t].onwall > 0) entities[t].state = entities[t].onwall;
				if (entities[t].onxwall > 0) entities[t].state = entities[t].onxwall;
			}
			if (testwallsy(t, entities[t].xp, entities[t].newyp)) {
				entities[t].yp = entities[t].newyp;
			}else {
				if (entities[t].onwall > 0) entities[t].state = entities[t].onwall;
				if (entities[t].onywall > 0) entities[t].state = entities[t].onywall;
				entities[t].jumpframe = 0;
			}
		}
		
		public static function gettype(t:String):Boolean {
			//Returns true is there is an entity of type t onscreen
			for (var i:int = 0; i < nentity; i++) {
				if(entities[i].type==t){
					return true;
				}
			}
			
			return false;
		}		
		
		public static function getplayer():int {
			//Returns the index of the first player entity
			for (var i:int = 0; i < nentity; i++) {
				if (entities[i].type == "player") {
					return i;
				}
			}
			
			return -1;
		}
		
		public static function getnpc(t:String):int {
			//Returns the index of the npc by name
			if (t == "player") return getplayer();
			
			for (var i:int = 0; i < nentity; i++) {
				if (entities[i].name == t) {
					return i;
				}
			}
			
			return 0;
			//return -1;
		}
		
		public static function rectset(xi:int, yi:int, wi:int, hi:int):void {
			temprect.x = xi; temprect.y = yi; temprect.width = wi; temprect.height = hi;
		}

		public static function rect2set(xi:int, yi:int, wi:int, hi:int):void {
			temprect2.x = xi; temprect2.y = yi; temprect2.width = wi; temprect2.height = hi;
		}

		public static function entitycollide(a:int, b:int):Boolean {
			//Do entities a and b collide?
			tempx = entities[a].xp + entities[a].cx; tempy = entities[a].yp + entities[a].cy;
			tempw = entities[a].w; temph = entities[a].h;
			rectset(tempx, tempy, tempw, temph);
			
			tempx = entities[b].xp + entities[b].cx; tempy = entities[b].yp + entities[b].cy;
			tempw = entities[b].w; temph = entities[b].h;
			rect2set(tempx, tempy, tempw, temph);
			if (temprect.intersects(temprect2)) return true;
			return false;
		}

		public static function checkdamage():Boolean{
			//Returns true if player entity (rule 0) collides with a damagepoint
			for(var i:int = 0; i < nentity; i++) {
				if (entities[i].rule == "player") {
					tempx = entities[i].xp + entities[i].cx; tempy = entities[i].yp + entities[i].cy;
					tempw = entities[i].w; temph = entities[i].h;
					rectset(tempx, tempy, tempw, temph);
					
					for (var j:int = 0; j < nblocks; j++) {
						if (blocks[j].type == def.DAMAGE && blocks[j].active){
							if(blocks[j].rect.intersects(temprect)) {
								return true;
							}
						}
					}
				}
			}
			return false;
		}

		public static function checktrigger():String{
			//Returns an int player entity (rule 0) collides with a trigger
			for(var i:int = 0; i < nentity; i++) {
				if (entities[i].rule == "player") {
					tempx = entities[i].xp + entities[i].cx; tempy = entities[i].yp + entities[i].cy;
					tempw = entities[i].w; temph = entities[i].h;
					rectset(tempx, tempy, tempw, temph);
					
					for (var j:int = 0; j < nblocks; j++) {
						if (blocks[j].type == def.TRIGGER && blocks[j].active){
							if (blocks[j].rect.intersects(temprect)) {
								activetrigger = blocks[j].trigger;
								blocks[j].active = false;
								return blocks[j].trigger;
							}
						}
					}
				}
			}
			return "null";
		}

		public static function checkdoor():String{
			//Returns an int player entity (rule 0) collides with a trigger
			for(var i:int = 0; i < nentity; i++) {
				if (entities[i].rule == "player") {
					tempx = entities[i].xp + entities[i].cx; tempy = entities[i].yp + entities[i].cy;
					tempw = entities[i].w; temph = entities[i].h;
					rectset(tempx, tempy, tempw, temph);
					
					for (var j:int = 0; j < nblocks; j++) {
						if (blocks[j].type == def.DOOR && blocks[j].active){
							if (blocks[j].rect.intersects(temprect)) {
								activetrigger = blocks[j].trigger;
								doortox = blocks[j].destx;
								doortoy = blocks[j].desty;
								actualdoor = j;
								return blocks[j].trigger;
							}
						}
					}
				}
			}
			return "null";
		}
				
		public static function checkblocks():Boolean {
			for (var i:int = 0; i < nblocks; i++) {
				if (blocks[i].active) {
					if (blocks[i].type == def.BLOCK){
						if (blocks[i].rect.intersects(temprect)) {
							return true;
						}
					}
				}
			}
			return false;
		}
				

		public static function checkwall():Boolean{
			//Returns true if entity setup in temprect collides with a wall
			//used for proper collision functions; you can't just, like, call it
			//whenever you feel like it and expect a response
			//
			//that won't work at all
			if (checkblocks()) return true;
			
			tempx = getgridpoint(temprect.x); tempy = getgridpoint(temprect.y);
			tempw = getgridpoint(temprect.x + temprect.width - 1); temph = getgridpoint(temprect.y + temprect.height - 1);
			if (map.collide(tempx, tempy)) return true;
			if (map.collide(tempw, tempy)) return true;
			if (map.collide(tempx, temph)) return true;
			if (map.collide(tempw, temph)) return true;
			if (temprect.height >= 12) {
				tpy1 = getgridpoint(temprect.y + 6);
				if (map.collide(tempx, tpy1)) return true;
				if (map.collide(tempw, tpy1)) return true;
				if (temprect.height >= 18) {
					tpy1 = getgridpoint(temprect.y + 12);
					if (map.collide(tempx, tpy1)) return true;
					if (map.collide(tempw, tpy1)) return true;
					if (temprect.height >= 24) {
						tpy1 = getgridpoint(temprect.y + 18);
						if (map.collide(tempx, tpy1)) return true;
						if (map.collide(tempw, tpy1)) return true;
					}
				}
			}
			if (temprect.width >= 12) {
				tpx1 = getgridpoint(temprect.x + 6);
			if (map.collide(tpx1, tempy)) return true;
			if (map.collide(tpx1, temph)) return true;
			}
			return false;
		}

		public static function entitycollidefloor(t:int):Boolean{
			//see? like here, for example!
			tempx = entities[t].xp + entities[t].cx; tempy = entities[t].yp + entities[t].cy + 1;
			tempw = entities[t].w; temph = entities[t].h;
			rectset(tempx, tempy, tempw, temph);
			
			if (checkwall()) return true;
			return false;
		}

		public static function testwallsx(t:int, tx:int, ty:int):Boolean{
			tempx = tx + entities[t].cx; tempy = ty + entities[t].cy;
			tempw = entities[t].w; temph = entities[t].h;
			rectset(tempx, tempy, tempw, temph);
			
			//Ok, now we check walls
			if (checkwall()) {
				if (entities[t].vx > 1) {
					entities[t].vx--;
					entities[t].newxp = int(entities[t].xp + entities[t].vx);
					return testwallsx(t, entities[t].newxp, entities[t].yp);
				}else if (entities[t].vx < -1) {
					entities[t].vx++;
					entities[t].newxp = int(entities[t].xp + entities[t].vx);
					return testwallsx(t, entities[t].newxp, entities[t].yp);
				}else {
					entities[t].vx=0;
					return false;
				}
			}
			return true;
		}

		public static function testwallsy(t:int, tx:int, ty:int):Boolean {
			tempx = tx + entities[t].cx; tempy = ty + entities[t].cy;
			tempw = entities[t].w; temph = entities[t].h;
			rectset(tempx, tempy, tempw, temph);
			
			//Ok, now we check walls
			if (checkwall()) {
				if (entities[t].vy > 1) {
					entities[t].vy--;
					entities[t].newyp = int(entities[t].yp + entities[t].vy);
					return testwallsy(t, entities[t].xp, entities[t].newyp);
				}else if (entities[t].vy < -1) {
					entities[t].vy++;
					entities[t].newyp = int(entities[t].yp + entities[t].vy);
					return testwallsy(t, entities[t].xp, entities[t].newyp);
				}else {
					entities[t].vy=0;
					return false;
				}
			}
			return true;
		}

		public static function applyfriction(t:int, xrate:Number, yrate:Number):void{
			if (entities[t].vx > 0) entities[t].vx -= xrate;
			if (entities[t].vx < 0) entities[t].vx += xrate;
			if (entities[t].vy > 0) entities[t].vy -= yrate;
			if (entities[t].vy < 0) entities[t].vy += yrate;
			if (entities[t].vy > 4) entities[t].vy = 4;
			if (entities[t].vy < -4) entities[t].vy = -4;
			if (entities[t].vx > 4) entities[t].vx = 4;
			if (entities[t].vx < -4) entities[t].vx = -4;
			
			if (Math.abs(entities[t].vx) <= xrate) entities[t].vx = 0;
			if (Math.abs(entities[t].vy) <= yrate) entities[t].vy = 0;
		}
		
		public static function stopmovement(i:int):void {
			entities[i].vx = 0;
			entities[i].vy = 0;
			entities[i].ax = 0;
			entities[i].ay = 0;
		}
		
		public static function cleanup():void {
			var i:int = 0;
			i = nentity - 1; while (i >= 0 && !entities[i].active) { nentity--; i--; }
			i = nparticles - 1; while (i >= 0 && !particles[i].active) { nparticles--; i--; }
		}
		
		public static function createparticle(xp:Number, yp:Number, t:String, ax:Number = 0, ay:Number = 0, vx:Number = 0, vy:Number = 0):void {
			//Find the first inactive case z that we can use to index the new entity
			var i:int, z:int;
			if (nparticles == 0) {
				//If there are no active entities, Z=0;
				z = 0; nparticles++;
			}else {
				i = 0; z = -1;
				while (i < nparticles) {
					if (!particles[i].active) {
						z = i; i = nparticles;
					}
					i++;
				}
				if (z == -1) {
					z = nparticles;
					nparticles++;
				}
			}
			
			particles[z].clear();
			particles[z].active = true;
			particles[z].type = t;
			particles[z].colour = 0;
			particles[z].life = 0;
			particles[z].xp = xp;
			particles[z].yp = yp;
			particles[z].ax = ax;
			particles[z].ay = ay;
			particles[z].vx = vx;
			particles[z].vy = vy;
			
			initparticle(z);
		}
		
		public static function updateparticles():Boolean {
			for (var i:int = 0; i < nparticles; i++) {
				if (particles[i].active) {
					if (particles[i].statedelay <= 0) {
						updateparticle(i);
					}else {
						particles[i].statedelay--;
						if (particles[i].statedelay < 0) particles[i].statedelay = 0;
					}
				}
			}
			return true;
		}
		
		include "../../../objs/particlelogic.as";
		
		//Actual entityclass stuff is here
		
		public static function createinitentity(xp:Number, yp:Number, t:String, para1:String = "", para2:String = "", para3:String = ""):int {
			var i:int = ninitentities;
			
			initentities[i].xp = xp;
			initentities[i].yp = yp;
			initentities[i].type = t;
			initentities[i].para1 = para1;
			initentities[i].para2 = para2;
			initentities[i].para3 = para3;
			
			initentities[i].entity = -1;
			initentities[i].drawframe = templates[entindex[t]].init_drawframe;
			initentities[i].para1_selection = 0;
			initentities[i].para2_selection = 0;
			initentities[i].para3_selection = 0;
			
			ninitentities++;
			return i;
		}
		
		public static function copyinitentity(a:int, b:int):void {
		  initentities[a].xp = initentities[b].xp;
			initentities[a].yp = initentities[b].yp;
			initentities[a].entity = initentities[b].entity;
			initentities[a].drawframe = initentities[b].drawframe;
			initentities[a].type = initentities[b].type;
			initentities[a].para1 = initentities[b].para1;
			initentities[a].para2 = initentities[b].para2;
			initentities[a].para3 = initentities[b].para3;
			initentities[a].para1_selection = initentities[b].para1_selection;
			initentities[a].para2_selection = initentities[b].para2_selection;
			initentities[a].para3_selection = initentities[b].para3_selection;
		}
		
		public static function removeinitentity(t:int):void {
			if (t != ninitentities - 1) {
				for (var i:int = t; i < ninitentities; i++) {
					copyinitentity(i, i + 1);
				}
			}
			ninitentities--;
		}
		
		public static function createentity(xp:Number, yp:Number, t:String, para1:String = "", para2:String = "", para3:String = ""):int {
			//Find the first inactive case z that we can use to index the new entity
			var i:int, z:int;
			if (nentity == 0) {
				//If there are no active entities, Z=0;
				z = 0; nentity++;
			}else {
				i = 0; z = -1;
				while (i < nentity) {
					if (!entities[i].active) {
						z = i; i = nentity;
					}
					i++;
				}
				if (z == -1) {
					z = nentity;
					nentity++;
				}
			}
			
			entities[z].clear();
			entities[z].active = true;
			entities[z].type = t;
			
			entities[z].xp = xp;
			entities[z].yp = yp;
			if (help.isNumber(para1)) entities[z].vx = int(para1);
			if (help.isNumber(para2)) entities[z].vy = int(para2);
			if (help.isNumber(para3)) entities[z].para = int(para3);
			
			templates[entindex[t]].create(z, xp, yp, para1, para2, para3);
			return z;
		}
		
		public static function updateentities(i:int):Boolean {
			if(entities[i].active){
				if (entities[i].statedelay <= 0) {
					templates[entindex[entities[i].type]].update(i);
				}else {
					entities[i].statedelay--;
					if (entities[i].statedelay < 0) entities[i].statedelay = 0;
				}
			}
			
			return true;
		}
		
		public static function animate_default(i:int):void {
			entities[i].drawframe = entities[i].tile;
		}
		
		public static function animateentities(i:int):void {
			if(entities[i].active){
				templates[entindex[entities[i].type]].animate(i);
			}
		}
		
		public static function entitycollisioncheck():void {
			var i:int, j:int;
			for (i = 0; i < nentity; i++) {
				if (entities[i].active) {
					for (j = 0; j < nentity; j++) {
						if (entities[j].active && i != j) {
							templates[entindex[entities[i].type]].collision(i, j);
						}
					}
				}
			}
			
			//can't have the player being stuck...
			j = getplayer();
			if (j > -1) {
				if (!testwallsx(j, entities[j].xp, entities[j].yp)) {
					//Let's try to get out...
					entities[j].yp -= 3;
				}
			}
			
			activetrigger = "null";
			if (checktrigger() > "null") {
				game.releasekeys();
				i = getplayer();
				stopmovement(i);
				game.load(activetrigger);
			}
			
			activedoor = "null";
			activedoor = checkdoor();
			if (activedoor != "null") {
				game.roomname = blocks[actualdoor].doorname;
				game.roomnamemode = -1;
			}
		}
		
		public static var nentity:int;
		public static var entities:Vector.<entclass> = new Vector.<entclass>;
		public static var tempx:int, tempy:int, tempw:int, temph:int, temp:int, temp2:int;
		public static var tpx1:int, tpy1:int, tpx2:int, tpy2:int;
		public static var colpoint1:Point, colpoint2:Point;
		public static var temprect:Rectangle, temprect2:Rectangle;
		public static var activetrigger:String;
		
    public static var blocks:Vector.<blockclass> = new Vector.<blockclass>;
    public static var nblocks:int;
		
		public static var doortox:int, doortoy:int, activedoor:String, activedoordest:String;
		public static var actualdoor:int; // Kludge: checkdoor returns trigger, but it is useful to know the actual door address for roomnames
		
		public static var templates:Vector.<ent_generic> = new Vector.<ent_generic>;
		public static var numtemplate:int;
		public static var entindex:Dictionary = new Dictionary;
		
		//Particles!
		public static var particles:Vector.<particleclass> = new Vector.<particleclass>;
		public static var nparticles:int;
		
		//Entity init states
		public static var initentities:Vector.<initentclass> = new Vector.<initentclass>;
		public static var ninitentities:int;
	}
}