var DEFAULT_GAMESPEED = 3;

var levelid = <?=$leveldata['id']?>;
var level = <?php include 'levels/'.$leveldata['filename'].'.json'; ?>;
var devices = {};
var playerPackets = [];

var packetFields = [
	{layer:"network", fields:[
		"srcip", "dstip"
	]},
	{layer:"transport", fields:[
		"proto", "ttl"
	]},
        {layer:"application", fields:[
                "type", "key"
	]}
];

var vpWidth = window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth;
var vpHeight = window.innerHeight|| document.documentElement.clientHeight|| document.getElementsByTagName('body')[0].clientHeight;

var game = new Phaser.Game(vpWidth, vpHeight, Phaser.AUTO, 'game', { preload: preload, create: create, update: update });
var grpPackets;
var grpDevices;
var grpLaunchers;
var pause, pause_, play, play_, fast, fast_;

function preload() {
	game.load.image('imac', 'includes/imac.png');
	game.load.image('iphone-1', 'includes/iphone-1.png');
	game.load.image('macbook', 'includes/macbook.png');
	game.load.image('monitor', 'includes/monitor.png');
	game.load.image('packet', 'includes/circle.png');
	game.load.image('server', 'includes/server.png');
	game.load.image('router', 'includes/router.png');

	game.load.image('reset', 'includes/ui/reset.png');
	game.load.image('pause', 'includes/ui/pause.png');
	game.load.image('pause_', 'includes/ui/pause_grey.png');
	game.load.image('play', 'includes/ui/play.png');
	game.load.image('play_', 'includes/ui/play_grey.png');
	game.load.image('fast', 'includes/ui/fast.png');
	game.load.image('fast_', 'includes/ui/fast_grey.png');
	game.load.image('edit', 'includes/ui/tabs.png');
	game.load.image('launch', 'includes/ui/launch.png');
	game.load.image('add', 'includes/ui/add.png');

	for (var i = 0; i <= 6; i++) game.load.image('meter-'+i, 'includes/ui/meter-'+i+'.png');
}

function create() {
	game.stage.backgroundColor = 0xDDDDDD;
	grpDevices = game.add.group();
	grpPackets = game.add.group();
	grpLaunchers = game.add.group();
	document.getElementById('pane').style.left = (vpWidth * 0.7) + 'px';
	document.getElementById('pane').style.width = (vpWidth * 0.3 - 40) + 'px';
	document.getElementById('pane').style.height = (vpHeight - 40) + 'px';

<?php if (LOGGEDIN) { ?>
	pause = game.add.sprite(80, 20, 'pause');
	play = game.add.sprite(140, 20, 'play');
	fast = game.add.sprite(200, 20, 'fast');

	game.add.button(20, 20, 'reset', btnReset);
	pause_ = game.add.button(80, 20, 'pause_', btnPause);
	play_ = game.add.button(140, 20, 'play_', btnPlay);
	fast_ = game.add.button(200, 20, 'fast_', btnFast);
	createLaunchers();

	fast_.visible = false;
<?php } ?>

	for (var i = 0; i < level.devices.length; i++) {
		var devSprite = grpDevices.create(0.7 * game.world.width * level.devices[i].x, game.world.height * level.devices[i].y, level.devices[i].image || 'imac');
		level.devices[i].sprite = devSprite;
		if (level.devices[i].hasOwnProperty("capacity")) {
			level.devices[i].capsprite = grpDevices.create(0.7 * game.world.width * level.devices[i].x + 128,   game.world.height * level.devices[i].y, 'meter-0');
		}
		devices[level.devices[i].id] = level.devices[i];
		devices[level.devices[i].id].ports = [];
		devices[level.devices[i].id].locked = false;
		devSprite.inputEnabled = true;
		devSprite.events.onInputDown.add(onDeviceClick, level.devices[i]);
	}

	var graphics = game.add.graphics(0,0);
	graphics.lineStyle(1, 0, 0);
	graphics.lineTo(1,1);
	graphics.lineStyle(1, 0x000000, 1);

	for (var i = 0; i < level.links.length; i++) {
		var src = devices[level.links[i].src];
		var dst = devices[level.links[i].dst];
		src.ports[ level.links[i].srcport ] = dst.id;
		dst.ports[ level.links[i].dstport ] = src.id;
		graphics.moveTo(src.sprite.centerX, src.sprite.centerY);
		graphics.lineTo(dst.sprite.centerX, dst.sprite.centerY);
	}

	var meshSprite = game.add.sprite(0, 0, graphics.generateTexture());
	meshSprite.sendToBack();
	graphics.destroy();

	if (!level.hasOwnProperty("triggers")) level.triggers = [];

	$("#loading").hide();

<?php if (LOGGEDIN) { ?>
	game.input.keyboard.onPressCallback = function(e){ if (e == " ") {
		if (game.paused) {
			if (game.time.slowMotion == 1) btnFast();
			else btnPlay();
		} else btnPause();
	}};
	loadPlayerPackets();
	btnReset();
<?php } else { ?>
	$("input[type=submit").button();
	initEvents();
<?php } ?>
}

function initEvents() {
	for (var i = 0; i < level.timeline.length; i++) {
		game.time.events.add(level.timeline[i].at * 3, playPacket, level.timeline[i]);
	}
}

function playPacket() {
	doPacketAnimation(this.from, getDefaultRecipient(this.from), this.payload);
}

function getDefaultRecipient(from) {
	for (var i = 0; i < level.links.length; i++) {
		if (level.links[i].src == from) return level.links[i].dst;
		else if (level.links[i].dst == from) return level.links[i].src;
	}
	return null;
}

function getPortRecipient(from, portNum) {
	for (var i = 0; i < level.links.length; i++) {
		if (level.links[i].src == from && level.links[i].srcport == portNum) return level.links[i].dst;
		if (level.links[i].dst == from && level.links[i].dstport == portNum) return level.links[i].src;
	}
	return null;

}

// WARNING: this should only be called by the animator
// devicescripts should not be able to access it
function getRemotePort(src, dst) {
	for (var i = 0; i < level.links.length; i++) {
		if (level.links[i].src == src && level.links[i].dst == dst) return level.links[i].dstport;
		if (level.links[i].src == dst && level.links[i].dst == src) return level.links[i].srcport;
	}
}

function update() {
	//todo: separate out the meter-X updates from satisfiesTrigger
	for (var i = 0; i < level.triggers.length; i++) {
		if (level.triggers[i].type == "flood") {
			satisfiesTrigger({dst: level.triggers[i].device}, {type:"flood",device:level.triggers[i].device,noup:true});
		}
	}
}

var levelOver = false;
function donePacket() {
	this.kill();
	var youWin = true;

	for (var i = 0; i < level.triggers.length; i++) {
		if (satisfiesTrigger(this, level.triggers[i])) {
			if (level.triggers[i].hasOwnProperty("times")) {
				if (--level.triggers[i].times <= 0) level.triggers[i].completed = true;
			} else level.triggers[i].completed = true;
		}

		if (!level.triggers[i].hasOwnProperty("completed")) youWin = false;
	}

	if (!levelOver && youWin) {
		levelOver = true;
		$.get("./solns.ajax.php?level="+levelid+"&method=win");
		$("#winner").dialog({
			title:"You win!",
			resizable:false,
			modal:true,
			buttons:[{text:"Go to the next level", click:function(){ location.href="./?level="+level.nextLevel; }}]
		});
	}

	if (devices[this.dst].hasOwnProperty("script")) {
		devices[this.dst].script.onPacketReceived(devices[this.dst], this.payload, this.portNum);
	}
}

function satisfiesTrigger(pkt, t) {
	if (pkt.dst != t.device) return false;
	
	if (t.type == "packet") {
		if (!t.hasOwnProperty("payload") && !t.hasOwnProperty("times")) return true;
		if (!pkt.hasOwnProperty("payload")) return false;
	
		var layers = t.hasOwnProperty("payload") ? Object.keys(t.payload) : [];
		for (var i = 0; i < layers.length; i++) {
			if (!pkt.payload.hasOwnProperty(layers[i])) return false;
	
			var fields = Object.keys(t.payload[ layers[i] ]);
			for (var j = 0; j < fields.length; j++) {
				if (!pkt.payload[ layers[i] ].hasOwnProperty(fields[j])) return false;
				if (pkt.payload[ layers[i] ][ fields[j] ].trim().toLowerCase() != t.payload[ layers[i] ][ fields[j] ].trim().toLowerCase()) return false;
			}
		}

		return true;
	} else if (t.type == "flood") {
		if (!devices[t.device].hasOwnProperty("floodCounter")) {
			devices[t.device].floodCounter = 0;
			devices[t.device].floodLast = 0;
		}

		var delta = game.time.events.ms - devices[t.device].floodLast;
		if (t.noup && devices[t.device].floodCounter > 0) {
			if (delta > 200) {
				devices[t.device].floodCounter--;
				devices[t.device].floodLast = game.time.events.ms;
			}
		} else if (delta < 120 / devices[t.device].capacity) {
			devices[t.device].floodCounter++;
			if (devices[t.device].floodCounter > 30) devices[t.device].floodCounter = 30;
		} else {
			devices[t.device].floodCounter -= Math.floor(delta / (120 / devices[t.device].capacity));
			if (devices[t.device].floodCounter < 0) devices[t.device].floodCounter = 0;
		}

		if (!t.noup) devices[t.device].floodLast = game.time.events.ms;
		devices[t.device].capsprite.loadTexture('meter-'+Math.floor( devices[t.device].floodCounter / 5 ));

		return devices[t.device].floodCounter == 30;
	} else {
		console.log("unknown trigger type: "+t.type);
		return false;
	}
}