/**
 * @author Martin Karkowski
 * @email m.karkowski@zema.de
 * @create date 2021-06-24 09:22:49
 * @modify date 2021-06-24 09:22:49
 * @desc [description]
 */
import * as go from "gojs";
import { ReactDiagram, ReactPalette } from "gojs-react";
import * as React from "react";
import { PETRINET as FLOWCHART } from "../../resources/ui/gojs/graph-types/petri-net";
import { addNodeNextTo } from "../../resources/ui/gojs/helpers/add-node-next-to";
import { transitionTemplate } from "../../resources/ui/gojs/nodes/transition";

// props passed in from a parent component holding state, some of which will be passed to ReactDiagram
interface WrapperProps {
  nodeDataArray: Array<go.ObjectData>;
  linkDataArray: Array<go.ObjectData>;
  modelData: go.ObjectData;
  skipsDiagramUpdate: boolean;
  onDiagramEvent: (e: go.DiagramEvent) => void;
  onModelChange: (e: go.IncrementalData) => void;
}

export class DiagramWrapper extends React.Component<WrapperProps, {}> {
  /**
   * Ref to keep a reference to the component, which provides access to the GoJS diagram via getDiagram().
   */
  private diagramRef: React.RefObject<ReactDiagram>;
  private paletteRef: React.RefObject<ReactPalette>;

  constructor(props: WrapperProps) {
    super(props);
    this.diagramRef = React.createRef();
    this.paletteRef = React.createRef();
  }

  /**
   * Get the diagram reference and add any desired diagram listeners.
   * Typically the same function will be used for each listener,
   * with the function using a switch statement to handle the events.
   * This is only necessary when you want to define additional app-specific diagram listeners.
   */
  public componentDidMount(): void {
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      // diagram.link
      const make = go.GraphObject.make;
      diagram.div.style.height = "800px";
      diagram.div.style.width = "1000px";
      diagram.nodeTemplateMap.add(
        "transition",
        transitionTemplate([
          make(
            "Button",
            {
              alignment: go.Spot.TopCenter,
              alignmentFocus: go.Spot.Right,
              click: (e, b) => {
                // take a button panel in an Adornment, get its Adornment, and then get its adorned Node
                const node: go.Node = b.part.adornedPart;
                addNodeNextTo(
                  node,
                  { category: "place" },
                  { category: "logicFlow", toPort: "LogicFlowIn" },
                  "to",
                  "n"
                );
              },
            },
            make(go.Shape, "PlusLine", { width: 6, height: 6 })
          ),
          make(
            "Button",
            {
              alignment: go.Spot.BottomCenter,
              click: (e, b) => {
                // take a button panel in an Adornment, get its Adornment, and then get its adorned Node
                const node: go.Node = b.part.adornedPart;
                addNodeNextTo(
                  node,
                  { category: "place" },
                  { category: "logicFlow", fromPort: "LogicFlowOut" },
                  "from",
                  "s"
                );
              },
              toolTip: make("ToolTip", ""),
            },
            make(go.Shape, "PlusLine", { width: 6, height: 6 })
          ),
        ])
      );

      // diagram.linkT;

      diagram.nodeTemplateMap.add(
        "place",
        make(
          go.Node,
          "Spot",
          {
            desiredSize: new go.Size(75, 75),
          },
          make(go.Shape, "Circle", {
            fill: "#52ce60" /* green */,
            stroke: null,
            portId: "test",
            fromLinkable: true,
            fromLinkableSelfNode: false,
            fromLinkableDuplicates: false,
            toLinkable: true,
            toLinkableSelfNode: false,
            toLinkableDuplicates: false,
            cursor: "pointer",
          }),
          make(go.TextBlock, "Start", {
            font: "bold 16pt helvetica, bold arial, sans-serif",
            stroke: "whitesmoke",
          })
        )
      );
      diagram.linkTemplateMap.add(
        "logicFlow",
        make(
          go.Link, // the whole link panel
          {
            routing: go.Link.Normal,
            corner: 15,
            curve: go.Link.Bezier,
            // adjusting: go.Link.Scale,
            reshapable: true,
            resegmentable: true,
            curviness: 20,
            // relinkableFrom: true,
            // relinkableTo: true,
            // toShortLength: 1,
            category: "logicFlow",
          },
          // new go.Binding("points").makeTwoWay(),
          // new go.Binding("curviness"),
          make(
            go.Shape, // the link shape
            { strokeWidth: 1.5 },
            new go.Binding("stroke", "progress", function (progress) {
              return progress ? "#52ce60" /* green */ : "black";
            }),
            new go.Binding("strokeWidth", "progress", function (progress) {
              return progress ? 2.5 : 1.5;
            })
          ),
          make(
            go.Shape, // the arrowhead
            { toArrow: "standard", stroke: null },
            new go.Binding("fill", "progress", function (progress) {
              return progress ? "#52ce60" /* green */ : "black";
            })
          ),
          make(
            go.Panel,
            "Auto",
            make(
              go.Shape, // the label background, which becomes transparent around the edges,
              "Rectangle",
              {
                fill: "white",
                stroke: null,
              }
            ),
            make(
              go.TextBlock,
              "transition", // the label text
              {
                textAlign: "center",
                font: "9pt helvetica, arial, sans-serif",
                margin: 4,
                editable: true, // enable in-place editing
              },
              // editing the text automatically updates the model data
              new go.Binding("text").makeTwoWay()
            )
          )
        )
      );

      diagram.addDiagramListener("LinkDrawn", (e) => {
        console.log(e.subject);
      });
    }

    if (!this.paletteRef.current) return;
    const palette = this.paletteRef.current.getPalette();

    palette.div.style.height = "800px";
    palette.div.style.width = "1000px";
  }

  /**
   * Get the diagram reference and remove listeners that were added during mounting.
   * This is only necessary when you have defined additional app-specific diagram listeners.
   */
  public componentWillUnmount() {
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      diagram.removeDiagramListener(
        "ChangedSelection",
        this.props.onDiagramEvent
      );
    }
  }

  onModelChange(...args) {
    console.log(...args);
  }

  /**
   * Diagram initialization method, which is passed to the ReactDiagram component.
   * This method is responsible for making the diagram and initializing the model, any templates,
   * and maybe doing other initialization tasks like customizing tools.
   * The model's data should not be set here, as the ReactDiagram component handles that via the other props.
   */
  private initDiagram(): go.Diagram {
    const diagram = FLOWCHART();

    // diagram.grid.visible = true;
    diagram.toolManager.draggingTool.isGridSnapEnabled = true;
    diagram.toolManager.resizingTool.isGridSnapEnabled = true;

    return diagram;
  }

  public render(): JSX.Element {
    return (
      <div style={{ background: "white" }}>
        <div draggable="true">Water</div>
        <ReactDiagram
          ref={this.diagramRef}
          divClassName="none"
          initDiagram={this.initDiagram}
          nodeDataArray={this.props.nodeDataArray}
          linkDataArray={this.props.linkDataArray}
          modelData={this.props.modelData}
          skipsDiagramUpdate={this.props.skipsDiagramUpdate}
        />
        <ReactPalette
          ref={this.paletteRef}
          initPalette={(...args) => {
            const myPalette = new go.Palette();
            // the list of data to show in the Palette

            myPalette.nodeTemplateMap.add("transition", transitionTemplate());
            // myPalette.nodeTemplateMap.add("test", );

            return myPalette;
          }}
          divClassName="none"
          nodeDataArray={[
            {
              label: "Transtion",
              guard: "x+1",
              category: "transition",
              icon: "https://cdn-images-1.medium.com/max/1600/0*F9ptwf7KyiVfrX0x.jpg",
              description: "General Legal Division Director",
              serviceName: "testservice",
              serviceInputs: [
                {
                  portId: 21,
                  label: "i1",
                },
                {
                  portId: 22,
                  label: "i2",
                },
                {
                  portId: 23,
                  label: "i3",
                },
              ],
              serviceOutputs: [
                {
                  portId: 10,
                  label: "outp",
                },
              ],
              guardInputs: [
                {
                  portId: 31,
                  label: "g1",
                },
                {
                  portId: 32,
                  label: "g2",
                },
                {
                  portId: 33,
                  label: "g3",
                },
              ],
              guardOutputs: [
                {
                  portId: 11,
                  label: "g1",
                },
              ],
            },
            {},
          ]}
        ></ReactPalette>
      </div>
    );
  }
}

export default DiagramWrapper;
