import { LOGGER_SERVICE_TOKEN } from '../logger';
import * as d3 from 'd3-selection';
import { each, forEach } from 'lodash';
import { map } from 'rxjs/operators';
import { VariableStateEnum } from '../communication';
import { MainElement } from '../elements/main-element';
import { PerViewVariableCommunicator } from '../elements/per-view-variable-communicator';
import { ViewPopupBackdropType, ViewPopupLocationType } from '../model';
import { ViewResizeMode, DisplayMode, GlobalSettings } from '../settings';
import { ConfigIsEmptyError } from '../shared';
import { Guid } from '../utils/guid';
export class GuiView {
    constructor(injector, bsModalService, context, parentView) {
        this.injector = injector;
        this.bsModalService = bsModalService;
        this.context = context;
        this.parentView = parentView;
        this.viewId = Guid.newGuid().toString('n');
        this.logger = injector.get(LOGGER_SERVICE_TOKEN);
        this.childViews = new Array();
        if (parentView) {
            parentView.childViews.push(this);
        }
        this.isMobileMode = DisplayMode.Mobile === injector.get(GlobalSettings).displayMode;
        this.signalRAppId = Guid.newGuid().toString('n');
    }
    get element() {
        return this.mainElement.rootElement;
    }
    get rawWidth() {
        return this.mainElement.rawWidth;
    }
    get rawHeight() {
        return this.mainElement.rawHeight;
    }
    resize(size, resizeMode, svgRootClass) {
        if (!size) {
            return;
        }
        if (!size.isReal()) {
            throw new Error('invalid size.');
        }
        switch (resizeMode) {
            case ViewResizeMode.HorizontalUniform:
                this.mainElement.horizontalStretch(size);
                break;
            default:
                this.mainElement.uniformStretch(size, svgRootClass);
                break;
        }
    }
    load(config, guiOptions) {
        const { context } = this;
        this.svgRootClass = guiOptions.svgRootClass;
        this.perViewVariableCommunicator = new PerViewVariableCommunicator(context.variableCommunicator);
        this.mainElement = new MainElement(this.injector, this.bsModalService, context, this.perViewVariableCommunicator, this, this.signalRAppId);
        this.mainElement.load(config.config, !!this.parentView, guiOptions);
        setTimeout(() => {
            this.loadElementState();
        }, 0);
        this.logger.debug('[GUI] View loaded.');
    }
    loadElementState() {
        if (!this.mainElement) {
            return;
        }
        this.mainElement.initElementState();
        if (this.openVariableStatesSubscription) {
            this.openVariableStatesSubscription.unsubscribe();
        }
        if (this.openVariableStatesSubscription) {
            this.openVariableStatesSubscription.unsubscribe();
        }
        this.openedVariableNames = this.mainElement.getVariableNames();
        if (this.openedVariableNames.length === 0) {
            return;
        }
        this.openVariableStatesSubscription =
            this.perViewVariableCommunicator.subscribeVariableStates(this.openedVariableNames).subscribe((states) => {
                this.mainElement.reportVariableStates(states);
                const normalVariableNames = [];
                each(states, v => {
                    if (v.state === VariableStateEnum.Normal && normalVariableNames.indexOf(v.variableName) === -1) {
                        normalVariableNames.push(v.variableName);
                    }
                });
                if (normalVariableNames.length > 0) {
                    this.normalOpenedVariableNames(normalVariableNames);
                }
            });
    }
    popView(viewIndex, hostContainerId, el) {
        return this.context.configStore.viewStore.getViewConfig(viewIndex).pipe(map(view => {
            if (!view.config || !view.config.length) {
                throw new ConfigIsEmptyError();
            }
            const popView = new GuiView(this.injector, this.bsModalService, this.context, this);
            const guiOptions = {
                svgRootClass: this.svgRootClass,
                hostContainerId: hostContainerId,
                el: el
            };
            popView.load(view, guiOptions);
            const settings = $(popView.element).data('model');
            if (!settings.popup) {
                return;
            }
            let x, y;
            if (settings.popup.locationType === ViewPopupLocationType.Custom && settings.popup.location) {
                x = settings.popup.location.x;
                y = settings.popup.location.y;
            }
            else {
                x = (this.rawWidth - popView.rawWidth) / 2;
                y = (this.rawHeight - popView.rawHeight) / 2;
            }
            switch (settings.popup.backdropType) {
                case ViewPopupBackdropType.Shadow:
                    if (!this.isMobileMode) {
                        popView.showShadow();
                    }
                    break;
                case ViewPopupBackdropType.Musk:
                    this.showMusk();
                    break;
            }
            popView.element.setAttribute('x', String(x));
            popView.element.setAttribute('y', String(y));
            this.element.appendChild(popView.element);
        }));
    }
    closeView() {
        if (!this.parentView) {
            return;
        }
        this.parentView.hideMusk();
        this.dispose();
        this.element.parentElement.removeChild(this.element);
    }
    moveView(movementX, movementY) {
        const newX = Number(this.element.getAttribute('x')) + movementX;
        const newY = Number(this.element.getAttribute('y')) + movementY;
        this.element.setAttributeNS(null, 'x', String(newX));
        this.element.setAttributeNS(null, 'y', String(newY));
    }
    showMusk() {
        if (!this.muskElement) {
            this.muskElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
            this.muskElement.setAttribute('width', String(this.rawWidth));
            this.muskElement.setAttribute('height', String(this.rawHeight));
            this.muskElement.setAttribute('fill', '#081625');
            this.muskElement.setAttribute('fill-opacity', '0.4');
        }
        this.element.appendChild(this.muskElement);
    }
    showShadow() {
        const filterId = `dropshadow-${this.viewId}`;
        const filter = d3.select(this.element).append('defs').append('filter').attr('id', filterId)
            .attr('x', 0).attr('y', 0).attr('color-interpolation-filters', 'sRGB')
            .attr('filterUnits', 'userSpaceOnUse');
        const feComponentTransfer = filter.append('feDropShadow');
        feComponentTransfer.attr('stdDeviation', '6').attr('flood-color', 'rgba(69,65,78,.3)');
        filter.append('feOffset').attr('dx', 0).attr('dy', 0).attr('result', 'shadow');
        filter.append('feComposite').attr('in', 'SourceGraphic').attr('in2', 'shadow').attr('operator', 'over');
        const container = this.element.children[0];
        if (!container) {
            return;
        }
        container.setAttribute('filter', `url(#${filterId})`);
        container.setAttribute('x', '6');
        container.setAttribute('y', '6');
        $(this.element).children('g').attr('transform', 'matrix(1, 0, 0, 1, 6, 6)');
    }
    hideMusk() {
        if (this.muskElement) {
            this.element.removeChild(this.muskElement);
            this.muskElement = null;
        }
    }
    dispose() {
        if (this.openVariableStatesSubscription) {
            this.openVariableStatesSubscription.unsubscribe();
        }
        if (this.openVariablesSubscription) {
            this.openVariablesSubscription.unsubscribe();
        }
        if (this.perViewVariableCommunicator) {
            this.perViewVariableCommunicator.dispose();
        }
        if (this.childViews && this.childViews.length) {
            forEach(this.childViews, v => v.dispose());
        }
        if (this.mainElement) {
            this.mainElement.dispose();
        }
        this.logger.debug('[GUI] View disposed.');
    }
    normalOpenedVariableNames(normalVariableNames) {
        if (normalVariableNames.length > 0) {
            this.openVariablesSubscription = this.perViewVariableCommunicator.openVariables(normalVariableNames, this.signalRAppId)
                .subscribe(variableValues => this.mainElement.reportVariableValues(variableValues));
        }
    }
}
