/* jslint:disable */
/*==================================================
 *  Exhibit.TabularView
 *==================================================
 */

Exhibit.TabularView = function(containerElmt, uiContext) {
    this._div = containerElmt;
    this._uiContext = uiContext;
    
    this._settings = { rowStyler: null, tableStyler: null };
    this._columns = [];
    this._rowTemplate = null;

    var view = this;
    this._listener = { 
        onItemsChanged: function() {
            view._reconstruct(); 
        }
    };
    uiContext.getCollection().addListener(this._listener);
};

Exhibit.TabularView._settingSpecs = {
    "sortAscending":        { type: "boolean", defaultValue: true },
    "sortColumn":           { type: "int",     defaultValue: 0 },
    "showSummary":          { type: "boolean", defaultValue: true },
    "showToolbox":          { type: "boolean", defaultValue: true },
    "border":               { type: "int",     defaultValue: 1 },
    "cellPadding":          { type: "int",     defaultValue: 5 },
    "cellSpacing":          { type: "int",     defaultValue: 3 }
};

Exhibit.TabularView.create = function(configuration, containerElmt, uiContext) {
    var view = new Exhibit.TabularView(
        containerElmt,
        Exhibit.UIContext.create(configuration, uiContext)
    );
    Exhibit.TabularView._configure(view, configuration);
    
    view._internalValidate();
    view._initializeUI();
    return view;
};

Exhibit.TabularView.createFromDOM = function(configElmt, containerElmt, uiContext) {
    var configuration = Exhibit.getConfigurationFromDOM(configElmt);
    
    uiContext = Exhibit.UIContext.createFromDOM(configElmt, uiContext);
    
    var view = new Exhibit.TabularView(
        containerElmt != null ? containerElmt : configElmt, 
        uiContext
    );
    
    Exhibit.SettingsUtilities.collectSettingsFromDOM(configElmt, Exhibit.TabularView._settingSpecs, view._settings);
    
    try {
        var expressions = [];
        var labels = Exhibit.getAttribute(configElmt, "columnLabels", ",") || [];
        
        var s = Exhibit.getAttribute(configElmt, "columns");
        if (s != null && s.length > 0) {
            expressions = Exhibit.ExpressionParser.parseSeveral(s);
        }
        
        for (var i = 0; i < expressions.length; i++) {
            var expression = expressions[i];
            view._columns.push({
                expression: expression,
                uiContext:  Exhibit.UIContext.create({}, view._uiContext, true),
                styler:     null,
                label:      i < labels.length ? labels[i] : null,
                format:     "list"
            });
        }
        
        var formats = Exhibit.getAttribute(configElmt, "columnFormats");
        if (formats != null && formats.length > 0) {
            var index = 0;
            var startPosition = 0;
            while (index < view._columns.length && startPosition < formats.length) {
                var column = view._columns[index];
                var o = {};
                
                column.format = Exhibit.FormatParser.parseSeveral(column.uiContext, formats, startPosition, o);
                
                startPosition = o.index;
                while (startPosition < formats.length && " \t\r\n".indexOf(formats.charAt(startPosition)) >= 0) {
                    startPosition++;
                }
                if (startPosition < formats.length && formats.charAt(startPosition) == ",") {
                    startPosition++;
                }
                
                index++;
            }
        }
        
        var tables = configElmt.getElementsByTagName("table");
        if (tables.length > 0 && tables[0].rows.length > 0) {
            view._rowTemplate = Exhibit.Lens.compileTemplate(tables[0].rows[0], false, uiContext);
        }
    } catch (e) {
        SimileAjax.Debug.exception(e, "TabularView: Error processing configuration of tabular view");
    }
    
    var s = Exhibit.getAttribute(configElmt, "rowStyler");
    if (s != null && s.length > 0) {
        var f = eval(s);
        if (typeof f == "function") {
            view._settings.rowStyler = f;
        }
    }
    s = Exhibit.getAttribute(configElmt, "tableStyler");
    if (s != null && s.length > 0) {
        f = eval(s);
        if (typeof f == "function") {
            view._settings.tableStyler = f;
        }
    }
        
    Exhibit.TabularView._configure(view, configuration);
    view._internalValidate();
    view._initializeUI();
    return view;
};

Exhibit.TabularView._configure = function(view, configuration) {
    Exhibit.SettingsUtilities.collectSettings(configuration, Exhibit.TabularView._settingSpecs, view._settings);
    
    if ("columns" in configuration) {
        var columns = configuration.columns;
        for (var i = 0; i < columns.length; i++) {
            var column = columns[i];
            var expr;
            var styler = null;
            var label = null;
            var format = null;
            
            if (typeof column == "string") {
                expr = column;
            } else {
                expr = column.expression;
                styler = column.styler;
                label = column.label;
                format = column.format;
            }
            
            var expression = Exhibit.ExpressionParser.parse(expr);
            if (expression.isPath()) {
                var path = expression.getPath();
                if (format != null && format.length > 0) {
                    format = Exhibit.FormatParser.parse(view._uiContext, format, 0);
                } else {
                    format = "list";
                }
                
                view._columns.push({
                    expression: expression,
                    styler:     styler,
                    label:      label,
                    format:     format,
                    uiContext:  view._uiContext 
                });
            }
        }
    }
    
    if ("rowStyler" in configuration) {
        view._settings.rowStyler = configuration.rowStyler;
    }
    if ("tableStyler" in configuration) {
        view._settings.tableStyler = configuration.tableStyler;
    }
};

Exhibit.TabularView.prototype._internalValidate = function() {
    if (this._columns.length == 0) {
        var database = this._uiContext.getDatabase();
        var propertyIDs = database.getAllProperties();
        for (var i = 0; i < propertyIDs.length; i++) {
            var propertyID = propertyIDs[i];
            if (propertyID != "uri") {
                this._columns.push(
                    {   expression: Exhibit.ExpressionParser.parse("." + propertyID),
                        styler:     null,
                        label:      database.getProperty(propertyID).getLabel(),
                        format:     "list"
                    }
                );
            }
        }
    }
    this._settings.sortColumn = 
        Math.max(0, Math.min(this._settings.sortColumn, this._columns.length - 1));
};

Exhibit.TabularView.prototype.dispose = function() {
    this._uiContext.getCollection().removeListener(this._listener);
    
    if (this._toolboxWidget) {
        this._toolboxWidget.dispose();
        this._toolboxWidget = null;
    }
    
    this._collectionSummaryWidget.dispose();
    this._collectionSummaryWidget = null;
    
    this._uiContext.dispose();
    this._uiContext = null;
    
    this._div.innerHTML = "";
    
    this._dom = null;
    this._div = null;
};

Exhibit.TabularView.prototype._initializeUI = function() {
    var self = this;
    
    this._div.innerHTML = "";
    this._dom = Exhibit.TabularView.createDom(this._div);
    this._collectionSummaryWidget = Exhibit.CollectionSummaryWidget.create(
        {}, 
        this._dom.collectionSummaryDiv, 
        this._uiContext
    );
    if (this._settings.showToolbox) {
        this._toolboxWidget = Exhibit.ToolboxWidget.createFromDOM(this._div, this._div, this._uiContext);
        this._toolboxWidget.getGeneratedHTML = function() {
            return self._dom.bodyDiv.innerHTML;
        };
    }
    
    if (!this._settings.showSummary) {
        this._dom.collectionSummaryDiv.style.display = "none";
    }
    
    this._reconstruct();
};

Exhibit.TabularView.prototype._reconstruct = function() {
    var self = this;
    var collection = this._uiContext.getCollection();
    var database = this._uiContext.getDatabase();
    
    var bodyDiv = this._dom.bodyDiv;
    bodyDiv.innerHTML = "";

    /*
     *  Get the current collection and check if it's empty
     */
    var items = [];
    var originalSize = collection.countAllItems();
    if (originalSize > 0) {
        var currentSet = collection.getRestrictedItems();
        currentSet.visit(function(itemID) { items.push({ id: itemID, sortKey: "" }); });
    }
    
    if (items.length > 0) {
        /*
         *  Sort the items
         */
        var sortColumn = this._columns[this._settings.sortColumn];
        items.sort(this._createSortFunction(items, sortColumn.expression, this._settings.sortAscending));
    
        /*
         *  Sort the items
         */
        var table = document.createElement("table");
        table.className = "exhibit-tabularView-body";
        if (this._settings.tableStyler != null) {
            this._settings.tableStyler(table, database);
        } else {
            table.cellSpacing = this._settings.cellSpacing;
            table.cellPadding = this._settings.cellPadding;
            table.border = this._settings.border;
        }
        
        /*
         *  Create the column headers
         */
        var tr = table.insertRow(0);
        var createColumnHeader = function(i) {
            var column = self._columns[i];
            if (column.label == null) {
                column.label = self._getColumnLabel(column.expression);
            }

            var td = document.createElement("th");
            Exhibit.TabularView.createColumnHeader(
                exhibit, td, column.label, i == self._settings.sortColumn, self._settings.sortAscending,
                function(elmt, evt, target) {
                    self._doSort(i);
                    SimileAjax.DOM.cancelEvent(evt);
                    return false;
                }
            );

            tr.appendChild(td);
        };
        for (var i = 0; i < this._columns.length; i++) {
            createColumnHeader(i);
        }

        /*
         *  Create item rows
         */
        var renderItem;
        if (this._rowTemplate != null) {
            renderItem = function(i) {
                var item = items[i];
                var tr = Exhibit.Lens.constructFromLensTemplate(item.id, self._rowTemplate, table, self._uiContext);
                
                if (self._settings.rowStyler != null) {
                    self._settings.rowStyler(item.id, database, tr, i);
                }
            }
        } else {
            renderItem = function(i) {
                var item = items[i];
                var tr = table.insertRow(table.rows.length);
                
                for (var c = 0; c < self._columns.length; c++) {
                    var column = self._columns[c];
                    var td = tr.insertCell(c);
                    
                    var results = column.expression.evaluate(
                        { "value" : item.id }, 
                        { "value" : "item" }, 
                        "value",
                        database
                    );
                    
                    var valueType = column.format == "list" ? results.valueType : column.format;
                    column.uiContext.formatList(
                        results.values, 
                        results.size,
                        valueType,
                        function(elmt) { td.appendChild(elmt); }
                    );
                    
                    if (column.styler != null) {
                        column.styler(item.id, database, td);
                    }
                }
                
                if (self._settings.rowStyler != null) {
                    self._settings.rowStyler(item.id, database, tr, i);
                }
            }
        }
        for (var i = 0; i < items.length; i++) {
            renderItem(i);
        }

        bodyDiv.appendChild(table);
    }
};

Exhibit.TabularView.prototype._getColumnLabel = function(expression) {
    var database = this._uiContext.getDatabase();
    var path = expression.getPath();
    var segment = path.getSegment(path.getSegmentCount() - 1);
    var propertyID = segment.property;
    var property = database.getProperty(propertyID);
    if (property != null) {
        return segment.forward ? property.getLabel() : property.getReverseLabel();
    } else {
        return propertyID;
    }
};

Exhibit.TabularView.prototype._createSortFunction = function(items, expression, ascending) {
    var database = this._uiContext.getDatabase();
    var multiply = ascending ? 1 : -1;
    
    var numericFunction = function(item1, item2) {
        return multiply * (item1.sortKey - item2.sortKey);
    };
    var textFunction = function(item1, item2) {
        return multiply * item1.sortKey.localeCompare(item2.sortKey);
    };
    
    var valueTypes = [];
    var valueTypeMap = {};
    for (var i = 0; i < items.length; i++) {
        var item = items[i];
        var r = expression.evaluate(
            { "value" : item.id }, 
            { "value" : "item" }, 
            "value",
            database
        );
        r.values.visit(function(value) {
            item.sortKey = value;
        });
        
        if (!(r.valueType in valueTypeMap)) {
            valueTypeMap[r.valueType] = true;
            valueTypes.push(r.valueType);
        }
    }
    
    var coercedValueType = "text"
    if (valueTypes.length == 1) {
        coercedValueType = valueTypes[0];
    } else {
        coercedValueType = "text";
    }
    
    var coersion;
    var sortingFunction;
    if (coercedValueType == "number") {
        sortingFunction = numericFunction;
        coersion = function(v) {
            if (v == null) {
                return Number.NEGATIVE_INFINITY;
            } else if (typeof v == "number") {
                return v;
            } else {
                var n = parseFloat(v);
                if (isNaN(n)) {
                    return Number.NEGATIVE_INFINITY;
                } else {
                    return n;
                }
            }
        }
    } else if (coercedValueType == "date") {
        sortingFunction = numericFunction;
        coersion = function(v) {
            if (v == null) {
                return Number.NEGATIVE_INFINITY;
            } else if (v instanceof Date) {
                return v.getTime();
            } else {
                try {
                    return SimileAjax.DateTime.parseIso8601DateTime(v).getTime();
                } catch (e) {
                    return Number.NEGATIVE_INFINITY;
                }
            }
        }
    } else if (coercedValueType == "boolean") {
        sortingFunction = numericFunction;
        coersion = function(v) {
            if (v == null) {
                return Number.NEGATIVE_INFINITY;
            } else if (typeof v == "boolean") {
                return v ? 1 : 0;
            } else {
                return v.toString().toLowerCase() == "true";
            }
        }
    } else if (coercedValueType == "item") {
        sortingFunction = textFunction;
        coersion = function(v) {
            if (v == null) {
                return Exhibit.l10n.missingSortKey;
            } else {
                var label = database.getObject(v, "label");
                return (label == null) ? v : label;
            }
        }
    } else {
        sortingFunction = textFunction;
        coersion = function(v) {
            if (v == null) {
                return Exhibit.l10n.missingSortKey;
            } else {
                return v.toString();
            }
        }
    }
    
    for (var i = 0; i < items.length; i++) {
        var item = items[i];
        item.sortKey = coersion(item.sortKey);
    }
    
    return sortingFunction;
};

Exhibit.TabularView.prototype._doSort = function(columnIndex) {
    var oldSortColumn = this._settings.sortColumn;
    var oldSortAscending = this._settings.sortAscending;
    var newSortColumn = columnIndex;
    var newSortAscending = oldSortColumn == newSortColumn ? !oldSortAscending : true;
    var settings = this._settings;
    
    var self = this;
    SimileAjax.History.addLengthyAction(
        function() {
            settings.sortColumn = newSortColumn;
            settings.sortAscending = newSortAscending;
            self._reconstruct();
        },
        function() {
            settings.sortColumn = oldSortColumn;
            settings.sortAscending = oldSortAscending;
            self._reconstruct();
        },
        Exhibit.TabularView.l10n.makeSortActionTitle(this._columns[columnIndex].label, newSortAscending)
    );
};

Exhibit.TabularView._constructDefaultValueList = function(values, valueType, parentElmt, uiContext) {
    uiContext.formatList(values, values.size(), valueType, function(elmt) {
        parentElmt.appendChild(elmt);
    });
};

Exhibit.TabularView.createDom = function(div) {
    var l10n = Exhibit.TabularView.l10n;
    var headerTemplate = {
        elmt:       div,
        className:  "exhibit-collectionView-header",
        children: [
            {   tag:    "div",
                field:  "collectionSummaryDiv"
            },
            {   tag:    "div",
                field:  "bodyDiv"
            }
        ]
    };
    return SimileAjax.DOM.createDOMFromTemplate(headerTemplate);
};

Exhibit.TabularView.createColumnHeader = function(
    exhibit, 
    th,
    label,
    sort,
    sortAscending,
    sortFunction
) {
    var l10n = Exhibit.TabularView.l10n;
    var template = {
        elmt:       th,
        className:  sort ? 
                    "exhibit-tabularView-columnHeader-sorted" : 
                    "exhibit-tabularView-columnHeader",
        title: sort ? l10n.columnHeaderReSortTooltip : l10n.columnHeaderSortTooltip,
        children: [ label ]
    };
    if (sort) {
        template.children.push({
            elmt: Exhibit.UI.createTranslucentImage(
                sortAscending ? "images/up-arrow.png" : "images/down-arrow.png")
        });
    }
    SimileAjax.WindowManager.registerEvent(th, "click", sortFunction, null);
    
    var dom = SimileAjax.DOM.createDOMFromTemplate(template);
    return dom;
};
