import standard;
import node;
import random;


//persistent members
var tilemap: opaque = null;
var stepCounter: opaque = null;
var player: opaque = null;
var enemies: [opaque] = null;

var collisionDataCache: [bool] = null;

var rng: opaque = null; //TODO: does this have an opaque tag?

//lifecycle functions
fn onInit(node: opaque) {
	//load the map stuff
	tilemap = node.loadChild("scripts:/gameplay/tilemap.toy");
	node.generateScene(clock().hash(), MAP_GRID_WIDTH, MAP_GRID_HEIGHT);

	//load and initialize the UI (depends on map stuff)
	stepCounter = node.loadChild("scripts:/gameplay/step-counter.toy");
	stepCounter.setNodeRect(0, 0, CAMERA_SCREEN_W, 32);
	stepCounter.callNodeFn("setMaxSteps", 100);
	stepCounter.setNodeLayer(100);

	//offset the scene node, so it shows the map outside separate from the UI
	node.setNodePositionY(16);
}

fn onFree(node: opaque) {
	//cleanup
	if (rng != null) {
		rng.freeRandomGenerator();
	}
}

// fn onUpdate(node: opaque, delta: int) {
// 	print "delta: " + string delta;
// }

//fn onStep(node: opaque) {
//	//
//}

fn onDraw(node: opaque) {
	//use the onDraw to sort - skip too many steps
	node.sortChildrenNode(depthComparator);

	var scaleX: float = player.getNodeWorldScaleX();
	var scaleY: float = player.getNodeWorldScaleY();

	//this offset is because the sprite cell for lejana is twice as big as the sprite cell for the floor tiles
	var originOffsetX: int = player.getNodeRectW() / 2;
	var originOffsetY: int = player.getNodeRectH() / 2;

	//use the room scene's onDraw to set the global camera position
	globalCameraX = floor((CAMERA_SCREEN_W / 2) - (player.getNodeWorldPositionX() * scaleX)) - originOffsetX;
	globalCameraY = floor((CAMERA_SCREEN_H / 2) - (player.getNodeWorldPositionY() * scaleY)) - originOffsetY;
}

//gameplay functions
fn generateScene(node: opaque, seed: int, width: int, height: int) {
	//reset the array
	enemies = [];
	
	if (rng != null) {
		rng.freeRandomGenerator();
	}
	rng = createRandomGenerator(seed);

	//generate map
	tilemap.callNodeFn("generateFromRng", rng, width, height);
	collisionDataCache = tilemap.callNodeFn("getCollisionData");

	//place player here, so drop dropping can avoid the player
	player = node.loadChild("scripts:/gameplay/lejana.toy");
	player.callNodeFn("setGridPosition", 1, 1);

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

		enemies.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.getWalkableAt(x, y) == false || x < width / 6 * 2 && y < height / 6 * 2) {
			x = rng.generateRandomNumber() % width;
			y = rng.generateRandomNumber() % height;
		}

		d.callNodeFn("setGridPosition", x, y);
		d.setNodePositionX(MAP_GRID_WIDTH * TILE_PIXEL_WIDTH);
		d.setNodePositionY(MAP_GRID_HEIGHT * TILE_PIXEL_HEIGHT);
	}
}

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

	//entities
	if (x == player.callNodeFn("getGridPositionX") && y == player.callNodeFn("getGridPositionY")) {
		return false;
	}

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

	//outside the grid
	if (y * MAP_GRID_WIDTH + x < 0 || y * MAP_GRID_WIDTH + x >= collisionDataCache.length()) {
		return false;
	}

	//default
	return collisionDataCache[y * MAP_GRID_WIDTH + x];
}

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

	stepCounter.callNodeFn("alterRemainingSteps", -1);
}

//utils - polyfills
fn loadChild(parent: opaque, fname: string) {
	//TODO: add this to the API proper
	var child: opaque = loadNode(fname);
	parent.pushNode(child);
	return child;
}

fn depthComparator(lhs: opaque, rhs: opaque) { //for sorting by depth
	var lhsPositionY = lhs.getNodeWorldPositionY();
	var rhsPositionY = rhs.getNodeWorldPositionY();

	if (lhsPositionY == null || rhsPositionY == null) { //BUGFIX: children without that function
		return true;
	}

	if (lhsPositionY == rhsPositionY) { //BUGFIX: prevent z-fighting
		var lhsPositionX = lhs.getNodeWorldPositionX();
		var rhsPositionX = rhs.getNodeWorldPositionX();

		return lhsPositionX < rhsPositionX;
	}

	return lhsPositionY < rhsPositionY;
}
