import { NgLifeCycleEvents } 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: NgLifeCycleEvents
    ) {
        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
    
    get branchContext(): PrivateDiagramBranchContext {
        return this._currentBranch
    }
    
    // ---------------
    // Branch Context
    
    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()
        }
    }
    
    dispPropsUpdated(touchUndo: boolean = true): void {
        this.canvasConfig.invalidate()
        
        if (this._currentBranch == null)
            return
        
        this._currentBranch.branchTuple.touchUpdateDate(false)
        
        if (touchUndo)
            this._currentBranch.branchTuple.touchUndo()
    }
    
    // ---------------
    // 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
        })
    }
    
}
