"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [0, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
var chart_def_1 = require("./chart-def");
var chai_1 = require("chai");
var opn = require("opn");
var path = require("path");
var Sugar = require("sugar");
var find_package_dir_1 = require("./find-package-dir");
var inflate_template_1 = require("inflate-template");
var capture_template_1 = require("capture-template");
//
// Reusable chart renderer.
// For improved performance.
//
// TODO :export let globalChartRenderer: IChartRenderer | null = null;
function findChartTemplatesPath() {
    return __awaiter(this, void 0, void 0, function () {
        var parentDir, chartTemplatesPath;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, find_package_dir_1.findPackageDir(__dirname)];
                case 1:
                    parentDir = _a.sent();
                    chartTemplatesPath = path.join(parentDir, "templates");
                    return [2 /*return*/, chartTemplatesPath];
            }
        });
    });
}
function startPlot() {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            return [2 /*return*/];
        });
    });
}
exports.startPlot = startPlot;
function endPlot() {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            return [2 /*return*/];
        });
    });
}
exports.endPlot = endPlot;
/**
 * Fluent API for configuring the plot.
 */
var AbstractPlotAPI = /** @class */ (function () {
    function AbstractPlotAPI(data, plotConfig, globalAxisMap) {
        this.data = data;
        this.plotConfig = plotConfig;
        this.globalAxisMap = globalAxisMap;
    }
    /***
     * Set the chart template to use.
     * This defaults to "c3".
     */
    AbstractPlotAPI.prototype.template = function (templateName) {
        this.plotConfig.template = templateName; // TODO: could call toLower, would have to also toLower the config.
        return this;
    };
    /**
     * Set the type of the chart to be plotted.
     *
     * @param chartType Specifies the chart type.
     */
    AbstractPlotAPI.prototype.chartType = function (chartType) {
        this.plotConfig.chartType = chartType; // TODO: could call toLower, would have to also toLower the config.
        return this;
    };
    /**
     * Set the width of the chart.
     */
    AbstractPlotAPI.prototype.width = function (width) {
        this.plotConfig.width = width;
        return this;
    };
    /**
     * Set the height of the chart.
     */
    AbstractPlotAPI.prototype.height = function (height) {
        this.plotConfig.height = height;
        return this;
    };
    /**
     * Configure the default x axis.
     */
    AbstractPlotAPI.prototype.x = function (seriesName) {
        this.globalAxisMap.x.series = seriesName;
        return new XAxisConfigAPI("x", seriesName, this.plotConfig.x, this.globalAxisMap.x, this.data, this.plotConfig, this.globalAxisMap);
    };
    /**
     * Configure the y axis.
     */
    AbstractPlotAPI.prototype.y = function (seriesName) {
        var singleAxisMap = { series: seriesName };
        this.globalAxisMap.y.push(singleAxisMap);
        return new YAxisConfigAPI("y", seriesName, this.plotConfig.y, singleAxisMap, this.data, this.plotConfig, this.globalAxisMap);
    };
    /**
     * Configure the y axis.
     */
    AbstractPlotAPI.prototype.y2 = function (seriesName) {
        var singleAxisMap = { series: seriesName };
        this.globalAxisMap.y2.push(singleAxisMap);
        return new YAxisConfigAPI("y2", seriesName, this.plotConfig.y2, singleAxisMap, this.data, this.plotConfig, this.globalAxisMap);
    };
    /**
     * Render the plot to an image file.
     */
    AbstractPlotAPI.prototype.renderImage = function (imageFilePath, renderOptions) {
        return __awaiter(this, void 0, void 0, function () {
            var chartDef, templatesPath, chartTemplatePath, captureOptions;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        chartDef = this.serialize();
                        return [4 /*yield*/, findChartTemplatesPath()];
                    case 1:
                        templatesPath = _a.sent();
                        chartTemplatePath = path.join(templatesPath, chartDef.plotConfig.template, "web");
                        captureOptions = {
                            electronPath: renderOptions && renderOptions.electronPath,
                        };
                        return [4 /*yield*/, capture_template_1.captureImage({ chartDef: chartDef }, chartTemplatePath, imageFilePath, captureOptions)];
                    case 2:
                        _a.sent();
                        if (renderOptions && renderOptions.openImage) {
                            opn(path.resolve(imageFilePath));
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Export an interactive web visualization of the chart.
     */
    AbstractPlotAPI.prototype.exportWeb = function (outputFolderPath, exportOptions) {
        return __awaiter(this, void 0, void 0, function () {
            var chartDef, templatesPath, chartTemplatePath, overwrite;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        chartDef = this.serialize();
                        return [4 /*yield*/, findChartTemplatesPath()];
                    case 1:
                        templatesPath = _a.sent();
                        chartTemplatePath = path.join(templatesPath, chartDef.plotConfig.template, "web");
                        overwrite = exportOptions && !!exportOptions.overwrite || false;
                        return [4 /*yield*/, inflate_template_1.exportTemplate({ chartDef: chartDef }, outputFolderPath, { templatePath: chartTemplatePath, overwrite: overwrite })];
                    case 2:
                        _a.sent();
                        if (exportOptions && exportOptions.openBrowser) {
                            opn("file://" + path.resolve(path.join(outputFolderPath, "index.html")));
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Export a Node.js project to host a web visualization of the char.
     */
    AbstractPlotAPI.prototype.exportNodejs = function (outputFolderPath, exportOptions) {
        return __awaiter(this, void 0, void 0, function () {
            var chartDef, templatesPath, chartTemplatePath, overwrite;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        chartDef = this.serialize();
                        return [4 /*yield*/, findChartTemplatesPath()];
                    case 1:
                        templatesPath = _a.sent();
                        chartTemplatePath = path.join(templatesPath, chartDef.plotConfig.template, "nodejs");
                        overwrite = exportOptions && !!exportOptions.overwrite || false;
                        return [4 /*yield*/, inflate_template_1.exportTemplate({ chartDef: chartDef }, outputFolderPath, { templatePath: chartTemplatePath, overwrite: overwrite })];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Serialize the plot definition so that it can be converted to JSON.
     * The JSON definition of the chart can be used to instantiate the chart in a browser.
     *
     * TODO: This function doesn't really belong in the abstract class, it would be better to live the concrete PlotAPI class.
     */
    AbstractPlotAPI.prototype.serialize = function () {
        var expandedPlotConfig = Object.assign({}, this.plotConfig);
        var defaultedGlobalAxis = Object.assign({}, this.globalAxisMap);
        if (defaultedGlobalAxis.y.length === 0) {
            // Default the primary Y axis.
            defaultedGlobalAxis.y = this.data.columnOrder
                .filter(function (columnName) { return columnName !== defaultedGlobalAxis.x.series; })
                .map(function (columnName) { return ({
                series: columnName,
            }); });
        }
        return {
            data: this.data,
            plotConfig: expandedPlotConfig,
            axisMap: defaultedGlobalAxis,
        };
    };
    // TODO: This function doesn't really belong in the abstract class, it would be better to live the concrete PlotAPI class.
    AbstractPlotAPI.prototype.getTypeCode = function () {
        return "plot";
    };
    return AbstractPlotAPI;
}());
exports.AbstractPlotAPI = AbstractPlotAPI;
/**
 * Fluent API for configuring the plot.
 */
var PlotAPI = /** @class */ (function (_super) {
    __extends(PlotAPI, _super);
    function PlotAPI(data, plotConfig, showLegendDefault, globalAxisMap) {
        var _this = this;
        chai_1.assert.isObject(data, "Expected 'data' parameter to PlotAPI constructor to be a serialized dataframe.");
        // Clone the def and plot map so they can be updated by the fluent API.
        var expandedPlotConfig = Object.assign({}, plotConfig);
        if (!expandedPlotConfig.chartType) {
            expandedPlotConfig.chartType = chart_def_1.ChartType.Line;
        }
        if (!expandedPlotConfig.width) {
            expandedPlotConfig.width = 800;
        }
        if (!expandedPlotConfig.height) {
            expandedPlotConfig.height = 600;
        }
        if (!expandedPlotConfig.template) {
            expandedPlotConfig.template = "c3";
        }
        if (!expandedPlotConfig.x) {
            expandedPlotConfig.x = {
                axisType: chart_def_1.AxisType.Default,
                label: {},
            };
        }
        else {
            if (!expandedPlotConfig.x.axisType) {
                expandedPlotConfig.x.axisType = chart_def_1.AxisType.Default;
            }
            if (!expandedPlotConfig.x.label) {
                expandedPlotConfig.x.label = {};
            }
        }
        if (!expandedPlotConfig.y) {
            expandedPlotConfig.y = {
                axisType: chart_def_1.AxisType.Default,
                label: {},
            };
        }
        else {
            if (!expandedPlotConfig.y.axisType) {
                expandedPlotConfig.y.axisType = chart_def_1.AxisType.Default;
            }
            if (!expandedPlotConfig.y.label) {
                expandedPlotConfig.y.label = {};
            }
        }
        if (!expandedPlotConfig.y2) {
            expandedPlotConfig.y2 = {
                axisType: chart_def_1.AxisType.Default,
                label: {},
            };
        }
        else {
            if (!expandedPlotConfig.y2.axisType) {
                expandedPlotConfig.y2.axisType = chart_def_1.AxisType.Default;
            }
            if (!expandedPlotConfig.y2.label) {
                expandedPlotConfig.y2.label = {};
            }
        }
        //
        //TODO: I'm sure if the expansion should happen here in the serialization?
        // I feel there should be another function that does this expansion and that made it should happen
        // separately when the chart is rendered.
        //
        if (!expandedPlotConfig.legend) {
            expandedPlotConfig.legend = {
                show: showLegendDefault,
            };
        }
        else if (expandedPlotConfig.legend.show === undefined) {
            expandedPlotConfig.legend = Object.assign({}, expandedPlotConfig.legend, {
                show: showLegendDefault,
            });
        }
        var expandedGlobalAxisMap = {
            x: {
                series: "__index__",
            },
            y: [],
            y2: [],
        };
        if (globalAxisMap) {
            if (globalAxisMap.x) {
                if (Sugar.Object.isString(globalAxisMap.x)) {
                    expandedGlobalAxisMap.x = {
                        series: globalAxisMap.x,
                    };
                }
                else {
                    expandedGlobalAxisMap.x = globalAxisMap.x;
                }
            }
            if (globalAxisMap.y) {
                if (Sugar.Object.isString(globalAxisMap.y)) {
                    expandedGlobalAxisMap.y = [
                        {
                            series: globalAxisMap.y,
                        },
                    ];
                }
                else if (Sugar.Object.isArray(globalAxisMap.y)) {
                    expandedGlobalAxisMap.y = globalAxisMap.y.map(function (series) {
                        if (Sugar.Object.isString(series)) {
                            return {
                                series: series,
                            };
                        }
                        else {
                            return series;
                        }
                    });
                }
                else {
                    expandedGlobalAxisMap.y = [
                        globalAxisMap.y,
                    ];
                }
            }
            else {
                expandedGlobalAxisMap.y = [];
            }
            if (globalAxisMap.y2) {
                if (Sugar.Object.isString(globalAxisMap.y2)) {
                    expandedGlobalAxisMap.y2 = [
                        {
                            series: globalAxisMap.y2,
                        },
                    ];
                }
                else if (Sugar.Object.isArray(globalAxisMap.y2)) {
                    expandedGlobalAxisMap.y2 = globalAxisMap.y2.map(function (series) {
                        if (Sugar.Object.isString(series)) {
                            return {
                                series: series,
                            };
                        }
                        else {
                            return series;
                        }
                    });
                }
                else {
                    expandedGlobalAxisMap.y2 = [
                        globalAxisMap.y2,
                    ];
                }
            }
        }
        _this = _super.call(this, data, expandedPlotConfig, expandedGlobalAxisMap) || this;
        return _this;
    }
    /**
     * Deserialize an instance of PlotAPI from a previously serialize chart def.
     *
     * @param chartDef The chart definition to deserialize from.
     */
    PlotAPI.deserialize = function (chartDef) {
        return new PlotAPI(chartDef.data, chartDef.plotConfig, true, chartDef.axisMap);
    };
    return PlotAPI;
}(AbstractPlotAPI));
exports.PlotAPI = PlotAPI;
/**
 * Fluent API for configuring an axis of the chart.
 */
var AxisConfigAPI = /** @class */ (function (_super) {
    __extends(AxisConfigAPI, _super);
    function AxisConfigAPI(axisName, seriesName, axisConfig, singleAxisMap, data, plotConfig, globalAxisMap) {
        var _this = _super.call(this, data, plotConfig, globalAxisMap) || this;
        _this.axisName = axisName;
        _this.seriesName = seriesName;
        _this.axisConfig = axisConfig;
        _this.singleAxisMap = singleAxisMap;
        return _this;
    }
    /**
     * Set the label for the axis.
     */
    AxisConfigAPI.prototype.axisLabel = function (label) {
        if (!this.axisConfig.label) {
            this.axisConfig.label = {};
        }
        else if (typeof (this.axisConfig.label) === "string") {
            this.axisConfig.label = {};
        }
        this.axisConfig.label.text = label;
        return this;
    };
    /**
     * Set the type of the axis.
     */
    AxisConfigAPI.prototype.type = function (axisType) {
        this.axisConfig.axisType = axisType;
        return this;
    };
    return AxisConfigAPI;
}(AbstractPlotAPI));
/**
 * Fluent API for configuring an axis of the chart.
 */
var XAxisConfigAPI = /** @class */ (function (_super) {
    __extends(XAxisConfigAPI, _super);
    function XAxisConfigAPI(axisName, seriesName, axisConfig, singleAxisMap, data, plotConfig, globalAxisMap) {
        return _super.call(this, axisName, seriesName, axisConfig, singleAxisMap, data, plotConfig, globalAxisMap) || this;
    }
    /**
     * Set the position for the label.
     */
    XAxisConfigAPI.prototype.axisLabelPosition = function (position) {
        if (!this.axisConfig.label) {
            this.axisConfig.label = {};
        }
        else if (typeof (this.axisConfig.label) === "string") {
            this.axisConfig.label = {
                text: this.axisConfig.label,
            };
        }
        this.axisConfig.label.position = position;
        return this;
    };
    return XAxisConfigAPI;
}(AxisConfigAPI));
/**
 * Fluent API for configuring an axis of the chart.
 */
var YAxisConfigAPI = /** @class */ (function (_super) {
    __extends(YAxisConfigAPI, _super);
    function YAxisConfigAPI(axisName, seriesName, axisConfig, singleAxisMap, data, plotConfig, globalAxisMap) {
        return _super.call(this, axisName, seriesName, axisConfig, singleAxisMap, data, plotConfig, globalAxisMap) || this;
    }
    /**
     * Set the label for the series.
     */
    YAxisConfigAPI.prototype.seriesLabel = function (label) {
        this.singleAxisMap.label = label;
        return this;
    };
    /**
     * Set the display format for values of this series.
     */
    YAxisConfigAPI.prototype.format = function (formatString) {
        this.singleAxisMap.format = formatString;
        return this;
    };
    /**
     * Set the position for the label.
     */
    YAxisConfigAPI.prototype.axisLabelPosition = function (position) {
        if (!this.axisConfig.label) {
            this.axisConfig.label = {};
        }
        else if (typeof (this.axisConfig.label) === "string") {
            this.axisConfig.label = {
                text: this.axisConfig.label,
            };
        }
        this.axisConfig.label.position = position;
        return this;
    };
    /**
     * Configure an explicit x axis for the y axis.
     */
    YAxisConfigAPI.prototype.x = function (seriesName) {
        if (!this.singleAxisMap.x) {
            this.singleAxisMap.x = { series: seriesName };
        }
        else if (Sugar.Object.isString(this.singleAxisMap.x)) {
            this.singleAxisMap.x = { series: seriesName };
        }
        else {
            this.singleAxisMap.x.series = seriesName;
        }
        return new XAxisConfigAPI("x", seriesName, this.plotConfig.x, this.singleAxisMap.x, this.data, this.plotConfig, this.globalAxisMap);
    };
    return YAxisConfigAPI;
}(AxisConfigAPI));
//# sourceMappingURL=plot-api.js.map