Skip to content
Snippets Groups Projects
Fact.cs 28.1 KiB
Newer Older
MaZiFAU's avatar
MaZiFAU committed
using JsonSubTypes;
using Newtonsoft.Json;
using REST_JSON_API;
MaZiFAU's avatar
MaZiFAU committed
using System;
using System.Collections;
MaZiFAU's avatar
MaZiFAU committed
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
MaZiFAU's avatar
MaZiFAU committed
using UnityEngine;
using UnityEngine.Networking;
public static class ParsingDictionary
    //TODO? get rid of this, use reflection? instead, if possible
    //TODO: docu
ki7077's avatar
ki7077 committed

    public static Dictionary<string, Func<List<Fact>, MMTFact, IEnumerator>> parseFactDictionary = new() {
        { MMTConstants.TypeType,
            GeneralFact.parseFact },
        { MMTConstants.Point,
        { MMTConstants.Metric,
        { MMTConstants.Angle,
        { MMTConstants.Eq,
            RightAngleFact.parseFact },
        { MMTConstants.RightAngle,
        { MMTConstants.LineType,
        { MMTConstants.LineOf,
        { MMTConstants.OnLine,
        { MMTConstants.ParallelLine,
            ParallelLineFact.parseFact },
        { MMTConstants.CircleType3d,
        { MMTConstants.OnCircle,
        { MMTConstants.AnglePlaneLine,
            AngleCircleLineFact.parseFact },
        { MMTConstants.RadiusCircleMetric,
        { MMTConstants.AreaCircle,
        { MMTConstants.OrthoCircleLine,
            OrthogonalCircleLineFact.parseFact },
        { MMTConstants.VolumeCone,
        { MMTConstants.TruncatedVolumeCone,
            TruncatedConeVolumeFact.parseFact },
        { MMTConstants.CylinderVolume,
            CylinderVolumeFact.parseFact },
        { MMTConstants.TestType,
        { MMTConstants.EqualityCircles,
            EqualCirclesFact.parseFact },
        { MMTConstants.UnEqualityCircles,
MaZiFAU's avatar
MaZiFAU committed
            UnEqualCirclesFact.parseFact },
MaZiFAU's avatar
MaZiFAU committed
        { MMTConstants.CreateTriangle,
            TriangleFact.parseFact },
        { MMTConstants.FunctionFact,
            FunctionFact.parseFact },
    // TODO: get rid of this
    public static Dictionary<string, string> parseTermsToId = new();
/// <summary>
/// %Fact representation of Unity; mostly mirrors Facts of MMT.
/// </summary>
[JsonConverter(typeof(JsonSubtypes), "s_type")]
[JsonSubtypes.KnownSubType(typeof(PointFact), nameof(PointFact))]
[JsonSubtypes.KnownSubType(typeof(LineFact), nameof(LineFact))]
[JsonSubtypes.KnownSubType(typeof(RayFact), nameof(RayFact))]
[JsonSubtypes.KnownSubType(typeof(OnLineFact), nameof(OnLineFact))]
[JsonSubtypes.KnownSubType(typeof(AngleFact), nameof(AngleFact))]
[JsonSubtypes.KnownSubType(typeof(CircleFact), nameof(CircleFact))]
[JsonSubtypes.KnownSubType(typeof(ParallelLineFact), nameof(ParallelLineFact))]
[JsonSubtypes.KnownSubType(typeof(OnCircleFact), nameof(OnCircleFact))]
[JsonSubtypes.KnownSubType(typeof(AngleCircleLineFact), nameof(AngleCircleLineFact))]
MaZiFAU's avatar
MaZiFAU committed
[JsonSubtypes.KnownSubType(typeof(OrthogonalCircleLineFact), nameof(OrthogonalCircleLineFact))]
[JsonSubtypes.KnownSubType(typeof(AreaCircleFact), nameof(AreaCircleFact))]
[JsonSubtypes.KnownSubType(typeof(RadiusFact), nameof(RadiusFact))]
[JsonSubtypes.KnownSubType(typeof(ConeVolumeFact), nameof(ConeVolumeFact))]
[JsonSubtypes.KnownSubType(typeof(TruncatedConeVolumeFact), nameof(TruncatedConeVolumeFact))]
[JsonSubtypes.KnownSubType(typeof(RightAngleFact), nameof(RightAngleFact))]
[JsonSubtypes.KnownSubType(typeof(CylinderVolumeFact), nameof(CylinderVolumeFact))]
[JsonSubtypes.KnownSubType(typeof(TestFact), nameof(TestFact))]
[JsonSubtypes.KnownSubType(typeof(EqualCirclesFact), nameof(EqualCirclesFact))]
[JsonSubtypes.KnownSubType(typeof(UnEqualCirclesFact), nameof(UnEqualCirclesFact))]
MaZiFAU's avatar
MaZiFAU committed
[JsonSubtypes.KnownSubType(typeof(FunctionFact), nameof(FunctionFact))]
[JsonSubtypes.KnownSubType(typeof(FunctionCallFact), nameof(FunctionCallFact))]
MaZiFAU's avatar
MaZiFAU committed
[JsonSubtypes.KnownSubType(typeof(ListFact), nameof(ListFact))]
MaZiFAU's avatar
MaZiFAU committed
[JsonSubtypes.KnownSubType(typeof(TupleFact), nameof(TupleFact))]
[JsonSubtypes.KnownSubType(typeof(DynamicListFact), nameof(DynamicListFact))]
[JsonSubtypes.KnownSubType(typeof(DynamicTupleFact), nameof(DynamicTupleFact))]
[JsonSubtypes.KnownSubType(typeof(QuadFact), nameof(QuadFact))]
[JsonSubtypes.KnownSubType(typeof(TriangleFact), nameof(TriangleFact))]
[JsonSubtypes.KnownSubType(typeof(RealLitFact), nameof(RealLitFact))]
public abstract class Fact
    [JsonIgnore]
    public Expression CompileBase
    {
        get
        {
            if (_CompileBase == null)
MaZiFAU's avatar
MaZiFAU committed
                CompileValue(HasServerTwin ? Defines() : ServerDefinition);
            return _CompileBase;
        }

        protected set => _CompileBase = value;
    }
    private Expression _CompileBase;

    [JsonIgnore]
    public object CompiledValue
    {
        get => _CompiledValue ??= GetCompiledValue();
        protected set => _CompiledValue = value;
    }
    private object _CompiledValue;
MaZiFAU's avatar
MaZiFAU committed
    protected virtual object GetCompiledValue()  // put to abstract, once we know what to do with every Fact
        => CompileValue(HasServerTwin ? Defines() : ServerDefinition);
MaZiFAU's avatar
MaZiFAU committed
    protected object CompileValue(SOMDoc compile_me)
    {
        Func<object[], object[]> builder =
MaZiFAU's avatar
MaZiFAU committed
            compile_me.PartialInvokeCastingLambdaExpression(out _CompileBase, out Type[] Signature);

        if (Signature.Length != 1)
            throw new InvalidCastException($"{nameof(ServerDefinition)} does not compile to constant!");

        return _CompiledValue = Signature[0] == typeof(object[])
            ? builder(new object[0])
            : builder(new object[0])[0];
    }

    /// <summary>
    /// Reference to <c>GameObject</c> that represents this Fact in the GameWorld.
    /// </summary>
MaZiFAU's avatar
MaZiFAU committed
    /// <seealso cref="FactObject3D"/>
    public FactWrapper WorldRepresentation;
    /// <summary>
    /// Collection of <c>Type</c>s of *all* available <see cref="Fact"/>s to choose from.
    /// </summary>
    [JsonIgnore]
MaZiFAU's avatar
MaZiFAU committed
    public static readonly Type[] Types = TypeExtensions<Fact>.UAssemblyInheritenceTypes;
    /// [ClassName] for JSON de-/serialization.
    /// Automatically set in <see cref="Fact()"/> for <b>NON-Generiy-Types</b>!
    /// Also add JsonSubtypes.KnownSubType decorator for deserialization to Fact!
    [JsonProperty]
    /// <returns><see langword="true"/> if Fact depends on other \ref Fact "Facts"; equivalent to <see cref="getDependentFactIds"/> returns non empty array</returns>
    [JsonIgnore]
MaZiFAU's avatar
MaZiFAU committed
    public virtual bool HasDependentFacts => DependentFactIds.Length > 0;

    /// <returns> array of Fact <see cref="Id"> Ids </see> on which this Fact depends.</returns>
    /// <example><see cref="AngleFact"/> needs 3 <see cref="PointFact"/>s to be defined.</example>
    public string[] DependentFactIds => _DependentFactIds ??= GetDependentFactIds();
    private string[] _DependentFactIds;

    /// <returns> array of Fact <see cref="Id"> Ids </see> on which this Fact depends.</returns>
    /// <example><see cref="AngleFact"/> needs 3 <see cref="PointFact"/>s to be defined.</example>
    protected abstract string[] GetDependentFactIds();
    /// <value>Unique Id. e.g.: MMT URI</value>
MaZiFAU's avatar
MaZiFAU committed
    [JsonIgnore]
    public string Id
    {
MaZiFAU's avatar
MaZiFAU committed
        get
        {
            //while (FetchURICoroutine.MoveNext()) ; //active wait for server
            return ServerDefinition?.ToString() ?? ScalaFact.ToString();
MaZiFAU's avatar
MaZiFAU committed
        }
    //private IEnumerator FetchURICoroutine = IEnumeratorExtensions.yield_break;
    /// <summary>AST which the Server understands. e.g.: OMS(MMT URI)</summary>
    [JsonIgnore]
MaZiFAU's avatar
MaZiFAU committed
    public SOMDoc ServerDefinition
    {
        get => _ServerDefinition ??= Defines();
        protected set => _ServerDefinition = value;
    }
    private SOMDoc _ServerDefinition = null;

    [JsonIgnore]
    public MMTFact ScalaFact
    {
        get => _ScalaFact ??= MakeMMTDeclaration();
        protected set => _ScalaFact = value;
    }
    private MMTFact _ScalaFact = null;

    //public MMTFact MMTFact => _MMTFact ??= MakeMMTDeclaration();
    //private MMTFact _MMTFact = null;

MaZiFAU's avatar
MaZiFAU committed
    [JsonIgnore]
    public bool HasServerTwin => ServerDefinition is OMS;
    /// <c>get</c> initiates and subsequently updates a human readable name.
    /// <c>set</c> calls <see cref="rename(string, FactRecorder)"/>
    public string GetLabel(FactRecorder name_space)
    {  // in case of renamed dependables
        return _LastLabel = (
               name_space == null // JsonSerialization toggle (_Facts.GetNumberOfFacts() == 0 && this is not PointFact) // JsonSerialization toggle && allow first (Point)Fact to be created
            || (hasCustomLabel && _CustomLabel != null)
            ? _CustomLabel
            : generateLabel(name_space));
    }
    protected internal string _LastLabel = "NaN";
    public bool SetLabel(string value, FactRecorder name_space)
        if (name_space == null) // JsonSerialization toggle)
            _CustomLabel = value;
            LabelId = -LabelId;
            return true;
        else
            return rename(value, name_space);
    /// Is true if Fact has a custom <see cref="GetLabel"/> which is not <c>null</c> or <c>""</c>.
    public bool hasCustomLabel => LabelId < 0;

    /// Stores custom <see cref="GetLabel"/> if set.
    /// Counter to organize auto generated <see cref="GetLabel"/>.
    /// Set to negative, if custom \ref Label is assigned.
    /// </summary>
    // property for JSON to set AFTER Label => declare AFTER Label
    public int LabelId { get; set; }
MaZiFAU's avatar
MaZiFAU committed
    [JsonIgnore]
MaZiFAU's avatar
MaZiFAU committed
    bool ForceRecalculateTransform = true;

MaZiFAU's avatar
MaZiFAU committed
    [JsonIgnore]
MaZiFAU's avatar
MaZiFAU committed
    public Vector3 Position
    {
        get
        {
            if (ForceRecalculateTransform)
                RecalculateTransform();
            return _Position;
        }
        protected set
        {
            ForceRecalculateTransform = false;
            _Position = value;
        }
    }
MaZiFAU's avatar
MaZiFAU committed
    private Vector3 _Position = Vector3.zero;
MaZiFAU's avatar
MaZiFAU committed

    [JsonIgnore]
    public Quaternion Rotation
    {
        get
        {
            if (ForceRecalculateTransform)
                RecalculateTransform();
            return _Rotation;
        }
        protected set
        {
            ForceRecalculateTransform = false;
            _Rotation = value;
        }
    }
MaZiFAU's avatar
MaZiFAU committed
    private Quaternion _Rotation = Quaternion.identity;
MaZiFAU's avatar
MaZiFAU committed
    [JsonIgnore]
MaZiFAU's avatar
MaZiFAU committed
    public Vector3 LocalScale
    {
        get
        {
            if (ForceRecalculateTransform)
                RecalculateTransform();
            return _LocalScale;
        }
        protected set
        {
            ForceRecalculateTransform = false;
            _LocalScale = value;
        }
    }
MaZiFAU's avatar
MaZiFAU committed
    private Vector3 _LocalScale = Vector3.one;
MaZiFAU's avatar
MaZiFAU committed

    /// <summary>
    /// Only being used by [JsonReader](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonReader.htm) to initiate empty \ref Fact "Facts".
    /// <summary>
    /// Copies <paramref name="fact"/> by initiating new MMT %Fact.
    /// </summary>
    /// <param name="fact">Fact to be copied</param>
    /// <param name="old_to_new"><c>Dictionary</c> mapping <paramref name="fact"/>.<see cref="getDependentFactIds"/> to corresponding <see cref="Fact.Id"/> in <paramref name="organizer"/> </param>
    /// <param name="organizer">for Labeling<see cref="GetLabel(FactRecorder)"/></param>
MaZiFAU's avatar
MaZiFAU committed
    public Fact ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer)
BenniHome's avatar
BenniHome committed
    {
        Fact ret = _ReInitializeMe(old_to_new);
MaZiFAU's avatar
MaZiFAU committed
        ret.LabelId = this.LabelId;
        if (ret.hasCustomLabel)
            ret._CustomLabel = this.GetLabel(organizer);
MaZiFAU's avatar
MaZiFAU committed

        return ret;
    protected abstract Fact _ReInitializeMe(Dictionary<string, string> old_to_new);
    /// Assignes a custom <see cref="GetLabel"/>, if <paramref name="newLabel"/> is not yet taken;
    /// or clears custom <see cref="GetLabel"/>.
    /// <param name="newLabel">To be new <see cref="GetLabel"/>. To reset to auto-generation set to <c>null</c> or <c>""</c>.</param>
    //TODO: notify about updated dependable Labelnames for UI
    //TODO: check for colissions with not yet generated names
    public bool rename(string newLabel, FactRecorder name_space)
    // returns true if succeded
    {
        if (string.IsNullOrEmpty(newLabel) && name_space.GetNumberOfFacts() != 0)
        // switch back to autogenerated
        {
            generateLabel(name_space);
            _CustomLabel = null;
            return true;
        }
        else
        // set CustomLabel if available
        {
            if (name_space.ContainsLabel(newLabel))
            freeAutoLabel(name_space);
            _CustomLabel = newLabel;
BenniHome's avatar
BenniHome committed
    }
MaZiFAU's avatar
MaZiFAU committed
    protected abstract void RecalculateTransform();
MaZiFAU's avatar
MaZiFAU committed

    public abstract MMTFact MakeMMTDeclaration();
MaZiFAU's avatar
MaZiFAU committed
    public abstract SOMDoc Defines();
    /// Frees ressources e.g. <see cref="GetLabel"/> and will eventually delete %Fact Server-Side in far-near future when feature is supported.
        //TODO: MMT: delete over there
        //freeAutoLabel(_Facts); //should be done already
        if (CommunicationEvents.VerboseURI)
            Debug.Log("Server removed Fact:\n" + this.Id);
    /// <summary>
    /// Compares \ref Fact "this" against <paramref name="f2"/>.
    /// </summary>
    /// <param name="f2">Fact to compare to</param>
    /// <returns><c>true</c> if <paramref name="f2"/> is semantical very similar to \ref Fact "this"</returns>
    public abstract bool Equivalent(Fact f2);

    /// <summary>
    /// Compares <paramref name="f1"/> against <paramref name="f2"/>.
    /// </summary>
    /// <param name="f1">Fact to compare to</param>
    /// <param name="f2">Fact to compare to</param>
    /// <returns><c>true</c> if <paramref name="f2"/> is semantical very similar to <paramref name="f1"/></returns>
    public abstract bool Equivalent(Fact f1, Fact f2);
    /// <summary>
    /// canonical
    /// </summary>
    /// <returns>unique-ish Hash</returns>
MaZiFAU's avatar
MaZiFAU committed
    public new virtual int GetHashCode()
MaZiFAU's avatar
MaZiFAU committed
            .Select(id => id.GetHashCode())
            .Aggregate((hash1, hash2) => hash1 ^ hash2);
    /// auto-generates <see cref="GetLabel"/> using generation variable(s) e.g. <see cref="LabelId"/>;
    /// if custom <see cref="GetLabel"/> is set, tries to restore original generated <see cref="GetLabel"/> **without** resetting <see cref="_CustomLabel"/>. If original <see cref="GetLabel"/> is already taken, a new one will be generated.
    /// <returns>auto-generated <see cref="GetLabel"/></returns>
    protected virtual string generateLabel(FactRecorder name_space)
            // reload Label if possible
            LabelId = name_space.UnusedLabelIds.Remove(-LabelId) ? -LabelId : 0;
            if (name_space.UnusedLabelIds.Count == 0)
                LabelId = ++name_space.MaxLabelId;
                LabelId = name_space.UnusedLabelIds.Min;
                name_space.UnusedLabelIds.Remove(LabelId);
        return ((char)(64 + LabelId)).ToString();
    }

    /// Parses <see cref="MMTFact"/> to actual Fact
    /// </summary>
    /// <param name="fact">instance to be parsed</param>
    /// <returns>parsed Fact</returns>
    public static IEnumerator parseFact(List<Fact> ret, MMTFact fact)
    public virtual SOMDoc GetMMTType()
        => new OMS(MMTConstants.TYPE_TO_OMS[GetType()]);

    /// Tells a <see cref="FactRecorder"/> that \ref Fact "this" no longer uses auto-generated <see cref="GetLabel"/>, but remembers current generation variable(s).
    public void freeAutoLabel(FactRecorder name_space)
            name_space.UnusedLabelIds.Add(LabelId);
            // store Label for name-persistance
MaZiFAU's avatar
MaZiFAU committed

MaZiFAU's avatar
MaZiFAU committed
    private static readonly List<Type> ServerBlacklist = new()
MaZiFAU's avatar
MaZiFAU committed
    {
        typeof(FunctionFact),
        typeof(FunctionCallFact),
MaZiFAU's avatar
MaZiFAU committed
        //typeof(DynamicListFact),
MaZiFAU's avatar
MaZiFAU committed
    };

    private bool SendedToMMT = false;
    public void SendToMMT()
    {
        if (ServerBlacklist.Contains(GetType()))
            return;

        if (!SendedToMMT && !HasServerTwin)
        {
            ServerDefinition = SendToMMT(MakeMMTDeclaration());
            SendedToMMT = true;
        }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <remarks>Asynchron version has proofen inefficent, since <see cref="Fact.Id"/> is usually called in close proximity.</remarks>
    // Asynchron version in comments
    public static SOMDoc SendToMMT(MMTFact mmtDecl)
MaZiFAU's avatar
MaZiFAU committed
    {
        //GlobalBehaviour.Instance.StartCoroutine(
        //    FetchURICoroutine = 
        //_URI =
        string uri =
            _SendAdd(
MaZiFAU's avatar
MaZiFAU committed
                CommunicationEvents.ServerAdress + "/fact/add",
MaZiFAU's avatar
MaZiFAU committed
        //,(string uri) => _SendURICallback(mmtDecl, uri)
MaZiFAU's avatar
MaZiFAU committed

        //return;
MaZiFAU's avatar
MaZiFAU committed

        //void _SendURICallback(MMTDeclaration mmtDecl, string uri)
        //{
        //    _URI = uri;
MaZiFAU's avatar
MaZiFAU committed

        if (mmtDecl is MMTGeneralFact mMTSymbol && mMTSymbol.defines != null)
            ParsingDictionary.parseTermsToId[mMTSymbol.defines.ToString()] = uri;// _URI;
                                                                                 //}
        if (uri == null)
        {
            Debug.LogWarning("Server rejected MMTFact; Fallback engaged.");
MaZiFAU's avatar
MaZiFAU committed
            return mmtDecl.GetDefines();
        /*IEnumerator*/
        static string _SendAdd(string path, string body)//, Action<string> uriCallback)
MaZiFAU's avatar
MaZiFAU committed
        {
            if (!CommunicationEvents.ServerRunning)
            {
                Debug.LogWarning("Server not running");
                //uriCallback(null);
                return null; // yield break;
MaZiFAU's avatar
MaZiFAU committed
            }

            if (CommunicationEvents.VerboseURI)
                Debug.Log("Sending to Server:\n" + body);

            //Put constructor parses stringbody to byteArray internally  (goofy workaround)
            using UnityWebRequest www = UnityWebRequest.Put(path, body);
            www.method = UnityWebRequest.kHttpVerbPOST;
            www.SetRequestHeader("Content-Type", "application/json");
            www.timeout = 1;

            //yield return 
            UnityWebRequestAsyncOperation web =
                www.SendWebRequest();

            while (!web.isDone // not enough as querry
                 && www.result == UnityWebRequest.Result.InProgress) ;
MaZiFAU's avatar
MaZiFAU committed

            if (www.result == UnityWebRequest.Result.ConnectionError
             || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogWarning(www.error);
                //uriCallback(null);
                return null; // yield break;
            while (!www.downloadHandler.isDone)
                //yield return null
                ;
MaZiFAU's avatar
MaZiFAU committed

            FactReference res = JsonUtility.FromJson<FactReference>(www.downloadHandler.text);
MaZiFAU's avatar
MaZiFAU committed

            if (CommunicationEvents.VerboseURI)
                Debug.Log("Server added Fact:\n" + res.uri);

            //uriCallback(res.uri);
            return res.uri; // yield break;
MaZiFAU's avatar
MaZiFAU committed
        }
    }
MaZiFAU's avatar
MaZiFAU committed

    /// <summary> Used by <see cref="MakeFact(List{Fact}, object, SOMDoc, bool, SOMDoc)"/> to create unique pseudo-URIs / <see cref="Id"/>s. </summary>
    /// <summary>
    /// Tries to parse <paramref name="payload"/> into <see cref="Fact"/>(s)
    /// </summary>
    /// <param name="ret">to add results to</param>
    /// <param name="payload">to be parsed</param>
    /// <param name="ServerDefinition">what the Server could use. Only relevant for certain types of <paramref name="payload"/>.</param>
    /// <param name="BypassServer">iff <c>true</c> avoids Server-side initialization</param>
    /// <param name="BypassURI">iff <c><paramref name="BypassServer"/>==true</c> used as Server reference; iff set <c>null</c> a unique pseudo reference will be generated</param>
    /// <exception cref="NotImplementedException">iff <paramref name="payload"/> is of unhandled type</exception>
    public static void MakeFact(List<Fact> ret, object payload, SOMDoc ServerDefinition, bool BypassServer, SOMDoc BypassURI = null)
        if (BypassServer
         && BypassURI == null)
            BypassURI = new OMLIT<uint>(++bypasscc);
            ret.Add(fact);
            ret.Add(BypassServer
                ? new RealLitFact(f, BypassURI)
                : new RealLitFact(f));
            ret.Add(BypassServer
                ? new PointFact(point, BypassURI)
                : new PointFact(point, Vector3.up));
        else
        if (FuncExtensions.IsFuncType(type, out _))
            ret.Add(BypassServer
                ? new FunctionFact(ServerDefinition, BypassURI)
                : new FunctionFact(ServerDefinition));
        else
        if (TupleExtensions.IsTupleType(type, out _))
            ret.AddRange(BypassServer
                ? DynamicTupleFact.MMTFactory(payload, ServerDefinition, SOMDoc.SOMDocType(type), BypassURI)
                : DynamicTupleFact.MMTFactory(payload, ServerDefinition, SOMDoc.SOMDocType(type)));
        else
        if (type.IsGenericType
         && type.GetGenericTypeDefinition() == typeof(List<>))
            ret.AddRange(BypassServer
MaZiFAU's avatar
MaZiFAU committed
                ? DynamicListFact.MMTFactory(Enumerable.ToList<dynamic>((dynamic)payload), ServerDefinition, SOMDoc.SOMDocType(type), BypassURI)
                : DynamicListFact.MMTFactory(Enumerable.ToList<dynamic>((dynamic)payload), ServerDefinition, SOMDoc.SOMDocType(type)));
MaZiFAU's avatar
MaZiFAU committed
        else
        if (type.IsAnonymousType()) // TODO? generic Fact
        else
            throw new NotImplementedException($"For Type {type}.");

    /// <summary>
    /// Compiles/Executes <paramref name="ingredient"/> and parses result into @Fact s
    /// </summary>
    /// <param name="ingredient">MMT package to be interpreted</param>
    /// <returns>containing all @Fact s succesfully parsed, or empty when failed.</returns>
    public static List<Fact> MMTFactory(MMTFact ingredient)
    {
        List<Fact> ret = new();

        SOMDoc indirect_payload = ingredient.GetDefines();
        if (indirect_payload == null)
            return ret;

        // First try static approach
        try
        {
            ParsingDictionary.parseFactDictionary[ingredient.getType()]
                .Invoke(ret, ingredient)
                .FastForward();
MaZiFAU's avatar
MaZiFAU committed

            if (ret.Count != 0)
                return ret;
MaZiFAU's avatar
MaZiFAU committed
        catch(Exception ex)
MaZiFAU's avatar
MaZiFAU committed
            Debug.LogException(ex);
            Debug.Log($"Could not statically parse {nameof(MMTFact)} {nameof(ingredient)}. Using dynamic Fallback...");
        }

        object CompiledValue;
        try
        {
            Func<object[], object[]> builder =
                indirect_payload.PartialInvokeCastingLambdaExpression(out _, out Type[] signature);

            if (signature.Length != 1)
                return new();

            CompiledValue = builder(new object[0])[0];
        }
        catch (Exception ex)
        {
            Debug.Log($"{nameof(ingredient)} could not be built/executed:");
            Debug.LogException(ex);
            return new();
        }

        //try // simplify
        //{
        //    indirect_payload = SOMDoc.SOMDocObject(CompiledValue);
        //}
        //catch (NotSupportedException) { }

        MakeFact(ret, CompiledValue, indirect_payload, false, ingredient.@ref); //true => server does not recognize fact
    public abstract IEnumerator UnpackMe(List<Fact> ret, bool BypassServer);
/// <summary>
/// Implements CRTP for <see cref="Fact"/>; Escalates constructors;
/// </summary>
/// <typeparam name="T">class, which inherits from FactWrappedCRTP</typeparam>
public abstract class FactWrappedCRTP<T> : Fact where T : FactWrappedCRTP<T>
BenniHome's avatar
BenniHome committed
{
    /// <summary>\copydoc Fact.Fact()</summary>
    protected FactWrappedCRTP() : base() { }

    public override bool Equivalent(Fact f2)
        => Equivalent(this, f2);
    /// \copydoc Fact.Equivalent(Fact, Fact)
    public override bool Equivalent(Fact f1, Fact f2)
MaZiFAU's avatar
MaZiFAU committed
        => f1.GetType() == f2.GetType()
        && EquivalentWrapped((T)f1, (T)f2); // || f1.ServerDefinition.Equivalent(f2.ServerDefinition); <= StackOverflow
    /// <summary>CRTP step of <see cref="Equivalent(Fact)"/> and <see cref="Equivalent(Fact, Fact)"/></summary>
    protected abstract bool EquivalentWrapped(T f1, T f2);
MaZiFAU's avatar
MaZiFAU committed

    protected bool DependentFactsEquivalent(T f1, T f2)
        => f1.DependentFactIds
            .Zip(f2.DependentFactIds,
MaZiFAU's avatar
MaZiFAU committed
                (id1, id2) =>
MaZiFAU's avatar
MaZiFAU committed
                    id1 == id2
MaZiFAU's avatar
MaZiFAU committed
                    || (FactRecorder.AllFacts.ContainsKey(id1)
                     && FactRecorder.AllFacts.ContainsKey(id2)
                     && FactRecorder.AllFacts[id1].Equivalent(FactRecorder.AllFacts[id2]))
MaZiFAU's avatar
MaZiFAU committed
            )
            .All(b => b);
#pragma warning disable // Testing...

ki7077's avatar
ki7077 committed
/// use this if you need to test certain implementations of facts.
ki7077's avatar
ki7077 committed
/// just for testing purposes  
/// </summary>
public class TestFact : FactWrappedCRTP<TestFact>
{
    /// <summary> \copydoc Fact.Fact </summary>
    public TestFact() : base()
    {
MaZiFAU's avatar
MaZiFAU committed
    protected override void RecalculateTransform() { }
MaZiFAU's avatar
MaZiFAU committed

    /// <summary>
    /// Bypasses initialization of new MMT %Fact by using existend URI, _which is not checked for existence_.
    /// </summary>
    /// <param name="Pid1">sets <see cref="Pid1"/></param>
    /// <param name="Pid2">sets <see cref="Pid2"/></param>
    /// <param name="radius">sets <see cref="radius"/></param>
    /// <param name="normal">sets <see cref="normal"/></param>
    /// <param name="backendURI">MMT URI</param>
    public TestFact(string backendURI) : base()
        this.ServerDefinition = ServerDefinition;
    }

    /// <summary>
    /// parses the Circlefact response of the MMT-Server
    /// </summary>
    /// \copydoc Fact.parseFact(ScrollFact) 
    public new static IEnumerator parseFact(List<Fact> ret, MMTFact fact)
    {
        string uri = fact.@ref.uri;
        Debug.Log("TestFact Uri:" + uri);
        ret.Add(new TestFact(uri));
        yield break;
    }

    /// \copydoc Fact.generateLabel
    protected override string generateLabel(FactRecorder _Facts)

    /// \copydoc Fact.hasDependentFacts
    public override bool HasDependentFacts => false;

    /// \copydoc Fact.getDependentFactIds
    protected override string[] GetDependentFactIds()

    /// \copydoc Fact.GetHashCode
MaZiFAU's avatar
MaZiFAU committed
    public override int GetHashCode()
        => base.GetHashCode();// this.Pid1.GetHashCode() ^ this.Pid2.GetHashCode();

    /// \copydoc Fact.Equivalent(Fact, Fact)
    protected override bool EquivalentWrapped(TestFact f1, TestFact f2)
    {
MaZiFAU's avatar
MaZiFAU committed
        return DependentFactsEquivalent(f1, f2);
    protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new)
        => new TestFact();
    public override MMTFact MakeMMTDeclaration()
    {
        throw new NotImplementedException();
    }
MaZiFAU's avatar
MaZiFAU committed
    
MaZiFAU's avatar
MaZiFAU committed
    public override SOMDoc Defines()
    {
        throw new NotImplementedException();
    }
}

#pragma warning restore // Testing over