using Newtonsoft.Json;
using REST_JSON_API;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/// <summary>
/// Point in 3D Space
/// </summary>
public class CuboidFact : FactWrappedCRTP<CuboidFact>
{


    //used points
    public string PidR, PidT;
    public Vector3 T;
    public RectangleFact RF = null;
    public float Volume = 0.0F;


    public RectangleFact GetR {get =>  (RectangleFact)FactRecorder.AllFacts[PidR];}
    public PointFact GetT {get =>  (PointFact)FactRecorder.AllFacts[PidT];}
    protected void calculate_vectors(){

        /*
        A = ((PointFact)FactRecorder.AllFacts[PidA]).Point + Vector3.zero;
        B = ((PointFact)FactRecorder.AllFacts[PidB]).Point + Vector3.zero;
        C = ((PointFact)FactRecorder.AllFacts[PidC]).Point + Vector3.zero;
        D = (A - B) + C;

        Area = (Vector3.Distance(A, B) * Vector3.Distance(B, C));

        Vector3 scale = new Vector3(Vector3.Distance(B, C), Vector3.Distance(A, B), 1.0F);
        
        LocalScale = scale * 0.5F;

        Position =  B + 0.5F*((A-B) + (C-B));

        Rotation = Quaternion.LookRotation(Vector3.Cross((A-B), (C-B)), Vector3.up);
        //Rotation = Quaternion.LookRotation(forward, new Vector3(1.0F, 0.0F, 0.0F));
        */   

        T = ((PointFact)FactRecorder.AllFacts[PidT]).Point + Vector3.zero;
        RF = ((RectangleFact)FactRecorder.AllFacts[PidR]);


        Vector3 A = RF.A, B = RF.B, C = RF.C;

        float sidelengthBA = Vector3.Distance(B, A);
        float sidelengthTB = Vector3.Distance(T, B);
        float sidelengthBC = Vector3.Distance(B, C);

        LocalScale = new Vector3(sidelengthBA+0.1F, sidelengthTB+0.1F, sidelengthBC+0.1F);

        Volume = sidelengthBA * sidelengthBC * sidelengthTB; 

        Position = B + 0.5F * ((A - B) + (T - B) + (C - B));

        Vector3 cross = Vector3.Cross((T - B), (A - B));
        Rotation = Quaternion.LookRotation(cross.normalized, (T - B).normalized);

        Rotation = Quaternion.identity;
    }

    public CuboidFact() : base(){
        this.PidR = null;
        this.PidT = null;
    }
    [JsonConstructor]
    public CuboidFact( string PidR, string PidT) : base()
    {

        this.PidR = PidR;
        this.PidT = PidT;

        calculate_vectors();

    }

    /// <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="ServerDefinition">MMT URI as OMS</param>
    public CuboidFact(string PidR, string PidT, SOMDoc ServerDefinition) : base()
    {
  

        this.PidR = PidR;
        this.PidT = PidT;

        this.ServerDefinition = ServerDefinition;


        calculate_vectors();
        


    }

    /// \copydoc Fact.parseFact(ScrollFact)
    public new static IEnumerator parseFact(List<Fact> ret, MMTFact fact)
    {
        if (((MMTGeneralFact)fact).defines is not OMA df)
            yield break;

        OMS rectangleR, pointT;

        rectangleR = (OMS)df.arguments[0];
        pointT = (OMS)df.arguments[1];

        string PidR = rectangleR.uri;
        string PidT = pointT.uri;

        

        ret.Add(new CuboidFact(PidR, PidT));

        //ParsingDictionary.parseTermsToId.TryAdd(defines.ToString(), fact.@ref.uri);
        //ret.Add(new PointFact(SOMDoc.MakeVector3(defines), fact.@ref));
    }


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

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

    /// \copydoc Fact.GetHashCode
    /* public override int GetHashCode()
        => this.Point.GetHashCode();
    */
    protected override void RecalculateTransform()
    {
        calculate_vectors();
    }
    /// \copydoc Fact.Equivalent(Fact, Fact)
    protected override bool EquivalentWrapped(CuboidFact f1, CuboidFact f2){
        
        return (

                Math3d.IsApproximatelyEqual(f1.T, f2.T)
                && f1.RF.isEqual(f2.RF)

        );

    }

    protected override Fact _ReInitializeMe(Dictionary<string, string> old_to_new){
        
        return new CuboidFact(this.PidR, this.PidT);

    }

    public override MMTFact MakeMMTDeclaration()
    {
        SOMDoc tp = new OMS(MMTConstants.CuboidType);

        return new MMTGeneralFact(_LastLabel, tp, Defines());
    }

    public override SOMDoc Defines()
        => new OMA(
                new OMS(MMTConstants.CuboidCons),
                new[] {
                        new OMS(PidR),
                        new OMS(PidT)
                }
            );
}