/**
 * Root folder.
 * @extends {dox.Folder}
 */
dox.Tree = dox.Folder.extend ({

	/**
	 * Current search query.
	 * @type {String}
	 */
	search : "",

	/**
	 * Focused node.
	 * @type {dox.Node}
	 */
	focusnode : null,

	/**
	 * Hello.
	 */
	onconstruct : function () {
		this._super.onconstruct ();
		this.addObserver ( this );
	},

	/**
	 * Set default focus while searching. 
	 * @param {Array<edb.Change>} changes
	 */
	onchange : function ( changes ) {
		changes.forEach ( function ( c ) {
			if ( c.name === "search" ) {
				var value = c.newValue;
				var query = value.trim ();
				if ( query.length !== value.length ) {
					this.search = query;
				} else {
					this._focusdefault ( query );
				}
			}
		}, this );
	},

	/**
	 * Focus node to match current document.
	 * @param {String} src
	 */
	onload : function ( src ) {
		var node = this._getnode ( src );
		if ( node ) {
			this._select ( node );
			this._focus ( node );
		}
	},

	/**
	 * On navigation key pressed, move focus to next node. 
	 * @param {String} key
	 */
	movefocus : function ( key ) {
		var next = this._nextfocus ( key, this.focusnode );
		if ( next ) {
			this._focus ( next ); 
		}
	},


	// Private ..........................................................
	
	/**
	 * Set focused node. Unfocus old node.
	 * @param {dox.Node} newnode
	 */
	_focus : function ( newnode ) {
		var oldnode;
		if (( oldnode = this.focusnode )) {
			oldnode.focused = false;
		}
		if (( this.focusnode = newnode || null )) {
			newnode.focused = true;
			while (( newnode = newnode.$parent )) {
				newnode.open = true;
			}
		}
	},

	/**
	 * Set selected node. Unselect old node.
	 * @param {dox.Node} newnode
	 */
	_select : function ( newnode ) {
		var oldnode;
		if (( oldnode = this.selectednode )) {
			oldnode.selected = false;
		}
		this.selectednode = newnode;
		newnode.selected = true;
	},

	/**
	 * @param {string} query
	 */
	_focusdefault : function ( query ) {
		var files = this._matchingfiles ( query );
		if ( files.indexOf ( this.focusnode ) === -1 ) {
			this._focus ( files [ 0 ]);
		}
	},

	/**
	 * Find next node. Somewhat recursively if there's a search going on.
	 * @param {String} key
	 * @param {dox.Node} node
	 * @returns {dox.Node}
	 */
	_nextfocus : function ( key, node ) {
		var next, nodes, root = this, q = this.search;
		function isopen ( node ) {
			return node.nodes && ( node.open || ( q && node.matches ( q )));
		}
		function hasnodes ( node ) {
			return node.nodes && node.nodes.length;
		}
		function hasparent ( node ) {
			return node.$parent && node.$parent !== root;
		}
		switch ( key ) {
			case "Up" :
				if (( next = node.prev ())) {
					while ( isopen ( next ) && hasnodes ( next )) {
						nodes = next.nodes;
						next = nodes [ nodes.length - 1 ];
					}
				} else if ( hasparent ( node )) {
					next = node.$parent;
				}
				break;
			case "Down" :
				if ( isopen ( node )) {
					next = node.nodes [ 0 ];
				} else {
					next = node.next ();
					while ( !next && hasparent ( node )) {
						next = node.$parent.next ();
						node = node.$parent;
					}
				}
				break;
			case "Left" :
				if ( node.nodes && isopen ( node )) { //node.open
					node.open = false;
				}
				break;
			case "Right" :
				if ( node.nodes && !isopen( node )) { // node.open
					node.open = true;
				}
				break;
		}
		if ( next && q ) {
			if ( !next.matches ( q )) {
				next = this._nextfocus ( key, next );
			}
		}
		return next;
	},

	/**
	 * Get node for src.
	 * @todo Probably index this once
	 * @param {String} src
	 * @returns {dox.Node}
	 */
	_getnode : function ( src ) {
		var hit = null;
		( function search ( folder ) {
			folder.nodes.every ( function ( node ) {
				if ( node.src === src ) {
					hit = node;
				} else if ( node.nodes ) {
					search ( node );
				}
				return !hit;
			});
		}( this ));
		return hit;
	},

	_matchingfiles : function ( query ) {
		var files = [];
		( function index ( folder ) {
			folder.nodes.forEach ( function ( node ) {
				if ( node.nodes ) {
					index ( node );
				} else if ( node.matches ( query )) {
					files.push ( node );
				}
			});
		}( this ));
		return files;
	}

});
