using System;
using System.Collections;
using TMPro;
using UnityEngine;
using static CommunicationEvents;

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

    void Start()
    {
        AddFactEvent.AddListener(SpawnFactRepresentation);
        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.WorldRepresentation.gameObject
            .AddComponent<AttachedPositionFunctionBehaviour>();
        attachedBehaviour.Fact = fact;

        fact.WorldRepresentation = attachedBehaviour;
    }

    public void SpawnPoint(PointFact fact)
    {
        GameObject point = GameObject.Instantiate(Sphere);
        fact.WorldRepresentation = point.GetComponent<FactObject3D>();
        fact.WorldRepresentation.Fact = fact;

        point.transform.SetPositionAndRotation(fact.Position, fact.Rotation);
    }

    public void SpawnLine(LineFact fact)
    {
        //Change FactRepresentation to Line
        GameObject line = GameObject.Instantiate(Line);
        fact.WorldRepresentation = line.GetComponentInChildren<FactObject3D>();
        fact.WorldRepresentation.Fact = fact;

        //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";
    }

    public void SpawnRay(RayFact fact)
    {
        //Change FactRepresentation to Line
        GameObject line = GameObject.Instantiate(Ray);
        fact.WorldRepresentation = line.GetComponentInChildren<FactObject3D>();
        fact.WorldRepresentation.Fact = fact;

        //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;
    }

    //Spawn an angle: point with id = angleFact.Pid2 is the point where the angle gets applied
    public void SpawnAngle(AbstractAngleFact fact)
    {
        //Change FactRepresentation to Angle
        GameObject angle = GameObject.Instantiate(Angle);
        fact.WorldRepresentation = angle.GetComponentInChildren<FactObject3D>();
        fact.WorldRepresentation.Fact = fact;

        angle.transform.SetPositionAndRotation(fact.Position, fact.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((float)fact.angle, 2) + "°";
        }

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

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

        ringAndCircleGO.transform.SetPositionAndRotation(fact.Position, fact.Rotation);
        fact.WorldRepresentation = ringAndCircleGO.AddComponent<FactObject3D>();
        fact.WorldRepresentation.Fact = fact;
    }

    public void SpawnRing(CircleFact circleFact, Transform parent = null)
    {
        GameObject ring = GameObject.Instantiate(Ring, parent);

        var tori = ring.GetComponentsInChildren<TorusGenerator>();
        foreach (var torus in tori)
            torus.torusRadius = circleFact.radius + (float)Math3d.vectorPrecission;
    }

    public void SpawnCircle(CircleFact circleFact, Transform parent = null)
    {
        GameObject circle = Instantiate(Circle, parent);
        //Set radius
        circle.transform.localScale = Vector3.Scale(circle.transform.localScale, circleFact.LocalScale);
    }

    public void AnimateNonExistingFactTrigger(Fact fact)
    {
        StartCoroutine(_BlossomAndDie(fact));

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

            yield return new WaitForSeconds(GlobalBehaviour.HintAnimationDuration);

            RemoveFactEvent.Invoke(fact);
        }
    }

}