function Controller( pathUI, level )
{
	this.pathUI = pathUI;
	this.positions = null;
	this.level = level;
	this.agents = [];
	
	this.levels = [
	{
		x: 20,
		y: 20,
		sand: [],
		enemies: [],
		startingAgent: [ new Agent( this, 0 ) ],
		message: "Experiment with game mechanics. There's no way not to succeed."
	},
	{ // Easy maze.
		x: 20,
		y: 20,
		sand: this.generateMaze(),
		enemies: [],
		startingAgent: [ new Agent( this, 0 ) ],
		message: "<table><tr><td><img src='tutorial10.png'/></td><td>This is sand. If you step in it, you die. If enemies step in it, they die too. <br/><br/>Strategically place nodes to get through the maze."
	},
	{ // intermediate, three simple enemies
		x: 20,
		y: 20,
		sand: [
				{ y: 8, x: 7 },
				{ y: 10, x: 7 },
				{ y: 9, x: 6 },
				{ y: 9, x: 8 },	
				],
		enemies: [ new SimpleEnemy( this, 8, 18 ), new SimpleEnemy( this, 16, 17 ), new SimpleEnemy( this, 16, 2 ) ],
		startingAgent: [ new Agent( this, 0 ) ],
		message: "These enemies are attracted towards you. Use the deadly sand patch to your advantage."
	},
	{ // difficult, two traps two enemies
		x: 20,
		y: 20,
		sand: [
				{ y: 10, x: 6 },
				{ y: 9, x: 7 },
				{ y: 9, x: 8 },
				{ y: 10, x: 9 },
				],
		enemies: [ new EremikophobicEnemy( this, 10, 15 ) ],
		startingAgent: [ new Agent( this, 0 ) ],
		message: "This enemy will not blindly walk into sand, but are not smart enough to go around it."
	},
		{ // intermediate, one trap one enemy
		x: 20,
		y: 20,
		sand: [
				{ y: 13, x: 6 },
				{ y: 12, x: 7 },
				{ y: 12, x: 8 },
				{ y: 13, x: 9 },
				{ y: 10, x: 14 },
				{ y: 9, x: 15 },
				{ y: 9, x: 16 },
				{ y: 10, x: 17 },
				],
		enemies: [ new EremikophobicEnemy( this, 8, 18 ), new EremikophobicEnemy( this, 16, 17 ) ],
		startingAgent: [ new Agent( this, 0 ) ],
		message: "Double the malice means double the fun."
	},
	{ // Actually impossible	
		x: 20,
		y: 20,
		enemies: [ new ImmortalEnemy( this, 10, 15 ) ],
		sand: this.generateMaze(),
		startingAgent: [ new Agent( this, 0 ) ],
		message: "Good luck."
	}
	]
	
	this.setLevel( this.level );
};

Controller.prototype.generateMaze = function()
{
	var sand = [];
	
	for ( var i = 0; i < 20; i++ )
	{
		for ( var j = 0; j < 20; j++ )
		{
			if ( Math.random() < 0.7
				 && j % 2 == 1 )
			{
				sand.push( { x: i, y: j } );
			}
		}
	}
	
	return sand;
}

Controller.prototype.setLevel = function( level )
{
	this.level = level;
	this.pathUI.showSand( this.levels[level].sand );
	
	var message = $( '<div><h2>Level ' + level + '</h2>' + this.levels[this.level].message + '</div>' );
	 $(message).dialog({
		modal: true,
		width: 500,
		buttons: {
			"Start!": function() {
				$( this ).dialog( "close" );
			}
		}
	});
}

/*
 * Either all agents reach their destination or
 *  someone gets eaten by an enemy.
 */
Controller.prototype.computeEnd = function()
{
	var level = this.getLevel();
	var agent_positions = [];
	var enemy_positions = [];

	var time_length = Object.keys(this.positions).length;
	
	for ( var i = 0; i < time_length; i++ )
	{
		// Compute enemy positions.
		var enemy_positions = [];
		for ( var j = 0; j < level.enemies.length; j++ )
		{
			enemy_positions.push( level.enemies[j].getPosition( i ) );
		}
		
		// Find agent positions.
		var agents = this.positions[i];
		for ( var j = 0; j < agents.length; j++ )
		{
			// Check whether the agent is in sand.
			for ( var k = 0; k < level.sand.length; k++ )
			{
				if ( agents[j].x == level.sand[k].x
					 && agents[j].y == level.sand[k].y )
				{
					this.endTime = i;
					return;
				}
			}
		
			// Check to see if any of the enemies are there.
			for ( var k = 0; k < enemy_positions.length; k++ )
			{
				if ( enemy_positions[k].isAlive == true
					 && enemy_positions[k].x == agents[j].x
					 && enemy_positions[k].y == agents[j].y )
				{
					this.endTime = i;
					return;
				}	
			}
		}
	}
	
	this.endTime = time_length - 1;
}

Controller.prototype.update = function()
{
	if ( this.positions == null || !this.pathUI.validated )
	{
		this.pathUI.validate();
		this.positions = this.pathUI.getPositions();
		this.computeEnd();
	}
}

Controller.prototype.getAgents = function()
{
	var agents = [];

	for ( var i = 0; i < this.pathUI.numAgents(); i++ )
	{
		agents.push( new Agent( this, i ) );
	}
	
	return agents;
};

Controller.prototype.getLevel = function()
{
	return this.levels[this.level];
};

Controller.prototype.getPositions = function()
{
	this.update();
	return this.positions;
}

Controller.prototype.getLength = function()
{
	return this.positions.length;
}

// true iff the user won.
Controller.prototype.won = function()
{
	return this.endTime == Object.keys(this.positions).length - 1;
}

/**
 * Return true if time is finished before the given time.
 */
Controller.prototype.isFinished = function( time )
{
	console.log( time );
	console.log( "End time: " + this.endTime );

	return this.endTime <= time;
}

/*******************************/
function Agent( controller, idx )
{
	this.targetX;
	this.targetY;
	this.controller = controller;
	this.idx = idx;
}

/**
 * This doesn't exactly follow the spec in that
 *  agents that have yet to become alive have 
 *  isAlive == false.
 */
Agent.prototype.getStatus = function( time )
{
	var positions = this.controller.getPositions();
	var pos_time = positions[time];

	if ( pos_time[this.idx] )
	{
		return {
			isAlive: true,
			x: pos_time[this.idx].x,
			y: pos_time[this.idx].y,
		};
	}
	else
	{
		return { isAlive: false };
	}
}
