using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static CommunicationEvents;

public class LotTool : Gadget
//constructs a Perpendicular between a Line and a Point
{
    /// \copydoc Gadget.s_type
    [JsonProperty]
    protected static new string s_type = "LotTool";

    //Cache for drawing Lines
    private AbstractLineFact BaseLine;
    private Vector3 BaseLineRoot;
    private Vector3 IntersectionPoint;
    private Vector3 BaseLineHit;


    public override void _Hit(RaycastHit hit)
    {
        void CreateRayAndAngles(string IntersectionId, string LotPointId, bool samestep)
        {
            FactManager.AddRayFact(IntersectionId, LotPointId, samestep, gadget: this);

            //TODO? create at all? / for all points on basline?
            FactManager.AddAngleFact(
                BaseLine.Pid1 == IntersectionId ? BaseLine.Pid2 : BaseLine.Pid1
                , IntersectionId, LotPointId, samestep: true, gadget: this);
        }

        string tempFactId = null;
        if (!((LayerMask) hit.transform.gameObject.layer).IsAnyByName(new string[] { "Default", "Tree" })
         && (!hit.transform.TryGetComponent(out FactObject obj)
           || Workflow.Contains(tempFactId = obj.URI)))
            return;

        Fact tempFact = tempFactId == null ? null : StageStatic.stage.factState[tempFactId];
        switch (Workflow.Count)
        {
            case 0: // select basline
                if (tempFact is not AbstractLineFact)
                    return;
                Workflow.Add(tempFactId);

                BaseLine = (AbstractLineFact) tempFact;
                BaseLineRoot = ((PointFact) StageStatic.stage.factState[BaseLine.Pid1]).Point;

                BaseLineHit = hit.point;
                ActivateLineDrawing();
                return;

            case 1: // select point perpendiculum/lot goes through
                if (tempFact is not PointFact)
                    return;

                IntersectionPoint = Math3d.ProjectPointOnLine(BaseLineRoot, BaseLine.Dir, hit.transform.position);
                if (Math3d.IsApproximatelyEqual(IntersectionPoint, hit.transform.position))
                {   // TempFact is on baseLine
                    Workflow.Add(tempFactId);
                    return;
                }
                else
                {   // create perpendicular through existing Point off Line
                    Vector3 normal = Vector3.Cross(BaseLine.Dir, hit.transform.position - IntersectionPoint).normalized;
                    normal *= Mathf.Sign(Vector3.Dot(normal, Vector3.up)); // point up
                    var intersectionId = FactManager.AddPointFact(IntersectionPoint, normal, gadget: this).Id;

                    if (BaseLine is RayFact) // Add OnLineFact only on Ray not Line
                        FactManager.AddOnLineFact(intersectionId, Workflow[0], true, gadget: this, is_certain: true);

                    CreateRayAndAngles(intersectionId, tempFactId, true);
                    ResetGadget();
                }
                break;

            case 2: // create perpendicular through new Point off Line
                if (!((LayerMask) hit.transform.gameObject.layer).IsAnyByName(new string[] { "Default", "Tree" }))
                    return;

                Vector3 LotPoint = Math3d.ProjectPointOnLine(hit.point, BaseLine.Dir, IntersectionPoint);
                CreateRayAndAngles(Workflow[1], FactManager.AddPointFact(LotPoint, hit.normal, gadget: this).Id, true);
                ResetGadget();
                return;
        }
    }

    protected override void _ActivateLineDrawing()
    {
        GadgetBehaviour.LineRenderer.positionCount = 3;

        GadgetBehaviour.LineRenderer.startWidth = 0.095f;
        GadgetBehaviour.LineRenderer.endWidth = 0.095f;

        //SetPositions(new Vector3[] { GadgetBehaviour.Cursor.transform.position, LotPoints[0], LotPoints[0] });
        //start at curser
        SetPosition(0, GadgetBehaviour.Cursor.transform.position);
        //Project curser perpendicular on Line for intersection-point
        SetPosition(1, Math3d.ProjectPointOnLine(BaseLineRoot, BaseLine.Dir, GetPosition(0)));
        //end at Point on the line (i.c.o. projection beeing outside a finite line)
        SetPosition(2, Math3d.ProjectPointOnLine(BaseLineRoot, BaseLine.Dir, BaseLineHit));
    }

    //Updates the points of the Lines when baseLine was selected in LineMode
    protected override void _UpdateLineDrawing()
    {
        if (Workflow.Count < 2) {
            SetPosition(0, GadgetBehaviour.Cursor.transform.position);
            SetPosition(1, Math3d.ProjectPointOnLine(BaseLineRoot, BaseLine.Dir, GetPosition(0)));
        }
        else {
            SetPosition(1, IntersectionPoint);
            SetPosition(0, Math3d.ProjectPointOnLine(GadgetBehaviour.Cursor.transform.position, BaseLine.Dir, GetPosition(1)));
        }
    }
}