using System;
using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UIElements;
using UnityStandardAssets.Vehicles.Car;
using static CommunicationEvents;
using static GlobalBehaviour;

public class FactSpawner : MonoBehaviour
{
    public GameObject
        Sphere,
        Line,
        Ray,
        Angle,
        Ring,
        Circle;

    void Start()
    {
        AddFactEvent.AddListener(SpawnFactRepresentation);
        RemoveFactEvent.AddListener(DeleteObject);

        AnimateNonExistingFactEvent.AddListener(animateNonExistingFactTrigger);
    }

    public void SpawnFactRepresentation(Fact fact)
    {
        switch (fact)
        {
            case PointFact PointFact:
                SpawnPoint(PointFact); break;
            case LineFact LineFact:
                SpawnLine(LineFact); break;
            //case RightAngleFact AngleFact: //needed for Hint System
            case AbstractAngleFact AngleFact:
                SpawnAngle(AngleFact); break;
            case RayFact RayFact:
                SpawnRay(RayFact); break;
            case CircleFact CircleFact:
                SpawnRingAndCircle(CircleFact); break;
            case AttachedPositionFunction AttachedPositionFunction:
                SpawnAttachedPositionFunction(AttachedPositionFunction); break;
            default: break;
        };
    }

    public void SpawnAttachedPositionFunction(AttachedPositionFunction fact)
    {
        var attachedBehaviour = fact.Fact.Representation.AddComponent<AttachedPositionFunctionBehaviour>();
        attachedBehaviour.URI = fact.Id;

        fact.Representation = attachedBehaviour.gameObject;
    }

    public void SpawnPoint(PointFact fact)
    {
        GameObject point = GameObject.Instantiate(Sphere);
        point.transform.position = fact.Position;
        point.transform.rotation = fact.Rotation;
        point.GetComponentInChildren<TextMeshPro>().text = fact.Label;
        point.GetComponent<FactObject>().URI = fact.Id;
        fact.Representation = point;
    }

    public void SpawnLine(LineFact fact)
    {
        //Change FactRepresentation to Line
        GameObject line = GameObject.Instantiate(Line);
        //Place the Line in the centre of the two points
        line.transform.position = fact.Position;

        //Change scale and rotation, so that the two points are connected by the line
        //and without affecting Scale of the Text
        line.transform.GetChild(0).localScale = fact.LocalScale;
        line.transform.GetChild(0).rotation = fact.Rotation;

        line.GetComponentInChildren<TextMeshPro>().text =
            fact.Label + " = " + Math.Round(fact.Distance, 2) + " m";
        line.GetComponentInChildren<FactObject>().URI = fact.Id;

        fact.Representation = line;
    }

    public void SpawnRay(RayFact fact)
    {
        //Change FactRepresentation to Line
        GameObject line = GameObject.Instantiate(Ray);
        //Place the Line in the centre of the two points
        line.transform.position = fact.Position;

        //Change scale and rotation, so that the two points are connected by the line
        //and without affecting Scale of the Text
        line.transform.GetChild(0).localScale = fact.LocalScale;
        line.transform.GetChild(0).rotation = fact.Rotation;

        line.GetComponentInChildren<TextMeshPro>().text = fact.Label;
        line.GetComponentInChildren<FactObject>().URI = fact.Id;

        fact.Representation = line;
    }

    //Spawn an angle: point with id = angleFact.Pid2 is the point where the angle gets applied
    public void SpawnAngle(AbstractAngleFact fact)
    {
        Vector3 Psotion = fact.Position;
        Quaternion Rotation = fact.Rotation;
        float angleValue = fact.angle;

        //Change FactRepresentation to Angle
        GameObject angle = GameObject.Instantiate(Angle);

        //Place the Angle at position of point2
        angle.transform.SetPositionAndRotation(Psotion, Rotation);

        //Set text of angle
        TextMeshPro[] texts = angle.GetComponentsInChildren<TextMeshPro>();
        foreach (TextMeshPro t in texts)
        {
            //Change Text not to the id, but to the angle-value (from both sides)
            t.text = Math.Round(angleValue, 2) + "°";
        }

        //Generate angle mesh
        CircleSegmentGenerator[] segments = angle.GetComponentsInChildren<CircleSegmentGenerator>();
        foreach (CircleSegmentGenerator c in segments)
            c.setAngle(angleValue);

        angle.GetComponentInChildren<FactObject>().URI = fact.Id;
        fact.Representation = angle;
    }

    public void SpawnRingAndCircle(CircleFact fact)
    {
        var ringAndCircleGO = new GameObject("RingAndCircle");
        SpawnRing(fact, ringAndCircleGO.transform);
        SpawnCircle(fact, ringAndCircleGO.transform);

        //Move Ring to middlePoint
        ringAndCircleGO.transform.position = fact.Position;

        fact.Representation = ringAndCircleGO;
    }

    public void SpawnRing(CircleFact circleFact, Transform parent = null)
    {
        GameObject ring = GameObject.Instantiate(Ring, parent);
        var tori = ring.GetComponentsInChildren<TorusGenerator>();
        var tmpText = ring.GetComponentInChildren<TextMeshPro>();
        var FactObj = ring.GetComponentInChildren<FactObject>();

        //Set radii
        foreach (var torus in tori)
            torus.torusRadius = circleFact.radius;

        tmpText.text = $"○{circleFact.Point1.Label}";

        FactObj.URI = circleFact.Id;
    }

    public void SpawnCircle(CircleFact circleFact, Transform parent = null)
    {
        GameObject circle = Instantiate(Circle, parent);
        var FactObj = circle.GetComponentInChildren<FactObject>();

        //Set radius
        circle.transform.localScale = Vector3.Scale(circle.transform.localScale, circleFact.LocalScale);

        FactObj.URI = circleFact.Id;
    }


    public void DeleteObject(Fact fact)
        => GameObject.Destroy(fact.Representation);

    public void animateNonExistingFactTrigger(Fact fact)
    {
        StartCoroutine(animateNonExistingFact(fact));

        IEnumerator animateNonExistingFact(Fact fact)
        {
            SpawnFactRepresentation(fact);

            ShinyThings.HighlightFact(fact, FactObject.FactMaterials.Hint);

            yield return new WaitForSeconds(GlobalBehaviour.hintAnimationDuration);

            GameObject.Destroy(fact.Representation);
        }
    }

}