import * as d3 from 'd3';
import { each, first, last } from 'lodash';
import * as moment from 'moment';
import * as nv from 'nvd3';
import { GetHistoryDataArgs } from '../../config';
import { HistoricalCurveTimeRange } from '../../config/history-data/historical-curve.time-range';
import { LOCALIZATION } from '../../localization';
import { ConditionalDisplayElement } from '../base/conditional-display-element';
import { HistoricalCurveElementStatus } from './historical-curve-element-status';
import { LOGGER_SERVICE_TOKEN } from '../../logger';
import { GlobalSettings, DisplayMode } from '../../settings';
import { CurveType } from '../../model/historical-curve/curve-type';
import { AxisRangeType } from '../../model/historical-curve/historical-curve-axis-settings';
export class HistoricalCurveElement extends ConditionalDisplayElement {
    constructor(element, injector, permissionChecker, variableCommunicator, variableStore, historyDataStore, signalRAppId) {
        super(element, permissionChecker, variableCommunicator, variableStore, signalRAppId);
        this.historyDataStore = historyDataStore;
        this.displayOption = {
            dataLimit: 500,
            dataZoomHeight: 32,
            marginLeft: 40,
            marginRight: 20,
            mobileMinWidth: 450,
            operationAreaHeight: 32,
            operationAreaMarginTop: 25,
            operationSelectFontSize: '16px',
            operationButtonWidth: 24,
            operationButtonHeight: 24,
            operationButtonMargin: 4
        };
        this.elementStatus = HistoricalCurveElementStatus.Loading;
        this.logger = injector.get(LOGGER_SERVICE_TOKEN);
        this.localization = injector.get(LOCALIZATION);
        this.timePeriods = this.getValidTimePeriods();
        this.updateTimeRange(this.model.displaySetting.displayTimePeriod);
        this.refreshIntervalId = setInterval(() => {
            this.loadFirstPage();
            this.logger.debug(`[GUI]Refresh Histoical Curve:${d3.time.format('%x %X')(new Date())}`);
        }, this.model.displaySetting.refreshInterval * 1000);
        this.isMobileMode = DisplayMode.Mobile === injector.get(GlobalSettings).displayMode;
        if (this.isMobileMode) {
            this.displayOption.operationAreaMarginTop = 35;
            if (this.model.displaySetting.size.width >= this.displayOption.mobileMinWidth) {
                this.displayOption.operationAreaHeight = 68;
                this.displayOption.operationSelectFontSize = '24px';
                this.displayOption.operationButtonWidth = 60;
                this.displayOption.operationButtonHeight = 60;
                this.displayOption.operationButtonMargin = 6;
            }
        }
        this.loadFirstPage();
    }
    dispose() {
        clearInterval(this.refreshIntervalId);
        if (this.chartElement) {
            this.chartElement.tooltip.hidden(true);
        }
        this.logger.debug(`[GUI]Dispose Histoical Curve Refresh Interval:${d3.time.format('%x %X')(new Date())}`);
    }
    getValidTimePeriods() {
        const timePeriods = new Array();
        timePeriods.push({ key: 6, name: this.localization.lastThirtyMinutes });
        timePeriods.push({ key: 1, name: this.localization.lastOneHour });
        timePeriods.push({ key: 7, name: this.localization.lastEightHour });
        timePeriods.push({ key: 2, name: this.localization.lastTwentyFourHours });
        timePeriods.push({ key: 3, name: this.localization.lastSevenDays });
        timePeriods.push({ key: 4, name: this.localization.lastThirtyDays });
        timePeriods.push({ key: 5, name: this.localization.lastOneYear });
        return timePeriods;
    }
    updateTimeRange(timePeriodType) {
        this.currentTimePeriod = +timePeriodType;
        this.updateQueryTimeRange();
    }
    updateQueryTimeRange() {
        this.endTime = moment();
        switch (this.currentTimePeriod) {
            case 1:
                this.startTime = moment().subtract(1, 'hours');
                break;
            case 3:
                this.startTime = moment().subtract(7, 'days');
                break;
            case 4:
                this.startTime = moment().subtract(30, 'days');
                break;
            case 5:
                this.startTime = moment().subtract(1, 'years');
                break;
            case 6:
                this.startTime = moment().subtract(30, 'minutes');
                break;
            case 7:
                this.startTime = moment().subtract(8, 'hours');
                break;
            default:
                this.startTime = moment().subtract(1, 'days');
        }
    }
    reRenderElement(startTime, endTime, limit, rangeType) {
        this.rootElement.selectAll('*').remove();
        this.chartElement = null;
        this.renderElement(startTime, endTime, limit, rangeType);
    }
    renderElement(startTime, endTime, limit, rangeType) {
        if (!this.model.dataSetting) {
            return;
        }
        const dataItemName = this.model.dataSetting.dataName;
        const dataSourceCode = this.model.dataSetting.dataSourceCode;
        const channelNames = this.model.dataSetting.channels.map(c => c.name);
        this.updateElementStatus(HistoricalCurveElementStatus.Loading);
        const input = new GetHistoryDataArgs(dataSourceCode, dataItemName, channelNames, startTime, endTime, limit, rangeType);
        this.historyDataStore.getHistoryData(input).subscribe(result => {
            if (result.error) {
                this.updateElementStatus(HistoricalCurveElementStatus.LoadFailed, result.error);
            }
            else {
                this.clearStatus();
                if (result.isUnbind) {
                    this.updateElementStatus(HistoricalCurveElementStatus.Unbound);
                }
                else {
                    this.updateElementStatus(HistoricalCurveElementStatus.Normal);
                }
                if (result.values.length) {
                    this.currentStartTime = moment(first(result.values).time);
                    this.currentEndTime = moment(last(result.values).time);
                }
                this.chartElement = this.renderChart(result.values);
            }
        });
    }
    renderChart(result) {
        const chartWidth = this.model.displaySetting.size.width;
        const chartHeight = this.model.displaySetting.size.height - this.displayOption.operationAreaHeight - this.displayOption.operationAreaMarginTop;
        const data = new Array();
        each(this.model.dataSetting.channels, (channel, key) => {
            const values = new Array();
            each(result, v => values.push({ x: moment(v.time).local().toDate().valueOf(), y: v.values[key] }));
            data.push({ key: channel.name, area: channel.projectEnabled, values: values });
        });
        nv.addGraph(() => {
            if (this.model.displaySetting.curveType === CurveType.BarGroup || this.model.displaySetting.curveType === CurveType.BarStack) {
                return this.getMultiBarWithFocusChart(chartWidth, chartHeight, data);
            }
            else {
                return this.getLineChart(chartWidth, chartHeight, data);
            }
        });
    }
    getLineChart(chartWidth, chartHeight, data) {
        const chart = nv.models.lineChart().showLegend(true)
            .margin({ top: 0, bottom: 0, left: this.displayOption.marginLeft, right: this.displayOption.marginRight })
            .noData(this.localization.chartNoData);
        if (this.model.displaySetting.axisSetting.yAxisRangeType === AxisRangeType.Custom) {
            chart.yDomain([this.model.displaySetting.axisSetting.yAxisMin, this.model.displaySetting.axisSetting.yAxisMax]);
        }
        if (!this.isMobileMode) {
            chart.focusEnable(true);
            chart.focus.margin({ top: 10, right: 0, bottom: 0, left: 0 });
            chart.focus.height(40);
            chart.focus.showXAxis(false);
        }
        this.renderCommonProperty(chart, chartWidth, chartHeight, data);
        this.renderOperationArea(chartWidth, chartHeight);
        return chart;
    }
    getMultiBarWithFocusChart(chartWidth, chartHeight, data) {
        const chart = nv.models.multiBarWithFocusChart()
            .margin({
            top: 0, bottom: 0, left: this.displayOption.marginLeft, right: this.displayOption.marginRight
        })
            .controlLabels({
            grouped: this.localization.grouped, stacked: this.localization.stacked
        })
            .noData(this.localization.chartNoData);
        if (this.model.displaySetting.curveType === CurveType.BarStack) {
            chart.stacked(true);
            chart.multibar.stacked(true);
            chart.multibar2.stacked(true);
        }
        if (this.model.displaySetting.axisSetting.yAxisRangeType === AxisRangeType.Custom) {
            chart.yDomain([this.model.displaySetting.axisSetting.yAxisMin, this.model.displaySetting.axisSetting.yAxisMax]);
        }
        if (!this.isMobileMode) {
            chart.focusEnable(true);
            chart.focusShowAxisX(false);
        }
        chart.xAxis.tickFormat(d => this.timeFormat(d, '%x %X'));
        this.renderCommonProperty(chart, chartWidth, chartHeight, data);
        this.renderOperationArea(chartWidth, chartHeight);
        const focusContext = this.rootElement.select('.nv-context');
        focusContext.select('.nv-barsWrap.nvd3-svg').attr('transform', 'translate(0,30)');
        focusContext.select('.nv-brushBackground').attr('transform', 'translate(0,30)');
        focusContext.select('.nv-x.nv-brush').attr('transform', 'translate(0,30)');
        if (!this.model.displaySetting.showAxis) {
            this.rootElement.select('.tick.zero').style('display', 'none');
            this.rootElement.select('.nv-x.nv-axis.nvd3-svg').style('display', 'none');
            this.rootElement.select('.nv-y.nv-axis.nvd3-svg').style('display', 'none');
        }
        chart.update();
        return chart;
    }
    renderCommonProperty(chart, chartWidth, chartHeight, data) {
        var _a, _b;
        chart.tooltip.headerFormatter(d => this.timeFormat(d, '%x %X'));
        if (this.model.displaySetting.showAxis) {
            chart.xAxis.showMaxMin(true).tickFormat(d => {
                if (this.currentTimePeriod === 3 || this.currentTimePeriod === 4 || this.currentTimePeriod === 5) {
                    return this.timeFormat(d, '%y-%m-%d');
                }
                return this.timeFormat(Number(d), '%X');
            });
            if (this.model.displaySetting.axisSetting) {
                if (this.model.displaySetting.axisSetting.showAxisLabel && this.model.displaySetting.axisSetting.axisLabelFont) {
                    chart.xAxis.fontSize(this.model.displaySetting.axisSetting.axisLabelFont.fontSize);
                    chart.yAxis.fontSize(this.model.displaySetting.axisSetting.axisLabelFont.fontSize).tickFormat(d => +d3.format('.9g')(d));
                }
                else {
                    chart.xAxis.tickFormat(() => '');
                    chart.yAxis.tickFormat(() => '');
                }
            }
        }
        else {
            chart.showXAxis(false);
            chart.showYAxis(false);
        }
        chart.width(chartWidth);
        chart.height(chartHeight);
        chart.color(this.model.dataSetting.channels.map(c => c.connectorColor));
        this.rootElement.append('g').datum(data).call(chart);
        this.rootElement.selectAll('.nv-noData').attr('x', chartWidth / 2).attr('y', chartHeight / 2 + this.displayOption.operationAreaHeight);
        nv.utils.windowResize(() => {
            chart.update();
            this.rootElement.selectAll('.nv-noData').attr('x', chartWidth / 2).attr('y', chartHeight / 2 + this.displayOption.operationAreaHeight);
        });
        const fontSize = (_b = (_a = this.model.displaySetting.axisSetting) === null || _a === void 0 ? void 0 : _a.axisLabelFont) === null || _b === void 0 ? void 0 : _b.fontSize;
        this.rootElement.selectAll('.domain').style('stroke-opacity', 1);
        if (this.model.displaySetting.showAxis && this.model.displaySetting.axisSetting) {
            const axisColor = this.model.displaySetting.axisSetting.axisColor;
            this.rootElement.selectAll('.domain').style('stroke', axisColor);
            if (fontSize && this.model.displaySetting.axisSetting.showAxisLabel) {
                this.rootElement.selectAll('.nv-axisMaxMin').select('text').style('font-size', fontSize);
            }
        }
        let strokeWidth = 0;
        if (this.model.displaySetting.axisSetting.showTick !== false) {
            strokeWidth = 1;
        }
        this.rootElement
            .selectAll('.nv-x')
            .selectAll('.tick')
            .selectAll('line')
            .attr('style', `stroke:${this.model.displaySetting.axisSetting.xAxisTickColor || 'rgb(127, 147, 159)'};stroke-width:${strokeWidth};`);
        this.rootElement
            .selectAll('.nv-y')
            .selectAll('.tick')
            .selectAll('line')
            .attr('style', `stroke:${this.model.displaySetting.axisSetting.yAxisTickColor || 'rgb(127, 147, 159)'};stroke-width:${strokeWidth};`);
        if (fontSize && this.currentTimePeriod === 3 || this.currentTimePeriod === 4 || this.currentTimePeriod === 5) {
            const self = this;
            this.rootElement
                .selectAll('.nv-x')
                .selectAll('.tick')
                .selectAll('text')
                .data(function (d) {
                return [self.timeFormat(Number(d), '%y-%m-%d'), self.timeFormat(Number(d), '%H:%M:%S')];
            })
                .enter()
                .append('text')
                .attr('class', 'full-date')
                .attr('x', 0)
                .attr('y', 0)
                .attr('dy', '2.3em')
                .style('text-anchor', 'middle')
                .style('font-size', fontSize)
                .text((d) => d);
            this.rootElement
                .selectAll('.nv-axisMaxMin-x')
                .selectAll('text')
                .data(function (d) {
                return [self.timeFormat(Number(d), '%y-%m-%d'), self.timeFormat(Number(d), '%H:%M:%S')];
            })
                .enter()
                .append('text')
                .attr('class', 'full-date')
                .attr('x', 0)
                .attr('y', 0)
                .attr('dy', '2.3em')
                .style('text-anchor', 'middle')
                .style('font-size', fontSize)
                .text((d) => d);
            const focusWrap = this.rootElement.selectAll('.nv-focusWrap');
            if (focusWrap.size()) {
                let h = focusWrap.attr('transform');
                if (h && h.length && h.indexOf(',') !== -1) {
                    h = h.slice(0, -1).split(',')[1];
                    this.rootElement
                        .selectAll('.nv-focusWrap')
                        .attr('transform', `translate(0,${Number(h) + 15})`);
                }
            }
            const resizeObserver = new window.MutationObserver(() => {
                this.rootElement
                    .selectAll('.nv-x')
                    .selectAll('.tick')
                    .selectAll('.full-date')
                    .remove();
                this.rootElement
                    .selectAll('.nv-x')
                    .selectAll('.tick')
                    .selectAll('text')
                    .data(function (d) {
                    return [self.timeFormat(Number(d), '%y-%m-%d'), self.timeFormat(Number(d), '%H:%M:%S')];
                })
                    .enter()
                    .append('text')
                    .attr('class', 'full-date')
                    .attr('x', 0)
                    .attr('y', 0)
                    .attr('dy', '2.3em')
                    .style('text-anchor', 'middle')
                    .style('font-size', fontSize)
                    .text((d) => d);
            });
            const extent = document.getElementsByClassName('extent');
            if (extent.length) {
                resizeObserver.observe(extent[0], { attributes: true });
            }
        }
        else {
            this.rootElement
                .selectAll('.full-date')
                .remove();
        }
    }
    renderOperationArea(chartWidth, chartHeight) {
        const operationArea = this.rootElement.append('g').attr('transform', `translate(0,${chartHeight + this.displayOption.operationAreaMarginTop})`)
            .append('foreignObject').attr('width', chartWidth).attr('height', this.displayOption.operationAreaHeight).attr('fill', 'none')
            .append('xhtml:div').style('height', (this.displayOption.operationAreaHeight - 4) + 'px').style('overflow', 'hidden').style('margin-top', '4px');
        const selectElement = operationArea.append('select').style('margin-left', this.displayOption.marginLeft + 'px')
            .style('font-size', this.displayOption.operationSelectFontSize).on('change', () => {
            const displayTimePeriod = this.rootElement.select('select').property('value');
            this.updateTimeRange(displayTimePeriod);
            this.reRenderElement(this.startTime, this.endTime, this.displayOption.dataLimit, HistoricalCurveTimeRange.BeginOpenEndOpen);
        });
        const options = selectElement.selectAll('option').data(this.timePeriods).enter().append('option');
        options.text(d => d.name).attr('value', d => d.key).property('selected', d => d.key === Number(this.currentTimePeriod));
        const buttonWidth = this.displayOption.operationButtonWidth + 'px', buttonHeight = this.displayOption.operationButtonHeight + 'px';
        operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
            .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/last_page.svg)')
            .on('click', () => { this.loadLastPage(); });
        operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
            .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/next_page.svg)')
            .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
            .on('click', () => { this.loadNextPage(); });
        operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
            .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/previous_page.svg)')
            .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
            .on('click', () => { this.loadPreviousPage(); });
        operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
            .style('float', 'right').style('background', 'white').style('background-image', 'url(assets/img/first_page.svg)')
            .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
            .on('click', () => { this.loadFirstPage(); });
    }
    timeFormat(datetime, specifier) {
        return d3.time.format(specifier)(new Date(datetime));
    }
    loadFirstPage() {
        this.updateQueryTimeRange();
        this.reRenderElement(this.startTime, this.endTime, this.displayOption.dataLimit, HistoricalCurveTimeRange.BeginOpenEndOpen);
    }
    loadNextPage() {
        this.reRenderElement(this.currentEndTime, this.endTime, this.displayOption.dataLimit, HistoricalCurveTimeRange.BeginOpenEndClose);
    }
    loadPreviousPage() {
        this.reRenderElement(this.startTime, this.currentStartTime, -this.displayOption.dataLimit, HistoricalCurveTimeRange.BeginCloseEndOpen);
    }
    loadLastPage() {
        this.updateQueryTimeRange();
        this.reRenderElement(this.startTime, this.endTime, -this.displayOption.dataLimit, HistoricalCurveTimeRange.BeginOpenEndOpen);
    }
    initElementStatus() {
        if (this.model.dataSetting && null != this.model.dataSetting.dataSourceCode) {
            this.updateElementStatus(HistoricalCurveElementStatus.Loading);
        }
    }
    updateElementStatus(status, error) {
        switch (status) {
            case HistoricalCurveElementStatus.Normal:
                if (this.elementStatus !== HistoricalCurveElementStatus.Normal) {
                    this.elementStatus = HistoricalCurveElementStatus.Normal;
                }
                break;
            case HistoricalCurveElementStatus.Unbound:
                this.elementStatus = HistoricalCurveElementStatus.Unbound;
                this.setStatusAsUnbound();
                break;
            case HistoricalCurveElementStatus.Loading:
                if (this.elementStatus !== HistoricalCurveElementStatus.Unbound) {
                    this.elementStatus = HistoricalCurveElementStatus.Loading;
                    this.setStatusAsLoading();
                }
                break;
            case HistoricalCurveElementStatus.LoadFailed:
                if (this.elementStatus !== HistoricalCurveElementStatus.Unbound) {
                    this.elementStatus = HistoricalCurveElementStatus.LoadFailed;
                    this.setStatusAsLoadFailed(error ? error : this.localization.abnormal);
                }
                break;
            default:
                this.logger.error(`Invalid HistoricalCurveElementStatus:${status}`);
                break;
        }
    }
    setStatusAsUnbound() {
        this.renderStatus('assets/img/unbind.svg', this.localization.unbind, '#586e7c');
    }
    setStatusAsLoading() {
        this.renderStatus('assets/img/loading.svg', this.localization.loading, '#226abc');
    }
    setStatusAsLoadFailed(error) {
        this.logger.error(`[GUI]Failed to load historical curve:${error}`);
        this.renderStatus('assets/img/abnormal.svg', error, '#f6a520');
    }
    renderStatus(icon, tip, stroke) {
        if (this.elementStatus === HistoricalCurveElementStatus.Normal) {
            this.clearStatus();
            return;
        }
        this.rootElement.append('rect').attr('id', 'StateFrame').attr('fill', 'transparent')
            .attr('width', this.model.displaySetting.size.width)
            .attr('height', this.model.displaySetting.size.height);
        const document = this.$element[0].ownerDocument;
        const currentRect = this.$element.find('rect#StateFrame').first();
        if (!currentRect.length) {
            return;
        }
        this.$element.find('image#StateImage').remove();
        const imgObj = document.createElementNS('http://www.w3.org/2000/svg', 'image');
        if (imgObj) {
            let x = Number(currentRect[0].getAttribute('width')) - 20;
            const currentRectX = currentRect[0].getAttribute('x');
            if (currentRectX !== null) {
                x += Number(currentRectX);
            }
            currentRect[0].setAttribute('stroke', stroke);
            currentRect[0].setAttribute('stroke-opacity', '0.5');
            imgObj.href.baseVal = icon;
            imgObj.setAttributeNS(null, 'id', 'StateImage');
            imgObj.setAttributeNS(null, 'x', x.toString());
            imgObj.setAttributeNS(null, 'y', '0');
            imgObj.setAttributeNS(null, 'height', '20px');
            imgObj.setAttributeNS(null, 'width', '20px');
            const titleElement = document.createElementNS('http://www.w3.org/2000/svg', 'title');
            const text = document.createTextNode(tip);
            titleElement.appendChild(text);
            imgObj.appendChild(titleElement);
            this.$element.append(imgObj);
        }
    }
    clearStatus() {
        const currentRect = this.$element.find('rect#StateFrame').first();
        if (!currentRect.length) {
            return;
        }
        const stroke = currentRect[0].getAttribute('stroke');
        if (stroke) {
            currentRect[0].removeAttribute('stroke');
        }
        this.$element.find('image#StateImage').remove();
    }
}
