var TreeNode = function(tnode, parent)
{
    this.tnode = tnode;            // tnode div
    this.tnode_label = null;       // tnode_label div
    this.tnode_children = null;    // tnode_children div
    this.label_but = null;
    this.parent = parent;          // Parent TreeNode object
    this.root = !parent ? this : parent.root;
    this.children = [];            // Array of TreeNode objects
    this.callback_ok = null;
    this.callback_cancel = null;
    this.nodetoadd = null;
    this.mode = 0;
    this.assoc_only = false;
    this.init();
}

TreeNode.prototype = {
    init:function()
    {
        this.tnode.get(0).treenode = this;

        // IF NOT ROOT
        if(!this.isRoot())
        {
            // FIND DIV CLASS = tnode_label
            this.tnode_label = this.tnode.children('.tnode_label').eq(0);

            // REPLACE NODE LABEL CONTENTS WITH TABLE
            if(this.hasLabel()) {
                this.tnode_label.html(
                    '<table class="tn_lbl"><tr class="tn_lbl"><td><img class="tn_lbl" src="/assets/trans.gif" border="0" width="11" height="11"></td><td class="tn_lbl">' +
                    this.tnode_label.html() +
                    '</td></tr></table>'
                    );
                this.tnode_label.find('td.tn_lbl:first').get(0).treenode = this;
            }
        }

        // FIND DIV CLASS = tnode_children
        this.tnode_children = this.tnode.children('.tnode_children').eq(0);

        // IF CHILDREN DIV EXISTS
        if(this.tnode_children.length) {
            // IF CHILDREN DIV HAS tnode DESCENDENTS
            var tnodes = this.tnode_children.children('.tnode');
            for(var i=0;i<tnodes.length;i++) {
                this.children[this.children.length] = new TreeNode(tnodes.eq(i), this);
            }
        }

        // MARK UP LABEL BUTTON
        if(this.hasLabel()) {
            this.label_but = this.tnode_label.find('img.tn_lbl:first');
            this.label_but.get(0).treenode = this;
            if(this.isActive()) {
                this.butStyle('active');
            } else {
                if(this.hasChildren()) {
                    if(this.isCollapsed()) {
                        this.butStyle('plus');
                        this.label_but.toggle(this.expand, this.collapse);
                    } else {
                        this.butStyle('minus');
                        this.label_but.toggle(this.collapse, this.expand);
                    }
                } else {
                    this.butStyle('empty');
                }
            }
        }
    },
    isRoot:function()
    {
        return this.parent==undefined || this.parent===null;
    },
    isActive:function()
    {
        return this.tnode_label.hasClass('is_active');
    },
    isCollapsed:function()
    {
        return this.hasChildren() && this.tnode_children.hasClass('collapsed');
    },
    isExpanded:function()
    {
        return this.hasChildren() && this.tnode_children.hasClass('expanded');
    },
    hasChildren:function()
    {
        return this.children.length>0;
    },
    hasLabel:function()
    {
        return this.tnode_label && this.tnode_label.length>0;
    },
    butStyle:function(which)
    {
        if(which==undefined) {
            if(this.label_but.hasClass('plus')) {
                return 'plus';
            }
            if(this.label_but.hasClass('minus')) {
                return 'minus';
            }
            if(this.label_but.hasClass('empty')) {
                return 'empty';
            }
            return null;
        }
        if(this.label_but) {
            this.label_but.removeClass("plus minus empty");
            this.label_but.addClass(which);
        }
    },
    expand:function(e)
    {
        if(this.treenode) {
            return arguments.callee.call(this.treenode, e);
        }
        if(this.hasChildren()) {
            this.tnode_children.removeClass('collapsed');
            this.tnode_children.addClass('expanded');
            this.butStyle('minus');
        }
    },
    collapse:function(e)
    {
        if(this.treenode) {
            return arguments.callee.call(this.treenode, e);
        }
        if(this.hasChildren()) {
            this.tnode_children.removeClass('expanded');
            this.tnode_children.addClass('collapsed');
            this.butStyle('plus');
        }
    },
    expandAll:function()
    {
        for(var i in this.children) {
            this.children[i].expand();
        }
    },
    collapseAll:function()
    {
        for(var i in this.children) {
            this.children[i].collapse();
        }
    },
    findById:function(id)
    {
        var tnode_div = $('#'+id).get(0);
        return tnode_div.treenode;
    },
    getParentId:function()
    {
        if(this.isRoot()) {
            alert("Root node!");
            return;
        }
        var aa = this.parent.isRoot();
        var bb = this.parent.tnode.attr('id');
        var cc = bb-0;
        return this.parent.isRoot() ? 0 : (this.parent.tnode.attr('id')-0);
    },
    getPosition:function()
    {
        if(this.isRoot()) {
            alert("Root node!");
            return;
        }
        for(var i in this.parent.children) {
            if(this.parent.children[i]==this) {
                return i-0;
            }
        }
        alert("Could not find self!");
    },
    /**
     *  addChild(): Adds new TreeNode after last child.
     *  addChild(position): Adds new TreeNode at position, pushing syblings downwards.
     *  addChild(null, treenode): Adds existing treenode after last child.
     *  addChild(position, treenode): Adds existing treenode at position, pushing syblings downwards.
     *
     *  position: integer | 'first' | 'last'
     *
     *  Returns the TreeNode which was added to children.
     */
    addChild:function(position, treenode)
    {
        if(!treenode) {
            treenode = TreeNode.createNode(this);
        } else {
            treenode.parent = this;
        }
        if(position==undefined) {
            position = 'last';
        }
        if(position=='first') {
            position = 0;
        }
        if(position=='last') {
            position = this.children.length;
        }
        if(position<0) {
            position = 0;
        }
        if(position>this.children.length) {
            position = this.children.length;
        }
        this.insertChild(position, treenode);
        // IF FIRST CHILD, EXPAND PREVIOUSLY HIDDEN CHILDREN DIV
        if(this.children.length==1) {
            this.expand();
        }
        return treenode;
    },
    insertChild:function(position, treenode)
    {
        this.attachChild(position, treenode);
        this.refreshChildren();
    },
    attachChild:function(position, treenode)
    {
        if(!this.hasChildren()) {
            this.tnode_children.append(treenode.tnode);
        } else {
            if(position==0) {
                this.tnode_children.children('.tnode').eq(0).before(treenode.tnode);
            } else {
                this.tnode_children.children('.tnode').eq(position-1).after(treenode.tnode);
            }
        }
    },
    removeChild:function(child)
    {
        var position = null;
        if(child.tnode) {
            for(var i in this.children) {
                if(this.children[i]==child) {
                    position = i-0;
                    break;
                }
            }
        } else {
            position = child-0;
        }
        this.detachChild(position);
        this.refreshChildren();
    },
    detachChild:function(position)
    {
        var aa = this.tnode_children;
        var bb = this.tnode_children.children('.tnode');
        var cc = this.tnode_children.children('.tnode').eq(position);
        this.tnode_children.children('.tnode').eq(position).remove();
    },
    refreshChildren:function()
    {
        this.children = [];
        var tnodes = this.tnode_children.children('.tnode');
        for(var i=0;i<tnodes.length;i++) {
            this.children[this.children.length] = tnodes.eq(i).get(0).treenode;
        }
    },
    remove:function()
    {
        if(this.isRoot()) {
            alert("Cannot remove root node!");
            return;
        }
        this.parent.removeChild(this);
    },
    label:function(value)
    {
        if(value==undefined) {
            return this.tnode_label;
        } else {
            this.tnode_label.find('td.tn_lbl:first').contents().replaceWith(value);
        }
    },
    startAddNode:function(nodetoadd, callback_ok, callback_cancel)
    {
        this.setMode(1);
        this.root.nodetoadd = nodetoadd;
        this.root.callback_ok = callback_ok;
        this.root.callback_cancel = callback_cancel;
        this.root.tnode.find('td.tn_lbl').bind('click.addnode',
            function(e) {
                this.treenode.showAdd();
                return false;
            }
        );
    },
    endAddNode:function(newnode)
    {
        this.hideAdd();
        this.root.tnode.find('td.tn_lbl').unbind('click.addnode');
        if(newnode) {
            if(this.root.callback_ok) {
                this.root.callback_ok(newnode);
            }
        } else {
            if(this.root.callback_cancel) {
                this.root.callback_cancel();
            }
        }
        this.setMode(0);
    },
    showAdd:function()
    {
        this.hideAdd();

        var add_pop = $('\
            <div class="add_pop">\
                <div class="add_pop_inner">\
                    <table class="add_node" border="0" cellspacing="0" cellpadding="0">\
                        <tr>\
                            <td class="add_above">sibling&nbsp;above</td>\
                        </tr>\
                        <tr>\
                            <td class="add_below">sibling&nbsp;below</td>\
                        </tr>\
                        <tr>\
                            <td class="add_child">child&nbsp;below</td>\
                        </tr>\
                        <tr>\
                            <td class="add_assoc">associate&nbsp;only</td>\
                        </tr>\
                        <tr>\
                            <td class="add_cancel">cancel</td>\
                        </tr>\
                    </table>\
                </div>\
            </div>');

        var tds = add_pop.find('table.add_node:first td');
        for(var i=0;i<tds.length;i++) {
            tds.eq(i).get(0).treenode = this;
        }

        add_pop.find('td').hover(
            function() {
                $(this).addClass('over');
            },
            function() {
                $(this).removeClass('over');
            }
        );

        add_pop.find('td.add_above').click(
            function() {
                var newnode = this.treenode.parent.addChild(this.treenode.getPosition(), this.treenode.root.nodetoadd);
                this.treenode.endAddNode(newnode);
            }
        );

        add_pop.find('td.add_below').click(
            function() {
                var newnode = this.treenode.parent.addChild(this.treenode.getPosition()+1, this.treenode.root.nodetoadd);
                this.treenode.endAddNode(newnode);
            }
        );

        add_pop.find('td.add_child').click(
            function() {
                var newnode = this.treenode.addChild(0, this.treenode.root.nodetoadd);
                this.treenode.endAddNode(newnode);
            }
        );

        add_pop.find('td.add_assoc').click(
            function() {
                var newnode = this.treenode.addChild(0, this.treenode.root.nodetoadd);
                newnode.assoc_only = true;
                this.treenode.endAddNode(newnode);
            }
        );

        add_pop.find('td.add_cancel').click(
            function() {
                this.treenode.endAddNode();
            }
        );

        this.tnode_children.after(add_pop);
    },
    hideAdd:function()
    {
        this.root.tnode.find('div.add_pop').remove();
    },
    getMode:function()
    {
        return this.root.mode;
    },
    setMode:function(m)
    {
        this.root.mode = m;
    }
}

TreeNode.createNode = function(parent)
{
    var tnode = $('<div class="tnode">\
                    <div class="tnode_label">\
                    </div>\
                    <div class="tnode_children">\
                    </div>\
                  </div>');
    return new TreeNode($(tnode), parent);
}
