define(function(require, exports, module) {
    var kity = require('./kity');
    var utils = require('./utils');
    var Minder = require('./minder');

    /**
     * @class MinderNode
     *
     * 表示一个脑图节点
     */
    var MinderNode = kity.createClass('MinderNode', {

        /**
         * 创建一个游离的脑图节点
         *
         * @param {String|Object} textOrData
         *     节点的初始数据或文本
         */
        constructor: function(textOrData) {

            // 指针
            this.parent = null;
            this.root = this;
            this.children = [];

            // 数据
            this.data = {
                id: utils.guid(),
                created: +new Date()
            };

            // 绘图容器
            this.initContainers();

            if (utils.isString(textOrData)) {
                this.setText(textOrData);
            } else if (utils.isObject(textOrData)) {
                utils.extend(this.data, textOrData);
            }
        },

        initContainers: function() {
            this.rc = new kity.Group().setId(utils.uuid('minder_node'));
            this.rc.minderNode = this;
        },

        /**
         * 判断节点是否根节点
         */
        isRoot: function() {
            return this.root === this;
        },

        /**
         * 判断节点是否叶子
         */
        isLeaf: function() {
            return this.children.length === 0;
        },

        /**
         * 获取节点的根节点
         */
        getRoot: function() {
            return this.root || this;
        },

        /**
         * 获得节点的父节点
         */
        getParent: function() {
            return this.parent;
        },

        getSiblings: function() {
            var children = this.parent.children;
            var siblings = [];
            var self = this;
            children.forEach(function(child) {
                if (child != self) siblings.push(child);
            });
            return siblings;
        },

        /**
         * 获得节点的深度
         */
        getLevel: function() {
            var level = 0,
                ancestor = this.parent;
            while (ancestor) {
                level++;
                ancestor = ancestor.parent;
            }
            return level;
        },

        /**
         * 获得节点的复杂度（即子树中节点的数量）
         */
        getComplex: function() {
            var complex = 0;
            this.traverse(function() {
                complex++;
            });
            return complex;
        },

        /**
         * 获得节点的类型（root|main|sub）
         */
        getType: function(type) {
            this.type = ['root', 'main', 'sub'][Math.min(this.getLevel(), 2)];
            return this.type;
        },

        /**
         * 判断当前节点是否被测试节点的祖先
         * @param  {MinderNode}  test 被测试的节点
         */
        isAncestorOf: function(test) {
            var ancestor = test.parent;
            while (ancestor) {
                if (ancestor == this) return true;
                ancestor = ancestor.parent;
            }
            return false;
        },

        getData: function(key) {
            return key ? this.data[key] : this.data;
        },

        setData: function(key, value) {
            if (typeof key == 'object') {
                var data = key;
                for (key in data) if (data.hasOwnProperty(key)) {
                    this.data[key] = data[key];
                }
            }
            else {
                this.data[key] = value;
            }
            return this;
        },

        /**
         * 设置节点的文本数据
         * @param {String} text 文本数据
         */
        setText: function(text) {
            return this.data.text = text;
        },

        /**
         * 获取节点的文本数据
         * @return {String}
         */
        getText: function() {
            return this.data.text || null;
        },

        /**
         * 先序遍历当前节点树
         * @param  {Function} fn 遍历函数
         */
        preTraverse: function(fn, excludeThis) {
            var children = this.getChildren();
            if (!excludeThis) fn(this);
            for (var i = 0; i < children.length; i++) {
                children[i].preTraverse(fn);
            }
        },

        /**
         * 后序遍历当前节点树
         * @param  {Function} fn 遍历函数
         */
        postTraverse: function(fn, excludeThis) {
            var children = this.getChildren();
            for (var i = 0; i < children.length; i++) {
                children[i].postTraverse(fn);
            }
            if (!excludeThis) fn(this);
        },

        traverse: function(fn, excludeThis) {
            return this.postTraverse(fn, excludeThis);
        },

        getChildren: function() {
            return this.children;
        },

        getIndex: function() {
            return this.parent ? this.parent.children.indexOf(this) : -1;
        },

        insertChild: function(node, index) {
            if (index === undefined) {
                index = this.children.length;
            }
            if (node.parent) {
                node.parent.removeChild(node);
            }
            node.parent = this;
            node.root = this.root;

            this.children.splice(index, 0, node);
        },

        appendChild: function(node) {
            return this.insertChild(node);
        },

        prependChild: function(node) {
            return this.insertChild(node, 0);
        },

        removeChild: function(elem) {
            var index = elem,
                removed;
            if (elem instanceof MinderNode) {
                index = this.children.indexOf(elem);
            }
            if (index >= 0) {
                removed = this.children.splice(index, 1)[0];
                removed.parent = null;
                removed.root = removed;
            }
        },

        clearChildren: function() {
            this.children = [];
        },

        getChild: function(index) {
            return this.children[index];
        },

        getRenderContainer: function() {
            return this.rc;
        },

        getCommonAncestor: function(node) {
            return MinderNode.getCommonAncestor(this, node);
        },

        contains: function(node) {
            return this == node || this.isAncestorOf(node);
        },

        clone: function() {
            var cloned = new MinderNode();

            cloned.data = utils.clone(this.data);

            this.children.forEach(function(child) {
                cloned.appendChild(child.clone());
            });

            return cloned;
        },

        compareTo: function(node) {

            if (!utils.comparePlainObject(this.data, node.data)) return false;
            if (!utils.comparePlainObject(this.temp, node.temp)) return false;
            if (this.children.length != node.children.length) return false;

            var i = 0;
            while (this.children[i]) {
                if (!this.children[i].compareTo(node.children[i])) return false;
                i++;
            }

            return true;
        },

        getMinder: function() {
            return this.getRoot().minder;
        }
    });

    MinderNode.getCommonAncestor = function(nodeA, nodeB) {
        if (nodeA instanceof Array) {
            return MinderNode.getCommonAncestor.apply(this, nodeA);
        }
        switch (arguments.length) {
            case 1:
                return nodeA.parent || nodeA;

            case 2:
                if (nodeA.isAncestorOf(nodeB)) {
                    return nodeA;
                }
                if (nodeB.isAncestorOf(nodeA)) {
                    return nodeB;
                }
                var ancestor = nodeA.parent;
                while (ancestor && !ancestor.isAncestorOf(nodeB)) {
                    ancestor = ancestor.parent;
                }
                return ancestor;

            default:
                return Array.prototype.reduce.call(arguments,
                    function(prev, current) {
                        return MinderNode.getCommonAncestor(prev, current);
                    },
                    nodeA
                );
        }
    };

    kity.extendClass(Minder, {

        getRoot: function() {
            return this._root;
        },

        setRoot: function(root) {
            this._root = root;
            root.minder = this;
        },

        getAllNode: function() {
            var nodes = [];
            this.getRoot().traverse(function(node) {
                nodes.push(node);
            });
            return nodes;
        },

        getNodeById: function(id) {
            return this.getNodesById([id])[0];
        },

        getNodesById: function(ids) {
            var nodes = this.getAllNode();
            var result = [];
            nodes.forEach(function(node) {
                if (ids.indexOf(node.getData('id')) != -1) {
                    result.push(node);
                }
            });
            return result;
        },

        createNode: function(textOrData, parent, index) {
            var node = new MinderNode(textOrData);
            this.fire('nodecreate', {
                node: node,
                parent: parent,
                index: index
            });
            this.appendNode(node, parent, index);
            return node;
        },

        appendNode: function(node, parent, index) {
            if (parent) parent.insertChild(node, index);
            this.attachNode(node);
            return this;
        },

        removeNode: function(node) {
            if (node.parent) {
                node.parent.removeChild(node);
                this.detachNode(node);
                this.fire('noderemove', {
                    node: node
                });
            }
        },

        attachNode: function(node) {
            var rc = this.getRenderContainer();
            node.traverse(function(current) {
                current.attached = true;
                rc.addShape(current.getRenderContainer());
            });
            rc.addShape(node.getRenderContainer());
            this.fire('nodeattach', {
                node: node
            });
        },

        detachNode: function(node) {
            var rc = this.getRenderContainer();
            node.traverse(function(current) {
                current.attached = false;
                rc.removeShape(current.getRenderContainer());
            });
            this.fire('nodedetach', {
                node: node
            });
        },

        getMinderTitle: function() {
            return this.getRoot().getText();
        }

    });

    module.exports = MinderNode;
});