Skip to content
Snippets Groups Projects
ScrollDetails.cs 12.85 KiB
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.Networking;
using Newtonsoft.Json;
using System.Linq;
using static CommunicationEvents;
using static SOMDocManager;
using System;
using static Scroll;

public class ScrollDetails : MonoBehaviour
{
    public static ScrollDetails Instance
    {
        get => _Instance;
        set
        {
            if (_Instance == null)
                _Instance = value;
            else
                Destroy(value);
        }
    }
    private static ScrollDetails _Instance;

    public WorldCursor cursor;
    public GameObject parameterDisplayPrefab;
    public Scroll ActiveScroll;
    public GameObject mmtAnswerPopUp;
    private PopupBehavior Popup;

    public static List<RenderedScrollFact> ParameterDisplays;
    private static List<ScrollAssignment> LatestCompletions;
    private static List<Fact> LatestRenderedHints;

    public string currentMmtAnswer;

    public bool DynamicScrollDescriptionsActive = true;
    public bool AutomaticHintGenerationActive = true;

    private bool DynamicScrollDone = true;
    private bool DynamicScrollInQue = false;

    void Awake()
    {
        Instance = this;

        if (cursor == null)
            cursor = FindObjectOfType<WorldCursor>();

        Popup = mmtAnswerPopUp.GetComponent<PopupBehavior>();
        Popup.gameObject.SetActive(true); // force Awake
    }

    private void OnEnable()
    {
        ScrollFactHintEvent.AddListener(animateHint);
        NewAssignmentEvent.AddListener(NewAssignmentTrigger);
    }

    private void OnDisable()
    {
        ScrollFactHintEvent.RemoveListener(animateHint);
        NewAssignmentEvent.RemoveListener(NewAssignmentTrigger);
    }

    private void OnDestroy()
    {
        _Instance = null;
    }

    public void SetScroll(Scroll scroll_to_set)
    {
        ActiveScroll = scroll_to_set;

        Transform originalScroll = gameObject.transform.GetChild(1);
        Transform originalScrollView = originalScroll.GetChild(1);
        Transform originalViewport = originalScrollView.GetChild(0);

        //Clear all current ScrollFacts
        originalViewport.GetChild(0).gameObject.DestroyAllChildren();

        originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = ActiveScroll.description;

        //ParameterDisplays.ForEach(gameObj => Destroy(gameObj));
        ParameterDisplays = new();
        for (int i = 0; i < ActiveScroll.requiredFacts.Count; i++)
        {
            GameObject originalObj =
                Instantiate(parameterDisplayPrefab, originalViewport.GetChild(0));

            RenderedScrollFact originalRSF =
                originalObj.GetComponentInChildren<RenderedScrollFact>();

            ParameterDisplays.Add(originalRSF);

            originalRSF.Populate(ActiveScroll, i);
        }

        foreach (int i in PrePopulateActiveScroll())
            ParameterDisplays[i].gameObject.SetActive(false);

        //set active scroll for ErrorMessagePopup
        Popup.ActiveScroll = ActiveScroll;
        Popup.ParameterDisplays = ParameterDisplays;
    }

    public bool SetNextEmptyTo(FactObjectUI activator)
    {
        RenderedScrollFact check = ParameterDisplays
            .Find(RSF => RSF != null
                      && RSF.Payload != null
                      && RSF.Payload.Equals(activator)
        );

        if (check != null)
        {
            check.URI = null;
            return false;
        }

        RenderedScrollFact empty = ParameterDisplays
            .Find(RSF => !RSF.IsSet);

        if (empty == null)
            return false;

        empty.SetByFactObject(activator);
        return true;
    }

    /// <summary>
    /// Secretly populates <see cref="Scroll"/>s
    /// </summary>
    /// <returns><c>Array</c> containing indicis of <see cref="Scroll.requiredFacts"/> to be hidden.</returns>
    private int[] PrePopulateActiveScroll()
    {
        switch (ActiveScroll.@ref)
        {
            case MMT_OMS_URI.ScrollCannonBall:
                return new int[] { 2 };

            default:
                return new int[0];
        }
    }

    public void MagicButtonTrigger()
    {
        StartCoroutine(_MagicButton());

        IEnumerator _MagicButton()
        {
            while (!DynamicScrollDone || DynamicScrollInQue)
                yield return null; // Wait for last assignment

            DynamicScrollDone = false;
            yield return SendView("/scroll/apply");
            DynamicScrollDone = true;

            if (currentMmtAnswer == null)
            {
                Debug.LogError("Magic FAILED");
                ScrollApplicationCheckingErrorEvent.Invoke(null);
            }
            else
            {
                if (VerboseURI)
                    Debug.Log("Magic answers:\n" + currentMmtAnswer);

                ScrollApplicationInfo pushout = JsonConvert.DeserializeObject<ScrollApplicationInfo>(currentMmtAnswer);

                if (pushout.acquiredFacts.Count == 0
                 || pushout.errors.Length > 0)
                {
                    ScrollApplicationCheckingErrorEvent.Invoke(pushout.errors);
                    PushoutFactFailEvent.Invoke();
                }

                ReadPushout(pushout.acquiredFacts);
            }
        }
    }

    public void NewAssignmentTrigger()
    {
        if (AutomaticHintGenerationActive || DynamicScrollDescriptionsActive)
            StartCoroutine(_NewAssignment());

        IEnumerator _NewAssignment()
        {
            if(DynamicScrollInQue)
                yield break; // only need next in que to finish

            DynamicScrollInQue = true;
            while (!DynamicScrollDone)
                yield return null; // if we dont wait => server will crash
            DynamicScrollInQue = false;

            DynamicScrollDone = false;
            yield return SendView("/scroll/dynamic");
            DynamicScrollDone = true;

            if (currentMmtAnswer == null)
            {
                Debug.LogError("Dynamic Scroll FAILED");
            }
            else
            {
                ScrollDynamicInfo scrollDynamicInfo;
                try
                {
                    scrollDynamicInfo = JsonConvert.DeserializeObject<ScrollDynamicInfo>(currentMmtAnswer);
                }
                catch (JsonSerializationException ex)
                {
                    Debug.LogException(ex);
                    Debug.LogError("Could not Deserialize MMT aswer for /scroll/dynamic\n" + currentMmtAnswer);
                    yield break;
                }

                ScrollApplicationCheckingError[] errors = scrollDynamicInfo.errors
                    .Where(err => err.kind != "nonTotal") // expected
                    .ToArray();

                if (errors.Length > 0)
                    ScrollApplicationCheckingErrorEvent.Invoke(errors);

                processScrollDynamicInfo(scrollDynamicInfo);
            }
        }
    }

    private IEnumerator SendView(string endpoint)
    {
        while (ParameterDisplays == null) // Wait for server
            yield return null;

        string body = prepareScrollAssignments();

        using UnityWebRequest www = UnityWebRequest.Put(ServerAdress + endpoint, body);
        www.method = UnityWebRequest.kHttpVerbPOST;
        www.SetRequestHeader("Content-Type", "application/json");

        System.DateTime sendTime = System.DateTime.UtcNow;
        yield return www.SendWebRequest();
        if (VerboseURI)
            Debug.LogFormat("Server answerd in : {0}ms"
                , (System.DateTime.UtcNow - sendTime).TotalMilliseconds);

        if (www.result == UnityWebRequest.Result.ConnectionError
         || www.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log(www.error);
            currentMmtAnswer = null;
        }
        else
        {
            string answer = www.downloadHandler.text;
            currentMmtAnswer = answer;
        }

        string prepareScrollAssignments()
        {
            List<ScrollAssignment> assignmentList = new();

            for (int i = 0; i < ParameterDisplays.Count; i++)
            {
                Fact tempFact = ParameterDisplays[i].Fact;
                if (tempFact != null)
                    assignmentList.Add(new ScrollAssignment(ActiveScroll.requiredFacts[i].@ref.uri, tempFact.Id));
            }

            return JsonConvert.SerializeObject(new FilledScroll(ActiveScroll.@ref, assignmentList));
        }
    }

    private void ReadPushout(List<MMTDeclaration> pushoutFacts)
    {
        Popup.HidePopUp(); //close error Window

        bool samestep = false;
        for (int i = 0; i < pushoutFacts.Count; i++)
        {
            Fact newFact = ParsingDictionary.parseFactDictionary[pushoutFacts[i].getType()].Invoke(pushoutFacts[i]);
            if (newFact != null)
            {
                AnimateExistingFactEvent.Invoke
                    (FactManager.AddFactIfNotFound(newFact, out _, samestep, null, ActiveScroll.label).Id
                    , FactWrapper.FactMaterials.Solution);

                samestep = true;
            }
            else
                Debug.Log("Parsing on pushout-fact returned null -> One of the dependent facts does not exist");
        }
    }

    private void processScrollDynamicInfo(ScrollDynamicInfo scrollDynamicInfo)
    {
        LatestCompletions = scrollDynamicInfo.completions.Count > 0
            ? scrollDynamicInfo.completions[0]
            : new List<ScrollAssignment>();

        List<string> hintUris = LatestCompletions
            .Select(completion => completion.fact.uri)
            .ToList();

        //Update Scroll, process data for later hints and update Uri-List for which hints are available
        _processRenderedScroll(scrollDynamicInfo.rendered, hintUris);

        if (AutomaticHintGenerationActive)
            //Show that Hint is available for ScrollParameter
            HintAvailableEvent.Invoke(hintUris);

        return;

        void _processRenderedScroll(Scroll rendered, List<string> hintUris)
        {
            if (DynamicScrollDescriptionsActive)
            { // Update scroll-description
                Transform scroll = gameObject.transform.GetChild(1).transform;
                scroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = rendered.description;
            }

            LatestRenderedHints = new();
            for (int i = 0; i < rendered.requiredFacts.Count; i++)
            {
                RenderedScrollFact RenderedScrollFact = ParameterDisplays
                    .Find(RSF => RSF.ScrollFactURI == rendered.requiredFacts[i].@ref.uri);

                if (DynamicScrollDescriptionsActive)
                    //Update ScrollParameter label
                    RenderedScrollFact.Scroll = rendered;

                //If ScrollFact is assigned -> No Hint
                if (!RenderedScrollFact.IsSet)
                {
                    Fact HintFact =
                        ParsingDictionary.parseFactDictionary[rendered.requiredFacts[i].getType()]
                        .Invoke(rendered.requiredFacts[i]);

                    //If the fact could not be parsed -> Therefore not all dependent Facts exist -> No Hint
                    if (HintFact != null)
                    {
                        hintUris.Add(HintFact.Id);
                        LatestRenderedHints.Add(HintFact);
                    }
                }
            }

            return;
        }
    }

    public void animateHint(string scrollParameterUri)
    {
        if (FactOrganizer.AllFacts.ContainsKey(scrollParameterUri))
            AnimateExistingFactEvent.Invoke(
                    scrollParameterUri,
                    FactWrapper.FactMaterials.Hint
                );

        Fact hintFact = LatestRenderedHints.Find(x => x.Id == scrollParameterUri);

        ScrollAssignment suitableCompletion =
            LatestCompletions.Find(x => x.fact.uri == scrollParameterUri);

        if (suitableCompletion != null)
        {
            if (FactOrganizer.AllFacts.ContainsKey(suitableCompletion.assignment.uri))
            {
                AnimateExistingFactEvent.Invoke(
                    suitableCompletion.assignment.uri,
                    FactWrapper.FactMaterials.Hint
                );
            }
        }
        else if (hintFact != null)
        {
            if (FactOrganizer.FindEquivalent(StageStatic.stage.factState.MyFactSpace, hintFact, out string found_key, out Fact _, out bool _, false))
                // existing fact -> Animate that
                AnimateExistingFactEvent.Invoke(
                    found_key,
                    FactWrapper.FactMaterials.Hint
                );

            else
            {   // Generate new FactRepresentation and animate it
                AnimateNonExistingFactEvent.Invoke(hintFact);
                AnimateExistingFactEvent.Invoke(
                    scrollParameterUri,
                    FactWrapper.FactMaterials.Hint
                );
            }
        }
    }
}