import {
    DispBase,
    DispBaseT,
    DispHandleI,
    DispType,
    PointI,
    PointsT
} from "./DispBase"
import {
    PeekCanvasShapePropsContext,
    ShapeProp,
    ShapePropType
} from "../canvas/PeekCanvasShapePropsContext"
import { ModelCoordSet } from "@peek/peek_plugin_diagram/_private/tuples"
import { DispGroup, DispGroupT } from "./DispGroup"
import { PrivateDiagramLookupService } from "@peek/peek_plugin_diagram/_private/services/PrivateDiagramLookupService"
import { BranchTuple } from "@peek/peek_plugin_diagram/_private/branch/BranchTuple"
import {
    calculateRotationFromHandleDelta,
    makeRotateHandlePoints
} from "./DispUtilRotate"
import { DispText } from "./DispText"
import { DispFactory } from "./DispFactory"

export interface DispGroupPointerT extends DispBaseT {
    
    // verticalScale
    vs: number;
    
    // horizontalScale
    hs: number;
    
    // targetGroupId
    tg: number;
    
    // targetGroupName
    tn: string;
    
    // Rotation
    r: number;
    
    // Temporary rotation, used for edit support
    tempRotation: number | null;
    
    // Disps that belong to this disp group
    // Set by the model compiler
    // COMPUTED PROPERTY, it's computed somewhere
    disps: DispBaseT[];
    
}

export class DispGroupPointer extends DispBase {
    
    static resetMoveData(disp): void {
        disp.tempRotation = null
        DispBase.resetMoveData(disp)
    }
    
    static targetGroupId(disp: DispGroupPointerT): number {
        return disp.tg
    }
    
    static setTargetGroupId(
        disp: DispGroupPointerT,
        val: number
    ): void {
        disp.tg = val
    }
    
    static setTargetGroupName(
        disp: DispGroupPointerT,
        coordSetId: number,
        name: string
    ): void {
        disp.tn = `${coordSetId}|${name}`
    }
    
    static targetGroupCoordSetId(disp: DispGroupPointerT): number | null {
        if (disp.tn == null || disp.tn.indexOf("|") === -1)
            return null
        return parseInt(disp.tn.split("|")[0])
    }
    
    static targetGroupName(disp: DispGroupPointerT): string | null {
        if (disp.tn == null || disp.tn.indexOf("|") === -1)
            return null
        return disp.tn.split("|")[1]
    }
    
    static verticalScale(disp: DispGroupPointerT): number {
        return disp.vs
    }
    
    static horizontalScale(disp: DispGroupPointerT): number {
        return disp.hs
    }
    
    static rotation(disp: DispGroupPointerT): number {
        if (disp.r == null)
            return 0
        return disp.r
    }
    
    static setRotation(
        disp: DispGroupPointerT,
        val: number
    ): void {
        disp.r = val
    }
    
    static center(disp: DispGroupPointerT): PointI {
        return {x: disp.g[0], y: disp.g[1]}
    }
    
    static setCenterPoint(
        disp: DispGroupPointerT,
        x: number,
        y: number
    ): void {
        disp.g = [x, y]
    }
    
    static geom(disp): PointsT {
        return disp.g
    }
    
    // ---------------
    // Delta move helpers
    
    static deltaMoveHandle(
        handle: DispHandleI,
        dx: number,
        dy: number
    ): void {
        const disp = <DispGroupPointerT>handle.disp
        const center = DispGroupPointer.center(disp)
        
        const data = calculateRotationFromHandleDelta(handle,
            dx, dy, DispGroupPointer.rotation(disp), disp.tempRotation, center)
        
        if (data == null)
            return
        
        // We've rotate the disps, now store the current rotation
        DispGroupPointer.setRotation(disp, data.newRotation)
        disp.tempRotation = data.tempRotation
        
        for (const childDisp of disp.disps) {
            const Wrapper = DispFactory.wrapper(childDisp)
            Wrapper.rotateAboutAxis(childDisp, center, data.deltaRotation)
        }
        disp.bounds = null
        
    }
    
    static rotateAboutAxis(
        disp,
        center: PointI,
        rotationDegrees: number
    ) {
        console.log("NOT IMPLEMENTED: Rotating child DispGroupPtrs")
    }
    
    // ---------------
    // Create Method
    
    static create(coordSet: ModelCoordSet): DispGroupPointerT {
        let newDisp = {
            ...DispBase.create(coordSet, DispBase.TYPE_DGP),
            // From Text
            "tg": null, // TextVerticalAlign.targetGroupId
            "vs": 1.0, // TextHorizontalAlign.verticalScale
            "hs": 1.0,  // TextHorizontalAlign.horizontalScale
        }
        
        DispGroupPointer.setSelectable(newDisp, true)
        DispGroupPointer.setCenterPoint(newDisp, 0, 0)
        
        return newDisp
    }
    
    static makeShapeContext(context: PeekCanvasShapePropsContext): void {
        DispBase.makeShapeContext(context)
        
        const disp = <DispGroupPointerT>context.disp
        
        if (disp.disps != null) {
            for (const childDisp of disp.disps) {
                if (DispBase.typeOf(childDisp) == DispType.text) {
                    context.addProp(new ShapeProp(
                        ShapePropType.MultilineString,
                        DispText.text,
                        DispText.setText,
                        "Text",
                        {alternateDisp: childDisp}
                    ))
                }
            }
        }
        
    }
    
    // ---------------
    // Represent the disp as a user friendly string
    
    static makeShapeStr(disp: DispGroupPointerT): string {
        let center = DispGroupPointer.center(disp)
        let str = DispBase.makeShapeStr(disp)
        if (DispGroupPointer.targetGroupName(disp))
            str += `\nName : ${DispGroupPointer.targetGroupName(disp)}`
        str += `\nAt : ${parseInt(<any>center.x)}x${parseInt(<any>center.y)}`
        return str
    }
    
    /** Set Disp Group
     *
     * Change the template that this disp groupd points to.
     *
     * This is achived by deleting the current disp items in this group,
     * and adding the new templates in.
     *
     * @param dispGroupPtr
     * @param groupDisp
     * @param lookupService
     * @param branchTuple
     */
    static setDispGroup(
        dispGroupPtr: DispGroupPointerT,
        groupDisp: DispGroupT,
        groupDispCoordSetId: number,
        lookupService: PrivateDiagramLookupService,
        branchTuple: BranchTuple
    ): void {
        let center = DispGroupPointer.center(dispGroupPtr)
        
        let oldDisps = branchTuple.disps
            .filter((d) => DispBase.groupId(d) == DispBase.id(dispGroupPtr))
        
        branchTuple.removeDisps(oldDisps)
        let coordSetId = branchTuple.coordSetId
        let thisCoordSetLevelsByOrder = {}
        for (let level of lookupService.levelsOrderedByOrder(coordSetId)) {
            thisCoordSetLevelsByOrder[level.order] = level
        }
        
        function findLevel(oldLevel): any {
            return thisCoordSetLevelsByOrder[oldLevel.order]
        }
        
        let newDisps = []
        for (let disp of DispGroup.items(groupDisp)) {
            // INTERIM FIX - DON'T COPY TEXT ITEMS
            // This will need to be sorted more elegantly when we want to use Peek to
            // to create patches.
            if (DispBase.typeOf(disp) == DispType.text)
                continue
            
            disp = DispBase.cloneDisp(disp)
            DispBase.setSelectable(disp, false)
            DispBase.setKey(disp, null)
            DispBase.setId(disp, null)
            DispBase.setReplacesHashId(disp, null)
            DispBase.setHashId(disp, null)
            
            lookupService._linkDispLookups(disp)
            
            // Convert this to this coord sets levels
            DispBase.setLevel(disp, findLevel(DispBase.level(disp)))
            
            DispBase.deltaMove(disp, center.x, center.y)
            DispBase.setGroupId(disp, DispBase.id(dispGroupPtr))
            newDisps.push(disp)
        }
        
        DispGroupPointer.setTargetGroupName(
            dispGroupPtr,
            groupDispCoordSetId,
            DispGroup.groupName(groupDisp)
        )
        branchTuple.addNewDisps(newDisps)
    }
    
    static handlePoints(
        disp,
        margin: number
    ): DispHandleI[] {
        return makeRotateHandlePoints(disp, margin,
            DispGroupPointer.center(disp),
            DispGroupPointer.rotation(disp)
        )
    }
}
