// ノードのMVCを管理 //
// treeNodeから適切なMVCの組合せを返す抽象クラス。
function AbstractNodeMVCFactory(treeNode,tree) {
	if(!(this instanceof AbstractNodeMVCFactory))throw Error("Inheritance error");
	if(!(tree instanceof Tree))throw Error("arguments error");
	if(!tree.isTreeNode(treeNode))throw Error("arguments error");
	var This = this;
	this.overrideMVC = function(model,view,controler) {
		if(!(model instanceof AbstractNodeModel))throw Error("arguments error");
		if(!(view instanceof AbstractNodeView))throw Error("arguments error");
		if(!(controler instanceof AbstractNodeControler))throw Error("arguments error");
		This.getModel = function() { return model; };
		This.getView = function() { return view; };
		This.getControler = function() { return controler; };
		This.overrideMVC = undefined;
	};
	this.getTree = function() { return tree; };
	// ノードの初期設定(MVCを結合しコントローラの検出を有効にする。)
	this.setup = function() {};
}
// ノードの動作を決定するモデルを表す抽象クラス。
function AbstractNodeModel(treeNode,tree) {
	if(!(this instanceof AbstractNodeModel))throw Error("Inheritance error");
	if(!(tree instanceof Tree))throw Error("arguments error");
	if(!tree.isTreeNode(treeNode))throw Error("arguments error");
}
AbstractNodeModel.prototype.open = notOverrided;
AbstractNodeModel.prototype.close = notOverrided;
// ノードの開閉を表示するビューを表す抽象クラス。
function AbstractNodeView(treeNode,tree) {
	if(!(this instanceof AbstractNodeView))throw Error("Inheritance error");
	if(!(tree instanceof Tree))throw Error("arguments error");
	if(!tree.isTreeNode(treeNode))throw Error("arguments error");
}
AbstractNodeView.prototype.open = notOverrided;
AbstractNodeView.prototype.close = notOverrided;
// ノードへのユーザの入力を検出するコントローラ。
function AbstractNodeControler(treeNode,tree) {
	if(!(this instanceof AbstractNodeControler))throw Error("Inheritance error");
	if(!(tree instanceof Tree))throw Error("arguments error");
	if(!tree.isTreeNode(treeNode))throw Error("arguments error");
}
AbstractNodeView.prototype.bind = notOverrided;
AbstractNodeView.prototype.unbind = notOverrided;

// 木構造の管理 //
// HTMLの木構造上にツリーウィジェットの木構造を定義します。
function Tree(root,isTreeNode,NodeMVCFactory) {
	if(!(this instanceof Tree))throw Error("Inheritance error");
	if(!root)throw Error("arguments error");
	if(typeof isTreeNode != "function")throw Error("arguments error");
	if(isTreeNode.length != 1)throw Error("arguments error");
	this.root = root;
	this.isTreeNode = isTreeNode;
	this.NodeMVCFactory = NodeMVCFactory;
}
// treeNodeから適切なMVCの組合せを返すクラスを設定する。
Tree.prototype.NodeMVCFactory = notOverrided;
// elementが木の要素(ノードとルート)かどうか判断する
Tree.prototype.isTreeElement = function(element) {
	return element == this.root || this.isTreeNode(element);
};
// 木の要素treeElementからその子要素を配列で取得する。
Tree.prototype.getChildNodes = function(treeElement) {
	if(!this.isTreeElement(treeElement))throw Error("arguments error");
	var This = this;
	return function(element) {
		var childNodes = new Array;
		for(var i=0;i<element.childNodes.length;i++)
		if(This.isTreeNode(element.childNodes[i]))
			childNodes.push(element.childNodes[i]);
		else
			childNodes = childNodes.concat(arguments.callee(element.childNodes[i]));
		return childNodes;
	}(treeElement);
};
// 木の要素treeElementからその親要素を取得する。
Tree.prototype.getParentNode = function(treeElement) {
	if(!this.isTreeElement(treeElement))throw Error("arguments error");
	for(var i=treeElement.parentNode;i!=null;i=i.parentNode)
	if(this.isTreeElement(i))
		return i;
	return null;
};
// 木の要素treeElementからそれを構成するHTML要素を取得する。
Tree.prototype.getMyElements = function(treeElement) {
	if(!this.isTreeElement(treeElement))throw Error("arguments error");
	var This = this;
	return function(element) {
		var myElements = new Array;
		for(var i=0;i<element.childNodes.length;i++)
		if(!This.isTreeNode(element.childNodes[i])) {
			myElements.push(element.childNodes[i]);
			myElements = myElements.concat(arguments.callee(element.childNodes[i]));
		}
		return myElements;
	}(treeElement);
};
// 木の要素treeElementからその兄弟要素を配列で取得する。
Tree.prototype.getSiblingNodes = function(treeElement) {
	if(!this.isTreeElement(treeElement))throw Error("arguments error");
	return this.getParentNode(treeElement)!=null
		? this.getChildNodes(this.getParentNode(treeElement)):[];
};
// ツリーを設定します。
Tree.prototype.setupTree = function() {
	var topLevelNodes = this.isTreeNode(this.root)?
		[this.root]:this.getChildNodes(this.root);
	for(var i=0;i<topLevelNodes.length;i++)
		(new this.NodeMVCFactory(topLevelNodes[i],this)).setup();
};
