using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using Newtonsoft.Json;
using JsonSubTypes;
using System.Linq;
using REST_JSON_API;
using System.Drawing;

public static class ParsingDictionary
{
    //TODO? get rid of this, use reflection? instead, if possible
    //TODO: docu

    public static Dictionary<string, Func<MMTFact, List<Fact>>> parseFactDictionary = new() {
        { MMTConstants.Point,
            PointFact.parseFact },
        { MMTConstants.Metric,
            LineFact.parseFact },
        { MMTConstants.Angle,
            AngleFact.parseFact },
        { MMTConstants.Eq,
            RightAngleFact.parseFact },
        { MMTConstants.RightAngle,
            RightAngleFact.parseFact },
        { MMTConstants.LineType,
            RayFact.parseFact },
        { MMTConstants.LineOf,
            RayFact.parseFact },
        { MMTConstants.OnLine,
            OnLineFact.parseFact },
        { MMTConstants.ParallelLine,
            ParallelLineFact.parseFact },
        { MMTConstants.CircleType3d,
            CircleFact.parseFact },
        { MMTConstants.OnCircle,
            OnCircleFact.parseFact },
        { MMTConstants.AnglePlaneLine,
            AngleCircleLineFact.parseFact },
        { MMTConstants.RadiusCircleMetric,
            RadiusFact.parseFact },
        { MMTConstants.AreaCircle,
            AreaCircleFact.parseFact },
        { MMTConstants.OrthoCircleLine,
            OrthogonalCircleLineFact.parseFact },
        { MMTConstants.VolumeCone,
            ConeVolumeFact.parseFact },
        { MMTConstants.TruncatedVolumeCone,
            TruncatedConeVolumeFact.parseFact },
        { MMTConstants.CylinderVolume,
            CylinderVolumeFact.parseFact },
        { MMTConstants.TestType,
            TestFact.parseFact },
        { MMTConstants.EqualityCircles,
            EqualCirclesFact.parseFact },
        { MMTConstants.UnEqualityCircles,
            UnEqualCirclesFact.parseFact },
        { MMTConstants.CreateWall,
            QuadFact.parseFact },
        { MMTConstants.ListType,
            DynamicListFact.parseFact },
        { MMTConstants.Product,
            DynamicTupleFact.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))]
[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))]
[JsonSubtypes.KnownSubType(typeof(FunctionFact), nameof(FunctionFact))]
[JsonSubtypes.KnownSubType(typeof(FunctionCallFact), nameof(FunctionCallFact))]
[JsonSubtypes.KnownSubType(typeof(ListFact), nameof(ListFact))]
[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(RealLitFact), nameof(RealLitFact))]
public abstract class Fact
{
    /// <summary>
    /// Reference to <c>GameObject</c> that represents this Fact in the GameWorld.
    /// </summary>
    /// <seealso cref="FactObject3D"/>
    [JsonIgnore]
    public FactWrapper WorldRepresentation;

    /// <summary>
    /// Collection of <c>Type</c>s of *all* available <see cref="Fact"/>s to choose from.
    /// </summary>
    [JsonIgnore]
    public static readonly Type[] Types = TypeExtensions<Fact>.UAssemblyInheritenceTypes;

    /// <value>
    /// [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!
    /// </value>
    [JsonProperty]
    protected string s_type;

    /// <returns><see langword="true"/> if Fact depends on other \ref Fact "Facts"; equivalent to <see cref="getDependentFactIds"/> returns non empty array</returns>
    [JsonIgnore]
    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>
    [JsonIgnore]
    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>
    [JsonIgnore]
    public string Id
    {
        get
        {
            //while (FetchURICoroutine.MoveNext()) ; //active wait for server
            return ServerDefinition.ToString();
        }
    }
    //private IEnumerator FetchURICoroutine = IEnumeratorExtensions.yield_break;

    /// <summary>AST which the Server understands. e.g.: OMS(MMT URI)</summary>
    [JsonProperty]
    public SOMDoc ServerDefinition { get; protected set; }

    /// <value>
    /// <c>get</c> initiates and subsequently updates a human readable name. <remarks>Should be called once a constructor call to be initiated.</remarks>
    /// <c>set</c> calls <see cref="rename(string)"/>
    /// </value>
    public string Label
    {
        get
        { // in case of renamed dependables
            return _Facts == null // JsonSerialization toggle (_Facts.GetNumberOfFacts() == 0 && this is not PointFact) // JsonSerialization toggle && allow first (Point)Fact to be created
                || (hasCustomLabel && _CustomLabel != null)
                ? _CustomLabel
                : generateLabel();
        }
        set
        {
            if (_Facts == null) // JsonSerialization toggle)
            {
                _CustomLabel = value;
                LabelId = -LabelId;
                return;
            }
            rename(value);
        }
    }

    /// <value>
    /// Is true if Fact has a custom <see cref="Label"/> which is not <c>null</c> or <c>""</c>.
    /// </value>
    public bool hasCustomLabel => LabelId < 0;

    /// <summary>
    /// Stores custom <see cref="Label"/> if set.
    /// </summary>
    protected string _CustomLabel = null;

    /// <summary>
    /// Counter to organize auto generated <see cref="Label"/>.
    /// 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; }

    /// <summary>
    /// Reference to <see cref="FactRecorder"/> in which this Fact and all its <see cref="getDependentFactIds">depending Facts</see> are beeing organized.
    /// </summary>
    /// <remarks>Now only relevant for Lables</remarks>
    [JsonIgnore]
    protected FactRecorder _Facts;

    [JsonIgnore]
    bool ForceRecalculateTransform = true;

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

    [JsonIgnore]
    public Quaternion Rotation
    {
        get
        {
            if (ForceRecalculateTransform)
                RecalculateTransform();
            return _Rotation;
        }
        protected set
        {
            ForceRecalculateTransform = false;
            _Rotation = value;
        }
    }
    private Quaternion _Rotation = Quaternion.identity;

    [JsonIgnore]
    public Vector3 LocalScale
    {
        get
        {
            if (ForceRecalculateTransform)
                RecalculateTransform();
            return _LocalScale;
        }
        protected set
        {
            ForceRecalculateTransform = false;
            _LocalScale = value;
        }
    }
    private Vector3 _LocalScale = Vector3.one;

    /// <summary>
    /// Only being used by [JsonReader](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonReader.htm) to initiate empty \ref Fact "Facts".
    /// <seealso cref="SOMDocManager"/>
    /// </summary>
    protected Fact()
    {
        this._Facts = null; // new FactOrganizer();
        LabelId = 0;

        s_type = this.GetType().Name;
    }

    /// <summary>
    /// Standard base-constructor.
    /// </summary>
    /// <param name="organizer"><see cref="_Facts"/></param>
    protected Fact(FactRecorder organizer) : this()
    {
        this._Facts = organizer;
    }

    /// <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"/> in <paramref name="fact"/>.<see cref="Fact._Facts"/> to corresponding <see cref="Fact.Id"/> in <paramref name="organizer"/> </param>
    /// <param name="organizer"><see cref="_Facts"/></param>
    public Fact ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer)
    {
        Fact ret = _ReInitializeMe(old_to_new, organizer);

        ret.LabelId = this.LabelId;
        if (ret.hasCustomLabel)
            ret._CustomLabel = this.Label;

        return ret;
    }
    protected abstract Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer);

    /// <summary>
    /// Assignes a custom <see cref="Label"/>, if <paramref name="newLabel"/> is not yet taken;
    /// or clears custom <see cref="Label"/>.
    /// </summary>
    /// <param name="newLabel">To be new <see cref="Label"/>. To reset to auto-generation set to <c>null</c> or <c>""</c>.</param>
    /// <returns></returns>

    //TODO: notify about updated dependable Labelnames for UI
    //TODO: check for colissions with not yet generated names
    public bool rename(string newLabel)
    // returns true if succeded
    {
        if (string.IsNullOrEmpty(newLabel) && _Facts.GetNumberOfFacts() != 0)
        // switch back to autogenerated
        {
            generateLabel();
            _CustomLabel = null;
            return true;
        }
        else
        // set CustomLabel if available
        {
            if (_Facts.ContainsLabel(newLabel))
                return false;

            freeAutoLabel();
            _CustomLabel = newLabel;

            return true;
        }
    }

    protected abstract void RecalculateTransform();

    public abstract MMTFact MakeMMTDeclaration();

    /// <summary>
    /// Frees ressources e.g. <see cref="Label"/> and will eventually delete %Fact Server-Side in far-near future when feature is supported.
    /// </summary>
    ~Fact()
    {
        //TODO: MMT: delete over there

        freeAutoLabel();

        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>
    public new virtual int GetHashCode()
        => DependentFactIds
            .Select(id => id.GetHashCode())
            .Aggregate((hash1, hash2) => hash1 ^ hash2);

    /// <summary>
    /// auto-generates <see cref="Label"/> using generation variable(s) e.g. <see cref="LabelId"/>;
    /// if custom <see cref="Label"/> is set, tries to restore original generated <see cref="Label"/> **without** resetting <see cref="_CustomLabel"/>. If original <see cref="Label"/> is already taken, a new one will be generated.
    /// </summary>
    /// <returns>auto-generated <see cref="Label"/></returns>
    protected virtual string generateLabel()
    {
        if (LabelId < 0)
            // reload Label if possible
            LabelId = _Facts.UnusedLabelIds.Remove(-LabelId) ? -LabelId : 0;

        if (LabelId == 0)
            if (_Facts.UnusedLabelIds.Count == 0)
                LabelId = ++_Facts.MaxLabelId;
            else
            {
                LabelId = _Facts.UnusedLabelIds.Min;
                _Facts.UnusedLabelIds.Remove(LabelId);
            }

        return ((char)(64 + LabelId)).ToString();
    }

    /// <summary>
    /// Parses <see cref="MMTFact"/> to actual Fact
    /// </summary>
    /// <param name="fact">instance to be parsed</param>
    /// <returns>parsed Fact</returns>
    public static List<Fact> parseFact(MMTFact fact)
        => null;

    /// <summary>
    /// Tells <see cref="_Facts"/> that \ref Fact "this" no longer uses auto-generated <see cref="Label"/>, but remembers current generation variable(s).
    /// </summary>

    // TODO? only get _Fact to freeLabel/
    public /*protected internal*/ void freeAutoLabel()
    {
        if (LabelId > 0)
        {
            _Facts.UnusedLabelIds.Add(LabelId);
            // store Label for name-persistance
            LabelId = -LabelId;
        }
    }

    protected SOMDoc SendToMMT()
        => ServerDefinition = SendToMMT(MakeMMTDeclaration());

    /// <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)
    {
        //GlobalBehaviour.Instance.StartCoroutine(
        //    FetchURICoroutine = 
        //_URI =
        string uri =
            _SendAdd(
                CommunicationEvents.ServerAdress + "/fact/add",
                JsonConvert.SerializeObject(mmtDecl)
        //,(string uri) => _SendURICallback(mmtDecl, uri)
        );//);

        //return;

        //void _SendURICallback(MMTDeclaration mmtDecl, string uri)
        //{
        //    _URI = uri;

        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.");
            return mmtDecl is MMTGeneralFact general ? general.defines
                 : mmtDecl is MMTValueFact value ? value.lhs
                 : throw new NotSupportedException($"{mmtDecl.GetType()}");
        }
        else
            return new OMS(uri);

        /*IEnumerator*/
        static string _SendAdd(string path, string body)//, Action<string> uriCallback)
        {
            if (!CommunicationEvents.ServerRunning)
            {
                Debug.LogWarning("Server not running");
                //uriCallback(null);
                return null; // yield break;
            }

            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) ;

            if (www.result == UnityWebRequest.Result.ConnectionError
             || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogWarning(www.error);
                //uriCallback(null);
                return null; // yield break;
            }
            else
            {
                while (!www.downloadHandler.isDone)
                    //yield return null
                    ;

                FactReference res = JsonUtility.FromJson<FactReference>(www.downloadHandler.text);

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

                //uriCallback(res.uri);
                return res.uri; // yield break;
            }
        }
    }
    
    private static uint bypasscc = 0;
    public static List<Fact> MakeFact(object payload, SOMDoc ServerDefinition, FactRecorder organizer, bool BypassServer)
    {
        Type type = payload.GetType();

        if (BypassServer) bypasscc++;
        SOMDoc BypassURI = new OMS($"{bypasscc}_{ServerDefinition}");

        if (payload is Fact fact)
            return new() { fact };
        else
        if (payload is float f)
            return new() { BypassServer 
                ? new RealLitFact(f, BypassURI, organizer) 
                : new RealLitFact(f, organizer) };
        else
        if (payload is Vector3 point)
            return new() { BypassServer
                ? new PointFact(point, BypassURI, organizer)
                : new PointFact(point, Vector3.up, organizer) };
        else
        if (FuncExtensions.IsFuncType(type, out _))
            return new() {  BypassServer
                ? new FunctionFact(ServerDefinition, BypassURI, organizer)
                : new FunctionFact(ServerDefinition, organizer)};
        else
        if (TupleExtensions.IsTupleType(type, out _))
            return BypassServer
                ? DynamicTupleFact.MMTFactory(payload, ServerDefinition, SOMDoc.SOMDocType(type), organizer, BypassURI)
                : DynamicTupleFact.MMTFactory(payload, ServerDefinition, SOMDoc.SOMDocType(type), organizer);
        else
        if (type.IsGenericType
         && type.GetGenericTypeDefinition() == typeof(List<>))
            return BypassServer
                ? DynamicListFact.MMTFactory((List<dynamic>)payload, ServerDefinition, SOMDoc.SOMDocType(type), organizer, BypassURI)
                : DynamicListFact.MMTFactory((List<dynamic>)payload, ServerDefinition, SOMDoc.SOMDocType(type), organizer);
        else
            throw new NotImplementedException($"For Type {type}.");
    }

    protected class ApproximationComparer : EqualityComparer<object>
    {
        public override bool Equals(object x, object y)
        {
            if (x.Equals(y))
                return true;
            if (x.GetType() != y.GetType())
                return false;

            if (x is float fx)
                return Mathf.Approximately(fx, (float)y);
            if (x is Vector3 vx)
                return Math3d.IsApproximatelyEqual(vx, (Vector3)y);

            return false;
        }

        public override int GetHashCode(object obj)
        {
            if (obj is float)
                return typeof(float).GetHashCode();
            if (obj is Vector3)
                return typeof(Vector3).GetHashCode();

            return obj.GetHashCode();
        }
    }
}

public interface IUnpackable
{
    public abstract List<Fact> UnpackMe(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>
{
    /// <summary>\copydoc Fact.Fact()</summary>
    protected FactWrappedCRTP() : base() { }

    /// <summary>\copydoc Fact.Fact(FactOrganizer)</summary>
    protected FactWrappedCRTP(FactRecorder organizer) : base(organizer) { }

    /// \copydoc Fact.Equivalent(Fact)
    public override bool Equivalent(Fact f2)
        => Equivalent(this, f2);

    /// \copydoc Fact.Equivalent(Fact, Fact)
    public override bool Equivalent(Fact f1, Fact f2)
        => f1.GetType() == f2.GetType() && EquivalentWrapped((T)f1, (T)f2);

    /// <summary>CRTP step of <see cref="Equivalent(Fact)"/> and <see cref="Equivalent(Fact, Fact)"/></summary>
    protected abstract bool EquivalentWrapped(T f1, T f2);

    protected bool DependentFactsEquivalent(T f1, T f2)
        => f1.DependentFactIds
            .Zip(f2.DependentFactIds,
                (id1, id2) =>
                    id1 == id2
                    || FactRecorder.AllFacts[id1].Equivalent(FactRecorder.AllFacts[id2])
            )
            .All(b => b);
}

/// <summary>
/// Point in 3D Space
/// </summary>
public class PointFact : FactWrappedCRTP<PointFact>
{
    /// <summary> Position </summary>
    public Vector3 Point;
    /// <summary> Orientation for <see cref="Fact.WorldRepresentation"/> </summary>
    [JsonProperty]
    private Vector3 Normal;

    /// <summary> \copydoc Fact.Fact </summary>
    public PointFact() : base()
    {
        this.Point = Vector3.zero;
        this.Normal = Vector3.up;
    }

    /// <summary>
    /// Standard Constructor:
    /// Initiates <see cref="Point"/>, <see cref="Normal"/>, <see cref="Fact._URI"/> and creates MMT %Fact Server-Side
    /// </summary>
    /// <param name="P">sets <see cref="Point"/></param>
    /// <param name="N">sets <see cref="Normal"/></param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public PointFact(Vector3 P, Vector3 N, FactRecorder organizer) : base(organizer)
    {
        this.Point = P;
        this.Normal = N;

        SendToMMT();
    }

    protected override void RecalculateTransform()
    {
        Position = Point;
        { // Rotation
            Vector3 notNormal = Vector3.forward != Normal ? Vector3.forward : Vector3.up;
            Rotation = Quaternion.LookRotation(
                Vector3.Cross(Normal, notNormal),
                Normal
            );
        }
    }

    /// <summary>
    /// Bypasses initialization of new MMT %Fact by using existend URI, _which is not checked for existence_.
    /// <see cref="Normal"/> set to <c>Vector3.up</c>
    /// </summary>
    /// <param name="point">sets <see cref="Point"/></param>
    /// <param name="uri">MMT URI</param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public PointFact(Vector3 point, SOMDoc _ServerDefinition, FactRecorder organizer) : base(organizer)
    {
        this.Point = point;
        this.Normal = Vector3.up;
        this.ServerDefinition = _ServerDefinition;
        _ = this.Label;
    }

    /// \copydoc Fact.parseFact(ScrollFact)
    public new static List<Fact> parseFact(MMTFact fact)
    {
        if (((MMTGeneralFact)fact).defines is not OMA defines)
            return new();
        ParsingDictionary.parseTermsToId.TryAdd(defines.ToString(), fact.@ref.uri);

        Vector3 point = SOMDoc.MakeVector3(defines);
        return new() { new PointFact(point, fact.@ref, StageStatic.stage.factState) };
    }

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

    /// \copydoc Fact.getDependentFactIds
    protected override string[] GetDependentFactIds()
        => new string[] { };

    /// \copydoc Fact.GetHashCode
    public override int GetHashCode()
        => this.Point.GetHashCode();

    /// \copydoc Fact.Equivalent(Fact, Fact)
    protected override bool EquivalentWrapped(PointFact f1, PointFact f2)
        => Math3d.IsApproximatelyEqual(f1.Point, f2.Point);

    protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer)
        => new PointFact(this.Point, this.Normal, organizer);

    public override MMTFact MakeMMTDeclaration()
    {
        SOMDoc tp = new OMS(MMTConstants.Point);
        SOMDoc df = new OMA(
                new OMS(MMTConstants.Tuple),
                new[] {
                        new OMF(Point.x),
                        new OMF(Point.y),
                        new OMF(Point.z),
                }
            );

        return new MMTGeneralFact(Label, tp, df);
    }
}

/// <summary>
/// A <see cref="PointFact"/> on a <see cref="AbstractLineFact"/>
/// </summary>
public class OnLineFact : FactWrappedCRTP<OnLineFact>
{
    /// <summary> <see cref="PointFact"/>.<see cref="Fact.Id">Id</see> </summary>
    public string Pid;
    [JsonIgnore]
    public PointFact Point { get => (PointFact)FactRecorder.AllFacts[Pid]; }

    /// <summary> <see cref="AbstractLineFact"/>.<see cref="Fact.Id">Id</see> </summary>
    public string Rid;
    [JsonIgnore]
    public AbstractLineFact Ray { get => (AbstractLineFact)FactRecorder.AllFacts[Rid]; }

    /// <summary> \copydoc Fact.Fact </summary>
    public OnLineFact() : base()
    {
        this.Pid = null;
        this.Rid = null;
    }

    /// <summary>
    /// Standard Constructor:
    /// Initiates <see cref="Pid"/>, <see cref="Rid"/>, <see cref="Fact._URI"/> and creates MMT %Fact Server-Side
    /// </summary>
    /// <param name="pid">sets <see cref="Pid"/></param>
    /// <param name="rid">sets <see cref="Rid"/></param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public OnLineFact(string pid, string rid, FactRecorder organizer) : base(organizer)
    {
        this.Pid = pid;
        this.Rid = rid;

        SendToMMT();
    }

    protected override void RecalculateTransform()
    {
        Position = Point.Position;
        { //Rotation
            Vector3 up = Point.Rotation * Vector3.up;
            Vector3 forward = Ray.Dir;

            if (Math3d.IsApproximatelyEqual(up, forward))
            {
                Vector3 arbitary = Math3d.IsApproximatelyEqual(forward, Vector3.forward)
                    ? Vector3.right
                    : Vector3.forward;

                up = Vector3.Cross(arbitary, forward);
            }

            Rotation = Quaternion.LookRotation(forward, up);
        }
    }

    /// <summary>
    /// Bypasses initialization of new MMT %Fact by using existend URI, _which is not checked for existence_.
    /// </summary>
    /// <param name="pid">sets <see cref="Pid"/></param>
    /// <param name="rid">sets <see cref="Rid"/></param>
    /// <param name="uri">MMT URI</param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public OnLineFact(string pid, string rid, SOMDoc _ServerDefinition, FactRecorder organizer) : base(organizer)
    {
        this.Pid = pid;
        this.Rid = rid;
        this.ServerDefinition = _ServerDefinition;
        _ = this.Label;
    }

    /// \copydoc Fact.parseFact(ScrollFact)
    public new static List<Fact> parseFact(MMTFact fact)
    {
        string pointUri = ((OMS)((OMA)((OMA)((MMTGeneralFact)fact).type).arguments[0]).arguments[1]).uri;
        string lineUri = ((OMA)((OMA)((MMTGeneralFact)fact).type).arguments[0]).arguments[0] is OMS
            // standard case
            ? ((OMS)((OMA)((OMA)((MMTGeneralFact)fact).type).arguments[0]).arguments[0]).uri
            // case when line Uri has a projl on the line Argument 
            : ((OMS)((OMA)((OMA)((OMA)((MMTGeneralFact)fact).type).arguments[0]).arguments[0]).arguments[0]).uri;

        if (!FactRecorder.AllFacts.ContainsKey(pointUri)
         || !FactRecorder.AllFacts.ContainsKey(lineUri))
            return new();

        return new() { new OnLineFact(pointUri, lineUri, fact.@ref, StageStatic.stage.factState) };
    }

    /// \copydoc Fact.generateLabel
    protected override string generateLabel()
        => Point.Label + "∈" + Ray.Label;

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

    /// \copydoc Fact.getDependentFactIds
    protected override string[] GetDependentFactIds()
        => new string[] { Pid, Rid };

    protected override bool EquivalentWrapped(OnLineFact f1, OnLineFact f2)
        => DependentFactsEquivalent(f1, f2);

    protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer)
        => new OnLineFact(old_to_new[this.Pid], old_to_new[this.Rid], organizer);

    public override MMTFact MakeMMTDeclaration()
    {
        SOMDoc tp = new OMA(
            new OMS(MMTConstants.Ded),
            new[] {
                new OMA(
                    new OMS(MMTConstants.OnLine),
                    new[]  {
                        new OMS(Rid),
                        new OMS(Pid)
        }),});

        SOMDoc df = null;

        return new MMTGeneralFact(this.Label, tp, df);
    }
}

/// <summary>
/// Two parallel Lines comprised of two <see cref="LineFact">LineFacts</see> 
/// </summary>
public class ParallelLineFact : FactWrappedCRTP<ParallelLineFact>
{
    /// @{ <summary>
    /// One <see cref="Fact.Id">Id</see> of two <see cref="LineFact"/> that are parallel [<see cref="Lid1"/>, <see cref="Lid2"/>].
    /// </summary>
    public string Lid1, Lid2;
    /// @}

    [JsonIgnore]
    public AbstractLineFact Ray1 { get => (AbstractLineFact)FactRecorder.AllFacts[Lid1]; }
    [JsonIgnore]
    public AbstractLineFact Ray2 { get => (AbstractLineFact)FactRecorder.AllFacts[Lid2]; }

    /// <summary> \copydoc Fact.Fact </summary>
    public ParallelLineFact() : base()
    {
        this.Lid1 = null;
        this.Lid2 = null;
    }

    /// <summary>
    /// Standard Constructor
    /// </summary>
    /// <param name="lid1">sets <see cref="Lid1"/></param>
    /// <param name="lid2">sets <see cref="Lid2"/></param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public ParallelLineFact(string lid1, string lid2, FactRecorder organizer) : base(organizer)
    {
        this.Lid1 = lid1;
        this.Lid2 = lid2;

        SendToMMT();
    }

    protected override void RecalculateTransform() { }

    /// <summary>
    /// Bypasses initialization of new MMT %Fact by using existend URI, _which is not checked for existence_.
    /// </summary>
    /// <param name="Lid1">sets <see cref="Lid1"/></param>
    /// <param name="Lid2">sets <see cref="Lid2"/></param>
    /// <param name="backendURI">MMT URI</param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public ParallelLineFact(string Lid1, string Lid2, SOMDoc _ServerDefinition, FactRecorder organizer) : base(organizer)
    {
        this.Lid1 = Lid1;
        this.Lid2 = Lid2;

        this.ServerDefinition = _ServerDefinition;
        _ = this.Label;
    }

    /// \copydoc Fact.parseFact(ScrollFact)
    public new static List<Fact> parseFact(MMTFact fact)
    {
        if (((MMTGeneralFact)fact).type is not OMA type) // proof DED
            return new();

        OMA parallel_lines_OMA = (OMA)type.arguments[0]; // parallel

        string lineAUri = ((OMS)parallel_lines_OMA.arguments[0]).uri;
        string lineBUri = ((OMS)parallel_lines_OMA.arguments[1]).uri;

        if (!FactRecorder.AllFacts.ContainsKey(lineAUri)
         || !FactRecorder.AllFacts.ContainsKey(lineBUri))
            return new();

        return new() { new ParallelLineFact(lineAUri, lineBUri, fact.@ref, StageStatic.stage.factState) };
    }

    /// \copydoc Fact.generateLabel
    protected override string generateLabel()
        => Ray1.Label + "||" + Ray2.Label;

    public override MMTFact MakeMMTDeclaration()
    {
        SOMDoc tp = new OMA(
            new OMS(MMTConstants.Ded),
            new[] {
                new OMA(
                    new OMS(MMTConstants.ParallelLine),
                    new[]  {
                        new OMS(Lid1),
                        new OMS(Lid2),
                    }
                ),
            }
        );

        SOMDoc df = null;

        return new MMTGeneralFact(this.Label, tp, df);
    }

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

    /// \copydoc Fact.getDependentFactIds
    protected override string[] GetDependentFactIds()
        => new string[] { Lid1, Lid2 };

    /// \copydoc Fact.Equivalent(Fact, Fact)
    protected override bool EquivalentWrapped(ParallelLineFact f1, ParallelLineFact f2)
        => DependentFactsEquivalent(f1, f2);

    protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer)
        => new ParallelLineFact(old_to_new[this.Lid1], old_to_new[this.Lid2], organizer);
}

/// <summary>
/// Used for BouncingScroll
/// </summary>
public class QuadFact : FactWrappedCRTP<QuadFact>
{
    /// <summary> Defining Corners; Order is Cyclic;
    /// <see cref="PointFact"/>.<see cref="Fact.Id">Id</see> </summary>
    public string[] Pids;
    [JsonIgnore]
    public PointFact[] Points
    {
        get => _Points ??= Pids.Select(pid => (PointFact)FactRecorder.AllFacts[pid]).ToArray();
    }
    private PointFact[] _Points;

    [JsonIgnore]
    public Vector3 Normal, Tangents, AltTangents;

    /// <summary> \copydoc Fact.Fact </summary>
    public QuadFact() : base() { }

    /// <summary>
    /// Standard Constructor:
    /// Initiates <see cref="Pids"/> and creates MMT %Fact Server-Side
    /// </summary>
    /// <param name="pid_corners">sets <see cref="Pids"/></param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public QuadFact(string[] pid_corners, FactRecorder organizer) : base(organizer)
    {
        Init(pid_corners);
        SendToMMT();
    }

    /// <summary>
    /// Bypasses initialization of new MMT %Fact by using existend URI, _which is not checked for existence_.
    /// </summary>
    /// <param name="pid_corners">sets <see cref="Pids"/></param>
    /// <param name="uri">MMT URI</param>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public QuadFact(string[] pid_corners, SOMDoc _ServerDefinition, FactRecorder organizer) : base(organizer)
    {
        Init(pid_corners);

        this.ServerDefinition = _ServerDefinition;
        _ = this.Label;
    }

    private void Init(string[] pid_corners)
    {
        Pids = pid_corners;

        Tangents = (Points[0].Point - Points[1].Point).normalized;
        AltTangents = (Points[0].Point - Points[2].Point).normalized;
        Normal = Vector3.Cross(Tangents, AltTangents).normalized;

        if (Math3d.vectorPrecission < Math.Abs(Vector3.Dot(Normal, Points[0].Point - Points[3].Point)))
            throw new ArgumentException("All Points must lie on the same Plane!");
    }

    /// \copydoc Fact.parseFact(ScrollFact)
    public new static List<Fact> parseFact(MMTFact fact)
    {
        throw new NotImplementedException();
    }

    public override MMTFact MakeMMTDeclaration()
    {
        SOMDoc tp = new OMS(MMTConstants.Wall);
        SOMDoc df = new OMA(
                new OMS(MMTConstants.CreateWall),
                Pids.Select(pid => new OMS(pid)).ToArray()
            ); ;

        return new MMTGeneralFact(Label, tp, df);
    }

    protected override bool EquivalentWrapped(QuadFact f1, QuadFact f2)
    {
        if (f1.Points.Length != f2.Points.Length)
            return false;

        int i = 0;
        for (; i < f2.Points.Length; i++) // 1st incedence
            if (f2.Points[i].Equivalent(f1.Points[0]))
                break;

        if (i == f2.Points.Length) // no match
            return false;

        for (int j = 1; j < f2.Points.Length; j++) // cyclic match
            if (!f2.Points[(i + j) % f2.Points.Length].Equivalent(f1.Points[j]))
                return false;

        return true;
    }

    public override bool HasDependentFacts => true;

    protected override string[] GetDependentFactIds()
        => Pids;

    protected override void RecalculateTransform() { }

    protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer)
        => new QuadFact(Pids.Select(pid => old_to_new[pid]).ToArray(), organizer);
}

#pragma warning disable // Testing...

/// TEST FACT
/// use this if you need to test certain implementations of facts.

/// <summary>
/// just for testing purposes  
/// </summary>
public class TestFact : FactWrappedCRTP<TestFact>
{
    /// <summary> \copydoc Fact.Fact </summary>
    public TestFact() : base()
    {

    }

    /// <summary>
    /// Standard Constructor:
    /// Initiates members and creates MMT %Fact Server-Side
    /// </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="organizer">sets <see cref="Fact._Facts"/></param>
    public TestFact(FactRecorder organizer) : base(organizer)
    {
        SendToMMT();
    }

    protected override void RecalculateTransform() { }

    /// <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>
    /// <param name="organizer">sets <see cref="Fact._Facts"/></param>
    public TestFact(string backendURI, FactRecorder organizer) : base(organizer)
    {

        this.ServerDefinition = ServerDefinition;
        _ = this.Label;
    }

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

    /// \copydoc Fact.generateLabel
    protected override string generateLabel()
        => "test";

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

    /// \copydoc Fact.getDependentFactIds
    protected override string[] GetDependentFactIds()
        => new string[] { };

    /// \copydoc Fact.GetHashCode
    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)
    {
        return DependentFactsEquivalent(f1, f2);
    }

    protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new, FactRecorder organizer)
        => new TestFact(organizer);

    public override MMTFact MakeMMTDeclaration()
    {
        throw new NotImplementedException();
    }
}

#pragma warning restore // Testing over