
var SysGame = new Game();

var ListOfGames = [];
var actor_stack =  [];
var trigger_stack =  [];

var constants = {
	
	// Define Actor state constants:
	actorState: {
		AsAttachment	: 0,	// the actor is entering / exitting a vehicle
		AsInCar			: 1,	// the actor is in a vehicle
		AsNormal		: 2,	// the actor is under control and can move & shoot
		AsFalling		: 3,	// the actor has no control, jump animation plays, after finishing it starts with fall animation, after fall animations it goes to AsStunned
		AsStunned		: 4,	// character has no control for a second, actor animation is on "dead on floor", after a while it goes to AsNormal
		AsJumping		: 5,	// the character defies gravity for a while, then if it is on floor goes to AsNormal, otherwise it goes to AsFalling, if it hits a object which is within character reach, he is put on it.
		toString: function(enumval) {
			
			for (var g in this) {
				
				if (this[g] = enumval)
					return g;
				
			}
			
			return null;
			
		}
	},
	// actor animation constants
	animation: { // TODO: these are now out of order, must correct this.
		anm_stand		: 0,
		anm_bikeseat	: 1,
		anm_dead		: 2,
		anm_blk1		: 3,
		anm_jump		: 4,
		anm_fall		: 5,
		anm_fallface	: 6,
		anm_fallback	: 7,
		anm_enter		: 8,
		anm_carseat		: 9,
		anm_walk		: 10,
		anm_run			: 11,
		anm_punch		: 12,
		anm_runp		: 13,
		anm_walkg		: 14,
		anm_rung		: 15,
		anm_walku		: 16,
		anm_runu		: 17,
		anm_walkr		: 18,
		anm_runr		: 19,
		anm_walkf		: 20,
		anm_runf		: 21,
		toString: function(enumval) {
			
			for (var g in this) {
				
				if (this[g] == enumval)
					return g;
				
			}
			
			return null;
			
		}
	},
	engineState: {
		em_sp		: 0,
		em_server	: 1,
		em_client	: 2,
		toString: function(enumval) {
			
			for (var g in this) {
				
				if (this[g] == enumval)
					return g;
				
			}
			
			return null;
			
		}
	},
	fontSize: {
		font_a : 0,
		font_b : 1,
		toString: function(enumval) {
			
			for (var g in this) {
				
				if (this[g] == enumval)
					return g;
				
			}
			
			return null;
			
		}
	},
	disconnectReason: {
		delete_player_disconnect	: 0,
  		delete_player_exit			: 1,
		toString: function(enumval) {
			
			for (var g in this) {
				
				if (this[g] == enumval)
					return g;
				
			}
			
			return null;
			
		}
	},
	damageType: {
	    bulletactor :				0,
	    bulletwall :				1,
	    bulletcar :					2,
	    vehiclecollision :			3,
	    actorburning :				4,
	    vehiclefall :				5,
        fall :						6,
	    drown :						7,
	    vehicleburning :			8,
	    vehicle2vehiclecollision :	9,
        vehicleforces :				10,
        explosion :					11,
        punchmelee :				12,
        toString: function(enumval) {
			
			for (var g in this) {
				
				if (this[g] == enumval)
					return g;
				
			}
			
			return null;
			
		}
    },
	aiClass: {
		
		AI_INVALID				: 99,
		AI_HUMANACTOR			: 0, // player actor(s) only
		AI_IDIOTKILLER			: 1,
		AI_CITIZEN				: 2,
		AI_SCARED_CITIZEN		: 3,
		AI_ZOMBIE				: 4,
		AI_IDIOTKILLER_CITIZEN	: 5,
		AI_POLICEOFFICER		: 7,
		AI_ARMYSOLDIER			: 8,
		AI_SCRIPTED				: 9,
		AI_FIREMAN				: 11,
        AI_MEDIC				: 12,
        toString: function(enumval) {
			
			for (var g in this) {
				
				if (this[g] == enumval)
					return g;
				
			}
			
			return null;
			
		}
    }
};

var NewGameInstance = function() {
	
	var go = new Game;
	
	ListOfGames.push(go);
	
	return go;
	
}

InternalCreateActorGame = function(ID, CinC) {
	
	var go = new Actor(ID, CinC);
	
	actor_stack.push(go);
	
	for (var w in ListOfGames) {
		
		if (typeof(ListOfGames[w].OnCreatePlayer) == "function")
			ListOfGames[w].OnCreatePlayer(go);
		
	}
	
	return go;
	
}

internalFindActor = function(ID, CinC) {
	
	for (var i in actor_stack) {
		
		var player_actor = actor_stack[i];
		
		if (ID != player_actor.internalid)
			continue;
		if (CinC != player_actor.internalcinc)
			continue;
		
		return player_actor;
		
	}
	
	return null;
	
}

InternalDestroyActorGame = function(ID, CinC, Reason) {
	
	// console.log('InternalDestroyActorGame id ' + ID + ' cinc ' + CinC);
	
	for (var i in actor_stack) { // on't use internalFindActor since actor stack is spliced here.
		
		var player_actor = actor_stack[i];
		
		if (ID != player_actor.internalid)
			continue;
		if (CinC != player_actor.internalcinc)
			continue;
		
		// console.log('found for deletion:');
		// console.log(player_actor);
		
		for (var w in ListOfGames) {
			
			if (typeof(ListOfGames[w].OnDestroyPlayer) == "function")
				ListOfGames[w].OnDestroyPlayer(player_actor, Reason);
			
		}
		
		return actor_stack.splice(i, 1);
		
	}
	
	// console.log(actor_stack);
	
}

InternalOnApplyDamage = function(ID, CinC, ID2, CinC2, HitOriginX, HitOriginY, HitOriginZ, OldHealth, NewHealth, DamageType) {
	
	// for (var i in actor_stack) {
	// 	
	// 	var player_actor = actor_stack[i];
	// 	
	// 	console.log(' actor name ' + player_actor.playername + ' id ' + player_actor.internalid + ', cinc = ' + player_actor.internalcinc);
	// 	
	// }
	
	var player_actor = internalFindActor(ID, CinC); // hit player
	var player_actor_2 = internalFindActor(ID2, CinC2); // other player / attacker?
	
	/*
	console.log('calling InternalOnApplyDamage - player_actor');
	console.log(player_actor); // null
	console.log('calling InternalOnApplyDamage - player_actor_2');
	console.log(player_actor_2); // ok handle
	*/
	
	for (var w in ListOfGames) {
		
		if (typeof(ListOfGames[w].OnDamagePlayer) == "function")
			ListOfGames[w].OnDamagePlayer(player_actor, player_actor_2, [HitOriginX, HitOriginY, HitOriginZ], OldHealth, NewHealth, DamageType);
		
	}
	
}

InternalCreateVehicleGame = function(ID, CinC) {
	
	var go = new Vehicle(ID, CinC);
	return go;
	
}

InternalStaticObjectDestroyed = function(namedfunction) {
	
	var fn = new Function('return ' + namedfunction)();
	fn();
	
}

InternalMapPickupPickedUp = function(namedfunction) {
	
	var fn = new Function('return ' + namedfunction)();
	fn();
	
}

InternalChatInput = function(ID, CinC, ChatText) {
	
	var player_actor = internalFindActor(ID, CinC);
	
	if (player_actor.OnChat != undefined)
		player_actor.OnChat(ChatText);
	
}

InternalPickupTriggerGame = function(ID, CinC) {
	
	for (var i in trigger_stack) {
		
		var temptrigger = trigger_stack[i];
		
		if (ID != temptrigger.internalid)
			continue;
		if (CinC != temptrigger.internalcinc)
			continue;
		
		if (typeof(temptrigger.OnPickup) == 'function') {
			
			temptrigger.OnPickup();
			
		}
		
	}
	
}

/*
Actor.EnterExitThisVehicle = function(Vehicle, carjack, immediately) {
	
	this.EnterExitThisVehicleInternal(Vehicle.internalid, Vehicle.internalcinc, carjack, immediately)
	
}
*/

Actor.Attack = function(OtherPlayer, AIClass) {
	
	this.AttackInternal(OtherPlayer.internalid, OtherPlayer.internalcinc, AIClass);
	
}

Game.NewActor = function(ActorStyle, AIClass, SpawnX, SpawnY, SpawnZ, Heading) {
	
	var tactor = new Actor(ActorStyle, AIClass, SpawnX, SpawnY, SpawnZ, Heading);
	actor_stack.push(tactor);
	
	//console.log('Game.NewActor');
	//console.log(actor_stack);
	
	return tactor;
}

Game.NewTrigger = function(pickupident, x, y, z, amount, heading) {
	
	var ttrigger = new Trigger(pickupident, x, y, z, amount, heading);
	trigger_stack.push(ttrigger);
	
	//console.log('Game.NewTrigger');
	//console.log(trigger_stack);
	
	return ttrigger;
}

Game.DeleteTrigger = function(ThisTrigger) {
	
	// print('Delete Trigger ' + ThisTrigger.playername);
	
	for (var i in trigger_stack) { // on't use internalFindActor since actor stack is spliced here.
		
		var temptrigger = trigger_stack[i];
		
		if (ThisTrigger.ID != temptrigger.internalid)
			continue;
		if (ThisTrigger.CinC != temptrigger.internalcinc)
			continue;
		
		trigger_stack.splice(i, 1);
		
	}
	
	ThisTrigger.free();
	
	delete ThisTrigger;
	
	return null;
	
}

Game.DeleteActor = function(ThisActor) {
	
	print('Delete Actor ' + ThisActor.playername);
	
	InternalDestroyActorGame(ThisActor.internalid, ThisActor.internalcinc, 'DeleteActor');
	
	ThisActor.free();
	
	// todo.. let engine know?
	
	delete ThisActor;
	
	return null;
	
}

Game.Explosion = function(x, y, z, radius) {
	
	SysGame.EmitParticle('boom',     x, y, z,    0, 0, 0,     0.0, 0, radius, 0, 0);
	
}

/*
Actor.prototype.OnCreate = function(OtherPlayer, AIClass) {
	
	console.log('actor_stack pushing - Actor.OnCreate')
	actor_stack.push(self);
	
}
*/

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
getRandomInt = function(max) {
	
	return Math.floor(Math.random() * Math.floor(max));
	
}