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 { SYSTEM_TEXT_LIBRARY_TYPES, TIME_PERIOD_KEYS } from '../../service';
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, systemTextLibraryService, languageService, guiContext) {
        super(element, permissionChecker, variableCommunicator, variableStore, signalRAppId);
        this.historyDataStore = historyDataStore;
        this.systemTextLibraryService = systemTextLibraryService;
        this.languageService = languageService;
        this.guiContext = guiContext;
        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.data = [];
        this.needResize = true;
        this.setNeedResize = () => {
            this.needResize = false;
            setTimeout(() => this.needResize = true, 500);
        };
        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();
        this.initKeyboardListener();
        // 订阅语种变化事件
        this.subscribeLanguageChange();
    }
    dispose() {
        clearInterval(this.refreshIntervalId);
        if (this.chartElement) {
            this.chartElement.tooltip.hidden(true);
        }
        if (this.resizeEventListener) {
            this.resizeEventListener.clear();
        }
        if (this.isAndroid) {
            window.removeEventListener('native.keyboardshow', this.setNeedResize);
            window.removeEventListener('native.keyboardhide', this.setNeedResize);
        }
        // 取消语种变化订阅
        if (this.languageChangeSubscription) {
            this.languageChangeSubscription.unsubscribe();
            this.languageChangeSubscription = undefined;
        }
        this.logger.debug(`[GUI]Dispose Histoical Curve Refresh Interval:${d3.time.format('%x %X')(new Date())}`);
    }
    initKeyboardListener() {
        this.isAndroid = !!navigator.userAgent.match(/(Android)/i);
        if (this.isAndroid) {
            window.addEventListener('native.keyboardshow', this.setNeedResize);
            window.addEventListener('native.keyboardhide', this.setNeedResize);
        }
    }
    /**
     * 订阅语种变化事件
     */
    subscribeLanguageChange() {
        if (this.guiContext && this.guiContext.languageChanged$) {
            this.languageChangeSubscription = this.guiContext.languageChanged$.subscribe(() => {
                // 只更新时间段选择器的文案，不重新查询数据
                this.updateLanguageTexts();
            });
        }
    }
    /**
     * 更新语种相关的文案（时间段选择器）
     */
    updateLanguageTexts() {
        const selectElement = this.rootElement.select('select');
        if (!selectElement.empty()) {
            // 重新生成时间段数据
            const updatedTimePeriods = this.getValidTimePeriods();
            // 更新选项文本
            const options = selectElement.selectAll('option');
            options.each(function (_d, i) {
                if (i < updatedTimePeriods.length) {
                    d3.select(this).text(updatedTimePeriods[i].name);
                }
            });
        }
    }
    /**
     * 获取当前语种的 culture 代码
     */
    getCurrentCulture() {
        var _a, _b, _c, _e, _f, _g;
        // 获取当前语种ID
        const currentLanguageId = (_c = (_b = (_a = this.guiContext) === null || _a === void 0 ? void 0 : _a.getCurrentLanguageId) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : null;
        // 确定要使用的语种代码（culture）
        const defaultLanguage = ((_e = this.languageService) === null || _e === void 0 ? void 0 : _e.getDefaultLanguage()) || 'zh-CN';
        if (currentLanguageId === null || currentLanguageId === undefined) {
            // 设备未设置当前语种，使用默认语种
            return defaultLanguage;
        }
        else {
            // 设备已设置当前语种，获取对应的语种代码
            const currentLanguage = (_g = (_f = this.guiContext) === null || _f === void 0 ? void 0 : _f.getLanguageCultureById) === null || _g === void 0 ? void 0 : _g.call(_f, currentLanguageId);
            if (currentLanguage) {
                return currentLanguage;
            }
            else {
                // 无法获取语种代码，使用默认语种
                return defaultLanguage;
            }
        }
    }
    getValidTimePeriods() {
        const timePeriods = new Array();
        timePeriods.push({ key: 6, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_THIRTY_MINUTES) });
        timePeriods.push({ key: 1, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_ONE_HOUR) });
        timePeriods.push({ key: 7, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_EIGHT_HOURS) });
        timePeriods.push({ key: 2, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_TWENTY_FOUR_HOURS) });
        timePeriods.push({ key: 3, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_SEVEN_DAYS) });
        timePeriods.push({ key: 4, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_THIRTY_DAYS) });
        timePeriods.push({ key: 5, name: this.getTimePeriodText(TIME_PERIOD_KEYS.LAST_ONE_YEAR) });
        return timePeriods;
    }
    /**
     * 获取时间段文案（从系统文本库获取多语种翻译）
     */
    getTimePeriodText(textKey) {
        const currentCulture = this.getCurrentCulture();
        const systemType = SYSTEM_TEXT_LIBRARY_TYPES.COMPONENT_BUILTIN;
        if (this.systemTextLibraryService) {
            const translation = this.systemTextLibraryService.getSystemTextValue(systemType, textKey, currentCulture);
            if (translation) {
                return translation;
            }
        }
        return '';
    }
    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 dataSourceCode = this.model.dataSetting.dataSourceCode;
        this.updateElementStatus(HistoricalCurveElementStatus.Loading);
        // 【新格式】如果有多条目配置，使用多条目查询
        if (this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length > 0) {
            const historyDataItems = this.model.dataSetting.dataItems.map(item => ({
                dataItemName: item.dataName,
                channelNames: item.channels.map(c => c.name)
            }));
            // 使用第一个条目的信息作为兼容参数
            const firstItem = this.model.dataSetting.dataItems[0];
            const input = new GetHistoryDataArgs(dataSourceCode, firstItem.dataName, firstItem.channels.map(c => c.name), startTime, endTime, limit, rangeType, historyDataItems // 传递多条目参数
            );
            this.historyDataStore.getHistoryData(input).subscribe(result => {
                this.handleQueryResult(result);
            });
        }
        // 【旧格式】单条目模式
        else {
            const dataItemName = this.model.dataSetting.dataName;
            const channelNames = this.model.dataSetting.channels.map(c => c.name);
            const input = new GetHistoryDataArgs(dataSourceCode, dataItemName, channelNames, startTime, endTime, limit, rangeType);
            this.historyDataStore.getHistoryData(input).subscribe(result => {
                this.handleQueryResult(result);
            });
        }
    }
    handleQueryResult(result) {
        // 【新格式】多条目模式下的错误处理
        if (result.historyDataItems && result.historyDataItems.length > 0) {
            // 只要有条目正常返回(即使没有数据),就走正常逻辑
            // 这样可以确保即使部分条目失败,只要有条目成功返回,就显示正常状态
            this.clearStatus();
            if (result.isUnbind) {
                this.updateElementStatus(HistoricalCurveElementStatus.Unbound);
            }
            else {
                this.updateElementStatus(HistoricalCurveElementStatus.Normal);
            }
            // 如果有错误信息,在控制台输出警告
            if (result.error) {
                this.logger.warn(`[历史曲线] 部分条目查询失败: ${result.error}`);
            }
            // 检查是否有任何条目有数据
            const hasAnyData = result.historyDataItems.some(item => item.rows && item.rows.length > 0);
            if (hasAnyData) {
                // 有数据,计算所有条目的最小和最大时间，确保时间轴覆盖所有数据
                let globalMinTime = null;
                let globalMaxTime = null;
                result.historyDataItems.forEach(item => {
                    if (item.rows && item.rows.length > 0) {
                        const itemMinTime = moment(first(item.rows).time);
                        const itemMaxTime = moment(last(item.rows).time);
                        if (!globalMinTime || itemMinTime.isBefore(globalMinTime)) {
                            globalMinTime = itemMinTime;
                        }
                        if (!globalMaxTime || itemMaxTime.isAfter(globalMaxTime)) {
                            globalMaxTime = itemMaxTime;
                        }
                    }
                });
                // 设置全局时间范围
                if (globalMinTime && globalMaxTime) {
                    this.currentStartTime = globalMinTime;
                    this.currentEndTime = globalMaxTime;
                }
            }
            // 无论是否有数据,都渲染曲线(没有数据会显示空曲线)
            this.chartElement = this.renderChartWithMultiItems(result.historyDataItems);
        }
        // 【旧格式】单条目模式的错误处理
        else 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);
        }
    }
    setupTooltipAutoHide(chart) {
        const chartContainer = this.rootElement.select('.nv-focus').node();
        if (!chartContainer || !chart)
            return;
        let timeoutId;
        // 鼠标移入图表时显示 tooltip
        chartContainer.addEventListener('mouseover', () => {
            hideTooltipAfterDelay();
        });
        const clearTooltipTimeout = () => {
            if (timeoutId) {
                clearTimeout(timeoutId);
                timeoutId = null;
            }
        };
        const hideTooltipAfterDelay = () => {
            clearTooltipTimeout();
            timeoutId = setTimeout(() => {
                chart.tooltip.hidden(true);
            }, 2000); // 2秒延迟
        };
    }
    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] }));
            const displayName = channel.name;
            data.push({ key: displayName, area: channel.projectEnabled, values: values });
        });
        this.data = data;
        nv.addGraph(() => {
            let chart;
            if (this.model.displaySetting.curveType === CurveType.BarGroup || this.model.displaySetting.curveType === CurveType.BarStack) {
                chart = this.getMultiBarWithFocusChart(chartWidth, chartHeight, data);
            }
            else {
                chart = this.getLineChart(chartWidth, chartHeight, data);
            }
            // 设置 tooltip 自动隐藏逻辑
            this.setupTooltipAutoHide(chart);
            return chart;
        });
    }
    /**
     * 【新格式】多条目独立数据的曲线渲染
     * 每个条目独立保持自己的时间戳，不会出现时间戳混乱的问题
     */
    renderChartWithMultiItems(historyDataItems) {
        const chartWidth = this.model.displaySetting.size.width;
        const chartHeight = this.model.displaySetting.size.height - this.displayOption.operationAreaHeight - this.displayOption.operationAreaMarginTop;
        const data = new Array();
        // 创建一个 Map 用于快速查找条目数据
        const itemDataMap = new Map();
        each(historyDataItems, item => {
            if (item && item.itemName) {
                itemDataMap.set(item.itemName, item.rows || []);
            }
        });
        // 判断是否只有一个条目
        const isSingleItem = this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length === 1;
        // 为每个条目的每个通道创建独立的曲线
        each(this.model.dataSetting.dataItems, (dataItem) => {
            const itemName = dataItem.dataName;
            // 通过 itemName 匹配数据，而不是通过索引
            const itemRows = itemDataMap.get(itemName) || [];
            each(dataItem.channels, (channel, channelIdx) => {
                const values = new Array();
                // 每个条目使用自己的时间戳和数据
                each(itemRows, row => {
                    const value = row.values && row.values[channelIdx] !== undefined ? row.values[channelIdx] : null;
                    values.push({ x: moment(row.time).local().toDate().valueOf(), y: value });
                });
                // 只有多个条目时才使用"条目名-通道名"格式，单条目时只显示通道名
                const displayName = isSingleItem ? channel.name : `${itemName}-${channel.name}`;
                data.push({ key: displayName, area: channel.projectEnabled, values: values });
            });
        });
        this.data = data;
        nv.addGraph(() => {
            let chart;
            if (this.model.displaySetting.curveType === CurveType.BarGroup || this.model.displaySetting.curveType === CurveType.BarStack) {
                chart = this.getMultiBarWithFocusChart(chartWidth, chartHeight, data);
            }
            else {
                chart = this.getLineChart(chartWidth, chartHeight, data);
            }
            // 设置 tooltip 自动隐藏逻辑
            this.setupTooltipAutoHide(chart);
            return chart;
        });
    }
    initPoint() {
        try {
            const legendList = this.$element
                .find('.nv-legend')
                .find('.nv-series');
            let hiddenCount = 0;
            // 获取所有通道（支持多条目和单条目模式）
            const allChannels = [];
            if (this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length > 0) {
                // 【新格式】多条目模式：收集所有条目的所有通道
                each(this.model.dataSetting.dataItems, dataItem => {
                    each(dataItem.channels, channel => {
                        allChannels.push(channel);
                    });
                });
            }
            else {
                // 【旧格式】单条目模式
                allChannels.push(...this.model.dataSetting.channels);
            }
            for (let i = 0; i < this.data.length; i++) {
                const channel = allChannels[i];
                if (legendList.eq(i).children().eq(0).css('fill-opacity') === '1') {
                    const pointList = this.$element
                        .find('.nv-scatterWrap')
                        .find('.nv-series-' + (i - hiddenCount))
                        .find('.nv-point');
                    if (pointList && pointList.length) {
                        for (let j = 0; j < pointList.length; j++) {
                            const point = pointList.eq(j);
                            const previousPoint = pointList.eq(j - 1);
                            if (j && point.attr('transform').split(',')[1] !== previousPoint.attr('transform').split(',')[1]) {
                                if (channel.enablePoint && channel.pointColor) {
                                    const pointStyle = {
                                        'stroke-opacity': 1,
                                        'stroke-width': '2px',
                                        'stroke': channel.pointColor,
                                        'fill-opacity': 1,
                                        'fill': channel.pointColor
                                    };
                                    point.addClass('nv-mark-point');
                                    point.css(pointStyle);
                                    previousPoint.addClass('nv-mark-point');
                                    previousPoint.css(pointStyle);
                                }
                            }
                        }
                    }
                }
                else {
                    hiddenCount++;
                }
            }
        }
        catch (e) {
            console.log(e);
        }
    }
    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);
        this.initPoint();
        chart.legend.dispatch.on('legendClick', () => {
            setTimeout(() => {
                this.$element.find('.nv-mark-point').css({
                    'stroke-opacity': 0,
                    'stroke-width': 0,
                    'stroke': 'unset',
                    'fill-opacity': 0,
                    'fill': 'unset'
                }).removeClass('nv-mark-point');
                this.initPoint();
            }, 1);
        });
        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 => {
                this.$element.find('.nv-mark-point').css({
                    'stroke-opacity': 0,
                    'stroke-width': 0,
                    'stroke': 'unset',
                    'fill-opacity': 0,
                    'fill': 'unset'
                }).removeClass('nv-mark-point');
                clearTimeout(this.timer);
                this.timer = undefined;
                this.timer = setTimeout(() => {
                    this.initPoint();
                }, 100);
                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);
        // 获取所有通道的颜色（支持多条目和单条目模式）
        let channelColors = [];
        if (this.model.dataSetting.dataItems && this.model.dataSetting.dataItems.length > 0) {
            // 【新格式】多条目模式：收集所有条目的所有通道颜色
            each(this.model.dataSetting.dataItems, dataItem => {
                each(dataItem.channels, channel => {
                    channelColors.push(channel.connectorColor);
                });
            });
        }
        else {
            // 【旧格式】单条目模式
            channelColors = this.model.dataSetting.channels.map(c => c.connectorColor);
        }
        chart.color(channelColors);
        this.rootElement.append('g').datum(data).call(chart);
        this.rootElement.selectAll('.nv-noData').attr('x', chartWidth / 2).attr('y', chartHeight / 2 + this.displayOption.operationAreaHeight);
        this.resizeEventListener = nv.utils.windowResize(() => {
            if (!this.needResize)
                return;
            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 backgroundColor = this.model.displaySetting.axisSetting.filterBackgroudColor || 'inherit';
        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('background-color', backgroundColor)
            .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 rect = this.$element.parent().parent().find('rect');
        const fillColor = rect.attr('fill');
        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))
            .style('background-color', this.model.displaySetting.axisSetting.filterBackgroudColor || fillColor);
        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-image', 'url(assets/img/black_last_page.png)')
            .style('background-color', backgroundColor).style('background-size', 'cover')
            .on('click', () => { this.loadLastPage(); });
        operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
            .style('float', 'right').style('background-image', 'url(assets/img/black_next_page.png)')
            .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
            .style('background-color', backgroundColor).style('background-size', 'cover')
            .on('click', () => { this.loadNextPage(); });
        operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
            .style('float', 'right').style('background-image', 'url(assets/img/black_previous_page.png)')
            .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
            .style('background-color', backgroundColor).style('background-size', 'cover')
            .on('click', () => { this.loadPreviousPage(); });
        operationArea.append('button').style('width', buttonWidth).style('height', buttonHeight).style('border', 'none')
            .style('float', 'right').style('background-image', 'url(assets/img/black_first_page.png)')
            .style('margin', `0 ${this.displayOption.operationButtonMargin}px 0 0`)
            .style('background-color', backgroundColor).style('background-size', 'cover')
            .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();
    }
}
