import * as React from 'react';
import * as go from 'gojs';
import { Diagram } from 'gojs';
import { GojsDiagram } from 'react-gojs';
import './MyDiagram.css';
import { BPMNLinkingTool } from './BPMNClasses';

class MyDiagram extends React.Component<any, any> {
    private _myDiagram: Diagram;
    constructor(props: any) {
        super(props);
        this.createDiagram = this.createDiagram.bind(this);
    }

    
    public addNodeData(node: any) {
        this._myDiagram.model.addNodeData(node);
    }

    public removeNodeData(node: any) {
        this._myDiagram.commandHandler.deleteSelection();
    }
    AddAction(myDiagram: any, evType: any, evDim: any) {
        myDiagram.startTransaction("addBoundaryEvent");
        myDiagram.selection.each(function (node:any) {
          // skip any selected Links
          if (!(node instanceof go.Node)) return;
          if (node.data && (node.data.category === "activity" || node.data.category === "subprocess")) {
            // compute the next available index number for the side
            var i = 0;
            var defaultPort = node.findPort("");
            while (node.findPort("be" + i.toString()) !== defaultPort) i++;           // now this new port name is unique within the whole Node because of the side prefix
            var name = "be" + i.toString();
            if (!node.data.boundaryEventArray) { myDiagram.model.setDataProperty(node.data, "boundaryEventArray", []); }       // initialize the Array of port data if necessary
            // create a new port data object
            var newportdata = {
              portId: name,
              eventType: evType,
              eventDimension: evDim,
              color: "white",
              alignmentIndex: i
              // if you add port data properties here, you should copy them in copyPortData above  ** BUG...  we don't do that.
            };
            // and add it to the Array of port data
            myDiagram.model.insertArrayItem(node.data.boundaryEventArray, -1, newportdata);
          }
        });
        myDiagram.commitTransaction("addBoundaryEvent");
        alert("Add User action")
    }
    private getContextMenu($ : any){
        return $(go.Adornment, "Vertical",
        $("ContextMenuButton",
            $(go.TextBlock, "Add User action", { margin: 3 }),
            { click: (e: any, obj: any) => { this.AddAction(this._myDiagram , 2, 5) } }),
        $("ContextMenuButton",
            $(go.TextBlock, "Add Timer Action", { margin: 3 }),
            { click: (e: any, obj: any) => { this.AddAction(this._myDiagram , 3, 5) } }),
        $("ContextMenuButton",
            $(go.TextBlock, "State Handlers", { margin: 3 }),
            { click: (e: any, obj: any) => { this.AddAction(this._myDiagram , 4, 5) } }),
        $("ContextMenuButton",
            $(go.TextBlock, "State Permissions", { margin: 3 }),
            { click: (e: any, obj: any) => { this.AddAction(this._myDiagram , 7, 5) } }),
        $("ContextMenuButton",
            $(go.TextBlock, "State Rules", { margin: 3 }),
            { click: (e: any, obj: any) => { this.AddAction(this._myDiagram , 10, 5) } }),
        $("ContextMenuButton",
            $(go.TextBlock, "Delete State", { margin: 3 }),
            { click: (e: any, obj: any) => { this.removeNodeData.bind(this) } }),
    )
    }
    private nodeEventTypeConverter(s: any) {  // order here from BPMN 2.0 poster
        var tasks = ["NotAllowed",
            "Empty",
            "BpmnTaskMessage",
            "BpmnEventTimer",
            "BpmnEventEscalation",
            "BpmnEventConditional",
            "Arrow",
            "BpmnEventError",
            "ThinX",
            "BpmnActivityCompensation",
            "Triangle",
            "Pentagon",
            "ThickCross",
            "Circle"];
        if (s < tasks.length) return tasks[s];
        return "NotAllowed"; // error
    }

    private nodeActivityBESpotConverter(s: any) {
        var EventNodeSize = 42;

        var x = 10 + (EventNodeSize / 2);
        if (s === 0) return new go.Spot(0, 1, x, 0);    // bottom left
        if (s === 1) return new go.Spot(1, 1, -x, 0);   // bottom right
        if (s === 2) return new go.Spot(1, 0, -x, 0);   // top right
        return new go.Spot(1, 0, -x - (s - 2) * EventNodeSize, 0);    // top ... right-to-left-ish spread
    } 
    private getBoundaryEventItemTemplate($:any):go.Part{
        var EventNodeSize = 42;
        var EventNodeInnerSize = EventNodeSize - 6;
        var EventNodeSymbolSize = EventNodeInnerSize - 14;

        return $(go.Panel, "Spot",
        {
            //contextMenu: boundaryEventMenu,
            alignmentFocus: go.Spot.Center,
            fromLinkable: true, toLinkable: false, cursor: "pointer", fromSpot: go.Spot.Bottom,
            fromMaxLinks: 1, toMaxLinks: 0
        },
        new go.Binding("portId", "portId"),
        new go.Binding("alignment", "alignmentIndex", this.nodeActivityBESpotConverter),
        $(go.Shape, "Circle",
            { desiredSize: new go.Size(EventNodeSize, EventNodeSize) },
            new go.Binding("strokeDashArray", "eventDimension", function (s) { return (s === 6) ? [4, 2] : null; }),
            new go.Binding("fromSpot", "alignmentIndex",
                function (s) {
                    //  nodeActivityBEFromSpotConverter, 0 & 1 go on bottom, all others on top of activity
                    if (s < 2) return go.Spot.Bottom;
                    return go.Spot.Top;
                }),
            new go.Binding("fill", "color")),
        $(go.Shape, "Circle",
            {
                alignment: go.Spot.Center,
                desiredSize: new go.Size(EventNodeInnerSize, EventNodeInnerSize), fill: null
            },
            new go.Binding("strokeDashArray", "eventDimension", function (s) { return (s === 6) ? [4, 2] : null; })
        ),
        $(go.Shape, "NotAllowed",
            {
                alignment: go.Spot.Center,
                desiredSize: new go.Size(EventNodeSymbolSize, EventNodeSymbolSize), fill: "white"
            },
            new go.Binding("figure", "eventType", this.nodeEventTypeConverter)
        )
    );
    }
    private getActivityNodeTemplate($:any):go.Part{
        return $(go.Node, "Spot",
        {
            locationObjectName: "SHAPE", locationSpot: go.Spot.Center,
            resizable: true, resizeObjectName: "PANEL",
            //toolTip: tooltiptemplate,
            selectionAdorned: false,  // use a Binding on the Shape.stroke to show selection
            contextMenu: this.getContextMenu($),
            itemTemplate: this.getBoundaryEventItemTemplate($)
        },
        new go.Binding("itemArray", "boundaryEventArray"),
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        // move a selected part into the Foreground layer, so it isn"t obscured by any non-selected parts
        new go.Binding("layerName", "isSelected", function (s) { return s ? "Foreground" : ""; }).ofObject(),
        $(go.Panel, "Auto",
            {
            name: "PANEL",
            //   minSize: new go.Size(ActivityNodeWidth, ActivityNodeHeight),
            //   desiredSize: new go.Size(ActivityNodeWidth, ActivityNodeHeight)
            },
            new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
            $(go.Panel, "Spot",
            $(go.Shape, "RoundedRectangle",  // the outside rounded rectangle
                {
                name: "SHAPE",
                //fill: ActivityNodeFill, stroke: ActivityNodeStroke,
                parameter1: 10, // corner size
                portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer",
                fromSpot: go.Spot.RightSide, toSpot: go.Spot.LeftSide
                },
                new go.Binding("fill", "color"),
                // new go.Binding("strokeWidth", "isCall",
                //   function (s) { return s ? ActivityNodeStrokeWidthIsCall : ActivityNodeStrokeWidth; })
            ),
            //        $(go.Shape, "RoundedRectangle",  // the inner "Transaction" rounded rectangle
            //          { margin: 3,
            //            stretch: go.GraphObject.Fill,
            //            stroke: ActivityNodeStroke,
            //            parameter1: 8, fill: null, visible: false
            //          },
            //          new go.Binding("visible", "isTransaction")
            //         ),
            // task icon
            $(go.Shape, "BpmnTaskScript",    // will be None, Script, Manual, Service, etc via converter
                {
                alignment: new go.Spot(0, 0, 5, 5), alignmentFocus: go.Spot.TopLeft,
                width: 22, height: 22
                },
                // new go.Binding("fill", "taskType", nodeActivityTaskTypeColorConverter),
                // new go.Binding("figure", "taskType", nodeActivityTaskTypeConverter)
            ), // end Task Icon
            //makeMarkerPanel(false, 1) // sub-process,  loop, parallel, sequential, ad doc and compensation markers
            ),  // end main body rectangles spot panel
            $(go.TextBlock,  // the center text
            {
                alignment: go.Spot.Center, textAlign: "center", margin: 12,
                editable: true
            },
            new go.Binding("text").makeTwoWay())
        )  // end Auto Panel
        );  // end go.Node, which is a Spot Panel with bound itemArray

    }
    // private mouseDrop (e:any){
    //         // when the selection is dropped in the diagram's background,
    //         // make sure the selected Parts no longer belong to any Group
    //         var ok = this._myDiagram.commandHandler.addTopLevelParts(this._myDiagram.selection, true);
    //         if (!ok) this._myDiagram.currentTool.doCancel();
    // }
    private createDiagram(diagramId: string): Diagram {
        let $ = go.GraphObject.make; 

        // ------------------------------- template for Activity / Task node in Palette  -------------------------------

        this._myDiagram =  $(go.Diagram, diagramId, {
            // initialContentAlignment: go.Spot.TopCenter,
            // layout: $(go.TreeLayout, {
            //     angle: 90,
            //     arrangement: go.TreeLayout.ArrangementVertical,
            //     treeStyle: go.TreeLayout.StyleLayered
            // }),
            // isModelReadOnly: false,
            // isReadOnly: false,
            // allowHorizontalScroll: true,
            // allowVerticalScroll: true,
            // allowZoom: false,
            // allowSelect: true,
            // autoScale: Diagram.Uniform,
            // contentAlignment: go.Spot.Center,
            
          // defined in DrawCommandHandler.js
        // default to having arrow keys move selected nodes
        
        linkTemplateMap: new go.Map("string", go.Link),
       // mouseDrop: this.mouseDrop.bind(this),
         linkingTool: new BPMNLinkingTool(), // defined in BPMNClasses.js
        // "SelectionMoved": this.relayoutDiagram.bind(this),  // defined below
        // "SelectionCopied": this.relayoutDiagram.bind(this)
        });
        
        this._myDiagram.initialContentAlignment = go.Spot.Center;
        this._myDiagram.nodeTemplate = $(
            go.Node,
            'Auto',
            // {
            //     selectionChanged: (node: Node) => this.props.onNodeSelection(node.key as string, node.isSelected)
            // },
            $(go.Shape, 'Circle', {
                strokeWidth: 0, alignment: go.Spot.Center,
                fromSpot: go.Spot.Left, toSpot: go.Spot.Right,
                stretch: go.GraphObject.Vertical, cursor: "pointer"
            }, new go.Binding('fill', 'color')),
            $(go.TextBlock, { margin: 8, cursor: "pointer" }, new go.Binding('text', 'text')), {
                selectionAdornmentTemplate:
                    $(go.Adornment, "Auto",
                        $(go.Shape, "RoundedRectangle",
                            { fill: null, stroke: "dodgerblue", strokeWidth: 8 }),
                        $(go.Placeholder)
                    )  // end Adornment
            }
        );
        // create the nodeTemplateMap, holding main view node templates:
        //var nodeTemplateMap = new go.Map("string", go.Node);
        var nodeTemplateMap = this._myDiagram.nodeTemplateMap;
        // for each of the node categories, specify which template to use
        nodeTemplateMap.add("activity", this.getActivityNodeTemplate($));

        return this._myDiagram;
    }
    componentDidMount() {
        this.props.onRef(this)
    }
    componentWillUnmount() {
        //this.props.onRef(undefined)
    }

    public render(): JSX.Element {
        return (
            <GojsDiagram
                diagramId="myDiagramDiv"
                model={this.props.model}
                createDiagram={this.createDiagram}
                className="myDiagram"
            />
        );
    }
}

export default MyDiagram;
