import standard;
import node;
import random;

//consts
var TILE_WIDTH: int const = 32;
var TILE_HEIGHT: int const = 32;

var MAP_WIDTH: int const = 16;
var MAP_HEIGHT: int const = 16;


var tilemap: opaque = null;
var player: opaque = null;

var enemies: [opaque] = [];

var entities: [opaque] = null; //full list of entities

var collisionMap: [bool] = null; //cache this, since it won't change during a level

var rng: opaque = null;

//debugging tools
var stepCounter: int = 0;
var drawCounter: int = 0;

var textNode: opaque = null;

//lifecycle functions
fn onLoad(node: opaque) {
	tilemap = node.loadChild("scripts:/gameplay/tilemap.toy");
	player = node.loadChild("scripts:/gameplay/lejana.toy");
	textNode = node.loadChild("scripts:/empty.toy");
}

fn onInit(node: opaque) {
	node.generateLevel(clock().hash(), MAP_WIDTH, MAP_HEIGHT);

	//debugging
//	textNode.setNodeText("fonts:/alphbeta.ttf", 48, "Hello world", 128, 0, 0, 255);
	textNode.setNodeText("fonts:/Ancient God.ttf", 48, "Hello world", 128, 0, 0, 255);
}

fn onFree(node: opaque) {
	rng.freeRandomGenerator();
}

fn onStep(node: opaque) {
	if (++stepCounter >= 30) {
		print "FPS: " + string drawCounter + " / 30";
		stepCounter = 0;
		drawCounter = 0;
	}

	entities = entities.sort(depthComparator);
}

fn onDraw(node: opaque) {
	drawCounter++;

	//call each child's custom draw fn
	tilemap.callNodeFn("customOnDraw");
	for (var i = 0; i < entities.length(); i++) {
		entities[i].callNodeFn("customOnDraw");
	}
}

//utils - polyfills
fn loadChild(parent: opaque, fname: string) {
	var child: opaque = loadNode(fname);
	parent.pushNode(child);
	return child;
}

//gameplay functions
fn generateLevel(node: opaque, seed: int, width: int, height: int) {
	//reset the array
	enemies = [];
	entities = [player]; //assume the player exists already

	if (rng != null) rng.freeRandomGenerator();
	rng = createRandomGenerator(seed);

	//place player
	player.callNodeFn("setGridPos", 1, 1);

	//generate map
	tilemap.callNodeFn("generateFromRng", rng, 16, 16);
	collisionMap = tilemap.callNodeFn("getCollisionMap");

	//generate drones
	var droneCount: int const = rng.generateRandomNumber() % 2 + 20;
	for (var i = 0; i < droneCount; i++) {
		var d = node.loadChild("scripts:/gameplay/drone.toy");
		d.initNode();

		enemies.push(d);
		entities.push(d);

		var x = 0;
		var y = 0;

		//while generated spot is a collision, or too close to the player
		while ((x == 0 && y == 0) || node.getCollisionAt(x, y) == false || (x < (width / 6 * 2) && y < (height / 6 * 2))) { //TODO: toy bug - no short-circuiting?
			x = rng.generateRandomNumber() % width;
			y = rng.generateRandomNumber() % height;
		}

		d.callNodeFn("setGridPos", x, y);
		d.callNodeFn("setRealPos", (width -1) * TILE_WIDTH, (height - 1) * TILE_HEIGHT);
	}
}

fn getCollisionAt(node: opaque, x: int, y: int) {
	if (collisionMap == null || x == null || y == null) {
		return false;
	}

	//entities
	var pos = player.callNodeFn("getGridPos");
	if (x == pos[0] && y == pos[1]) {
		return false;
	}

	for (var i = 0; i < enemies.length(); i++) {
		var pos = enemies[i].callNodeFn("getGridPos");
		if (x == pos[0] && y == pos[1]) {
			return false;
		}
	}

	//default
	return collisionMap[y * MAP_WIDTH + x];
}

fn runAI(node: opaque) {
	for (var i = 0; i < enemies.length(); i++) {
		enemies[i].callNodeFn("runAI", rng);
	}
}

fn depthComparator(lhs: opaque, rhs: opaque) { //for sorting by depth
	var lhsPos = lhs.callNodeFn("getRealPos");
	var rhsPos = rhs.callNodeFn("getRealPos");

	return lhsPos[1] < rhsPos[1];
}