import { GraphType } from '../../config';
import { LOGGER_SERVICE_TOKEN } from '../../logger';
import { AlertModalComponent } from '../../modal/alert/alert-modal.component';
import { ConfirmOperationModalComponent } from '../../modal/confirm-operation/confirm-operation-modal.component';
import { WriteCharacterModalArgs } from '../../modal/write-character/write-character-modal-args';
import { WriteCharacterModalComponent } from '../../modal/write-character/write-character-modal.component';
import { State } from '../../model';
import { AccessPermissionService, GraphUtil } from '../../utils';
import { ReadableElement } from '../base/readable-element';
import { VerifyPasswordModalComponent } from '../../modal/verify-password/verify-password-modal.component';
import { VariableUtil } from '../../utils/variable-util';
import { TextElementModal } from '../shared/text/text-element';
export class CharacterDisplayElement extends ReadableElement {
    constructor(element, injector, modalService, variableCommunicator, graphStore, permissionChecker, operationRecordService, securityChecker, variableStore, localization, signalRAppId) {
        super(element, permissionChecker, variableCommunicator, variableStore, localization, signalRAppId);
        this.modalService = modalService;
        this.graphStore = graphStore;
        this.operationRecordService = operationRecordService;
        this.securityChecker = securityChecker;
        this.displayValue = '';
        this.graphs = new Map();
        this.logger = injector.get(LOGGER_SERVICE_TOKEN);
        this.accessPermissionService = injector.get(AccessPermissionService);
        this.initElement();
        this.initClickEvent();
    }
    get readVariableName() {
        if (!this.model) {
            throw new Error('Model cannot be null');
        }
        if (null == this.model.version || this.model.version < 1) {
            return this.model.readVariableName;
        }
        else {
            const variable = this.model.readVariable;
            if (!variable) {
                return undefined;
            }
            return VariableUtil.getConvertedVariableName(this.variableStore, variable);
        }
    }
    get writeVariableName() {
        if (!this.model) {
            throw new Error('Model cannot be null');
        }
        if (null == this.model.version) {
            if (this.model.isSameVariable) {
                return this.model.readVariableName;
            }
            else {
                return this.model.writeVariableName;
            }
        }
        else {
            const variable = this.model.writeVariable;
            if (!variable) {
                return undefined;
            }
            return VariableUtil.getConvertedVariableName(this.variableStore, variable);
        }
    }
    updateVariableValue(value) {
        const valueText = this.getVariableValueText(value);
        // filter control character
        this.displayValue = valueText.replace(/[\u0000-\u001F]|[\u007F-\u00A0]/g, '');
        this.updateDisplayValue(this.displayValue);
    }
    changeStates() {
        super.changeStates();
        if (this.state === State.Normal || this.state === State.Disable) {
            this.updateDisplayValue(this.displayValue);
        }
        else {
            this.updateDisplayValue('');
        }
    }
    updateDisplayValue(text) {
        const textElement = this.$element.find('foreignObject').find('i');
        if (textElement) {
            textElement.text(text);
        }
    }
    getVariableValueText(value) {
        return this.characterDisplayText(this.model.isPassword, value);
    }
    characterDisplayText(isPassword, value) {
        let displayText = '';
        if (null == value || value.toString().trim().length <= 0) {
            return displayText;
        }
        displayText = value.toString();
        if (isPassword) {
            displayText = '*'.repeat(displayText.length);
        }
        return displayText;
    }
    initElement() {
        const { rootElement } = this;
        rootElement.selectAll('*').remove();
        const label = this.model.label;
        let width = null, height = null;
        if (this.model.size) {
            width = this.model.size.width;
            height = this.model.size.height;
        }
        if (!width || !height) {
            width = 60;
            height = 38;
        }
        rootElement.append('rect').attr('id', 'StateFrame').attr('width', width).attr('height', height).attr('fill', 'transparent');
        this.initVariableText(width, height);
        let translateX = '';
        if (this.model.showVariableText) {
            translateX = `translate(${width * 7 / 11},0)`;
            width = width * 4 / 11;
        }
        else {
            translateX = 'translate(0,0)';
        }
        if (this.model.useGraph) {
            rootElement.append('image')
                .attr('width', width)
                .attr('height', height)
                .attr('preserveAspectRatio', 'none')
                .attr('fill', 'none')
                .attr('crossorigin', 'anonymous')
                .attr('transform', translateX);
        }
        const font = label.font;
        if (!font) {
            throw new Error('font missing.');
        }
        const textElement = rootElement.append('foreignObject')
            .attr('width', width)
            .attr('height', height)
            .attr('fill', 'none')
            .attr('transform', translateX);
        const bodyElement = textElement.append('xhtml:div')
            .attr('class', 'svgBody')
            .html('<div></div>');
        const divElement = bodyElement.select('div')
            .style('height', height + 'px')
            .style('overflow', 'hidden')
            .style('display', 'flex')
            .style('align-items', 'center');
        const iElement = divElement.append('i');
        iElement.style('word-break', 'break-all')
            .style('text-align', font.textAlign)
            .style('alignment-baseline', 'central')
            .style('transform', `translate(${width / 2},${height / 2})`).text(label.content)
            .style('color', font.color)
            .style('font-size', font.fontSize)
            .style('font-style', 'normal')
            .style('width', '100%');
        if (font.fontStyle) {
            const fontStyle = font.fontStyle.split(' ');
            const isBold = $.inArray('bold', fontStyle);
            const italic = $.inArray('Italic', fontStyle);
            const fontFamily = fontStyle.pop();
            if (isBold >= 0) {
                iElement.style('font-weight', 'bold');
            }
            if (italic >= 0) {
                iElement.style('font-style', 'italic');
            }
            iElement.style('font-family', fontFamily ? fontFamily + ',msyh' : 'msyh');
        }
        if (font.isUnderline) {
            iElement.style('text-decoration', 'underline');
        }
        if (this.model.useGraph) {
            if (this.graphs.has(0)) {
                const graph = this.graphs.get(0);
                // 已经发出过图形请求，不再重复请求。
                if (graph == null) {
                    return;
                }
                this.changeGraph(graph);
            }
            else {
                // 标记，防止重复请求图形。
                this.graphs.set(0, null);
                this.graphStore.getGraph(this.model.graphSetting.id, 0, GraphUtil.getExtendedStyle(this.model.graphSetting, 0))
                    .subscribe(graphResult => {
                    this.graphs.set(0, graphResult);
                    this.changeGraph(graphResult);
                });
            }
        }
    }
    initVariableText(width, height) {
        if (!this.model.showVariableText) {
            return;
        }
        const font = {
            isUnderline: this.model.variableText.font.isUnderline,
            isBold: this.model.variableText.font.isBold,
            isItalic: this.model.variableText.font.isItalic,
            fontSize: this.model.variableText.font.fontSize,
            fontFamily: this.model.variableText.font.fontFamily,
            color: this.model.variableText.font.color,
            textAlign: this.model.variableText.font.textAlign
        };
        const textElement = new TextElementModal(this.model.variableText.content + ':', font, width * 7 / 11, height, true);
        this.$element.append(textElement.Element);
    }
    initClickEvent() {
        if (!this.writeVariableName || !this.accessPermissionService.canWrite(this.model.accessPermission)) {
            return;
        }
        this.rootElement.style('cursor', 'hand');
        setTimeout(() => {
            const elementBox = this.$element[0].getBBox();
            this.$element.find('rect').attr('width', elementBox.width);
            this.$element.find('rect').attr('height', elementBox.height);
        }, 0);
        this.$element.click(__ => {
            if (!this.isInitialized) {
                return;
            }
            if (!this.isEnable) {
                const message = !this.isGrantedPermission ? this.localization.permissiontip : this.localization.conditionIsNotMetTip;
                const modalRef = this.modalService.show(AlertModalComponent, { initialState: { alermMessage: message }, backdrop: 'static', class: 'gui-modal-dialog-position', animated: false });
                const onClosedSub = modalRef.content.onClosed.subscribe(() => {
                    modalRef.hide();
                    onClosedSub.unsubscribe();
                });
                return;
            }
            if (this.model.useSecurity) {
                const modalRef = this.modalService.show(ConfirmOperationModalComponent, {
                    backdrop: 'static', class: 'gui-modal-dialog-position', animated: false
                });
                const onClosedSub = modalRef.content.onClosed.subscribe(result => {
                    modalRef.hide();
                    onClosedSub.unsubscribe();
                    if (result) {
                        this.checkElementPassword();
                    }
                });
            }
            else {
                this.checkElementPassword();
            }
        });
    }
    checkElementPassword() {
        if (this.model.enablePassword) {
            const modalRef = this.modalService.show(VerifyPasswordModalComponent, { initialState: { securityChecker: this.securityChecker }, backdrop: 'static', class: 'gui-modal-dialog-position', animated: false });
            const onClosedSub = modalRef.content.onClosed.subscribe(result => {
                modalRef.hide();
                onClosedSub.unsubscribe();
                if (result) {
                    this.doWriteCharacterValue();
                }
            });
        }
        else {
            this.doWriteCharacterValue();
        }
    }
    doWriteCharacterValue() {
        if (null == this.writeVariableName) {
            return;
        }
        const args = new WriteCharacterModalArgs(this.writeVariableName, this.model.isPassword, this.model.charCount, this.displayValue);
        const modalRef = this.modalService.show(WriteCharacterModalComponent, {
            initialState: { args: args }, backdrop: 'static', class: 'gui-modal-dialog-position', animated: false
        });
        const onClosedSub = modalRef.content.onClosed.subscribe(result => {
            modalRef.hide();
            onClosedSub.unsubscribe();
            if (result) {
                this.variableCommunicator.write(this.writeVariableName, result.value).subscribe();
                this.recordOperation(result.value);
            }
        });
    }
    recordOperation(newValue) {
        if (this.model.isRecordOperation || this.model.isRecordOperation === undefined) {
            this.operationRecordService.record({
                variableOptionModel: {
                    variableName: this.writeVariableName,
                    originValue: this.displayValue,
                    newValue: newValue
                },
                operationDescription: this.model.operationDescription
            });
        }
    }
    changeGraph(graphResult) {
        if (!graphResult.failed) {
            const graph = graphResult.graph;
            switch (graph.graphType) {
                case GraphType.Image:
                    this.rootElement.select('image').attr('xlink:href', graph.source);
                    break;
                case GraphType.SVG:
                    this.rootElement.select('image').attr('xlink:href', graph.source);
                    break;
                default:
                    this.logger.error(new Error(`Unknown graph ${graph.graphType}.`));
                    this.rootElement.select('image').attr('xlink:href', null);
                    break;
            }
        }
        else {
            this.rootElement.select('image').attr('xlink:href', null);
        }
    }
}
