using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using static CommunicationEvents;
public class FactManager : MonoBehaviour
{



    public GameObject SmartMenu;
    private Stack<int> NextEmptyStack = new Stack<int>();

    // Start is called before the first frame update
    void Start()
    {
        CommunicationEvents.ToolModeChangedEvent.AddListener(OnToolModeChanged);
        CommunicationEvents.TriggerEvent.AddListener(OnHit);

        CommunicationEvents.RemoveFactEvent.AddListener(DeleteFact);//we also need the listener here at the moment so we can react to UI delete events

        NextEmptyStack.Push(0);
  
    }


    void AddLineFact(int pid1, int pid2, int id)
    {
       Facts.Insert(id, new LineFact
        {
            Id = id,
            Pid1 = pid1,
            Pid2 = pid2
        });
    }

    void AddAngleFact(int pid1, int pid2, int pid3, int id)
    {
        Facts.Insert(id, new AngleFact
        {
            Id = id,
            Pid1 = pid1,
            Pid2 = pid2,
            Pid3 = pid3
        });
    }


    PointFact AddPointFact(RaycastHit hit, int id)
    {
       
        Facts.Insert(id, new PointFact
        {
            Id = id,
            Point = hit.point
        });

        return Facts[id] as PointFact;
    }

    void DeleteFact(Fact fact)
    {
       
        NextEmptyStack.Push(fact.Id);
        Facts.RemoveAt(fact.Id);
    }


    // Update is called once per frame
    void Update()
    {


        //Je nachdem ob erster oder der zweite Punkt angeklickt wurde behandeln

        //Wenn erster Punkt einen Point-Collider erwischt hat:
        //Linie aktivieren und Cursor folgen
        //Wenn erster Punkt keinen Point-Collider erwischt hat:
        //Nichts tun -> Evtl Hint einblenden

        //Wenn zweiter Punkt einen Point-Collider erwischt hat:
        //Event senden um GameObject-Line zu erzeugen
        //Wenn zweiter Punkt keinen Point-Collider erwischt hat:
        //Linie deaktivieren -> Evtl Hint einblenden

        //LayerMask for Points
        int layerMask = 1 << LayerMask.NameToLayer("Point"); //only hit Point
        /*
        //Wenn bereits der erste Punkt markiert wurde
        if (this.lineRendererActivated) //instead: bool variable....
        {
            //If a second Point was Hit
            if (Physics.Raycast(ray, out Hit, 30f, layerMask)) //instead: another hitevent, refer to OnHit
            {
                //Event for Creating the Line
                Vector3 point1 = this.linePositions[0];
                Vector3 point2 = Hit.transform.gameObject.transform.position;
                this.DeactivateLineRenderer();
                CommunicationEvents.AddLineEvent.Invoke(point1, point2);
                break;
            }
            //If no Point was hit
            else
            {
                //TODO: Hint that only a line can be drawn between already existing points
                this.DeactivateLineRenderer();
            }
        }
        //Wenn der erste Punkt noch nicht markiert wurde
        else
        {
            //Check if a Point was hit
            if (Physics.Raycast(ray, out Hit, 30f, layerMask))
            {
                //Set LineRenderer activated
                this.lineRendererActivated = true;
                //Add the position of the hit Point for the start of the Line
                Vector3 temp = Hit.transform.gameObject.transform.position;
                //temp += Vector3.up;

                linePositions.Add(temp);
                //The second point is the same point at the moment
                linePositions.Add(temp);
                this.lineRenderer.SetPosition(0, linePositions[0]);
                this.lineRenderer.SetPosition(1, linePositions[1]);
            }
            else
            {
                //TODO: Hint that only a line can be drawn between already existing points
            }
        }

        */

    }


    public int GetFirstEmptyID()
    {

        /* for (int i = 0; i < Facts.Length; ++i)
         {
             if (Facts[i] == "")
                 return i;
         }
         return Facts.Length - 1;*/

        int id = NextEmptyStack.Pop();
        if (NextEmptyStack.Count == 0)
            NextEmptyStack.Push(id + 1);

     
        return id;


    }

    public void OnToolModeChanged(ToolMode ActiveToolMode)
    {
        switch (ActiveToolMode)
        {
            case ToolMode.MarkPointMode:
                //If MarkPointMode is activated we want to have the ability to mark the point
                //everywhere, independent of already existing facts
                foreach (Fact fact in Facts)
                {
                   GameObject gO = fact.Representation;
                   gO.GetComponentInChildren<Collider>().enabled = false;
                }
                break;
            case ToolMode.CreateLineMode:
                //If CreateLineMode is activated we want to have the ability to select points for the Line
                //but we don't want to have the ability to select Lines or Angles
                foreach (Fact fact in Facts)
                {
                    GameObject gO = fact.Representation;
                    if (gO.layer == LayerMask.NameToLayer("Line") || gO.layer == LayerMask.NameToLayer("Angle"))
                    {
                        gO.GetComponentInChildren<Collider>().enabled = false;
                    }
                    else if (gO.layer == LayerMask.NameToLayer("Point"))
                    {
                        gO.GetComponentInChildren<Collider>().enabled = true;
                    }
                }
                break;
            case ToolMode.CreateAngleMode:
                //If CreateAngleMode is activated we want to have the ability to select Lines for the Angle
                //but we don't want to have the ability to select Points or Angles
                foreach (Fact fact in Facts)
                {
                    GameObject gO = fact.Representation;
                    if (gO.layer == LayerMask.NameToLayer("Point") || gO.layer == LayerMask.NameToLayer("Angle"))
                    {
                        gO.GetComponentInChildren<Collider>().enabled = false;
                    }
                    else if (gO.layer == LayerMask.NameToLayer("Line"))
                    {
                        gO.GetComponentInChildren<Collider>().enabled = true;
                    }
                }
                break;
            case ToolMode.DeleteMode:
                //If DeleteMode is activated we want to have the ability to delete every Fact
                //independent of the concrete type of fact
                foreach (Fact fact in Facts)
                {
                    GameObject gO = fact.Representation;
                    gO.GetComponentInChildren<Collider>().enabled = true;
                }
                break;
            case ToolMode.ExtraMode:
                foreach (Fact fact in Facts)
                {

                }
                break;



        }
    }

    public void OnHit(RaycastHit hit)
    {
        Debug.Log(CommunicationEvents.ActiveToolMode);
        if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Point"))
        {
            //hit existing point, so delete it
            if (CommunicationEvents.ActiveToolMode == ToolMode.ExtraMode)
            {
                var menu = GameObject.Instantiate(SmartMenu);
                menu.GetComponent<Canvas>().worldCamera = Camera.main;
                menu.transform.SetParent(hit.transform);
                menu.transform.localPosition = Vector3.up - Camera.main.transform.forward;
            }
            else
            {
                char letter = hit.transform.gameObject.GetComponentInChildren<TextMeshPro>().text.ToCharArray()[0];
                int id = letter - 65;
                CommunicationEvents.RemoveFactEvent.Invoke(Facts[id]);
            }

        }
        else
        {
            PointFact fact = AddPointFact(hit, GetFirstEmptyID());
            CommunicationEvents.AddFactEvent.Invoke(fact);
        }
    }



}