/**
 * UI widget for creating paths in the game.
 *
 * @author John Kloosterman for CS352 at Calvin College
 * @date Nov. 24, 2012
 */

function PathUI( container, x, y )
{
	this.container = $( container );
	this.containerText = container;
	this.x = x;
	this.y = y;
	this.isHidden = false;

	this.reset();
}

PathUI.prototype.reset = function()
{
	this.paths = [ [] ];
	this.grid_paths = [ [] ];
	this.pathBeginnings = [ { x: 0, y: 0 } ];
	this.pathEndings = [ { x: this.x - 1, y: this.y - 1 } ];
	this.parents = [ null ];
	this.currentPath = 0;

	this.dragHandleIndex = 0;
	this.isDragging = false;

	this.validated = false;
	this.container.html( "" );
	this.draw();
}

// To see whether we need to ask for all the positions again,
//  since that's expensive.
PathUI.prototype.validate = function()
{
	this.validated = true;
}

PathUI.prototype.showSand = function( sand )
{
	for ( var i = 0; i < sand.length; i++ )
	{
		var selector = this.containerText + " ." + this.getCellClass( sand[i].x, sand[i].y );
		$( selector ).addClass( 'sand' );
	}
}

PathUI.prototype.getColour = function( idx )
{
	switch( idx )
	{
	case 0:
		return "#4BC8FF";
	case 1:
		return "#FF6D56";
	case 2:
		return "#0A7D00";
	case 3:
		return "#FFA556";
	}
}

PathUI.prototype.numAgents = function()
{
	return this.paths.length;
}

PathUI.prototype.draw = function() {
	var ui = $( "<table class='PathUI'><tr><td class='grid'></td><td class='controls'></td></table>" );
	this.container.append( ui );

	var table = $( "<table class='PathUITable'></table>" );

    var genClickClosure = function( object, x, y )
    {
        return function() {
            object.click( x, y );
        }
    }
    var genMouseDownClosure = function( object, x, y )
    {
        return function() {
            object.mouseDown( x, y );
        }
    }
    var genMouseMoveClosure = function( object, x, y )
    {
        return function() {
            object.mouseMove( x, y );
        }
    }
    var genMouseUpClosure = function( object, x, y )
    {
        return function() {
            object.mouseUp( x, y );
        }
    }

    for ( var i = 0; i < this.x; i++ )
    {
        var row = $( "<tr class='PathUIRow'></tr>" );
        for ( var j = 0; j < this.y; j++ )
		{
            var cell = $( "<td class='PathUICell'></td>" );
            cell.addClass( this.getCellClass( j, i ) );
            cell.click( genClickClosure( this, j, i ) );
            cell.mousedown( genMouseDownClosure( this, j, i ) );
            cell.mousemove( genMouseMoveClosure( this, j, i ) );
			cell.mouseup( genMouseUpClosure( this, j, i ) );
            row.append( cell );
        }
        table.append( row );
    }

	$( this.containerText + " .grid" ).append( table );
	this.updateDisplay();
	this.updatePathSelector();
};

PathUI.prototype.click = function( x, y )
{
	if ( this.isHidden )
		return;

	// If there is a handle on this square, start a new path.
	var current_handle = this.currentHasHandle( x, y );
	if ( current_handle !== false )
	{
		var new_idx = this.paths.length;

		// Using the same handle object is intentional.
		this.pathBeginnings.push( current_handle );
		this.pathEndings.push( this.pathEndings[ this.currentPath ] );
		this.paths.push( [] );
		this.parents.push( this.currentPath );

		this.currentPath = new_idx;
		this.updatePathSelector();
		this.updateDisplay();
		this.validated = false;

		return;
	}

	// Otherwise, create a handle here and draw it.
	var handle = new Handle( this, x, y, this.currentPath );
	handle.draw( this.containerText + " ." + this.getCellClass( x, y ) );
	
	// If this location is on the current path, put it in the
	//  path such that nothing changes.
	if ( this.isOnPath( x, y ) )
	{
		var next_handle_idx = this.getNextHandle( x, y );
		this.paths[this.currentPath].splice( next_handle_idx, 0, handle );

		console.log( next_handle_idx );
		console.log( this.paths[0][next_handle_idx] );
	}
	else
	{
		// Otherwise, make it a new node in order.
		this.paths[this.currentPath].push( handle );
	}

	// Redraw the paths.
	this.updateDisplay();
	this.validated = false;
}

PathUI.prototype.mouseDown = function( x, y )
{
	if ( this.isHidden )
		return;

	// Find a handle at this location, if there is one.
	var handles = this.paths[ this.currentPath ];
	var handle = -1;
	for ( var i = 0; i < handles.length; i++ )
	{
		if ( handles[i].x == x && handles[i].y == y )
		{
			handle = i;
			break;
		}
	}

	// There was no handle here.
	if ( handle == -1 )
		return;

	// There is a handle at this location.
	this.dragHandleIndex = handle;
	this.isDragging = true;

	console.log( "Dragging: ", x, y );
}

PathUI.prototype.mouseMove = function( x, y )
{
	if ( !this.isDragging )
		return;
	
	// Screwy things happen when there are two handles at the
	//  same coordinate.
	if ( this.hasHandle( x, y ) )
		return;

	var handle = this.paths[ this.currentPath ][ this.dragHandleIndex ];
	if ( handle.x != x || handle.y != y )
	{
		handle.undraw( this.containerText + " ." + this.getCellClass( handle.x, handle.y ) );

		handle.x = x;
		handle.y = y;
		handle.draw( this.containerText + " ." + this.getCellClass( x, y ) );

		this.updateDisplay();
		this.validated = false;
	}
}

PathUI.prototype.mouseUp = function( x, y )
{
	if ( this.isHidden )
		return;
		
	/// @todo Handle the case when there is already a handle here.

	if ( this.isDragging )
	{
		this.isDragging = false;
		this.dragHandleIndex = -1;
	}
}

/**
 * If the point is on the current path,
 *  return the location in the current handle
 *  array the new point would need to go.
 *
 * Otherwise, return false
 */
PathUI.prototype.isOnPath = function( x, y )
{
	var path = this.grid_paths[ this.currentPath ];

	for ( var i = 0; i < path.length; i++ )
	{
		if ( path[i].x == x && path[i].y == y )
			return true;
	}

	return false;
}

/*
 * Precondition: x,y is actually on the route.
 */
PathUI.prototype.getNextHandle = function( x, y )
{
	var next_handle;

	var path = this.grid_paths[ this.currentPath ];
	var handles = this.paths[ this.currentPath ];

	// Find the handle after the location.
	var location_found = false;
	for ( var i = 0; i < path.length; i++ )
	{
		if ( !location_found )
		{
			if (path[i].x == x 
				&& path[i].y == y )
			{
				location_found = true;
			}
		}
		else
		{
			// See if any handles are at this location.
			for ( var j = 0; j < handles.length; j++ )
			{
				if ( handles[j].x == path[i].x
					 && handles[j].y == path[i].y )
//					next_handle = j;
					return j;
			}
		}
	}

	// If we didn't find anything, the next handle
	//  is the length of the handle array.
	if ( location_found && !next_handle )
	{
//		next_handle = handles.length;
		return handles.length;
	}
}

PathUI.prototype.getCellClass = function( x, y )
{
    return "cell_" + x + "_" + y;
}

/**
 * Is there a handle at a given grid coordinate?
 *
 * @return
 *   true if there is a handle at the coordinate,
 *   false otherwise
 */
PathUI.prototype.hasHandle = function( x, y )
{
	for ( var i = 0; i < this.paths.length; i++ )
	{
		for ( var j = 0; j < this.paths[i].length; j++ )
		{
			if ( this.paths[i][j].x == x
				 && this.paths[i][j].y == y )
				return true;
		}
	}

	return false;
}

PathUI.prototype.currentHasHandle = function( x, y )
{
	var handles = this.paths[this.currentPath];
	
	for ( var i = 0 ; i < handles.length; i++ )
	{
		if ( handles[i].x == x && handles[i].y == y )
			return handles[i];
	}

	return false;
}

/**
 * We go through each of the points in the order they are in
 *  the array.
 */
PathUI.prototype.computePath = function( index )
{
	var handles = this.paths[index];
	var path = [];

	// First, go from start to the first.
	var start = this.pathBeginnings[index];
	var end = this.pathEndings[index];

	// Case when there are no handles
	if ( !handles.length )
	{
		return this.gridPath( start.x, start.y, end.x, end.y );
	}
	
	// Otherwise, there is at least 1 handle.
	path = path.concat( this.gridPath( start.x, start.y, handles[0].x, handles[0].y ) );

	for ( var i = 1; i < handles.length; i++ )
	{
		path = path.concat( 
			this.gridPath( 
				handles[i-1].x, 
				handles[i-1].y, 
				handles[i].x,
				handles[i].y 
			) 
		);
	}
	
	path = path.concat( 
		this.gridPath( 
			handles[handles.length - 1 ].x,
			handles[handles.length - 1 ].y,
			end.x,
			end.y
		)
	);

	return path;
}

/**
 * Append the moves to get from (x1, y1) to (x2,y2)
 *  to @c path.
 *
 * @param path
 *  An array that will be filled with elements:
 *   "L", "R", "U", "D", depending on what the move is.
 */
PathUI.prototype.gridPath = function( x1, y1, x2, y2, path )
{
	var x = x1, y = y1;
	var path = [];

	while ( !( x == x2 && y == y2 ) )
	{
		if ( Math.abs( x2 - x ) > Math.abs( y2 - y ) )
		{
			if ( x2 > x )
			{
				path.push( "R" );
				x++;
			}
			else
			{
				path.push( "L" );
				x--;
			}
		}
		else
		{
			if ( y2 > y )
			{
				path.push( "D" );
				y++;
			}
			else
			{
				path.push( "U" );
				y--;
			}
		}
	}

	return path;
};

PathUI.prototype.updateDisplay = function()
{
	// Clear previous paths.
	for ( var i = 0; i < this.x; i++ )
	{
		for ( var j = 0; j < this.y; j++ )
		{
			$( this.containerText + " ." + this.getCellClass( i, j ) )
				.html( "" );
		}
	}

	for ( var i = 0; i < this.paths.length; i++ )
	{
		var path = this.computePath( i );
		this.grid_paths[i] = this.drawPath( this.pathBeginnings[i], path, i );
	}
};

// Call this when new paths are created or destroyed.
PathUI.prototype.updatePathSelector = function()
{
	var genClickClosure = function( object, i ) {
		return function() {
			object.currentPath = i;
		
			$( object.containerText + " .PathUIPathSelector" )
				.removeClass( 'active' );
			$( object.containerText + " .selector" + i )
				.addClass( 'active' );
		}
	};

	var controls = $( this.containerText + " .controls" );
	var selectors = $( '<table/>' );

	for ( var i = 0; i < this.paths.length; i++ )
	{
		var colour = this.getColour( i );
		var selector = $( '<div class="PathUIPathSelector selector' + i + '"/>' );
		selector.css( 'background-color', colour );
		if ( i == this.currentPath )
			selector.addClass( 'active' );
		selector.click( genClickClosure( this, i ) );

		selectors.append( '<tr><td>', [ selector, '</tr></td>' ] );
	}	

	controls.html( selectors );
};

/**
 * Draw the path through the points in order,
 *  and find the grid squares that are on the path.
 *
 * @return {array}
 *   An array of { x, y } objects representing
 *    the grid squares on the path.
 */
PathUI.prototype.drawPath = function( start, path, idx )
{
	var covered_path = [];
	var position = { x: start.x, y: start.y };
	var updatePosition = function( move )
	{
		if ( move == "U" )
			position.y--;
		else if ( move == "D" )
			position.y++;
		else if ( move == "L" )
			position.x--;
		else
			position.x++;

		covered_path.push( { x: position.x, y: position.y } );
	}

	for ( var i = 0; i < path.length; i++ )
	{
		var prev;
		if ( i == 0 )
			prev = "D"
		else
			prev = path[i-1];

		this.drawPathSegment( prev, path[i], position.x, position.y, idx );
		updatePosition( path[i] );
	}

	return covered_path;
};

PathUI.prototype.drawPathSegment = function( prev, current, x, y, idx )
{
	var elem;
	var colour = this.getColour( idx );

	if (( current == "U" || current == "D" ) && (prev == "U" || prev == "D"))
		elem = "<span style='color:" + colour + "'>┃</span>";
	else if (( current == "L" || current == "R" ) && (prev == "L" || prev == "R"))
		elem = "<span style='color:" + colour + "'>━</span>";
	else if ( current == "D" ) {
		if ( prev == "L" )
			elem = "<span style='color:" + colour + "'>┏</span>";
		else if ( prev == "R" )
			elem = "<span style='color:" + colour + "'>┓</span>";
	}
	else if ( current == "U" ){
		if ( prev == "L" )
			elem = "<span style='color:" + colour + "'>┗</span>";
		else if ( prev = "R" )
			elem = "<span style='color:" + colour + "'>┛</span>";
	}
	else if ( current == "L" ){
		if ( prev == "U" )
			elem = "<span style='color:" + colour + "'>┓</span>";
		if ( prev == "D" )
			elem = "<span style='color:" + colour + "'>┛</span>";
	}
	else if (current == "R" ){
		if ( prev == "U" )
			elem = "<span style='color:" + colour + "'>┏</span>";
		else if ( prev = "D" )
			elem = "<span style='color:" + colour + "'>┗</span>"; 
	}
	
	$( this.containerText + " ." + this.getCellClass( x, y ) )
		.html( '<div class="PathUICellContainer">' + elem + '</div>' );
}

PathUI.prototype.getPositions = function()
{
	// Maybe store parents?
	// Figure out start time of each branch and swizzle them together.
	
	var positions = [];
	var t = 0;

	// if the node is the same object as the start of some other
	//  path, then it's a fork.
	
	var paths = [];
	var started = [];
	for ( var i = 0; i < this.paths.length; i++ )
	{
		paths.push( this.computePath( i ) );
		started.push( false );
	}
	
	// Trace each path. When we're at a start point of some path,
	//  as the right parent, start that path.
	var position_dict = {};
	
	this._positions( 
		0, 
		paths, 
		0,
		this.pathBeginnings[0].x,
		this.pathBeginnings[0].y,
		position_dict
	);

	return position_dict;
}

// Recursive: a path and its children; combine them.
// position_dict: an object: key: time value, value: array of positions.
PathUI.prototype._positions = function( path_idx, paths, start_time, x, y, position_dict )
{
	// First, find the starting positions of our children.
	// (1) find our children
	var children = [];
	for ( var i = 0; i < this.parents.length; i++ )
	{
		if ( this.parents[i] == path_idx )
			children.push( i );
	}

	// (2) get their starting positions.
	var child_starts = [];
	for ( var i = 0; i < children.length; i++ )
	{
		child_starts.push({ 
			idx: children[i], 
			x: this.pathBeginnings[children[i]].x, 
			y: this.pathBeginnings[children[i]].y 
		});
	}

	var path = paths[path_idx];
	var t = start_time;
	for ( var i = 0; i < path.length; i++ )
	{
		if ( position_dict[t] == undefined )
			position_dict[t] = [];
			
		position_dict[t][path_idx] = { x: x, y: y };

		// If it is the start of a child, then recursively
		//  find the position of that child.
		for ( var j = 0; j < child_starts.length; j++ )
		{
			if ( x == child_starts[j].x && y == child_starts[j].y )
			{
				this._positions( 
					child_starts[j].idx, 
					paths, 
					t, 
					x, 
					y, 
					position_dict
				);

				// Make sure we don't start it twice if the path
				//  overlaps itself.
				child_starts[j].x = -1;
			}
		}
		if ( path[i] == "U" )
			y--;
		else if ( path[i] == "D" )
			y++;
		else if ( path[i] == "L" )
			x--;
		else
			x++;

		t++;
	}
}

PathUI.prototype.hide = function()
{
//	this.container.css( 'opacity', '0.4' );
	this.isHidden = true;
};

PathUI.prototype.show = function()
{
//	this.container.css( 'opacity', '1' );
	this.isHidden = false;
}

/**************** Handle object **********/
function Handle( pathUI, x, y, idx )
{
	this.pathUI = pathUI;
	this.x = x;
	this.y = y;
	this.idx = idx;
}

Handle.prototype.draw = function( container )
{
	var colour = this.pathUI.getColour( this.idx );
	$( container ).css( 'background-color', colour );
}

Handle.prototype.undraw = function( container )
{
	$( container ).css( 'background', 'none' );
}
