//
// This work is licensed under the Creative Commons Attribution 2.5 License. To 
// view a copy of this license, visit
// http://creativecommons.org/licenses/by/2.5/
// or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San
// Francisco, California, 94105, USA.
//
// All copies and derivatives of this source must contain the license statement 
// above and the following attribution:
//
// Author: Kyle Scholz      http://kylescholz.com/
// Copyright: 2006
//

/**
 * DOMFDGraphController:
 * 
 * This is the Controller in our MVC. It performs most or all of the interactions with
 * the Model and View components, so most applications will only need to interact
 * with this class.
 */
var DOMFDGraphController = function( frameWidth, frameHeight, frameLeft, frameTop ) {
	this.initialize( frameWidth, frameHeight, frameLeft, frameTop );
};
DOMFDGraphController.prototype = {

	nodes: {},
	
	defaultEdgeWeight: 80,
	
	selectedNode: null,

	/*
	 * Setup the environment, instantiating and connecting the Timer, 
	 * Model, and View components.
	 */
	initialize: function( frameWidth, frameHeight, frameLeft, frameTop ) {
		// instantiate our 'model' and 'view'
		this['model'] = new FDGraphModel( frameWidth, frameHeight );
		this['view'] = new DOMGraphView( this['model'], frameLeft, frameTop );

		// subscribe the view to the model
		this['model'].subscribe( this['view'] );

		// create a timer
		this['timer'] = new Timer();
		this['timer'].initialize( 1 );

		// subscribe the model to the timer
		this['timer'].subscribe( this['model'] );
		
		// start the timer
		this['timer'].start();

		// attach an onresize event
	    window.onresize = new EventHandler( this, this.handleResizeEvent );

		// attach an onmousemove event
	    window.onmousemove = new EventHandler( this, this.handleMouseMoveEvent );

		// attach an onmouseup event
	    window.onmouseup = new EventHandler( this, this.handleMouseUpEvent );
 	},

	/*
	 * Add a node. In a Tree Graph, nodes should be added using 
	 * DOMTreeGraphController.addRootNode() or Node.addChild(). The parameters to each 
	 * of these methods are the same and they are broken down below:
	 * 
	 * - domElement:	A reference to an element in the DOM that should either be
	 * 					cloned, or added directly to the graph.
	 * - doClone:		A boolean indicated that specifies whether the domElement
	 * 					should be cloned or added directly.
	 * - mass:			The mass of the new node, used in force calculations.
	 * - isStatic:		A boolean that indicates whether this node can move.
	 * - startX:		The initial x-coordinate of this node's position.
	 * - startY:		The initial y-coordinate of this node's position.
	 */
	addNode: function( domElement, doClone, mass, isStatic, startX, startY ) {
		domElement.style.display='block';
		domElement.style.position='absolute';

		if ( !startX ) { startX = parseInt( domElement.style.left ) + this['view']['frameLeft']; }
		if ( !startY ) { startY = parseInt( domElement.style.top ) + this['view']['frameTop']; }

		if ( doClone ) {
			domElement = domElement.cloneNode(true);
		}		
		var modelNode = this['model'].addNode( mass, isStatic, startX, startY );
		var viewNode = this['view'].addNode( modelNode, domElement );
		var node = new DOMFDNode( this, modelNode, viewNode );

	    // add a mousedown event handler
	    viewNode.onmousedown = new EventHandler( node, node.handleMouseDownEvent );

		return node;
	},

	/*
	 * Add a node directly to the graph without specifying a parent.
	 */
	addRootNode: function( domElement, doClone, mass, isStatic, startX, startY ) {
		var node = this.addNode( domElement, doClone, mass, isStatic, startX, startY );
		this['model']['updateQueue'].push( node['modelNode'] );
		return node;
	},
	
	/*
	 * Handle a resize event.
	 */
	handleResizeEvent: function() {

	},

	/*
	 * Handle a mouse move event.
	 */
	handleMouseMoveEvent: function( e ) {
		if ( this['selectedNode'] && !this['selectedNode']['modelNode']['isStatic'] ) {

			var mouseX;
			var mouseY;

			// get the cursor position
			if (document.all) {
				mouseX = event.clientX + document.body.scrollLeft;
				mouseY = event.clientY + document.body.scrollTop;
			} else {
				mouseX = e.pageX;
				mouseY = e.pageY;
			}

			mouseX -= this['view'].frameLeft;
			mouseY -= this['view'].frameTop;

			// set the node position
			this['selectedNode']['modelNode'].position['x']=mouseX;
			this['selectedNode']['modelNode'].position['y']=mouseY;

			// if the timer is interupted, we still want the graph to move while we
			// move the selected node around
			if ( this.timer['interupt'] ) {
				this['model'].update();
			}
	    }
	},

	/*
	 * Handle a mouse up event.
	 */
	handleMouseUpEvent: function() {
		if ( this['timer']['interupt'] ) {
			this['timer'].start();
		}
		this.clearSelected();
	},

	/*
	 * Handle a pause event.
	 */
	handlePauseEvent: function() {
		this['timer'].stop();
	},

	/*
	 * Handle an unpause event.
	 */
	handleUnpauseEvent: function() {

	},
	
	/*
	 * 
	 */
	setSelected: function( node ) {
		if ( !this['timer']['interupt'] ) {
//			this['timer'].stop();
		}
		this['selectedNode'] = node;
		this['selectedNode']['modelNode'].selected = true;
	},

	/*
	 * 
	 */
	clearSelected: function() {
		this['timer'].start();
		if ( this['selectedNode'] ) {
			this['selectedNode']['modelNode'].selected = false;
		}
		this['selectedNode'] = null;
	}

}

/**
 * DOMFDNode:
 * 
 * Encapsulates the Model and View perspectives of a node.
 */
var DOMFDNode = function( graphControl, modelNode, viewNode ) {
	this['graphControl'] = graphControl;
	this['modelNode'] = modelNode;
	this['viewNode'] = viewNode;
	
	/*
	 * Adds a node to the graph as a child of an existing node. These fields will 
	 * be inherited from the parent if they are omitted:
	 * - radius
	 * - fanAngle
	 * - startX
	 * - startY
	 */
	this['addEdge'] = function( domElement, doClone, visible, weight, mass, isStatic, startX, startY ) {
		if ( !domElement ) { domElement=this['viewNode']; doClone=true; }
		if ( visible != true && visible != false ) { visible=true; }
	if ( !weight ) { weight=this['graphControl'].defaultEdgeWeight; }
		if ( !mass ) { mass = this['modelNode']['mass']; }
		if ( !isStatic ) { isStatic = false; }
		if ( !startX ) { startX = this['modelNode']['position']['x']; }
		if ( !startY ) { startY = this['modelNode']['position']['y']; }

		var node = this['graphControl'].addNode( domElement, doClone, mass, isStatic, startX, startY );
		this.graphControl.model.addEdge( this['modelNode'], node['modelNode'], weight );
		this['graphControl']['view'].addEdge( this['modelNode'], node['modelNode'], visible );
		return node;
	};

	/*
	 * Handle a mouse down event.
	 */
	this['handleMouseDownEvent'] = function() {
		this['graphControl'].setSelected( this );
	};

}
