public void StartSimulation()
    {
        if (interactionPhase != InteractionPhases.ingame_default)
        {
            return;
        }
        playerInteraction_UI.loadingOverlay.ClosePanel();
        interactionPhase = InteractionPhases.simulation;

        GridObjectBehavior[] gridObjs = GameManager.Instance.GetGridManager().RetrieveComponentsOfType("thread");
        foreach (GridObjectBehavior g in gridObjs)
        {
            g.GetComponent <SpriteRenderer>().sortingOrder = Constants.ComponentSortingOrder.thread_simulation;
        }

        playerInteraction_UI.simulationButton.interactable = false;
        playerInteraction_UI.simulationButton.gameObject.SetActive(false);
        playerInteraction_UI.submitButton.interactable = false;
        //playerInteraction_UI.submitButton.gameObject.SetActive(false);
        playerInteraction_UI.stopSimulationButton.interactable = true;
        playerInteraction_UI.stopSimulationButton.gameObject.SetActive(true);

        simulationTime = 0f;
        playerInteraction_UI.goalOverlay.userInput = PlayerInteraction_UI.Goal_UIOverlay.UserInputs.none;
        StartCoroutine(SimulationBehavior());
    }
    void EndSimulation()
    {
        StopCoroutine(SimulationBehavior());

        interactionPhase = InteractionPhases.ingame_default;

        ResetStartValues();

        playerInteraction_UI.simulationButton.interactable = true;
        playerInteraction_UI.simulationButton.gameObject.SetActive(true);
        playerInteraction_UI.submitButton.interactable         = true;
        playerInteraction_UI.stopSimulationButton.interactable = false;
        playerInteraction_UI.simulationButton.gameObject.SetActive(true);
    }
    IEnumerator SimulationBehavior()
    {
        Level lvl               = GameManager.Instance.GetDataManager().currentLevelData;
        int   currentStep       = 0;
        int   maxStep           = 0;
        int   maxGoalsCompleted = 0;
        Dictionary <int, List <StepData> > stepDictionary           = new Dictionary <int, List <StepData> >();
        Dictionary <int, List <int> >      componentStepsDictionary = new Dictionary <int, List <int> >();

        for (int i = 0; i < lvl.execution.Count; i++)
        {
            StepData step = lvl.execution[i];

            if (step.timeStep > maxStep)
            {
                maxStep = step.timeStep;
            }

            if (step.eventType == "M")
            {
                if (!componentStepsDictionary.ContainsKey(step.componentID))
                {
                    componentStepsDictionary.Add(step.componentID, new List <int>()); componentStepsDictionary[step.componentID].Add(i);
                }
                else
                {
                    componentStepsDictionary[step.componentID].Add(i);
                }
            }

            if (stepDictionary.ContainsKey(step.timeStep))
            {
                if (step.eventType == "D")
                {
                    stepDictionary[step.timeStep].Insert(0, step);
                }
                else
                {
                    stepDictionary[step.timeStep].Add(step);
                }
            }
            else
            {
                stepDictionary[step.timeStep] = new List <StepData>();
                stepDictionary[step.timeStep].Add(step);
            }
        }

        foreach (int componentId in componentStepsDictionary.Keys)
        {
            //componentStepsDictionary[componentId].Sort();
            for (int listIndex = 0; listIndex < componentStepsDictionary[componentId].Count - 1; listIndex++)
            {
                int executionIndex     = componentStepsDictionary[componentId][listIndex];
                int nextExecutionIndex = componentStepsDictionary[componentId][listIndex + 1];
                lvl.execution[executionIndex].SetNextStep(nextExecutionIndex);
            }
        }

        while (interactionPhase == InteractionPhases.simulation && currentStep <= maxStep)
        {
            if (stepDictionary.ContainsKey(currentStep))
            {
                foreach (StepData step in stepDictionary[currentStep])
                {
                    if (step.componentID == 0)
                    {
                        if (step.componentStatus == null)
                        {
                            continue;
                        }
                        if (step.componentStatus.goals_completed != null)
                        {
                            if (maxGoalsCompleted < step.componentStatus.goals_completed)
                            {
                                maxGoalsCompleted = step.componentStatus.goals_completed;
                            }
                        }
                        if (step.componentStatus.final_condition != null && step.componentStatus.final_condition != -1)
                        {
                            string goalString = "";
                            switch (step.componentStatus.final_condition)
                            {
                            case 2:
                            case 8:
                            case 10:
                                goalString = "Successfully completed the level!";
                                break;

                            default:
                                goalString = "";
                                if ((step.componentStatus.final_condition & 1) != 0)
                                {
                                    goalString += "You hit a dead end. ";
                                }
                                if ((step.componentStatus.final_condition & 16) != 0)
                                {
                                    goalString += "You missed some deliveries. ";
                                }
                                if ((step.componentStatus.final_condition & 32) != 0)
                                {
                                    goalString += "You " +
                                                  "have starvation. ";
                                }
                                goalString += "Sorry, try again!";
                                break;
                            }

                            yield return(StartCoroutine(playerInteraction_UI.TriggerGoalPopUp(goalString)));
                        }
                    }
                    else
                    {
                        GridObjectBehavior g = GameManager.Instance.GetGridManager().GetGridObjectByID(step.componentID);
                        if (g != null)
                        {
                            g.DoStep(step);
                        }
                        else
                        {
                            Debug.Log("Could not find " + step.componentID);
                        }
                    }
                }
                yield return(new WaitForSeconds(0.5f));
            }
            currentStep++;
            Debug.Log(currentStep);
        }


        //yield return new WaitForSeconds(1f);
        //todo: switch statement of the selected goal option

        ResetStartValues();

        switch (playerInteraction_UI.goalOverlay.userInput)
        {
        case PlayerInteraction_UI.Goal_UIOverlay.UserInputs.exit:
        case PlayerInteraction_UI.Goal_UIOverlay.UserInputs.levels:
            TriggerPlayPhaseEnd();
            EndSimulation();
            break;

        case PlayerInteraction_UI.Goal_UIOverlay.UserInputs.replay:

            interactionPhase = InteractionPhases.ingame_default;
            GameManager.Instance.TriggerLevelSimulation(LinkJava.SimulationFeedback.none);

            break;

        default:
            GameManager.Instance.TriggerLoadLevel();
            EndSimulation();
            break;
        }
    }
    public void PlayerInteractionListener()
    {
        switch (interactionPhase)
        {
        case InteractionPhases.ingame_default:
            if (playerInteraction_UI.IsSubPanelOpen())
            {
                return;
            }

            /*
             * if player LEFT clicks during basic play, they can
             * (1) Click and drag movable elements
             */
            if (Input.GetKeyDown(KeyCode.Mouse0))
            {
                if (GameManager.Instance.GetGridManager().IsEditableElement(Input.mousePosition))
                {
                    currentGridObject = GameManager.Instance.GetGridManager().RetrieveEditableGridObject(Input.mousePosition);
                    currentGridObject.BeginDrag();
                    interactionPhase = InteractionPhases.ingame_dragging;
                    GameManager.Instance.tracker.CreateEventExt("BeginReposition", currentGridObject.component.type);

                    if (currentGridObject.component.type == "signal" && connectVisibilityLock)
                    {
                        Signal_GridObjectBehavior s = (Signal_GridObjectBehavior)currentGridObject;
                        s.SetHighlight(false);
                    }
                }
                if (hoverObject)
                {
                    if (!connectVisibility)
                    {
                        hoverObject.EndHoverBehavior();
                    }
                    hoverObject = null;
                }
            }

            /*
             * if player RIGHT clicks during basic play, they can:
             * (1) link connectable elements through Signals
             * (2) Open/Close Semaphores
             */
            else if (Input.GetKeyDown(KeyCode.Mouse1))
            {
                if (GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "signal") /*&& GameManager.Instance.GetGridManager().IsEditableElement( Input.mousePosition )*/)
                {
                    currentGridObject = GameManager.Instance.GetGridManager().RetrieveGridObjectOfType(Input.mousePosition, "signal");
                    currentGridObject.EnableGridObjectEventBehaviors(GridObjectBehavior.InteractTypes.rightClick);
                    interactionPhase = InteractionPhases.ingame_connecting;
                    currentGridObject.BeginInteraction();

                    List <GridObjectBehavior> otherSignals = GameManager.Instance.GetGridManager().GetGridComponentsOfType(new List <string>()
                    {
                        "signal"
                    });
                    foreach (GridObjectBehavior otherSignal in otherSignals)
                    {
                        if (currentGridObject != otherSignal)
                        {
                            otherSignal.GetComponent <SpriteRenderer>().sortingOrder = Constants.ComponentSortingOrder.connectionOverlay - 1;
                        }
                    }

                    GameManager.Instance.tracker.CreateEventExt("BeginLink", currentGridObject.component.type);

                    playerInteraction_UI.onHoverLightbox.OpenPanel();
                }
                else if (GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "semaphore") && GameManager.Instance.GetGridManager().IsEditableElement(Input.mousePosition))
                {
                    currentGridObject = GameManager.Instance.GetGridManager().RetrieveGridObjectOfType(Input.mousePosition, "semaphore");
                    currentGridObject.EnableGridObjectEventBehaviors(GridObjectBehavior.InteractTypes.rightClick);
                    currentGridObject.BeginInteraction();
                    GameManager.Instance.tracker.CreateEventExt("BeginLink", currentGridObject.component.type);
                }

                if (hoverObject /*&& !connectVisibilityLock*/)
                {
                    hoverObject.EndHoverBehavior();
                    hoverObject = null;
                }
            }

            /*
             * if a player isn't clicking the mouse, we should check for hover behaviors
             */
            else
            {
                if (Input.mousePosition == stationaryMousePosition)                         //if mouse is stationary
                {
                    if (hoverObject == null)
                    {
                        stationaryTime += Time.deltaTime;
                        if (stationaryTime >= 0.2f)
                        {
                            if (
                                GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "signal") ||
                                GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "diverter") ||
                                GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "exchange") ||
                                GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "delivery") ||
                                GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "pickup") ||
                                GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "conditional") ||
                                GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "semaphore")
                                )
                            {
                                hoverObject = GameManager.Instance.GetGridManager().GetGridObjectByMousePosition(Input.mousePosition);
                                hoverObject.OnHoverBehavior();
                                GameManager.Instance.tracker.CreateEventExt("OnHoverBehavior", hoverObject.component.type);
                            }
                        }
                    }
                }
                else                         //if mouse has moved since last frame
                {
                    stationaryMousePosition = Input.mousePosition;
                    if (hoverObject)
                    {
                        if (GameManager.Instance.GetGridManager().IsOccupied(Input.mousePosition))
                        {
                            if (hoverObject != GameManager.Instance.GetGridManager().GetGridObjectByMousePosition(Input.mousePosition))
                            {
                                EndHoverEvent();
                            }
                        }
                        else
                        {
                            EndHoverEvent();
                        }
                    }
                    else
                    {
                        stationaryTime = 0f;
                    }

                    //pop up tooltip close check
                    if (playerInteraction_UI.tooltipOverlay.tooltipActive && Time.time - playerInteraction_UI.tooltipOverlay.openTime > 0.5f)
                    {
                        playerInteraction_UI.tooltipOverlay.ClosePanel();
                    }
                }
                stationaryMousePosition = Input.mousePosition;
                GridObjectBehavior hoverObject__ = GameManager.Instance.GetGridManager().GetGridObjectByMousePosition(Input.mousePosition);
                if (hoverObject__ != null && hoverObject__ != hoverObject_)
                {
                    hoverObject_ = hoverObject__;
                    if (hoverObject_.component != null)
                    {
                        GameManager.Instance.tracker.CreateEventExt("OnMouseComponent", hoverObject_.component.type.ToString() + "/" + hoverObject_.component.id.ToString());
                    }
                }
                if (hoverObject__ == null && hoverObject_ != null)
                {
                    GameManager.Instance.tracker.CreateEventExt("OutMouseComponent", hoverObject_.component.type.ToString() + "/" + hoverObject_.component.id.ToString());
                    hoverObject_ = null;
                }
            }
            break;

        case InteractionPhases.ingame_dragging:
            if (Input.GetKey(KeyCode.Mouse0))
            {
                if (currentGridObject != null)
                {
                    currentGridObject.ContinueDrag();
                    if (trashHover)
                    {
                    }
                    else
                    {
                    }
                }
                else
                {
                    interactionPhase = InteractionPhases.ingame_default;
                }
            }
            else
            {
                if (trashHover)
                {
                    GameManager.Instance.GetGridManager().ForgetGridElement(currentGridObject);
                    if (currentGridObject.component.configuration.link != null && currentGridObject.component.configuration.link > 0)
                    {
                        //	GridObjectBehavior g = GameManager.Instance.GetGridManager().GetGridObjectByID( currentGridObject.component.configuration.link );
                        //	g.component.configuration.link = 0;
                    }
                    GameManager.Instance.tracker.CreateEventExt("Destroying", currentGridObject.component.type);
                    Destroy(currentGridObject.gameObject);
                    currentGridObject = null;
                    interactionPhase  = InteractionPhases.ingame_default;
                }
                else
                {
                    GameManager.Instance.tracker.CreateEventExt("EndReposition", currentGridObject.component.type);
                    currentGridObject.EndDrag();

                    if (currentGridObject.component.type == "signal" && connectVisibilityLock)
                    {
                        Signal_GridObjectBehavior s = (Signal_GridObjectBehavior)currentGridObject;
                        s.SetHighlight(true);
                    }

                    currentGridObject = null;
                    interactionPhase  = InteractionPhases.ingame_default;
                }
            }
            break;

        case InteractionPhases.ingame_connecting:

            if (Input.GetKeyDown(KeyCode.Mouse1))
            {
                currentGridObject.EndInteraction();
                if (GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "semaphore"))
                {
                    GridObjectBehavior g = GameManager.Instance.GetGridManager().GetGridObjectByMousePosition(Input.mousePosition);
                    currentGridObject.LinkTo(g);
                    GameManager.Instance.tracker.CreateEventExt("LinkTo", currentGridObject.component.type);
                }

                else if (GameManager.Instance.GetGridManager().IsObjectOfType(Input.mousePosition, "conditional"))
                {
                    GridObjectBehavior g = GameManager.Instance.GetGridManager().GetGridObjectByMousePosition(Input.mousePosition);
                    currentGridObject.LinkTo(g);
                    GameManager.Instance.tracker.CreateEventExt("LinkTo", currentGridObject.component.type);
                }

                playerInteraction_UI.onHoverLightbox.ClosePanel();

                List <GridObjectBehavior> otherSignals = GameManager.Instance.GetGridManager().GetGridComponentsOfType(new List <string>()
                {
                    "signal"
                });
                foreach (GridObjectBehavior otherSignal in otherSignals)
                {
                    if (currentGridObject != otherSignal)
                    {
                        otherSignal.GetComponent <SpriteRenderer>().sortingOrder = Constants.ComponentSortingOrder.connectionComponents;
                    }
                    if (connectVisibilityLock)
                    {
                        otherSignal.SetHighlight(true);
                    }
                }


                interactionPhase = InteractionPhases.ingame_default;
            }
            else
            {
                currentGridObject.ContinueInteraction();
            }
            break;

        case InteractionPhases.simulation:
            simulationTime += Time.deltaTime;
            break;
        }
    }