import {ComponentLifecycleEventEmitter} from "@synerty/vortexjs";
import {PrivateDiagramLookupService} from "@peek/peek_plugin_diagram/_private/services/PrivateDiagramLookupService";
import {PrivateDiagramPositionService} from "@peek/peek_plugin_diagram/_private/services/PrivateDiagramPositionService";
import {
    PrivateDiagramBranchContext,
    PrivateDiagramBranchService
} from "@peek/peek_plugin_diagram/_private/branch";
import { BalloonMsgService } from "@synerty/peek-plugin-base-js"
import {PeekCanvasInput} from "../canvas-input/PeekCanvasInput.web";
import {PeekCanvasInputEditSelectDelegate} from "../canvas-input/PeekCanvasInputEditSelectDelegate.web";
import {PeekCanvasInputSelectDelegate} from "../canvas-input/PeekCanvasInputSelectDelegate.web";
import {PeekCanvasModel} from "./PeekCanvasModel.web";
import {PeekCanvasConfig} from "./PeekCanvasConfig.web";
import {EditorToolType} from "./PeekCanvasEditorToolType.web";
import {DispLevel} from "@peek/peek_plugin_diagram/lookups";
import {PeekCanvasEditorProps} from "./PeekCanvasEditorProps";
import {GridObservable} from "../cache/GridObservable.web";

/**
 * Peek Canvas Editor
 *
 * This class is the central controller for Edit support.
 *
 */
export class PeekCanvasEditor {


    private _currentBranch: PrivateDiagramBranchContext | null = null;

    private readonly _props: PeekCanvasEditorProps;

    constructor(public balloonMsg: BalloonMsgService,
                public canvasInput: PeekCanvasInput,
                public canvasModel: PeekCanvasModel,
                private canvasConfig: PeekCanvasConfig,
                private gridObservable: GridObservable,
                public lookupService: PrivateDiagramLookupService,
                private positionService: PrivateDiagramPositionService,
                private branchService: PrivateDiagramBranchService,
                private lifecycleEventEmitter: ComponentLifecycleEventEmitter) {
        this.branchService
            .startEditingWithContextObservable()
            .takeUntil(lifecycleEventEmitter.onDestroyEvent)
            .subscribe((branchContext: PrivateDiagramBranchContext) => {
                this.gridObservable.resetAllDispComputedProperties();
                this._props.setCanvasData(this.modelSetId, this.coordSetId);

                if (this.branchContext)
                    this.branchContext.close();

                this.branchContext = branchContext;

                this.branchContext
                    .branchUpdatedObservable
                    .takeUntil(this.lifecycleEventEmitter.onDestroyEvent)
                    .subscribe((modelUpdateRequired: boolean) => {
                        if (modelUpdateRequired) {
                            this.canvasModel.recompileModel();
                            this.canvasModel.selection.clearSelection();
                            this.setEditorSelectTool();
                        }
                    });

                this.branchContext.open();

                const lastPos = branchContext.branchTuple.lastEditPosition;
                if (lastPos != null) {
                    this.positionService
                        .position(lastPos.coordSetKey, lastPos.x,
                            lastPos.y, lastPos.zoom);
                }

                this.setInputEditDelegate(PeekCanvasInputEditSelectDelegate);
                this.canvasModel.selection.clearSelection();
                this.canvasConfig.editor.active = true;
                this.canvasConfig.updateEditedBranch(branchContext.branchTuple.key);
                this.canvasConfig.setModelNeedsCompiling();
            });

        this.branchService
            .stopEditingObservable()
            .takeUntil(lifecycleEventEmitter.onDestroyEvent)
            .subscribe(() => {
                this.gridObservable.resetAllDispComputedProperties();
                if (this.branchContext)
                    this.branchContext.close();

                this.branchContext = null;
                this.props.closeContext();
                this.canvasInput.setDelegate(PeekCanvasInputSelectDelegate);
                this.canvasConfig.editor.active = false;
                this.canvasModel.selection.clearSelection();
                this.canvasConfig.updateEditedBranch(null);
                this.canvasConfig.setModelNeedsCompiling();
            });

        this._props = new PeekCanvasEditorProps(this.lookupService);

        this.canvasModel.selection.selectionChangedObservable()
            .takeUntil(lifecycleEventEmitter.onDestroyEvent)
            .subscribe(() => {
                if (!this.canvasConfig.editor.active)
                    return;
                this._props.setSelectedShapes(
                    this.canvasModel, this._currentBranch.branchTuple
                )
            });
    };


    // ---------------
    // Getters

    get modelSetId(): null | number {
        let cs = this.canvasConfig.controller.coordSet;
        return cs == null ? null : cs.modelSetId;
    }

    get coordSetId(): null | number {
        let cs = this.canvasConfig.controller.coordSet;
        return cs == null ? null : cs.id;
    }

    get props(): PeekCanvasEditorProps {
        return this._props;
    }

    // ---------------
    // Shape Props

    dispPropsUpdated(touchUndo: boolean = true): void {
        this.canvasConfig.invalidate();

        if (this._currentBranch == null)
            return;

        this._currentBranch.branchTuple.touchUpdateDate(false);

        if (touchUndo)
            this._currentBranch.branchTuple.touchUndo();
    }

    // ---------------
    // Branch Context

    get branchContext(): PrivateDiagramBranchContext {
        return this._currentBranch;
    }

    set branchContext(val: PrivateDiagramBranchContext | null) {
        this.canvasConfig.editor.activeBranchTuple = null;
        this._currentBranch = val;
        if (val != null) {
            this.canvasConfig.editor.activeBranchTuple = val.branchTuple;
            this.canvasConfig.setModelNeedsCompiling();
        }
    }

    // ---------------
    // Properties, used by UI mainly

    isEditing(): boolean {
        return this._currentBranch != null;
    }

    selectedTool(): EditorToolType {
        return this.canvasInput.selectedDelegateType();
    }

    isShapeSelected(): boolean {
        return true;
    }

    isLevelVisible(level: DispLevel): boolean {
        return level.isVisibleAtZoom(this.canvasConfig.viewPort.zoom);
    }

    setEditorSelectTool(): void {
        this.setInputEditDelegate(PeekCanvasInputEditSelectDelegate);
    }

    // ---------------
    // Methods called by toolbar

    closeEditor() {
        // TODO: Ask the user
        this.branchService.stopEditing();
    }

    save() {
        this._currentBranch.branchTuple.lastEditPosition = ({
            x: this.canvasConfig.viewPort.pan.x,
            y: this.canvasConfig.viewPort.pan.y,
            zoom: this.canvasConfig.viewPort.zoom,
            coordSetKey: this.canvasConfig.coordSet.key
        });

        this._currentBranch.save()
            .then(() => this.balloonMsg.showSuccess("Branch Save Successful"))
            .catch((e) => this.balloonMsg.showError("Failed to save branch\n" + e));
    }

    doUndo(): void {
        this._currentBranch.branchTuple.doUndo(this.lookupService);
    }

    doRedo(): void {
        this._currentBranch.branchTuple.doRedo(this.lookupService);

    }

    setInputEditDelegate(Delegate) {
        this.canvasInput.setDelegate(Delegate, {
            setEditorSelectTool: () => this.setEditorSelectTool(),
            doUndo: () => this.doUndo(),
            doRedo: () => this.doRedo(),
            branchContext: this.branchContext,
            editToolbarProps: this.props,
            lookupService: this.lookupService
        });
    }

}
