
function Graph(goJsKey) {
    var gridSize = {
        x:60,
        y:110
    }
    this.gridSize = gridSize

    go.Diagram.licenseKey = goJsKey;
    var myDiagram;
    var MyMake = go.GraphObject.make;
    function hasTouchScreen() {
        try{ document.createEvent("TouchEvent"); return true; }
        catch(e){ return false; }
    }

    const graph = this;
    const portSize = hasTouchScreen() ? 16 : 16;
    const imageBorder = 10;
    const commentDecorationSize = 20;
    const buttonPictureSize = 44;

    var templates = [
        {
            "category": "Intent",
            "shape": "Rectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#2facb4", 1.0: "#04d511" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#04d511",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "center",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "Speech",
            "shape": "Rectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#7019fe", 1.0: "#245cfe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#245cfe",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "GeneratedSpeech",
            "shape": "Diamond",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#7019fe", 1.0: "#245cfe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#245cfe",
            "width": 100,
            "height": 100,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "Sound",
            "shape": "Ellipse",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#7019fe", 1.0: "#245cfe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": null,
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel": false,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "Image",
            "shape": "RoundedRectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#7019fe", 1.0: "#245cfe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": null,
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel": false,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "Command",
            "shape": "Rectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#221e41", 1.0: "#222670" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#222670",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "SetPersona",
            "shape": "Rectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#221e41", 1.0: "#222670" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#222670",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "SetStyle",
            "shape": "Rectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#221e41", 1.0: "#222670" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#222670",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "SubDialogue",
            "shape": "Diamond",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#7019fe", 1.0: "#c204e8" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": null,
            "width": 100,
            "height": 100,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "UserInput",
            "shape": "Diamond",
            "ports": ["T","B","L","R"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#2facb4", 1.0: "#17c25c" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 4,
            "strokeColor": null,
            "height": 60,
            "width": 60,
            "validation": "node",
            "type": "Spot",
            "align": "center",
            "showLabel":false,
            "commentX": 0.82,
            "commentY": 0.82
        },
        {
            "category": "ReInput",
            "shape": "Diamond",
            "ports": ["T","B","L","R"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#2d8598", 1.0: "#2facb4" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 4,
            "strokeColor": null,
            "height": 60,
            "width": 60,
            "validation": "node",
            "type": "Spot",
            "align": "center",
            "showLabel":false,
            "commentX": 0.82,
            "commentY": 0.82
        },
        {
            "category": "GlobalIntent",
            "shape": "Rectangle",
            "ports": ["B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#1b7b81", 1.0: "#009509" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#009509",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "left",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "Function",
            "shape": "Diamond",
            "ports": ["T","B","L","R"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#221e41", 1.0: "#222670" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 5,
            "strokeColor": null,
            "height": 60,
            "width": 60,
            "validation": "function",
            "type": "Spot",
            "align": "center",
            "showLabel":false,
            "commentX": 0.82,
            "commentY": 0.82
        },
        {
            "category": "GoBack",
            "shape": "Circle",
            "ports": ["T"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#c204e8", 1.0: "#e46ffe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 5,
            "strokeColor": null,
            "height": 80,
            "width": 80,
            "validation": "function",
            "type": "Spot",
            "align": "center",
            "showLabel":true,
            "commentX": 0.82,
            "commentY": 0.86
        },
        {
            "category": "Sleep",
            "shape": "Circle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#c204e8", 1.0: "#e46ffe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 5,
            "strokeColor": null,
            "height": 80,
            "width": 80,
            "validation": "function",
            "type": "Spot",
            "align": "center",
            "showLabel":true,
            "commentX": 0.82,
            "commentY": 0.86
        },
        {
            "category": "End",
            "shape": "Circle",
            "ports": ["T"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#c204e8", 1.0: "#e46ffe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 4,
            "strokeColor": null,
            "height": 80,
            "width": 80,
            "validation": "function",
            "type": "Spot",
            "align": "center",
            "showLabel":true,
            "commentX": 0.82,
            "commentY": 0.86
        },
        {
            "category": "Exit",
            "shape": "Circle",
            "ports": ["T"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#c204e8", 1.0: "#e46ffe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 4,
            "strokeColor": null,
            "height": 80,
            "width": 80,
            "validation": "function",
            "type": "Spot",
            "align": "center",
            "showLabel":true,
            "commentX": 0.82,
            "commentY": 0.86
        },
        {
            "category": "Enter",
            "shape": "Circle",
            "ports": ["B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#c204e8", 1.0: "#e46ffe" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 4,
            "strokeColor": null,
            "height": 80,
            "width": 80,
            "validation": "function",
            "type": "Spot",
            "align": "center",
            "showLabel":true,
            "commentX": 0.82,
            "commentY": 0.86
        },
        {
            "category": "Action",
            "shape": "Rectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#2d8598", 1.0: "#2facb4" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#2facb4",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "center",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        },
        {
            "category": "GlobalAction",
            "shape": "Rectangle",
            "ports": ["T","B"],
            "fillColor": MyMake(go.Brush, "Linear", { 0.0: "#064e5c", 1.0: "#058891" }),
            "fontColor": "#FFF",
            "isVisible": false,
            "margin": 2,
            "strokeColor": "#058891",
            "width": 100,
            "height": 75,
            "validation": "node",
            "type": "Auto",
            "align": "center",
            "showLabel":true,
            "commentX": 0.90,
            "commentY": 0.86
        }
    ];

    go.GraphObject.defineBuilder('ToolTip', function (args) {
        var ad = go.GraphObject.make(go.Adornment, 'Auto',
            {
                isShadowed: true,
                shadowColor: 'rgba(0, 0, 0, .4)',
                shadowOffset: new go.Point(0, 3),
                shadowBlur: 5
            },
            go.GraphObject.make(go.Shape,
                {
                    name: 'Border',
                    figure: 'RoundedRectangle',
                    parameter1: 1,
                    parameter2: 1,
                    fill: '#000000',
                    stroke: '#000000',
                    spot1: new go.Spot(0, 0, 4, 6),
                    spot2: new go.Spot(1, 1, -4, -4)
                }
            )
        );
        return ad;
    });

    this.init = function() {
        const graph = this;

        myDiagram = MyMake(go.Diagram);

        myDiagram.grid =
            MyMake(go.Panel, "Grid",
                {gridCellSize: new go.Size(gridSize.x, gridSize.y)},
                MyMake(go.Shape, "LineH", {stroke: "#eee"}),
                MyMake(go.Shape, "LineV", {stroke: "#eee"}),
                MyMake(go.Shape, "LineH", {stroke: "#ddd", interval: 5}),
                MyMake(go.Shape, "LineV", {stroke: "#ddd", interval: 5})
            );
        templates.forEach(function (template) {
            addNodeToTemplate(MyMake, template, graph);
        });

        myDiagram.linkTemplate =
            MyMake(go.Link,  // the whole link panel
                {
                    routing: go.Link.AvoidsNodes,
                    curve: go.Link.JumpOver,
                    corner: 5, toShortLength: 4,
                    relinkableFrom: false,
                    relinkableTo: false,
                    reshapable: true,
                    resegmentable: true,
                    // mouse-overs subtly highlight links:
                    mouseEnter: function (e, link) {
                        link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.2)";
                    },
                    mouseLeave: function (e, link) {
                        link.findObject("HIGHLIGHT").stroke = "transparent";
                    },
                    toolTip: MyMake("ToolTip",
                        MyMake(go.TextBlock,
                            {
                                stroke: "#FFFFFF",
                                background: "#000000"
                            },
                            new go.Binding("text", "name")
                        )
                    )
                },
                new go.Binding("points", "points", function (v) {
                    return v.map(go.Point.parse)
                }).makeTwoWay(function (v) {
                    return v.map(go.Point.stringify)
                }),
                MyMake(go.Shape,  // the highlight shape, normally transparent
                    {isPanelMain: true, strokeWidth: 8, stroke: "transparent", name: "HIGHLIGHT"}),
                MyMake(go.Shape,  // the link path shape
                    {
                        isPanelMain: true,
                        stroke: "#5d7fa5",
                        strokeWidth: 2
                    }, new go.Binding("strokeWidth").makeTwoWay()),
                MyMake(go.Shape,  // the arrowhead
                    {
                        toArrow: "triangle",
                        stroke: null,
                        fill: "#5d7fa5",
                        scale: 1
                    }, new go.Binding("scale").makeTwoWay()),

                MyMake(go.TextBlock, "name",
                    {
                        segmentOffset: new go.Point(0, -10),
                        name: "name",
                        visible: false,
                        font: "600 11pt \"Century Gothic\"",
                        //   segmentOrientation: go.Link.Horizontal
                    }, new go.Binding("text", "name").makeTwoWay()),
            );

        myDiagram["lightGroupTemplate"] = MyMake(go.Group, "Vertical",
            {
                selectionObjectName: "PANEL",  // selection handle goes around shape, not label
                ungroupable: true,  // enable Ctrl-Shift-G to ungroup a selected Group
                avoidable: false
            },
            MyMake(go.Panel, "Auto",
                { name: "PANEL"},
                MyMake(go.Shape, "Rectangle",
                    {
                        fill: "rgba(225,233,239,0.5)",
                        stroke: "rgba(174,195,215,1)",
                        strokeWidth: 1,
                        fromLinkable: false,
                        toLinkable: false
                    }
                ),
                MyMake(go.Panel, "Vertical",
                    MyMake(go.Panel, "Horizontal",
                        { alignment: go.Spot.Left },
                        MyMake("SubGraphExpanderButton", {margin: 10}),
                        MyMake(go.TextBlock,
                            {
                                font: "bold 26px \"Century Gothic\"",
                                margin:10,
                                isMultiline:true
                            },
                            new go.Binding("text", "label").makeTwoWay(),
                            new go.Binding("stroke", "color")),
                    ),
                    MyMake(go.Placeholder, { margin: 10, background: "transparent" })  // represents where the members are
                )
            )
        );

        myDiagram["darkGroupTemplate"] = MyMake(go.Group, "Vertical",
            {
                selectionObjectName: "PANEL",  // selection handle goes around shape, not label
                ungroupable: true,  // enable Ctrl-Shift-G to ungroup a selected Group
                avoidable: false
            },
            MyMake(go.Panel, "Horizontal",
                MyMake("SubGraphExpanderButton", {margin: 10}),
                MyMake(go.TextBlock,
                    {
                        font: "bold 26px \"Century Gothic\"",
                        isMultiline: false,
                        stroke: "#8a90a0"
                    },
                    new go.Binding("text", "label").makeTwoWay(),
                    new go.Binding("stroke", "color")),
            ),
            MyMake(go.Panel, "Auto",
                { name: "PANEL" },
                MyMake(go.Shape, "Rectangle",
                    {
                        fill: "rgba(51,72,75,0.5)", stroke: "rgba(18,25,37,1)", strokeWidth: 1,
                        fromLinkable: false,
                        toLinkable: false
                    }),
                MyMake(go.Placeholder, { margin: 10, background: "transparent" })  // represents where the members are
            )
        );

        myDiagram.groupTemplate = myDiagram["lightGroupTemplate"]

        this.diagram = myDiagram;
        return this;
    };

    function addNodeToTemplate(MyMake, template, graph) {
        let label = MyMake(go.Panel)
        if (template.showLabel == true) label =
            MyMake(go.TextBlock,
                {
                    margin: template.margin,
                    wrap: go.TextBlock.WrapFit,
                    textAlign: template.align,
                    editable: false,
                    font: "600 11pt \"Century Gothic\"",
                    name: "name",
                    stroke: template.fontColor,
                },
        new go.Binding("text", "label").makeTwoWay()
        );

        var playButton = MyMake(go.Panel);
        var picturePanel = MyMake(go.Panel);

        if (template.category === "Image") {
            picturePanel = MyMake(
                go.Panel,
                "Spot",
                { isClipping: true  },
                MyMake(
                    go.Shape,
                    template.shape,
                    {
                        width: template.width,
                        height: template.height - imageBorder
                    }
                ), // END OF CLIPPING SHAPE
                MyMake(
                    go.Picture,
                    {
                        source: "",
                        width: template.width,
                        height: template.height - imageBorder,
                        imageStretch: go.GraphObject.Uniform,
                        toolTip:                       // define a tooltip for each node
                            MyMake(
                                go.Adornment,
                                "Spot",      // that has several labels around it
                                {
                                    background: "transparent"
                                },  // avoid hiding tooltip when mouse moves
                                MyMake(
                                    go.Picture,
                                    {
                                        alignment: go.Spot.Top,
                                        width: 400,
                                        height: 300,
                                        imageStretch: go.GraphObject.Uniform,
                                    },
                                    new go.Binding("source", "source", function(s) { return s; })
                                )
                            )
                    },
                    new go.Binding("source", "source")
                ), // END OF MAIN PICTURE

            )
        }
        if (template.category === "Sound") {
            playButton = MyMake(
                go.Panel,
                "Vertical",
                {
                    width: buttonPictureSize,
                    height: buttonPictureSize,
                    visible: false
                },
                MyMake(
                    go.Panel,
                    {
                        width: buttonPictureSize,
                        height: buttonPictureSize,
                        margin: 2,
                        click: function(e, obj) { graph.handleClick(e, obj) }
                    },
                    MyMake(
                        go.Picture,
                        {
                            alignment: go.Spot.Center,
                            margin: 0,
                            background: 'transparent',
                            source: "/img/icon-play.png",
                            width: buttonPictureSize,
                            height: buttonPictureSize,
                        },
                        new go.Binding("source", "buttonPictureSource")
                    ), // END OF BUTTON PICTURE
                ), // END OF BUTTON
                new go.Binding("visible", "source", function(v) { return !!v; }
                ),
            )
        }

        myDiagram.nodeTemplateMap.add(template.category,
            MyMake(
                go.Node,
                "Spot",
                nodeStyle(template.spot),
                MyMake(
                    go.Panel,
                    template.type,
                    {
                        maxSize: new go.Size(template.width, template.height),
                    },
                    MyMake(
                        go.Shape,
                        template.shape,
                        {
                            fill: template.fillColor,
                            stroke: template.strokeColor,
                            toMaxLinks: 2,
                            height: template.height,
                            width: template.width
                        },
                        new go.Binding("figure", "figure"),
                        new go.Binding("fill", "fill")),
                        label
                    ), // END OF MAIN SHAPE
                    picturePanel,
                    // four named ports, one on each side:
                    ...template.ports.map(function (port) { return makePort(port, MyMake);}),
                    MyMake(
                        go.Picture,
                        {
                            source: "/img/icon-comment.png",
                            alignment: new go.Spot(template.commentX, template.commentY),
                            width: commentDecorationSize,
                            height: commentDecorationSize,
                            visible: false
                        },
                        new go.Binding("visible", "comment", function(v) { return v && v !== ''; }),
                        new go.Binding("visible", "visible", function(v) { return v;})
                    ), // END OF COMMENT PICTURE
                    playButton,
                ) // END OF NODE
            ); // END OF ADD

        }

    var portDict = {
        'T': {
            'input': true,
            'spot': go.Spot.Top
        },
        'L': {
            'input': false,
            'spot': go.Spot.Left
        },
        'R': {
            'input': false,
            'spot': go.Spot.Right
        },
        'B': {
            'input': false,
            'spot': go.Spot.Bottom
        },
    };
    function makePort(name, MyMake, fromMaxLinks = 99999) {
        const portTemplate = portDict[name];
        return MyMake(go.Shape, "Rectangle",
            {
                fill: "transparent",
                opacity: 0.3,
                stroke: null,  // this is changed to "white" in the showPorts function
                desiredSize: new go.Size(portSize, portSize),
                alignment: portTemplate.spot, alignmentFocus: portTemplate.spot,  // align the port on the main Shape
                portId: name,  // declare this object to be a "port"
                fromSpot: portTemplate.spot, toSpot: portTemplate.spot,  // declare where links may connect at this port
                fromLinkable: !portTemplate.input, toLinkable: portTemplate.input, // declare whether the user may draw links to/from here
                fromMaxLinks: fromMaxLinks,
                cursor: "pointer"  // show a different cursor to indicate potential link point
            });
    }
    function nodeStyle(nodeLocationSpot = go.Spot.Center) {
        return [
            new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
            new go.Binding("zOrder").makeTwoWay(),
            {
                zOrder: 0,
                locationSpot: nodeLocationSpot,
                mouseEnter: function (e, obj) {
                    showPorts(obj.part, true);
                },
                mouseLeave: function (e, obj) {
                    showPorts(obj.part, false);
                }
            }
        ];
    }
    function showPorts(node, show) {
        var nodeDiagram = node.diagram;
        if (!nodeDiagram || nodeDiagram.isReadOnly || !nodeDiagram.allowLink) return;
        node.ports.each(function (port) {
            if (show) {
                // port.stroke = "#000000"
                port.fill = "#FFFFFF"
            } else {
                // port.stroke = null
                port.fill = "transparent"
            }
        });
    }
}
