public void CreateNewNode(Vector2 position, NodeType nodeType)
    {
        //This creates a BRAND new node through the Add Speech/Response button
        SpeakEasyNode node = new SpeakEasyNode
        {
            nodeID   = Guid.NewGuid().ToString(), //Generates the GUID for this node
            nodeType = nodeType
        };

        AddElement(InitializeNode(node, position, nodeType));
    }
    private void LoadNode(SpeakEasyFile_NodeData data)
    {
        Vector2       dataPosition = data.nodePosition;
        NodeType      dataType     = data.nodeType;
        SpeakEasyNode node         = new SpeakEasyNode
        {
            nodeID          = data.nodeID,
            nodeType        = dataType,
            stringReference = data.stringReference,
            isEntryPoint    = data.isEntryPoint,
            priority        = data.nodePriority,
            scriptEvent     = data.scriptEventRef,
            scriptTest      = data.scriptTestRef,
        };

        AddElement(InitializeNode(node, dataPosition, dataType));
    }
    private void AddNewTableEntry(Button pressedButton, SpeakEasyNode node, IntegerField indexField, TextField textField)
    {
        //Check if we by any chance already have a reference.
        for (int i = 0; i < localText.localizedText.Count; i++)
        {
            if (node.stringReference == "" || node.stringReference == System.String.Empty)
            {
                EditorUtility.DisplayDialog("Oops!", "Sorry, cannot add a new entry without a proper Identifier.", "OK");
                return;
            }

            if (node.stringReference == localText.localizedText[i].id)
            {
                //We already have a point of reference
                pressedButton.SetEnabled(false);
                node.stringIndex = i;
                indexField.SetValueWithoutNotify(node.stringIndex);
                textField.SetValueWithoutNotify(GetStringIndex(node.stringIndex));
                textField.SetEnabled(true);

                return;
            }
        }

        pressedButton.SetEnabled(false); //We no longer need a new reference point.

        //Give this node a new string ID
        node.stringIndex = localText.localizedText.Count; //the Count is always +1 which means it will place the new reference at the right index.. right?
        localText.localizedText.Add(new LocalizedText()
        {
            id = node.stringReference
        });                                                                           //we add the new address for the list.

        //Now we need to connect the three fields
        indexField.SetValueWithoutNotify(node.stringIndex);
        textField.SetValueWithoutNotify(GetStringIndex(node.stringIndex));
        textField.SetEnabled(true);
    }
    private void PrintValues(SpeakEasyNode node, Port ingoing, Port outgoing)
    {
        Debug.Log("Our Node ID is: " + node.nodeID);
        Debug.Log("Our Node Type is: " + node.nodeType);
        Debug.Log("Are we an EntryPoint node?: " + node.isEntryPoint);
        Debug.Log("Our Node's priority is: " + node.priority);
        Debug.Log("Our String Reference is: " + node.stringReference);
        Debug.Log("Our Test Script reference ID is: " + node.scriptTest);
        Debug.Log("Our Event Script reference reference ID is: " + node.scriptEvent);

        Debug.Log("The number of incoming connections we have are: " + ingoing.connections.ToArray <Edge>().Length);
        Debug.Log("The number of outgoing connections we have are: " + outgoing.connections.ToArray <Edge>().Length);


        int i = 0;

        foreach (Edge e in ingoing.connections.ToArray <Edge>())
        {
            //We need to cast the node
            SpeakEasyNode output = (e.output.node as SpeakEasyNode);
            Debug.Log("Edge " + i + " recieves a speech from node: " + output.nodeID);

            i++;
        }


        i = 0;

        foreach (Edge e in outgoing.connections.ToArray <Edge>())
        {
            SpeakEasyNode input = (e.input.node as SpeakEasyNode);
            Debug.Log("Edge " + i + " leads to response node: " + input.nodeID);

            i++;
        }
    }
    public override List <Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
    {
        //Handles the logic for determining which ports can be connected to.
        SpeakEasyNode startNode = (startPort.node as SpeakEasyNode);

        Debug.Log("Determining compatible ports for node: " + startNode.nodeID);
        List <Port> compatiblePorts = new List <Port>();
        Port        startPortView   = startPort; //startPort is the port that we are "holding".

        //OOOH GOD YOU ARE SO STUPID - you were making an array of Edges EACH time you searched for a port.. dummy.

        ports.ForEach((port) =>
        {
            Port availablePort = port; //availablePort are the available ports that we can connect to.

            //Do not allow the port to connect to itself through the same port or the same node.
            if (startPortView == availablePort || startPortView.node == availablePort.node)
            {
                return;
            }

            //Do not allow to connect to the port of the same direction
            if (startPortView.direction == availablePort.direction)
            {
                return;
            }

            //Prevent connection between Response nodes.
            if (startNode.nodeType == NodeType.response && (availablePort.node as SpeakEasyNode).nodeType == NodeType.response)
            {
                return;
            }
            //If no conditions were met, add as an avilable port.
            compatiblePorts.Add(port);
        });

        //Do not allow to connect to the same port twice.
        //We need to get the reference of this ports connections
        //List<Edge> currentConnections = startPort.connections;

        Debug.Log("Found " + compatiblePorts.Count + " eligible ports.");

        Debug.Log("Found " + startPortView.connections.ToArray <Edge>().Length + " connections.");
        //I can't help but consider there's an easier way to grab this information.
        foreach (Edge e in startPortView.connections.ToArray <Edge>())
        {
            if (startPortView.direction == Direction.Input)
            {
                SpeakEasyNode node = (e.output.node as SpeakEasyNode);
                Debug.Log("Found an output connection with node " + node.nodeID);
                for (int i = compatiblePorts.Count - 1; i >= 0; i--)
                {
                    if (node == compatiblePorts[i].node)
                    {
                        compatiblePorts.RemoveAt(i);
                    }
                }
            }

            else
            {
                SpeakEasyNode node = (e.input.node as SpeakEasyNode);
                Debug.Log("Found an input connection with node " + node.nodeID);
                for (int i = compatiblePorts.Count - 1; i >= 0; i--)
                {
                    if (node == compatiblePorts[i].node)
                    {
                        compatiblePorts.RemoveAt(i);
                    }
                }
            }
        }

        return(compatiblePorts);
    }
    public bool GraphIsDirty(SpeakEasyFile file)
    {
        bool isDirty = false;

        //Compare data, returning a "isDirty" state.
        //We need a copy of the Nodes and Edges

        Debug.Log("Checking lenghts...");


        List <SpeakEasyNode> nodeList = nodes.ToList().Cast <SpeakEasyNode>().ToList();

        foreach (SpeakEasyNode n in nodeList)
        {
            Debug.Log(n.nodeID);
        }

        Debug.Log(nodeList.Count);
        Debug.Log(file.nodeData.Count);

        if (file.nodeData.Count == nodeList.Count)
        {
            //The length of the file's nodeData and nodeList in the graph are the same, keep going.
            //Now we need to compare individual node's data

            Debug.Log("Saved Data and NodeList appear to be of the same length");

            List <SpeakEasyFile_NodeData> data = file.nodeData;
            for (int i = 0; i < nodeList.Count; i++)
            {
                //Iterate through the graph's nodeList.
                if (nodeList[i].nodeType == data[i].nodeType &&
                    nodeList[i].nodeID == data[i].nodeID &&
                    nodeList[i].stringReference == data[i].stringReference &&
                    nodeList[i].isEntryPoint == data[i].isEntryPoint &&
                    nodeList[i].priority == data[i].nodePriority &&
                    nodeList[i].scriptTest == data[i].scriptTestRef &&
                    nodeList[i].scriptEvent == data[i].scriptEventRef &&
                    nodeList[i].GetPosition().position == data[i].nodePosition
                    )
                {
                    //Now we need to iterate through this node's outputs
                    Debug.Log("The Data in this node appears to be match the Data in the saved file.");

                    List <string> connectedNodes = new List <string>();
                    foreach (Edge e in nodeList[i].outputConnection.connections)
                    {
                        //Retrieve the guid of each node.
                        SpeakEasyNode input = (e.input.node as SpeakEasyNode);
                        connectedNodes.Add(input.nodeID);
                    }

                    //And run compare it to the saved data
                    if (connectedNodes.Count == file.nodeData[i].connectionData.Count)
                    {
                        Debug.Log("The connection data in this node has the same amount of connections as the saved file for this node.");

                        for (int j = 0; j < connectedNodes.Count; j++)
                        {
                            if (connectedNodes[j] == file.nodeData[i].connectionData[j])
                            {
                                Debug.Log("The connection data in this node matches the connection data in the saved file.");
                                Debug.Log("This file appears to be clean.");
                            }

                            else
                            {
                                isDirty = true;
                            }
                        }
                    }

                    else
                    {
                        isDirty = true;
                    }
                }

                else
                {
                    isDirty = true;  //If any of the above isn't matching, then we made a change to the nodeList.
                }
            }
        }

        else
        {
            isDirty = true;
        }

        return(isDirty);
    }
    public void SaveData(SpeakEasyFile file)
    {
        if (file != null)
        {
            List <Edge>          edgeList = edges.ToList();
            List <SpeakEasyNode> nodeList = nodes.ToList().Cast <SpeakEasyNode>().ToList();

            Debug.Log("Found " + edgeList.Count + " edges in the Graph");
            Debug.Log("Found " + nodeList.Count + " nodes in the Graph");

            //Clear the list in the file
            file.nodeData.Clear();

            EditorUtility.SetDirty(file);

            foreach (SpeakEasyNode n in nodeList)
            {
                //We need to grab our connections first.
                List <string> savedConnectionData = new List <string>();
                foreach (Edge e in n.outputConnection.connections)
                {
                    //Retrieve the guid of each node.
                    SpeakEasyNode input = (e.input.node as SpeakEasyNode);
                    savedConnectionData.Add(input.nodeID);
                    Debug.Log("Saved connection starting at " + n.nodeID + " node and ending with " + input.nodeID + " node.");
                }


                file.nodeData.Add(new SpeakEasyFile_NodeData
                {
                    nodeType        = n.nodeType,
                    nodeID          = n.nodeID,
                    stringReference = n.stringReference,
                    isEntryPoint    = n.isEntryPoint,
                    nodePriority    = n.priority,
                    scriptTestRef   = n.scriptTest,
                    scriptEventRef  = n.scriptEvent,
                    nodePosition    = n.GetPosition().position,
                    connectionData  = savedConnectionData
                });
                Debug.Log("Saved node " + n.nodeID);
            }

            //We need to re-save the list
            localText.Save(file.localePath);

            /*
             * foreach(Edge e in edgeList)
             * {
             *  SpeakEasyNode input = (e.input.node as SpeakEasyNode);
             *  SpeakEasyNode output = (e.output.node as SpeakEasyNode);
             *  string startingPoint = output.nodeID;
             *  string endingPoint = input.nodeID;
             *  file.connectionData.Add(new SpeakEasyFile_ConnectionData
             *  {
             *      originNodeID = startingPoint,
             *      targetNodeID = endingPoint
             *  });
             *
             *  Debug.Log("Saved connection starting at " + startingPoint + " node and ending with " + endingPoint + " node.");
             * }
             */
        }
    }
    private Port GetPortInstance(SpeakEasyNode node, Direction nodeDirection, Port.Capacity capacity = Port.Capacity.Multi)
    {
        //Returns an instance (look at you using big programmer words) of the Node. The "typeofFloat" is arbirtary here, because we have no intention of passing any data between the nodes itself

        return(node.InstantiatePort(Orientation.Horizontal, nodeDirection, capacity, typeof(float)));
    }
    public SpeakEasyNode InitializeNode(SpeakEasyNode node, Vector2 position, NodeType nodeTypeChoice)
    {
        //Actual logic for the creation of the node
        //This is the UI layout

        //Set the color of the node so it's not super transparent.
        node.mainContainer.style.backgroundColor = new Color(0.25f, 0.25f, 0.25f, 0.75f);

        //Initialize the Label of the Node, determining if it's a SPEECH or RESPONSE node
        Label nodeTypeLabel = new Label();

        nodeTypeLabel.style.fontSize  = 15;
        nodeTypeLabel.style.alignSelf = Align.Center;
        if (node.nodeType == NodeType.speech)
        {
            nodeTypeLabel.text = "SPEECH";
            node.titleContainer.style.backgroundColor = speechColor;
        }

        else
        {
            nodeTypeLabel.text = "RESPONSE";
            node.titleContainer.style.backgroundColor = responseColor;
        }

        node.titleContainer.Add(nodeTypeLabel);

        //The integer field, this field references the index in the Localization file. It's a read only field.
        IntegerField stringIndexField = new IntegerField("String Index:");

        stringIndexField.SetEnabled(false); //We shouldn't be able to directly edit this value.
        node.mainContainer.Add(stringIndexField);

        //This is the "text preview" which we morphed into a auto-localization file. The editor on save stores this data back into the specified XML file.
        TextField textPreviewField = new TextField();

        textPreviewField.multiline        = true;
        textPreviewField.style.maxWidth   = 350;
        textPreviewField.style.whiteSpace = WhiteSpace.Normal;
        textPreviewField.SetEnabled(false);
        textPreviewField.isDelayed = true;
        node.mainContainer.Add(textPreviewField);


        //adds an ID reference so we can see the GUID
        Label idReference = new Label(node.nodeID);

        node.mainContainer.Add(idReference);
        idReference.style.color = new Color(0.75f, 0.75f, 0.75f);


        //This is the stringRefField, aka the identifier for our string. This is what we store in our file data.
        TextField stringRefField = new TextField("String Reference:");

        stringRefField.isDelayed = true;
        stringRefField.RegisterValueChangedCallback(evt =>
        {
            node.stringReference = evt.newValue;                       //set the node's string reference to the new Value
            Debug.Log("Saving localization data");
            SetLocalizationIdentifier(node.stringIndex, evt.newValue); //Sets the localization identifier for the index if one is specified.
        });
        node.mainContainer.Add(stringRefField);
        stringRefField.SetValueWithoutNotify(node.stringReference);
        stringIndexField.SetValueWithoutNotify(FindStringIndex(node.stringReference)); //Look up our string reference identifier and return the index if one exists.
        //We need to set the stringIndex of the node too.
        node.stringIndex = stringIndexField.value;                                     // set the node's internal value to the stringIndexField value.
        textPreviewField.SetValueWithoutNotify(GetStringIndex(node.stringIndex));      //Update the localized box auto match the .. you know what this does.

        textPreviewField.RegisterValueChangedCallback(evt =>
        {
            Debug.Log("Saving localization data");
            SetLocalizationText(node.stringIndex, evt.newValue); //If we change the text in this field, update the localization table to match our new entry.
        });

        //This button will give us a brand new spanking place in the localization file hashtag smiley face
        Button addNewTableEntryButton = null;  //I do not understand this logic...

        addNewTableEntryButton = new Button(() => AddNewTableEntry(addNewTableEntryButton, node, stringIndexField, textPreviewField))
        {
            text = "Add Table Entry"
        };

        if (localText == null)
        {
            addNewTableEntryButton.SetEnabled(false);
        }

        node.mainContainer.Add(addNewTableEntryButton);
        //Check if we already have this data
        if (CheckIfStringReferenceExists(node.stringReference))
        {
            //We already have our connection
            addNewTableEntryButton.SetEnabled(false);
            textPreviewField.SetEnabled(true);
        }

        //Serialize Save/Load for Audio Clips and Animation Trigger
        ObjectField audioField = new ObjectField("Audio Field");

        audioField.objectType        = typeof(AudioClip);
        audioField.allowSceneObjects = false;
        audioField.SetEnabled(false);
        node.mainContainer.Add(audioField);

        TextField animationTriggerField = new TextField("Animation Trigger");

        animationTriggerField.SetEnabled(false);
        node.mainContainer.Add(animationTriggerField);

        UnityEngine.UIElements.Toggle entryPointToggle = new UnityEngine.UIElements.Toggle("Entry Point");
        entryPointToggle.RegisterValueChangedCallback(evt =>
        {
            node.isEntryPoint = evt.newValue;
        });
        node.mainContainer.Add(entryPointToggle);

        entryPointToggle.SetValueWithoutNotify(node.isEntryPoint);

        IntegerField priorityField = new IntegerField("Priority:");

        priorityField.RegisterValueChangedCallback(evt =>
        {
            node.priority = evt.newValue;
        });
        priorityField.SetValueWithoutNotify(node.priority);
        node.mainContainer.Add(priorityField);

        /*
         * ObjectField testRef = new ObjectField("OnConditional");
         * testRef.objectType = typeof(SpeakEasyLogics_Test);
         * testRef.allowSceneObjects = false;
         * node.mainContainer.Add(testRef);
         *
         * ObjectField eventRef = new ObjectField("OnFire");
         * eventRef.objectType = typeof(SpeakEasyLogics_Event);
         * eventRef.allowSceneObjects = false;
         * node.mainContainer.Add(eventRef);
         */

        //adds a OnConditional reference
        ObjectField testRefField = new ObjectField("Test Script Ref:");

        testRefField.objectType        = typeof(SpeakEasyLogics_Test);
        testRefField.allowSceneObjects = false;
        testRefField.RegisterValueChangedCallback(evt =>
        {
            node.scriptTest = (evt.newValue as SpeakEasyLogics_Test);
            //SpeakEasyNode startNode = (startPort.node as SpeakEasyNode);
        });

        node.mainContainer.Add(testRefField);
        testRefField.SetValueWithoutNotify(node.scriptTest);

        //adds a OnFire reference
        ObjectField eventRefField = new ObjectField("Event Script Ref:");

        eventRefField.objectType        = typeof(SpeakEasyLogics_Event);
        eventRefField.allowSceneObjects = false;
        eventRefField.RegisterValueChangedCallback(evt =>
        {
            node.scriptEvent = (evt.newValue as SpeakEasyLogics_Event);
        });

        node.mainContainer.Add(eventRefField);
        eventRefField.SetValueWithoutNotify(node.scriptEvent);

        //This port is the IN connection, this is where we will lead the conversation TO. The Input can recieve a number of connections from conversation choices
        Port inputPort = GetPortInstance(node, Direction.Input, Port.Capacity.Multi);

        node.inputConnection = inputPort;
        inputPort.portName   = "Connections";
        node.inputContainer.Add(inputPort);


        Port outputPort = GetPortInstance(node, Direction.Output, Port.Capacity.Multi);

        node.outputConnection = outputPort;
        outputPort.portName   = "Responses";
        node.outputContainer.Add(outputPort);


        //
        //node.outputContainer.style.flexDirection <-- this is what I was looking for last night! controls the direction of the container?

        node.RefreshExpandedState();
        node.RefreshPorts();

        node.SetPosition(new Rect(position, defaultNodeSize));

        Button debugTestValues = new Button(() => PrintValues(node, inputPort, outputPort))
        {
            text = "Print Values"
        };

        node.mainContainer.Add(debugTestValues);

        return(node);
    }