var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
define(["require", "exports", "utils", "strings", "settings", "combinationRenderer", "templateManager"], function (require, exports, _utils, _strings, PhoenixSettings, PhoenixCombinationRenderer, PhoenixTemplates) {
    var PhoenixItemInCombinationArrayPointer = (function () {
        function PhoenixItemInCombinationArrayPointer(combinationArray, index) {
            this.combinationArray = combinationArray;
            this.index = index;
            return this;
        }
        PhoenixItemInCombinationArrayPointer.prototype.getRepresentativeItem = function () {
            return this.combinationArray[this.index].getRepresentativeItem();
        };
        return PhoenixItemInCombinationArrayPointer;
    })();
    // TODO: Need to investigate (DOM in array => append) vs. (Reused DOM rows => html) to see which is more efficient
    var PhoenixCombitronRenderer = (function (_super) {
        __extends(PhoenixCombitronRenderer, _super);
        function PhoenixCombitronRenderer(context, combinationManager) {
            _super.call(this, context, combinationManager);
            if (combinationManager.getTables().length !== 1) {
                new Error("Combination manager passed to PhoenixSingleTableRenderer does not contain a single table.");
            }
        }
        PhoenixCombitronRenderer.prototype.getWindowIndices = function () {
            return this.windowIndices;
        };
        PhoenixCombitronRenderer.prototype.setWindowIndices = function (startIndex, endIndex) {
            this.windowIndices = [startIndex, endIndex];
            return this;
        };
        PhoenixCombitronRenderer.prototype.getFactor = function () {
            return this.factor;
        };
        PhoenixCombitronRenderer.prototype.setFactor = function (factor) {
            this.factor = factor;
            return this;
        };
        PhoenixCombitronRenderer.prototype.getTable = function () {
            return this.getCombinationManager().getTables()[0];
        };
        PhoenixCombitronRenderer.prototype.getTableColumns = function () {
            if (this.tableColumns === undefined) {
                this.tableColumns = this.getTable().getTableColumns();
            }
            return this.tableColumns;
        };
        // TODO: Still only single table support
        // TODO: Need to break this up so that combination store is handled separately
        PhoenixCombitronRenderer.prototype.initialiseCombinationArray = function () {
            this.combinationArray = this
                .getCombinationManager()
                .convertCombinationDescriptionsToCombinations()
                .getCombinationStore()
                .convertValuesIntoArray()[0];
            return this;
        };
        PhoenixCombitronRenderer.prototype.getCombinationArray = function () {
            if (this.combinationArray === undefined) {
                this.initialiseCombinationArray();
            }
            return this.combinationArray;
        };
        PhoenixCombitronRenderer.prototype.prepareColumnFormatters = function () {
            var _this = this;
            this.columnFormatters = [];
            var tableColumns = this.getTableColumns();
            $.each(tableColumns, function (i, columnDef) {
                var formatterSource = columnDef["format"];
                if (!formatterSource) {
                    formatterSource = "";
                }
                var compiledFunction = dust.loadSource(dust.compile(formatterSource));
                _this.columnFormatters.push(compiledFunction);
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.renderCategoryInfo = function () {
            PhoenixTemplates.renderTemplate("categoryInfo", 
            // this.getTable().getMetadataStore().reveal(), this.ctx("#category-info"));
            this.getTable().getData(), this.ctx("#category-info"));
            return this;
        };
        PhoenixCombitronRenderer.prototype.renderTableHeader = function () {
            var tableColumns = this.getTableColumns();
            var headerDom = $("<div>").addClass("plan header");
            $.each(tableColumns, function (i, columnDef) {
                var headerCellDom = $("<div>")
                    .addClass("cell")
                    .text(columnDef["name"])
                    .appendTo(headerDom);
            });
            headerDom.appendTo(this.ctx(_strings.elem.tableContainer));
            return this;
        };
        PhoenixCombitronRenderer.prototype.applyFilterToTableRowDomOrder = function (dim, order) {
            var combinationArray = this.getCombinationArray();
            // TODO: Implement a stable sorting algorithm to override most unstable implementations in browsers
            // TODO: Either memoise property retrieval or convert array to map before sorting to speed up (and potentially have stable sort)
            var filterFunc = function (indexA, indexB) {
                if (order === -1) {
                    _a = [indexB, indexA], indexA = _a[0], indexB = _a[1];
                }
                var valA = _utils.returnField(combinationArray[indexA].getRepresentativeItem().getData(), dim);
                var valB = _utils.returnField(combinationArray[indexB].getRepresentativeItem().getData(), dim);
                // Unlimited cases
                if (valA === -1 && valB !== -1) {
                    return 1;
                }
                if (valA !== -1 && valB === -1) {
                    return -1;
                }
                // Standard cases
                if (valA > valB) {
                    return 1;
                }
                if (valA < valB) {
                    return -1;
                }
                return 0;
                var _a;
            };
            this.tableRowDomArrayOrder.sort(filterFunc);
            return this;
        };
        PhoenixCombitronRenderer.prototype.renderFilters = function () {
            var templateName = "filterBlock";
            var filterDomElemArray = [];
            // let filters = this.getTable().getMetadata("filters");
            var filters = this.getTable().getData("filters");
            // Make sure template is fetched first
            PhoenixTemplates.fetchTemplate(templateName).done(function () {
                $.each(filters.dimensions, function (k, v) {
                    var context = {
                        // title: v.name + " [" + v.type + "]"
                        title: v.name
                    };
                    var filterDomElem = $("<div>");
                    PhoenixTemplates.renderTemplate(templateName, context, filterDomElem);
                    filterDomElem = $(filterDomElem.html());
                    filterDomElem.prop("id", k);
                    // Asc/Desc sort buttons
                    var ascButton = $("<button>").text("Asc").addClass("order-sort").data({
                        "dimension": v.field,
                        "order": 1
                    });
                    var descButton = $("<button>").text("Desc").addClass("order-sort").data({
                        "dimension": v.field,
                        "order": -1
                    });
                    filterDomElem.append(ascButton).append(descButton);
                    // // List box for `list` = true
                    // if (_utils.returnField(filters["_multiDimList"], k) !== undefined) {
                    //     let selectElem = $('<select>').addClass("list-multi").prop("multiple", true).data({
                    //         "dimension": k
                    //     });
                    //     $.each(_utils.returnField(filters["_multiDimList"], k), (k,v) => {
                    //         selectElem
                    //             .append($("<option>")
                    //                 .data("value", v)
                    //                 .prop("value", v)
                    //                 .text(v));
                    //     });
                    //     filterDomElem.append(selectElem);
                    // }
                    filterDomElemArray.push(filterDomElem);
                });
                // Invert sort button
                filterDomElemArray.push($("<button>").text("Invert whole table").addClass("order-invert"));
                $(_strings.elem.filterContainer).append(filterDomElemArray);
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.returnNewTableRowDom = function () {
            var tableColumns = this.getTableColumns();
            var tableRowDomTemplate = $("<div>").addClass("plan item");
            var tableCellDomTemplate = $("<div>").addClass("cell");
            var tableCellDomTemplateHTML = "<div class='cell'></div>";
            var combinationArray = this.getCombinationArray();
            // Pregenerate row with blank cells
            var rowHTML = "";
            $.each(tableColumns, function () {
                rowHTML += tableCellDomTemplateHTML;
            });
            tableRowDomTemplate.html(rowHTML);
            return tableRowDomTemplate;
        };
        PhoenixCombitronRenderer.prototype.tableRowDomArraySlice = function (startIndex, endIndex) {
            var length = endIndex - startIndex;
            var rows = new Array(length);
            for (var i = 0; i < length; ++i) {
                rows[i] = this.getRowDom(startIndex + i);
            }
            return rows;
        };
        // TODO: Still only single table support
        PhoenixCombitronRenderer.prototype.getRowDom = function (index) {
            index = this.tableRowDomArrayOrder[index];
            var rowDom = this.tableRowDomArray[index];
            if (rowDom === undefined) {
                return this.tableRowDomArray[index] = this.returnNewTableRowDom()
                    .data("_PhoenixItemPointer", new PhoenixItemInCombinationArrayPointer(this.getCombinationArray(), index));
            }
            return rowDom;
        };
        // TODO: Still only single table support
        PhoenixCombitronRenderer.prototype.initialiseTableRowDomArray = function () {
            var combinationArray = this.getCombinationArray();
            // Blank out
            this.tableRowDomArray = new Array(combinationArray.length);
            this.tableRowDomArrayOrder = new Array(combinationArray.length);
            for (var i = 0; i < combinationArray.length; ++i) {
                this.tableRowDomArrayOrder[i] = i;
            }
            return this;
        };
        // TODO: Still only single table support
        PhoenixCombitronRenderer.prototype.updateTableRowsWithHourFactor = function () {
            var _this = this;
            var tableColumns = this.getTableColumns();
            var _a = this.getWindowIndices(), startIndex = _a[0], endIndex = _a[1];
            for (var i = startIndex; i < (endIndex + 1); ++i) {
                var tableRowDom = this.getRowDom(i);
                if (tableRowDom.data("_PhoenixCombitronRenderer_factor") === this.getFactor()) {
                    continue;
                }
                // NOTE: Downgraded from `let` to `var` due to ES6 requirements
                var representativeItem = tableRowDom.data("_PhoenixItemPointer").getRepresentativeItem();
                var rowChildrenCells = $(tableRowDom).children(".cell");
                $.each(tableColumns, function (j, columnDef) {
                    dust.render(_this.columnFormatters[j], representativeItem.getScaledData(_this.factor), function (err, html) {
                        rowChildrenCells.eq(j).html(html);
                    });
                });
                // Tag with factor
                tableRowDom.data("_PhoenixCombitronRenderer_factor", +this.getFactor());
            }
            return this;
        };
        PhoenixCombitronRenderer.prototype.presentRows = function () {
            var tableContainer = this.ctx(_strings.elem.tableContainer);
            // Get rows to be presented
            var _a = this.getWindowIndices(), startIndex = _a[0], endIndex = _a[1];
            var rowsToBePresented = this.tableRowDomArraySlice(startIndex, endIndex + 1);
            // Append presented rows
            tableContainer.append(rowsToBePresented);
            // Detach all irrelevant ones
            // Should be at head (since we appended the relevant ones to the end)
            // Therefore, the m irrelevant rows v.s. n relevant rows are s.t. m+n = length
            var rowsCurrentlyPresented = tableContainer.children(".plan.item");
            var difference = rowsCurrentlyPresented.length - (endIndex - startIndex + 1);
            // For loop v.s. Array.slice()?
            for (var i = 0; i < difference; ++i) {
                $(rowsCurrentlyPresented[i]).detach();
            }
            // TODO: For some reason after this, a bunch of jQ event handles are messed with, which *adds a ~3-4x hit* to execution time.
            // TODO: Investigate reuse of DOM rows
        };
        PhoenixCombitronRenderer.prototype.updateAndPresentRows = function () {
            this.updateTableRowsWithHourFactor();
            this.presentRows();
            return this;
        };
        PhoenixCombitronRenderer.prototype.setFactorAndUpdateRows = function (factor) {
            this.setFactor(factor);
            this.updateTableRowsWithHourFactor();
            return this;
        };
        PhoenixCombitronRenderer.prototype.setWindowIndicesAndUpdateAndPresentRows = function (startIndex, endIndex) {
            this.setWindowIndices(startIndex, endIndex);
            this.updateTableRowsWithHourFactor();
            this.presentRows();
            return this;
        };
        PhoenixCombitronRenderer.prototype.renderTableRowsForFirstTime = function () {
            var paginationItemIndices = this.ctx("#pagination > input");
            this.setWindowIndices((+paginationItemIndices.eq(0).val()) - 1, (+paginationItemIndices.eq(1).val()) - 1);
            this.setFactor(+this.ctx("#normaliser > input").val());
            this.updateAndPresentRows();
            // Temporary
            this.ctx("#pagination").append("(" + this.tableRowDomArray.length + ")");
            return this;
        };
        PhoenixCombitronRenderer.prototype.renderItemDetail = function (item) {
            var originalCombinationItems = item["_PhoenixCombination"].getItems();
            var arrayOfItems = originalCombinationItems.map(function (item) {
                return $("<p>").text(JSON.stringify(item, null, 2));
            });
            this.ctx("#plan-detail").empty().append(arrayOfItems);
            return this;
        };
        PhoenixCombitronRenderer.prototype.openItemDetailPane = function () {
            this.ctx("#plan-wrapper").addClass("pane-open");
            return this;
        };
        PhoenixCombitronRenderer.prototype.closeItemDetailPane = function () {
            this.ctx("#plan-wrapper")
                .removeClass("pane-open")
                .trigger("PlanDetailBoxClosed");
            return this;
        };
        PhoenixCombitronRenderer.prototype.applyInitialFiltersToTable = function () {
            var _this = this;
            // $.each(this.getTable().getMetadata("filters").presort, (i, obj) => {
            $.each(this.getTable().getData("filters").presort, function (i, obj) {
                _this.applyFilterToTableRowDomOrder(obj.dimension, obj.order);
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachFilterButtonHandler = function () {
            var _this = this;
            $(_strings.elem.filterContainer)
                .on("click", "button.order-sort", function (e) {
                var thisData = $(e.currentTarget).data();
                _this.applyFilterToTableRowDomOrder(thisData.dimension, thisData.order);
                _this.updateAndPresentRows();
            })
                .on("click", "button.order-invert", function (e) {
                _this.tableRowDomArrayOrder.reverse();
                _this.updateAndPresentRows();
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachPaginationListener = function () {
            var _this = this;
            var paginationItemIndices = this.ctx("#pagination > input");
            this.ctx("#pagination")
                .on("change keydown keyup", "input", function (e) {
                _this.setWindowIndicesAndUpdateAndPresentRows((+paginationItemIndices.eq(0).val()) - 1, (+paginationItemIndices.eq(1).val()) - 1);
            })
                .on("click", "button", function (e) {
                var lBoundVal = +paginationItemIndices.eq(0).val();
                var hBoundVal = +paginationItemIndices.eq(1).val();
                var diff = hBoundVal - lBoundVal + 1;
                if ($(e.currentTarget).is(".prev")) {
                    diff = -diff;
                }
                lBoundVal += diff;
                hBoundVal += diff;
                if (lBoundVal < 1) {
                    lBoundVal = 1;
                }
                if (hBoundVal > _this.tableRowDomArray.length) {
                    hBoundVal = _this.tableRowDomArray.length;
                }
                if (lBoundVal > hBoundVal) {
                    hBoundVal = lBoundVal;
                }
                paginationItemIndices.eq(0).val("" + (lBoundVal));
                paginationItemIndices.eq(1).val("" + (hBoundVal)).trigger("change");
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachNormaliserListener = function () {
            var _this = this;
            this.ctx("#normaliser")
                .on("change keydown keyup", "input", function (e) {
                _this.setFactor(+$(e.currentTarget).val());
                _this.updateTableRowsWithHourFactor();
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachRowClickHandler = function () {
            var _this = this;
            this.ctx(_strings.elem.tableContainer)
                .on("click", ".plan.item", function (e) {
                var item = $(e.currentTarget).data("_PhoenixItemPointer").getRepresentativeItem();
                _this.renderItemDetail(item)
                    .openItemDetailPane();
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachPrevNextItemDetailPaneClickHandler = function () {
            // TODO: Rework to be more specific to the pane itself
            this.ctx("#plan-wrapper")
                .on("click", "#prev", function (e) {
                // TODO
            })
                .on("click", "#next", function (e) {
                // TODO
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachCloseItemDetailPaneClickHandler = function () {
            var _this = this;
            // TODO: Rework to be more specific to the pane itself
            this.ctx("#plan-wrapper").on("click", "#close", function (e) {
                _this.closeItemDetailPane();
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachOpenItemDetailsOnMouseEnterHandler = function () {
            var _this = this;
            this.ctx(_strings.elem.tableContainer)
                .on("mouseenter", ".plan.item", function (e) {
                // Activate if no pinned rows AND pane is open
                if (_this.ctx("#plan-wrapper").hasClass("pane-open")
                    && !_this.ctx(_strings.elem.tableContainer).find(".plan.item.pinned").length) {
                    _this.renderItemDetail($(e.currentTarget).data("_PhoenixItemPointer").getRepresentativeItem());
                }
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.setupItemRowPinning = function () {
            var _this = this;
            this.ctx(_strings.elem.tableContainer)
                .on("click", ".plan.item", function (e) {
                if (!$(e.currentTarget).hasClass("pinned")) {
                    // If this row does not have pin, remove pin from other rows and set pin on this one
                    _this.ctx(_strings.elem.tableContainer).find(".plan.item.pinned").removeClass("pinned");
                    $(e.currentTarget).addClass("pinned");
                }
                else {
                    // Remove pin
                    $(e.currentTarget).removeClass("pinned");
                }
            });
            // Ensure we get rid of the pin state on close
            this.ctx("#plan-wrapper").on("PlanDetailBoxClosed", function (e) {
                _this.ctx(_strings.elem.tableContainer).find(".plan.item.pinned").removeClass("pinned");
            });
            return this;
        };
        PhoenixCombitronRenderer.prototype.attachHandlers = function () {
            this.attachRowClickHandler()
                .attachCloseItemDetailPaneClickHandler()
                .attachPaginationListener()
                .attachNormaliserListener()
                .attachFilterButtonHandler();
            // .attachPrevNextItemDetailPaneClickHandler();
            if (PhoenixSettings.get("openPlanItemOnHover")) {
                this.attachOpenItemDetailsOnMouseEnterHandler()
                    .setupItemRowPinning();
            }
            return this;
        };
        PhoenixCombitronRenderer.prototype.renderTable = function () {
            return this
                .initialiseTableRowDomArray()
                .prepareColumnFormatters()
                .applyInitialFiltersToTable()
                .renderCategoryInfo()
                .renderFilters()
                .renderTableHeader()
                .renderTableRowsForFirstTime();
        };
        PhoenixCombitronRenderer.prototype.present = function () {
            return this
                .renderTable()
                .attachHandlers();
        };
        return PhoenixCombitronRenderer;
    })(PhoenixCombinationRenderer);
    return PhoenixCombitronRenderer;
});
