import { each, forEach, isEqual } from 'lodash';
import { LOCALIZATION } from '../localization';
import { LOGGER_SERVICE_TOKEN } from '../logger';
import { Size } from '../model';
import { ViewBackgroundFillType } from '../model/settings/background/view-background-fill-type';
import { GuiConsts } from '../shared';
import { BarGraphElement } from './bar-graph-element';
import { ReadableElement } from './base';
import { CharacterDisplayElement } from './character-display/character-display-element';
import { DatetimeDisplayElement } from './datetime-display/datetime-display-element';
import { HistoricalCurveElement } from './historical-curve/historical-curve.element';
import { MeterElement } from './meter-element';
import { NumericalDisplayElement } from './numerical-display/numerical-display-element';
import { PipeElement } from './pipe/pipe-element';
import { RingGraphElement } from './ring-graph/ring-graph-element';
import { GraphStateElement } from './shared/graph/graph-state-element';
import { ImageElement } from './static-elements/image-element';
import { TextElement } from './static-elements/text-element';
import { SwitchIndicatorLightElement } from './switch-indicator-light/switch-indicator-light-element';
import { EllipseElement } from './vector-graphics/ellipse-element';
import { RectangleElement } from './vector-graphics/rectangle-element';
import { SectorElement } from './vector-graphics/sector-element';
import { ViewOperationElement } from './view-operation/view-operation.element';
import { StraightLineElement } from './vector-graphics/straight-line-element';
import { PolyLineElement } from './vector-graphics/poly-line-element';
import { PolygonElement } from './vector-graphics/polygon-element';
import { HyperlinkElement } from './static-elements/hyperlink-element';
import { VideoElement } from './video/video-element';
import { WeatherElement } from './weather/weater-element';
import { AirQualityElement } from './air-quality/air-quality-element';
import { AlarmElement } from './alarm/alarm-element';
export class MainElement {
    constructor(injector, bsModalService, context, variableCommunicator, popupViewService, signalRAppId) {
        this.injector = injector;
        this.bsModalService = bsModalService;
        this.context = context;
        this.variableCommunicator = variableCommunicator;
        this.popupViewService = popupViewService;
        this.signalRAppId = signalRAppId;
        this.elements = new Array();
        this.logger = injector.get(LOGGER_SERVICE_TOKEN);
    }
    get rootElement() {
        return this.root[0];
    }
    load(data, overflowVisible = false, guiOptions) {
        this.hostContainerId = guiOptions.hostContainerId;
        if (this.isLoaded) {
            throw new Error('The Main Element already loaded.');
        }
        this.root = $(data);
        this.root.attr('style', `margin: 0 auto;display: block;${overflowVisible ? 'overflow: visible;' : ''}`);
        // eslint-disable-next-line radix
        this.rawWidth = Number.parseInt(this.root.attr('width'));
        // eslint-disable-next-line radix
        this.rawHeight = Number.parseInt(this.root.attr('height'));
        // TODO 实现一个elementParser解析components.
        const components = this.root.find('[data-model]');
        each(components, element => {
            const category = $(element).data('model').category;
            switch (category) {
                case GuiConsts.components.numericalDisplayKey:
                    const numericalDisplayElement = new NumericalDisplayElement(element, this.injector, this.bsModalService, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.graphStore, this.context.operationRecordService, this.context.releasedVariableService, this.context.securityChecker, this.context.configStore.variableStore, this.injector.get(LOCALIZATION), this.signalRAppId);
                    this.elements.push(numericalDisplayElement);
                    break;
                case GuiConsts.components.imageKey:
                    this.elements.push(new ImageElement(element, this.injector, this.context.configStore.imageStore, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.configStore.graphStore, this.signalRAppId));
                    break;
                case GuiConsts.components.meterKey:
                    this.elements.push(new MeterElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.injector.get(LOCALIZATION), this.signalRAppId));
                    break;
                case GuiConsts.components.pipeKey:
                    this.elements.push(new PipeElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.injector.get(LOCALIZATION), this.signalRAppId));
                    break;
                case GuiConsts.components.switchIndicatorLightKey:
                    this.elements.push(new SwitchIndicatorLightElement(element, this.injector, this.bsModalService, this.variableCommunicator, this.context.configStore.graphStore, this.context.permissionChecker, this.context.operationRecordService, this.context.securityChecker, this.context.configStore.variableStore, this.injector.get(LOCALIZATION), this.signalRAppId));
                    break;
                case GuiConsts.components.barGraphKey:
                    this.elements.push(new BarGraphElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.injector.get(LOCALIZATION), this.signalRAppId));
                    break;
                case GuiConsts.components.toggleViewKey:
                case GuiConsts.components.viewOperationElement:
                    this.elements.push(new ViewOperationElement(element, this.injector, this.bsModalService, this.variableCommunicator, this.context.configStore.variableStore, this.context.configStore.graphStore, this.context.permissionChecker, this.context.operationRecordService, this.context.securityChecker, this.injector.get(LOCALIZATION), this.popupViewService, guiOptions.hostContainerId, guiOptions.el, this.signalRAppId));
                    break;
                case GuiConsts.components.characterKey:
                    this.elements.push(new CharacterDisplayElement(element, this.injector, this.bsModalService, this.variableCommunicator, this.context.configStore.graphStore, this.context.permissionChecker, this.context.operationRecordService, this.context.securityChecker, this.context.configStore.variableStore, this.injector.get(LOCALIZATION), this.signalRAppId));
                    break;
                case GuiConsts.components.textKey:
                    this.elements.push(new TextElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.configStore.graphStore, this.signalRAppId));
                    break;
                case GuiConsts.components.hyperlinkKey:
                    this.elements.push(new HyperlinkElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.configStore.graphStore, this.signalRAppId));
                    break;
                case GuiConsts.components.straightLineKey:
                    this.elements.push(new StraightLineElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.signalRAppId));
                    break;
                case GuiConsts.components.polyLineKey:
                    this.elements.push(new PolyLineElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.signalRAppId));
                    break;
                case GuiConsts.components.ellipseKey:
                    this.elements.push(new EllipseElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.signalRAppId));
                    break;
                case GuiConsts.components.rectangleKey:
                    this.elements.push(new RectangleElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.signalRAppId));
                    break;
                case GuiConsts.components.sectorkey:
                    this.elements.push(new SectorElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.signalRAppId));
                    break;
                case GuiConsts.components.datetimeDisplayKey:
                    this.elements.push(new DatetimeDisplayElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.configStore.graphStore, this.signalRAppId));
                    break;
                case GuiConsts.components.historicalCurveKey:
                    this.elements.push(new HistoricalCurveElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.configStore.historyDataStore, this.signalRAppId));
                    break;
                case GuiConsts.components.ringGraphKey:
                    this.elements.push(new RingGraphElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.injector.get(LOCALIZATION), this.signalRAppId));
                    break;
                case GuiConsts.components.polygonKey:
                    this.elements.push(new PolygonElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.signalRAppId));
                    break;
                case GuiConsts.components.videokey:
                    this.elements.push(new VideoElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.videoService, new Size(this.rawWidth, this.rawHeight), guiOptions.svgRootClass, this.signalRAppId));
                    break;
                case GuiConsts.components.tableKey:
                    this.elements.push(new PolygonElement(element, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.signalRAppId));
                    break;
                case GuiConsts.components.weatherKey:
                    this.elements.push(new WeatherElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.weatherService, this.signalRAppId));
                    break;
                case GuiConsts.components.airQualityKey:
                    this.elements.push(new AirQualityElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.weatherService, this.signalRAppId));
                    break;
                case GuiConsts.components.alarmKey:
                    this.elements.push(new AlarmElement(element, this.injector, this.context.permissionChecker, this.variableCommunicator, this.context.configStore.variableStore, this.context.configStore.alarmsStore, this.signalRAppId));
                    break;
            }
        });
        this.initBackground();
        this.isLoaded = true;
        this.logger.debug('[GUI] Main element loaded.');
    }
    /**
     * 保证在显示区域内显示画面所有内容，等比缩放画面大小以铺满整个显示区域
     * @param size 显示区域大小
     */
    uniformStretch(size, svgRootClass) {
        if (!size.isReal()) {
            throw new Error('size is not real.');
        }
        this.checkIsLoaded();
        const restrictScale = this.rawHeight / this.rawWidth;
        const newScale = size.height / size.width;
        if (newScale > restrictScale) {
            size.height = size.width * restrictScale;
        }
        else {
            size.width = size.height / restrictScale;
        }
        const oldSize = this.oldSize ? this.oldSize : size;
        setTimeout(() => {
            this.changeVideoStyle(size, oldSize, svgRootClass);
        }, 200);
        this.root.attr('width', size.width);
        this.root.attr('height', size.height);
        if (!this.root.attr('viewBox')) {
            const sx = size.width / this.rawWidth;
            this.root.find('g:first').attr('transform', `scale(${sx})`);
        }
        this.oldSize = size;
    }
    changeVideoStyle(size, oldSize, svgRootClass) {
        const self = this;
        $('div.' + svgRootClass + ' video').each(function () {
            const videoWidth = size.width / oldSize.width * $(this).width();
            if (videoWidth > $('body').width() || videoWidth.toFixed(0) === $('body').width().toFixed(0)) {
                return;
            }
            $(this).css('width', videoWidth + 'px');
            $(this).css('height', size.height / oldSize.height * $(this).height() + 'px');
            let videoLeft = size.width * Number($(this).attr('scareX'));
            let videoTop = size.height * Number($(this).attr('scareY'));
            if (self.hostContainerId) {
                const hostContainerObject = $('#' + self.hostContainerId);
                if (hostContainerObject.length) {
                    const hostContainerWidth = hostContainerObject[0].clientWidth;
                    const hostContainerHight = hostContainerObject[0].clientHeight;
                    videoLeft = videoLeft + (hostContainerWidth - size.width) / 2;
                    videoTop = videoTop + (hostContainerHight - size.height) / 2;
                }
            }
            $(this).css('left', videoLeft + 'px');
            $(this).css('top', videoTop + 'px');
        });
    }
    /**
     * 保证在显示区域内横向铺满
     * @param size 显示区域大小
     */
    horizontalStretch(size) {
        if (!size.isReal()) {
            throw new Error('size is not real.');
        }
        this.checkIsLoaded();
        size.height = (size.width / this.rawWidth) * this.rawHeight;
        this.root.attr('width', size.width);
        this.root.attr('height', size.height);
        if (!this.root.attr('viewBox')) {
            const sx = size.width / this.rawWidth;
            this.root.find('g:first').attr('transform', `scale(${sx})`);
        }
    }
    initElementState() {
        forEach(this.elements, element => {
            if (element instanceof ReadableElement) {
                element.init(element.readVariableName || element.maxVariableName || element.minVariableName);
            }
            else if (element instanceof HistoricalCurveElement) {
                element.initElementStatus();
            }
        });
        this.logger.debug('[GUI] Main element enabled.');
    }
    getVariableNames() {
        const variableNames = new Array();
        forEach(this.elements, element => {
            var _a, _b;
            if (element instanceof ReadableElement) {
                const maxVariableName = (_a = element) === null || _a === void 0 ? void 0 : _a.maxVariableName;
                const maxValueType = element.maxValueType;
                const minVariableName = (_b = element) === null || _b === void 0 ? void 0 : _b.minVariableName;
                const minValueType = element.minValueType;
                const variableName = element.readVariableName;
                // 最大最小值选择变量类型时，其中一个未绑定变量不进行开点操作 - FLEXCLOUD-1817
                if (maxValueType && maxVariableName && !variableNames.find(v => isEqual(maxVariableName, v))) {
                    variableNames.push(maxVariableName);
                }
                if (minValueType && minVariableName && !variableNames.find(v => isEqual(minVariableName, v))) {
                    variableNames.push(minVariableName);
                }
                if (variableName && !variableNames.find(v => isEqual(variableName, v))) {
                    variableNames.push(element.readVariableName);
                }
            }
        });
        return variableNames;
    }
    reportVariableStates(states) {
        this.checkIsLoaded();
        each(states, state => {
            each(this.elements, element => {
                if (element instanceof ReadableElement) {
                    const variableName = element.readVariableName;
                    if (variableName && variableName === state.variableName) {
                        element.reportStateChanged(state);
                    }
                }
            });
        });
    }
    reportVariableValues(values) {
        this.checkIsLoaded();
        each(values, value => {
            each(this.elements, e => {
                if (e instanceof ReadableElement) {
                    if (e.readVariableName === value.variableName || e.minVariableName === value.variableName || e.maxVariableName === value.variableName) {
                        e.reportValueChanged(value);
                    }
                }
            });
        });
    }
    dispose() {
        each(this.elements, e => {
            if (e instanceof HistoricalCurveElement || e instanceof VideoElement
                || e instanceof WeatherElement || e instanceof NumericalDisplayElement || e instanceof TextElement
                || e instanceof AirQualityElement) {
                e.dispose();
            }
        });
    }
    checkIsLoaded() {
        if (!this.isLoaded) {
            throw new Error('The Main Element is not loaded.please call load method first.');
        }
    }
    initBackground() {
        const settings = this.root.data('model');
        if (settings) {
            const background = settings.background;
            switch (background.fillType) {
                case ViewBackgroundFillType.SolidColor:
                    if (background.solidColor) {
                        const backgroundElement = $(document.createElementNS('http://www.w3.org/2000/svg', 'rect')).attr({
                            width: String(this.rawWidth), height: String(this.rawHeight), fill: background.solidColor
                        });
                        this.root.prepend(backgroundElement);
                    }
                    break;
                case ViewBackgroundFillType.Image:
                    if (background.imageId) {
                        this.root.prepend(this.getImageEl(background.imageId));
                    }
                    break;
                case ViewBackgroundFillType.Graph:
                    if (background.graphSetting && background.graphSetting.id > 0) {
                        const stateElement = new GraphStateElement(background.graphSetting, this.rawWidth, this.rawHeight, this.context.configStore.graphStore, this.logger);
                        stateElement.switchToState(0);
                        this.root.prepend(stateElement.Element);
                        const backgroundElement = $(document.createElementNS('http://www.w3.org/2000/svg', 'rect')).attr({
                            width: String(this.rawWidth), height: String(this.rawHeight), fill: '#fff'
                        });
                        this.root.prepend(backgroundElement);
                    }
                    break;
            }
        }
    }
    getImageEl(imageId) {
        const imageElement = document.createElementNS('http://www.w3.org/2000/svg', 'image');
        imageElement.setAttribute('width', this.rawWidth + '');
        imageElement.setAttribute('height', this.rawHeight + '');
        imageElement.setAttribute('fill', 'none');
        imageElement.setAttribute('preserveAspectRatio', 'none');
        imageElement.setAttribute('crossorigin', 'anonymous');
        imageElement.setAttributeNS('http://www.w3.org/1999/xlink', 'href', this.context.configStore.imageStore.getImageUrl(imageId));
        return imageElement;
    }
}
