import * as tslib_1 from "tslib";
import { Component, ChangeDetectionStrategy, Input, ElementRef, ViewChild, NgZone } from '@angular/core';
import { generateColorsBySeries, generateColorsByDataPoints } from './color.helpers';
import { fromEvent } from 'rxjs/observable/fromEvent';
import { CurrencyPipe } from '@angular/common';
import * as Chart from 'chart.js';
import * as _ from 'lodash';
import * as moment from 'moment';
/**
 * @internal
 */
var MULTI_SERIES_BY_DEFAULT = ['line', 'bar', 'horizontalBar'];
/**
 * @internal
 */
export function getTooltipLabelCallBack(currency, percentage) {
    return function (tooltipItem, data) {
        var ds = data.datasets || [];
        var label = ds.length > 1 ? ds[tooltipItem.datasetIndex || 0].label || '' : '';
        var dsData = ds[tooltipItem.datasetIndex || 0].data || [];
        var point = dsData[tooltipItem.index || 0];
        var value = _.isNumber(point) ? point : point.y;
        if (label) {
            label += ' 	\r\n';
        }
        if (percentage !== 'only') {
            if (currency) {
                label += formatMoney(value, currency);
            }
            else {
                label += tooltipItem.yLabel || value;
            }
        }
        if (percentage) {
            var total = _.sumBy(dsData, function (d) { return (_.isNumber(d) ? d : d.y); });
            label += " \t\r\n" + (value && total ? (value * 100 / total).toFixed(2) : 0) + "%";
        }
        return label;
    };
}
/**
 * @internal
 */
export function getTooltipTitleCallBack(horizontal) {
    return function (tooltipItems, data) {
        // Pick first xLabel for now
        var title = '';
        var labels = data.labels || [];
        var labelCount = labels.length;
        if (tooltipItems.length > 0) {
            var item = tooltipItems[0];
            if (item.xLabel || (horizontal && item.yLabel)) {
                title = (horizontal ? item.yLabel : item.xLabel) || '';
            }
            else if ((labelCount > 0 && item.index) || 0 < labelCount) {
                title = labels[item.index || 0];
            }
        }
        return title;
    };
}
/**
 * @internal
 */
export function formatScale(val, currency) {
    var n = Number(val);
    if (n === 0) {
        return '0';
    }
    var base = '';
    if (n >= 1000) {
        n = n / 1000;
        base = 'K';
    }
    if (n >= 1000) {
        n = n / 1000;
        base = 'M';
    }
    return (formatMoney(n, currency) || '').replace(/\.0+?$/, '') + base;
}
/**
 * @internal
 */
export function formatMoney(val, currency) {
    if (val && currency) {
        var pipe = new CurrencyPipe(moment.locale());
        return pipe.transform(val, currency, 'symbol-narrow');
    }
}
var ChartComponent = /** @class */ (function () {
    function ChartComponent(_zone) {
        var _this = this;
        this._zone = _zone;
        this._config = {};
        this._prevConfig = {};
        this._params = {};
        this._prevParams = {};
        this._wndEvSubs = fromEvent(window, 'resize').subscribe(function () {
            _this._resized = true;
        });
    }
    Object.defineProperty(ChartComponent.prototype, "labels", {
        /**
         * An array of strings, corresponding to Chart.ChartConfiguration.data.labels
         * @property
         */
        set: function (l) {
            this._params.labels = l;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "type", {
        /**
         * The chart type, corresponding to Chart.ChartConfiguration.type
         * @default 'bar'
         * @property
         */
        set: function (t) {
            this._params.type = t;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "datasets", {
        /**
         * An array of Chart.ChartDataSets, corresponding to Chart.ChartConfiguration.data.datasets
         * @property
         */
        set: function (ds) {
            this._params.datasets = ds;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "colors", {
        /**
         * An array of strings in hex, rgb, or rgba format, to define the base colors of datasets, or the data points of each dataset. If this
         * property is absent, or the colors defined are less than the number of datasets (or the data points of each dataset), the missing colors will
         * be assigned from the defult palette
         * @property
         */
        set: function (c) {
            this._params.colors = c;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "colorsFor", {
        /**
         * Specify what the colors (either defined or generated) are for.
         *  * 'series' - each color corresponds to a dataset
         *  * 'data' - each color corresponds to a data point of each dataset
         *  * 'auto' - use 'series' for chart types 'bar', 'horizontalBar' and 'line'; and use 'data' for other chart types
         *  * 'none' - turn off the color generator. Use this value if colors are specified via #options
         * @default 'auto'
         * @property
         */
        set: function (cf) {
            this._params.colorsFor = cf;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "ratio", {
        /**
         * The aspect ratio of the chart
         * @default 2
         * @property
         */
        set: function (r) {
            this._params.ratio = r;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "legend", {
        /**
         * Specifiy how to display the legend
         *  * if the value is a boolean, it corresponds to Chart.ChartConfiguration.options.legend.display
         *  * if the value is 'auto', the legend will be arranged automatically. For example, the legend will be hidden if the chart area is too small
         *  * if the value is of type Chart.ChartLegendOptions, it corresponds to Chart.ChartConfiguration.options.legend.position
         *  * if the value is of type Chart.ChartLegendOptions, it corresponds to Chart.ChartConfiguration.options.legend
         * @default 'auto'
         * @property
         */
        set: function (l) {
            this._params.legend = l;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "currency", {
        /**
         * An ISO 4217 currency code. If specified, it will be used to format the scales of the main axis, as well as the values in the tooltips.
         * @property
         */
        set: function (curr) {
            this._params.currency = curr;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "options", {
        /**
         * Corresponds to Chart.ChartConfiguration.options. This overrides any other settings.
         * @property
         */
        set: function (opt) {
            this._params.options = opt;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "timeFormat", {
        /**
         * A moment.js format string. When specified, the X axis will be configured with time scales, and this value will be used to format the tooltips.
         * @property
         */
        set: function (tf) {
            this._params.timeFormat = tf;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ChartComponent.prototype, "percentage", {
        /**
         * Specify whether to show the overall percentage values in the popover tooltips.
         * @property
         */
        set: function (p) {
            this._params.percentage = p;
        },
        enumerable: true,
        configurable: true
    });
    ChartComponent.prototype.ngDoCheck = function () {
        var _this = this;
        if (this._debounceTimer) {
            clearTimeout(this._debounceTimer);
        }
        this._zone.runOutsideAngular(function () {
            _this._debounceTimer = setTimeout(function () { return _this._checkUpdate(); }, 50);
        });
    };
    ChartComponent.prototype.ngOnDestroy = function () {
        if (this._chart) {
            this._chart.destroy();
        }
        this._wndEvSubs.unsubscribe();
    };
    ChartComponent.prototype._checkUpdate = function () {
        var dataOrParamsChanged = false;
        if (!_.isEqual(this._params, this._prevParams) || this._resized) {
            this._applyConfig();
            dataOrParamsChanged = true;
        }
        dataOrParamsChanged = dataOrParamsChanged || !_.isEqual(this._config.data, this._prevConfig.data);
        var configChanged = !_.isEqual(JSON.stringify(this._config.options), JSON.stringify(this._prevConfig.options)) ||
            this._config.type !== this._prevConfig.type;
        configChanged = configChanged || this._params.colorsFor !== this._prevParams.colorsFor;
        configChanged = configChanged || !_.isEqual(this._params.colors, this._prevParams.colors);
        if (dataOrParamsChanged || configChanged || this._resized) {
            this._refresh(configChanged || this._resized);
        }
        this._resized = false;
    };
    ChartComponent.prototype._refresh = function (configChanged) {
        this._prevConfig = _.cloneDeep(this._config);
        this._prevParams = _.cloneDeep(this._params);
        if (configChanged || !this._chart) {
            if (this._chart) {
                this._chart.destroy();
            }
            var cfg = _.cloneDeep(this._prevConfig);
            this._applyColors(this._params.colors || [], this._params.colorsFor || 'auto', (cfg.data || {}).datasets || []);
            this._createNewChart(cfg);
        }
        else {
            this._chart.config.data = _.cloneDeep(this._config.data || {});
            this._applyColors(this._params.colors || [], this._params.colorsFor || 'auto', this._chart.config.data.datasets || []);
            this._chart.update();
        }
    };
    ChartComponent.prototype._createNewChart = function (cfg, retryIfTooSmall) {
        var _this = this;
        if (retryIfTooSmall === void 0) { retryIfTooSmall = true; }
        this._zone.runOutsideAngular(function () {
            var container = _this._chartContainer.nativeElement;
            var nodes = container.getElementsByTagName('canvas');
            if (nodes.length) {
                container.removeChild(nodes.item(0));
            }
            var canvas = document.createElement('canvas');
            container.appendChild(canvas);
            _this._chart = new Chart(canvas, cfg);
            var width = _this._chart.chartArea.right - _this._chart.chartArea.left;
            var height = _this._chart.chartArea.bottom - _this._chart.chartArea.top;
            if ((width < 100 || height < 100) && retryIfTooSmall) {
                _this._chart.destroy();
                ((cfg.options || {}).legend || {}).display = false;
                _this._createNewChart(cfg, false);
            }
        });
    };
    ChartComponent.prototype._applyConfig = function () {
        var _this = this;
        var multiType = MULTI_SERIES_BY_DEFAULT.indexOf(this._params.type || '') >= 0;
        this._config.type = this._params.type || 'bar';
        this._config.options = this._config.options || {};
        this._config.options.legend = this._config.options.legend || {};
        this._config.data = this._config.data || {};
        var ds = (this._config.data.datasets = _.cloneDeep(this._params.datasets || []));
        if (!(this._config.data.datasets || []).some(function (d) { return (d.label ? true : false); })) {
            if (multiType) {
                this._config.options.legend.display = false;
            }
        }
        var labels = (this._config.data.labels = _.cloneDeep(this._params.labels || []));
        _.set(this._config.options || {}, 'aspectRatio', this._params.ratio || 2);
        var legend = this._params.legend || 'auto';
        if (_.isString(legend) && ['top', 'right', 'bottom', 'left'].indexOf(legend) >= 0) {
            this._config.options.legend.position = legend;
        }
        else if (_.isBoolean(legend)) {
            this._config.options.legend.display = legend;
        }
        else if (_.isObject(legend)) {
            this._config.options.legend = _.cloneDeep(legend);
        }
        else if (legend === 'auto') {
            var multiPoints = ds.every(function (d) { return (d.data || []).length > 1; });
            var maxPoints = _.max(ds.map(function (d) { return (d.data || []).length; })) || 0;
            this._config.options.legend.display = (multiType && ds.length > 1) || (multiPoints && !multiType);
            this._config.options.legend.position = multiType ? 'top' : 'right';
        }
        this._config.options.scales = {};
        if (this._params.currency) {
            var curr_1 = this._params.currency;
            if (multiType) {
                var axes = {
                    ticks: { callback: function (val) { return formatScale(val, curr_1); } }
                };
                if (ds.every(function (d) { return _this._isYAxisAllNumbers(d.data || []); })) {
                    if (this._params.type !== 'horizontalBar') {
                        this._config.options.scales.yAxes = [axes];
                    }
                    else {
                        this._config.options.scales.xAxes = [axes];
                    }
                }
            }
        }
        var timeScaleConfigured = false;
        if (this._params.type === 'line') {
            var isTimeScale = ds.every(function (d) {
                return (d.data || []).every(function (item) { return (item.x ? moment(item.x).isValid() : false); });
            });
            if (isTimeScale) {
                this._config.options.scales = this._config.options.scales || {};
                var minTime = _.min(_.flatMap(ds, function (d) { return d.data; }).map(function (p) { return moment(p.x); }));
                ds.every(function (d) {
                    return (d.data || []).every(function (item) { return (item.x ? moment(item.x).isValid() : false); });
                });
                this._config.options.scales.xAxes = [
                    {
                        type: 'time',
                        time: { tooltipFormat: this._params.timeFormat || 'L', min: minTime ? minTime.toISOString() : undefined }
                    }
                ];
                timeScaleConfigured = true;
            }
        }
        if (ds.length === 0 || (ds[0].data || []).length === 0) {
            console.warn('ezy-chart: empty datasets. ');
        }
        else if ((ds[0].data || []).length > labels.length && !timeScaleConfigured) {
            console.warn('ezy-chart: wrong number of labels. ');
        }
        if (_.isEmpty(this._config.options.scales)) {
            this._config.options = _.pickBy(this._config.options, function (val, key) { return key !== 'scales'; });
        }
        this._config.options.tooltips = this._config.options.tooltips || {};
        this._config.options.tooltips.callbacks = this._config.options.tooltips.callbacks || {};
        this._config.options.tooltips.callbacks.label = getTooltipLabelCallBack(this._params.currency, this._params.percentage || false);
        this._config.options.tooltips.callbacks.title = getTooltipTitleCallBack(this._params.type === 'horizontalBar');
        if (this._params.options) {
            _.merge(this._config.options, this._params.options);
        }
    };
    ChartComponent.prototype._isYAxisAllNumbers = function (data) {
        return data.every(function (d) { return _.isNumber(d) || _.isNumber(d.y); });
    };
    ChartComponent.prototype._applyColors = function (colors, colorsFor, datasets) {
        if (colorsFor === 'auto') {
            if (MULTI_SERIES_BY_DEFAULT.indexOf(this._params.type || '') >= 0) {
                colorsFor = 'series';
            }
            else {
                colorsFor = 'data';
            }
        }
        if (colorsFor === 'series') {
            var colorGroups = generateColorsBySeries(colors, datasets.length, this._params.type || 'bar');
            _.merge(datasets, colorGroups);
        }
        else if (colorsFor === 'data') {
            for (var _i = 0, datasets_1 = datasets; _i < datasets_1.length; _i++) {
                var ds = datasets_1[_i];
                var colorGroup = generateColorsByDataPoints(colors, (ds.data || []).length, this._params.type || 'bar');
                _.merge(ds, colorGroup);
            }
        }
    };
    tslib_1.__decorate([
        ViewChild('chartContainer', { read: ElementRef }),
        tslib_1.__metadata("design:type", ElementRef)
    ], ChartComponent.prototype, "_chartContainer", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Array),
        tslib_1.__metadata("design:paramtypes", [Array])
    ], ChartComponent.prototype, "labels", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String),
        tslib_1.__metadata("design:paramtypes", [String])
    ], ChartComponent.prototype, "type", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Array),
        tslib_1.__metadata("design:paramtypes", [Array])
    ], ChartComponent.prototype, "datasets", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Array),
        tslib_1.__metadata("design:paramtypes", [Array])
    ], ChartComponent.prototype, "colors", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String),
        tslib_1.__metadata("design:paramtypes", [String])
    ], ChartComponent.prototype, "colorsFor", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Number),
        tslib_1.__metadata("design:paramtypes", [Number])
    ], ChartComponent.prototype, "ratio", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [Object])
    ], ChartComponent.prototype, "legend", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String),
        tslib_1.__metadata("design:paramtypes", [String])
    ], ChartComponent.prototype, "currency", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [Object])
    ], ChartComponent.prototype, "options", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String),
        tslib_1.__metadata("design:paramtypes", [String])
    ], ChartComponent.prototype, "timeFormat", null);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Object),
        tslib_1.__metadata("design:paramtypes", [Object])
    ], ChartComponent.prototype, "percentage", null);
    ChartComponent = tslib_1.__decorate([
        Component({
            selector: 'ezy-chart',
            template: "<div #chartContainer></div>",
            changeDetection: ChangeDetectionStrategy.OnPush
        }),
        tslib_1.__metadata("design:paramtypes", [NgZone])
    ], ChartComponent);
    return ChartComponent;
}());
export { ChartComponent };
//# sourceMappingURL=chart.component.js.map