/*
 * Created by MaiXinRong on 2017/2/9.
 * 项目管理 专用树结构
 */

(function($) {
    var _setting = {
 		tree: {
 			id: 'ID',
 			pid: 'ParentID',
 			nid: 'NextSiblingID',
 			btnColumn: 1,
			nullId: -1
 		},
 		columns: [
 		{
 			head: '工程列表',
 			data: 'name',
 			static: false,
 			width: '100%',
            event: {
                getText: null,
                getIcon: null
            }
 		}
 		],
 		dataTemp: {
			id: -1,
			parentId: -1,
			nextId: -1,
			isTemp: true
 		},
        viewEvent: {
            beforeSelect: null,
            onSelectNode: null
        }
 	};

 	var Node = (function () {
 		function Node(tree, data) {
 			this.parent = null;
 			this.nextSibling = null;
 			this.children = [];

 			this.tree = tree;
 			this.data = data;
            this.setting = tree.setting;

 			this.expanded = true;

 			this.row = null;
 			this.expandBtn = null;
 		}

 		Node.prototype.firstChild = function() {
            return (this.children.length === 0) ? null : this.children[0];
 		};

 		Node.prototype.lastChild = function () {
 			return (this.children.length === 0) ? null : this.children[this.children.length - 1];
 		}

        Node.prototype.deepestRow = function () {
            return (this.children.length === 0) ? this.row : this.lastChild().deepestRow();
        }

 		Node.prototype.addChild = function (child, childNext) {
 			if (childNext){
 				this.children.push(child);
 			} else {
 				if (this.childIndex(childNext) > -1){
 					this.children.splice(this.childIndex(childNext) - 1, 0, child);
 				} else {
 					this.children.push(child);
 				}
 			}
 			return child;
 		};

 		Node.prototype.childIndex = function (child) {
 			return this.children.indexOf(child);
 		};

 		Node.prototype.depth = function () {
			return this.parent ? this.parent.depth() + 1 : 0;
 		};

 		Node.prototype.domId = function () {
            return this.data ? this.tree.domId + '_' + this.data[this.setting.tree.id] : '';
 		};

 		Node.prototype.expand = function (bool) {
 			this.expanded = bool;
 			_view._refreshTreeBtn(this);
 			if (this.expanded) {
 				_view._showNodes(this.children);
 			} else {
 				_view._hideNodes(this.children);
 			}
 		};

		Node.prototype.preSibling = function () {
			var iIndex = this.parent.childIndex(this);
			if (iIndex === -1){
				return null;
			} else {
				return iIndex > 0 ? this.parent.children[iIndex-1] : null;
			}
		};

 		Node.prototype.setParent = function (parent) {
 			if (parent && this.parent !== parent) {
 				this.parent = parent;
 				this.data[this.setting.tree.pid] = this.pid();
 			}
 		};

 		Node.prototype.setNextSibling = function (nextSibling) {
 			if (this.nextSibling !== nextSibling) {
 				this.nextSibling = nextSibling;
                this.data[this.setting.tree.nid] = this.nid();
 			}
 		}

 		Node.prototype.id = function () {
			return this.data ? this.data[this.setting.tree.id] : -1;
 		};

 		Node.prototype.pid = function () {
			return this.parent ? this.parent.id() : -1;
 		};

 		Node.prototype.nid = function () {
			return this.nextSibling ? this.nextSibling.id() : -1;
 		};

		Node.prototype.propertyJoin = function (dataName) {
			return this.parent ? this.parent.propertyJoin(dataName) + ';' + this.data[dataName] : this.data[dataName];
		}

 		return Node;
 	})();

 	var Tree = (function () {
 		function Tree(obj, setting) {
 			this._root = new Node(this);

 			this.treeObj = obj;
 			this.domId = obj.attr('id');
 			this.treeHeadObj = _view._makeTableHead(this.treeObj, setting);
 			this.treeBodyObj = _view._makeTableBody(this.treeObj);

            this.setting = setting;

 			var _maxNodeId = 0;
 			this.newNodeId = function (id) {
 				if (arguments.length > 0){
 					_maxNodeId = (id > _maxNodeId) ? id : _maxNodeId;
 				} else {
 					_maxNodeId += 1;
 					return _maxNodeId;
 				}
 			};
			this.maxNodeId = function (id){
				if (arguments.length > 0) {
					_maxNodeId = Math.max(id, _maxNodeId);
				} else {
					return _maxNodeId;
				}
			}
 		};

 		Tree.prototype.firstNode = function (){
 			return this._root.firstChild();
 		};

 		Tree.prototype.traverseDF = function(callback){
 			var recurse = function (node) {
 				var i;
 				if (node !== this._root) {
 					callback(node);
 				}
 				for (i = 0; i < node.children.length; i++){
 					recurse(node.children[i]);
 				}
 			}
 			recurse(this._root);
 		};

 		Tree.prototype.findNode = function (id){
 			var treenode = null,
 				callback = function (node) {
 					if (node.data && node.data[node.setting.tree.id] === id){
 						treenode = node;
 					}
 				};
 			this.traverseDF.call(this, callback);
 			return treenode;
 		};

		Tree.prototype.findNodeByNid = function (nid) {
			let treenode = null,
				callback = function (node) {
					if(node.data && node.data[node.setting.tree.nid] === nid){
						treenode = node;
					}
				};
			this.traverseDF.call(this, callback);
			return treenode;
		}

 		Tree.prototype.findNodeByDomId = function (domId) {
 			var treenode = null,
 				callback = function (node) {
 					if (node.domId === domId) {
 						treenode = node;
 					}
 				};
 			this.traverseDF.call(this, callback);
 			return treenode;
 		};

 		Tree.prototype.removeNode = function (node) {
 			var iIndex;
 			if (node) {
 				iIndex = node.parent.childIndex(node);
 				if (iIndex > 0) {
 					node.parent.children[iIndex - 1].setNextSibling(node.nextSibling);
 				}
 				node.parent.children.splice(iIndex, 1);
 			}
 			_view._removeNodesRowDom([node]);
 		};

 		/*Tree.prototype.loadData = function (arrData) {
 			var i, that = this;
			var createTempNode = function (id, setting) {
				var tempData = {};
				tempData[setting.tree.id] = id;
				return new Node(that, tempData);
			};
			var loadNode = function (data, setting) {//mark
				var	node = that.findNode(data[setting.tree.id]) || null,
 					parent = that.findNode(data[setting.tree.pid]) || null,
 					next = that.findNode(data[setting.tree.nid]) || null,
 					tempData;

 				if (!node) {
 					node = new Node(that, data);
 				}
 				node.data = data;
				that.maxNodeId(node.id());

				if (!parent){
					if (data[setting.tree.pid] === setting.tree.nullId) {
 						parent = that._root;
 					} else {
 						parent = createTempNode(data[setting.tree.pid], setting);
 						parent.parent = that._root;
 						that._root.children.push(parent);
 					}
 				}
 				if (node.parent && node.parent !== parent) {
 					node.parent.children.splice(node.parent.childIndex(node), 1);
 				}
 				node.parent = parent;

 				if (!next && data[setting.tree.nid] !== setting.tree.nullId) {
 					next = createTempNode(data[setting.tree.nid], setting);
 					next.parent = parent;
					parent.children.push(next);
 				}
 				node.nextSibling = next;

 				if (parent.childIndex(node) === -1){
 					if (!next){
 						parent.children.push(node);
 					} else if (parent.childIndex(next) === -1){
 						parent.children.push(node);
 						parent.children.push(next);
 					} else {
 						parent.children.splice(parent.childIndex(next), 0, node);
 					}
 				} else if (node.nextSibling && parent.childIndex(node) !== parent.childIndex(node.nextSibling) - 1) {
					parent.children.splice(parent.childIndex(next), 1);
					parent.children.splice(parent.childIndex(node) + 1, 0, next);
				};
				let pre = that.findNodeByNid(node.data.ID) || null;
				if(pre && parent.childIndex(pre) !== parent.childIndex(node) - 1){
					parent.children.splice(parent.childIndex(pre), 1);
					parent.children.splice(parent.childIndex(node), 0, pre);
				}
			};

 			for (i = 0; i < arrData.length; i++){
 				loadNode(arrData[i], this.setting);
 			}
 		};*/

		Tree.prototype.loadData = function (arrData) {
			let i, that = this;
			let nodesIndex = {};
			function getPreNode(id){
				for(let index in nodesIndex){
					let node = nodesIndex[index];
					if(node['data'][node.setting.tree.nid] === id){
						return node;
					}
				}
				return null;
			}
			function loadNode (data, setting) {//mark
				var	node = nodesIndex[data[setting.tree.id]] || null,
					parent = nodesIndex[data[setting.tree.pid]] || that._root,
					next = nodesIndex[data[setting.tree.nid]] || null,
					pre = getPreNode(data[setting.tree.id]) || null,
					tempData;

				if (!node) {
					node = new Node(that, data);
				}
				that.maxNodeId(node.id());
				if(parent.childIndex(node) === -1){
					if(pre && parent.childIndex(pre) !== -1){
						parent.children.splice(parent.childIndex(pre) + 1, 0, node);
					}
					else if(next && parent.childIndex(next) !== -1){
						parent.children.splice(parent.childIndex(next), 0, node);
					}
					else {
						parent.children.push(node);
					}
				}
				if(pre && parent.childIndex(pre) === -1){
					parent.children.splice(parent.childIndex(node), 0, pre);
				}
				if(next && parent.childIndex(next) === -1){
					parent.children.splice(parent.childIndex(node) + 1, 0, next);
				}
				if(pre && parent.childIndex(pre) !== parent.childIndex(node) - 1){
					parent.children.splice(parent.childIndex(pre), 1);
					parent.children.splice(parent.childIndex(node), 0, pre);
				}
				if(next && parent.childIndex(next) !== parent.childIndex(node) + 1){
					parent.children.splice(parent.childIndex(next), 1);
					parent.children.splice(parent.childIndex(node) + 1, 0, next);
				}
				node.parent = parent;
				node.nextSibling = next;
			}
			//建立索引
			for(let data of arrData){
				nodesIndex[data.ID] = new Node(that, data);
			}
			for (i = 0; i < arrData.length; i++){
				loadNode(arrData[i], this.setting);
			}
		};

    	Tree.prototype.refreshNodesDom = function (nodes, recurse){
			var that = this;
			nodes.forEach(function (node) {
				if (node.row) {
					$('td', node.row).remove();
					_view._makeTableRowCells(node.row, node);
				} else if (node !== that._root) {
					_view._makeRowDom(that.treeBodyObj, node);
				}
				if (recurse) {
					that.refreshNodesDom(node.children, recurse);
				}
			})
    	};

 		Tree.prototype.refreshTreeDom = function () {
			this.refreshNodesDom(this._root.children, true);
 		};

		Tree.prototype.addNodeData = function (data, parent, nextSibling) {
			var node = null;
			var pNode = parent ? parent : this._root;
			if (!nextSibling || (nextSibling.parent === pNode && pNode.childIndex(nextSibling) > -1)) {
				node = new Node(this, data);
                this.maxNodeId(data[this.setting.tree.id]);
				node.row = _view._makeRowDom(this.treeBodyObj, node);
				this.move(node, pNode, nextSibling);
			}
			return node;
		}

 		Tree.prototype.move = function(node, parent, nextSibling) {
 			var iIndex = -1, pre;
 			if (parent && (!nextSibling || (nextSibling.parent === parent && parent.childIndex(nextSibling) > -1))) {
 				if (node) {
 					if (node.parent) {
 						iIndex = node.parent.childIndex(node);
 						if (iIndex > 0) {
 							node.parent.children[iIndex - 1].setNextSibling(node.nextSibling);
 						}
 						node.parent.children.splice(iIndex, 1);
						this.refreshNodesDom([node.parent], false);
 					}

 					if (nextSibling) {
 						iIndex = parent.childIndex(nextSibling);
 						if (iIndex > 0){
 							pre = parent.children[iIndex - 1];
 							pre.setNextSibling(node);
 							//parent.children.splice(iIndex - 1, 0, node);
 							parent.children.splice(iIndex, 0, node);
 						} else {
 							parent.children.splice(0, 0, node);
 						}
 					} else {
 						if (parent.children.length > 0){
 							pre = parent.lastChild();
 							pre.setNextSibling(node);
 						}
 						parent.children.push(node);
 					}
 					node.setParent(parent);
 					node.setNextSibling(nextSibling);
 					if (node.row) {
 						if (pre) {
 							_view._moveRowDom(node, pre.deepestRow());
 						} else if (parent.id() !== this.setting.tree.nullId) {
                            _view._moveRowDom(node, parent.row);
                        } else if (nextSibling) {
 							_view._moveRowDomBefore(node, nextSibling.row);
 						}
 					}
                    if (node.parent.row) {
                        this.refreshNodesDom([node.parent], false);
                    }
 				}
 			} else {
 				this.e.throw('Error: information of moving node has mistake.');
 			}
 		};

 		Tree.prototype.selected = (function () {
 			var selectNode = null;

 			var select = function (node) {
 				if (arguments.length === 0) {
 					return selectNode;
 				} else {
 					if (node) {
 						if (selectNode && selectNode.row) {
 							selectNode.row.removeClass('table-active');
 						}
 						selectNode = node;
 						node.row.addClass('table-active');
 					}
 				}
 			}
 			return select;
 		})();

 		return Tree;
 	})();

 	_data = {
		clone: function (obj) {
			if (obj === null) return null;

			var o = _data.isArray(obj) ? [] : {};
			for (var i in obj) {
				o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === "object" ? _data.clone(obj[i]) : obj[i]);
			}
			return o;
		},
		isArray: function (arr) {
			return Object.prototype.toString.apply(arr) === "[object Array]";
		}
 	},

 	_event = {
 		_selectProj: function (node) {
            if (node.setting.viewEvent.beforeSelect) {
                node.setting.viewEvent.beforeSelect(node.tree.selected());
            }
 			node.tree.selected(node);
            if (node.setting.viewEvent.onSelectNode) {
                node.setting.viewEvent.onSelectNode(node);
            }
 		}
 	}

 	_view = {
 		_makeTableHeadCell: function (obj, col){
 			var th;
 			th = $('<th>');
 			th.attr('width', col.width);
 			th.text(col.head);
 			obj.append(th);
 		},
 		_makeTableHead: function (obj, setting){
 			var thead, tr, i;
 			thead = $('<thead>');
 			tr = $('<tr>');
 			for (i = 0; i < setting.columns.length; i++){
 				_view._makeTableHeadCell(tr, setting.columns[i]);
 			}
 			thead.append(tr);
 			obj.append(thead);
 			return thead;
 		},
 		_makeTableBody: function (obj){
 			var tbody;
 			tbody = $('<tbody>');
 			obj.append(tbody);
 			return tbody;
 		},
 		_makeTableRowCell: function (obj, node, columns, index) {
 			var html = [], td, i, url;

 			html.push('<td ');
 			if (index === node.setting.tree.btnColumn){
 				html.push('class=', 'in-', node.depth().toString(), '>');
                if (node.children.length !== 0) {
                    html.push('<a href="#" class="tree-open" title="收起"><i class="fa fa-minus-square-o mr-1"></i></a>');
                } else {
                    html.push('<a href="#" class="tree-open" title="收起"><i></i></a>');
                }
 			} else {
 				html.push('>');
 			}

            if (columns.event.getIcon) {
                columns.event.getIcon(html, node);
            }

 			if (columns.name !== '') {
                if (columns.event.getText) {
                    columns.event.getText(html, node, node.data[columns.data]);
                } else {
                    html.push(node.data[columns.name]);
                }
 			}
 			html.push('</td>');

 			td = $(html.join(''));

 			if (index === node.setting.tree.btnColumn) {
 				node.expandBtn = $('.tree-open>.fa', td);
 				node.expandBtn.bind('click', {node: node}, function(e){
 					e.data.node.expand(!e.data.node.expanded);
 				});

 				node.icon = $('.tree-icon', td);
 			}

			if (columns.event.tdBindEvent) {
				columns.event.tdBindEvent(td, node);
			}

 			obj.append(td);
 			return td;
 		},
 		_makeTableRowCells: function (obj, node) {
 			for (i = 0; i < node.setting.columns.length; i++){
 				_view._makeTableRowCell(obj, node, node.setting.columns[i], i);
 			}
 		},
 		_makeRowDom: function (obj, node) {
 			var tr, i;
 			tr = $('<tr>');
 			obj.append(tr);
 			tr.attr('id', node.domId());
 			node.row = tr;
 			_view._makeTableRowCells(tr, node);
 			tr.click(function(){
                _event._selectProj(node);
 			});

			/*$(tr).draggable({
        		helper: "clone",
        		opacity: .75,
        		refreshPositions: true, // Performance?
        		revert: "invalid",
        		revertDuration: 300,
        		scroll: true
      		});

 			tr.droppable({
 				drop: function (e, ui) {
 					var target = node.tree.findNodeByDomId($(this).attr('id')),
 						drag = node.tree.findNodeByDomId($(ui).attr('id'));
 					tree.move(drag, target);
 				}
 			});	*/
 			return tr;
 		},
        _moveRowDomBefore: function (node, destination) {
            var moveNodesRowDomBefore = function (nodes) {
                nodes.forEach(function (node) {
                    node.row.insertBefore(destination);
                    moveNodesRowDomBefore(node.children);
                });
            }
            moveNodesRowDomBefore([node]);
			_view._refreshNodesLevelCss([node]);
        },
 		_moveRowDom: function (node, destination) {
 			var pre = destination,
 				moveNodeRowDom = function (node) {
 					node.row.insertAfter(pre);
 					pre = node.row;
 					moveChildrenRowDom(node);
 				},
 				moveChildrenRowDom = function(node){
 					var i;
 					for (i = 0; i < node.children.length; i++){
 						moveNodeRowDom(node.children[i]);
 					}
 				};
 			moveNodeRowDom(node);
			_view._refreshNodesLevelCss([node]);
 		},
 		_refreshTreeBtn: function (node) {
 			var i;
 			if (node.children.length === 0) {
 				node.expandBtn.hide();
 			} else {
 				node.expandBtn.show();
 				if (node.expanded) {
 					node.expandBtn.removeClass('fa-plus-square-o');
 					node.expandBtn.addClass('fa-minus-square-o');
 				} else {
 					node.expandBtn.removeClass('fa-minus-square-o');
 					node.expandBtn.addClass('fa-plus-square-o');
 				}
 			}
 		},
		_refreshNodesLevelCss: function (nodes) {
			nodes.forEach(function(node){
				var td = $('td:eq(' + node.setting.tree.btnColumn.toString() + ')', node.row);
				td.attr('class', 'in-'+ node.depth().toString());
				_view._refreshNodesLevelCss(node.children);
			})
		},
 		_hideNodes: function (nodes) {
 			var i, node;
 			for (i = 0; i < nodes.length; i++){
 				node = nodes[i];
 				node.row.hide();
 				_view._hideNodes(node.children);
 			}
 		},
 		_showNodes: function (nodes) {
 			var i, node;
 			for (i = 0; i < nodes.length; i++){
 				node = nodes[i];
 				node.row.show();
 				if (node.expanded) {
 					_view._showNodes(node.children);
 				}
 			}
 		},
 		_removeNodesRowDom: function (nodes) {
 			var i, node;
 			for (i = 0; i < nodes.length; i++){
 				node = nodes[i];
 				_view._removeNodesRowDom(node.children);
 				if (node.row) {
 					node.row.remove();
 				}
 			}
 		}
 	};

 	$.fn.treeTable = {
 		init: function(obj, setting, arrData){
            var _treeSetting, _tree;
            _treeSetting = _data.clone(_setting);
            $.extend(true, _treeSetting, setting);
 			var _tree = new Tree(obj, _treeSetting);
 			_tree.loadData(arrData);
 			_tree.refreshTreeDom();
 			return _tree;
 		}
 	}
 })(jQuery);
