Ext.ux.tree.GridView = Ext.extend(Ext.grid.GridView, {

    useArrows: true,

    staticTree: false,

    animate: false,

    constructor: function(config) {
        this.emptyIcon    = Ext.BLANK_IMAGE_URL;

        Ext.ux.tree.GridView.superclass.constructor.call(this, config);
        this.templates            = {};
        this.templates.master    = new Ext.Template(
            '<div class="x-grid3 tq-treegrid" hidefocus="true">',
                '<div class="x-grid3-viewport">',
                    '<div class="x-grid3-header">',
                        '<div class="x-grid3-header-inner">',
                            '<div class="x-grid3-header-offset" style="{ostyle}">{header}</div>',
                        '</div>',
                        '<div class="x-clear"></div>',
                    '</div>',
                    '<div class="x-grid3-scroller">',
                        '<div class="x-grid3-body ', (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') , '" style="{bstyle}">{body}</div>',
                        '<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
                    '</div>',
                '</div>',
                '<div class="x-grid3-resize-marker"> </div>',
                '<div class="x-grid3-resize-proxy"> </div>',
            '</div>'
        );

        this.templates.row    = new Ext.Template(
            '<div class="x-grid3-row {alt}" style="{tstyle}">',
                '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
                    '<tbody class="x-tree-node">',
                        '<tr>{cells}</tr>',
                        (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
                    '</tbody>',
                '</table>',
            '</div>'
        );
        this.templates.treeCell    = new Ext.Template(
            '<td class="tq-treegrid-col x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
                '<div ext:tree-node-id="{nodeId}" class="x-grid3-col-{id} x-tree-node-el x-unselectable" unselectable="on" {attr}>',
                    '<div class="tq-treegrid-icons">',
                        '<span class="x-tree-node-indent">{nodeIndent}</span>',
                        '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow {nodeTreeIconCls}" />',
                        '<img src="', this.emptyIcon, '" class="x-tree-node-icon {nodeIconCls}" unselectable="on" />',
                    '</div>',
                    '<div class="x-grid3-cell-inner" unselectable="on" {attr}>',
                        '{value}',
                    '</div>',
                '</div>',
            '</td>'
        );

        this.addEvents('beforecollapsenode', 'beforeexpandnode', 'collapsenode', 'expandnode');
    },

    getChildIndentUI: function(node) {
        var indentBuffer    = [];
        var parentNode        = node.parentNode;
        while (parentNode){
            if (!parentNode.isRoot){
                if (!parentNode.isLast()) {
                    indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
                } else {
                    indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
                }
            }
            parentNode = parentNode.parentNode;
        }
        return indentBuffer.join("");
    },

    getTreeNodeIcon: function(node) {
        var treeIcon    = node.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
        if (node.hasChildNodes() && !this.staticTree){
            if (node.expanded){
                treeIcon += "-minus";
            } else {
                treeIcon += "-plus";
            }
            treeIcon += ' tq-tree-node-control';
        }
        return treeIcon;
    },

    // private
    doRender: function(cs, rs, ds, startRow, colCount, stripe){
        // buffers
        var rowBuffer = [];
        var cellBuffer;
        var cell;
        var cellTemplate;
        var cellProperties = {};
        var rowProperties = { 
            tstyle: 'width:'+this.getTotalWidth()+';'
        };
        var record;
        var depthBuffer    = [];
        var hasTreeCol;

        for (var j = 0, len = rs.length; j < len; j++){
            record            = rs[j];
            cellBuffer        = [];
            hasTreeCol        = false;
            var rowIndex    = (j+startRow);

            for (var i = 0; i < colCount; i++){
                cell                    = cs[i];
                cellProperties.id        = cell.id;
                cellProperties.css        = (i === 0) ? 'x-grid3-cell-first ' : (i == (colCount - 1) ? 'x-grid3-cell-last ' : '');
                cellProperties.attr        = cellProperties.cellAttr = '';
                cellProperties.value    = cell.renderer.call(cell.scope, record.data[cell.name], cellProperties, record, rowIndex, i, ds);
                cellProperties.style    = cell.style;
                if (Ext.isEmpty(cellProperties.value)){
                    cellProperties.value = ' ';
                }
                if (this.markDirty && record.dirty && Ext.isDefined(record.modified[cell.name])){
                    cellProperties.css += ' x-grid3-dirty-cell';
                }

                if (cell.scope.treeCol && !hasTreeCol && record.node) {
                    hasTreeCol    = true;
                    var node    = record.node;
                    
                    cellProperties.nodeIndent    = this.getChildIndentUI(node);
                    cellProperties.nodeIconCls    = node.attributes.iconCls || '';
                
                    cellProperties.nodeTreeIconCls    = this.getTreeNodeIcon(node);

                    cellTemplate = this.templates.treeCell;
                } else {
                    cellTemplate = this.templates.cell;
                }
                cellBuffer[cellBuffer.length] = cellTemplate.apply(cellProperties);

            }
            var alt = [];

            if (record.depth && record.node) {
                // the check for startRow == 0 prevents hierarchy divs to be inserted
                // when we only do a partial rendering - happens when data is changed 
                if (startRow == 0 && depthBuffer.length < record.depth) {
                    var cls = "x-tree-node-ct";
                    if (record.node.parentNode.isRoot || record.node.parentNode.expanded) {
                        cls += " tq-tree-node-ct-expanded";
                    } else {
                        cls += " tq-tree-node-ct-collapsed";
                    }
                    rowBuffer.push('<div class="'+cls+'">');
                    depthBuffer.push('</div>');
                } else {
                    while (depthBuffer.length > record.depth) {
                        rowBuffer.push(depthBuffer.pop());
                    }
                }

                if (this.staticTree) {
                    alt.push('tq-treegrid-static');
                }

                if (node.isLeaf()) {
                    alt.push('x-tree-node-leaf');
                } else if (node.expanded){
                    alt.push('x-tree-node-expanded');
                } else {
                    alt.push('x-tree-node-collapsed');
                }

                rowProperties.nodeId    = record.node.id;
            }
            rowProperties.cols        = colCount;
            rowProperties.cells        = cellBuffer.join('');

            if(stripe && ((rowIndex+1) % 2 === 0)){
                alt.push('x-grid3-row-alt');
            }
            if(record.dirty){
                alt.push(' x-grid3-dirty-row');
            }

            if (this.getRowClass){
                alt.push(this.getRowClass(record, rowIndex, rowProperties, ds));
            }
            rowProperties.alt    = alt.join(' ');
            
            rowBuffer[rowBuffer.length] =  this.templates.row.apply(rowProperties);
        }

        while (depthBuffer.length) {
            rowBuffer.push(depthBuffer.pop());
        }
        return rowBuffer.join('');
    },

    afterRender: function() {
        Ext.ux.tree.GridView.superclass.afterRender.call(this);

        if (!this.staticTree) {
            this.mainBody.on('click', function(ev, el) {
                this.toggleNodeEl(el);
            }, this, {
                delegate: '.tq-tree-node-control'
            });
            this.mainBody.on('dblclick', function(ev, el) {
                this.toggleNodeEl(el);
            }, this, {
                delegate: '.x-tree-node-el'
            });
        }

        this.grid.relayEvents(this, [ 'beforecollapsenode', 'beforeexpandnode', 'collapsenode', 'expandnode' ]);
    },

    expandAll: function(maxDepth) {
        maxDepth = maxDepth || Number.MAX_VALUE;
        this.grid.getStore().each(function(r) {
            if (!r.node.expanded && r.depth < maxDepth) {
                this.toggleNodeRecord(r);
            }
        }, this);
    },

    collapseAll: function(maxDepth) {
        maxDepth = maxDepth || 0;
        this.grid.getStore().each(function(r) {
            if (r.node.expanded && r.depth > maxDepth) {
                this.toggleNodeRecord(r);
            }
        }, this);
    },

    toggleNodeRecord: function(record) {
        var rowIndex    = this.grid.getStore().indexOf(record);
        var row            = Ext.get(this.getRow(rowIndex));
        this.toggleNodeRow(row);
    },

    toggleNodeRow: function(row) {
        var record    = this.grid.getStore().getAt(row.dom.rowIndex);
        var node    = record.node;

        var childCnt    = row.next('.x-tree-node-ct', false);
        var nodeCtrl    = row.child('.tq-tree-node-control', false);

        if (childCnt && nodeCtrl) {
            if (node.expanded && this.fireEvent('beforecollapsenode', node, record, this.grid, this) !== false) {
                childCnt.enableDisplayMode('block');
                childCnt.stopFx();

                row.removeClass('x-tree-node-expanded');
                nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
                row.addClass('x-tree-node-collapsed');
                nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

                if (this.animate) {
                    childCnt.slideOut('t', {
                        callback : function(){
                            node.expanded    = false;
                            childCnt.removeClass('tq-tree-node-ct-expanded');
                            childCnt.addClass('tq-tree-node-ct-collapsed');
                            this.fireEvent('collapsenode', node, record, this.grid, this);
                        },
                        scope: this,
                        duration: .25
                    });
                } else {
                    childCnt.hide();
                    node.expanded    = false;
                    childCnt.removeClass('tq-tree-node-ct-expanded');
                    childCnt.addClass('tq-tree-node-ct-collapsed');
                    this.fireEvent('collapsenode', node, record, this.grid, this);
                }

            } else if (this.fireEvent('beforeexpandnode', node, record, this.grid, this) !== false) {
                childCnt.stopFx();

                row.addClass('x-tree-node-expanded');
                nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
                row.removeClass('x-tree-node-collapsed');
                nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

                if (this.animate) {
                    childCnt.slideIn('t', {
                       callback : function(){
                            node.expanded    = true;
                            childCnt.removeClass('tq-tree-node-ct-collapsed');
                            childCnt.addClass('tq-tree-node-ct-expanded');
                            this.fireEvent('expandnode', node, record, this.grid, this);
                        },
                        scope: this,
                        duration: .25
                    });
                } else {
                    childCnt.show();
                    node.expanded    = true;
                    childCnt.removeClass('tq-tree-node-ct-collapsed');
                    childCnt.addClass('tq-tree-node-ct-expanded');
                    this.fireEvent('expandnode', node, record, this.grid, this);
                }
            }
        }
    },

    toggleNodeEl: function(el) {
        var row        = Ext.get(this.findRow(el));
        this.toggleNodeRow(row);
    },

    getRows: function() {
        return this.hasRows() ? this.mainBody.query(this.rowSelector) : [];
    }

});  
