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>();

    //Variable for LineMode distinction
    public bool lineModeFirstPointSelected = false;
    public Fact firstPointSelected = null;

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

        //We also need the listener here at the moment so we can react to UI delete events in ExtraMode -> Delete-Button
        CommunicationEvents.RemoveFactEvent.AddListener(DeleteFact);

        NextEmptyStack.Push(0);
  
    }

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

    }

    PointFact AddPointFact(RaycastHit hit, int id)
    {

        Facts.Insert(id, new PointFact
        {
            Id = id,
            Point = hit.point,
            Normal = hit.normal
        });

        return Facts[id] as PointFact;
    }

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

        return Facts[id] as LineFact;
    }

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

        return Facts[id] as AngleFact;
    }

    public void DeleteFact(Fact fact)
    {
        if (Facts.Contains(fact)) {
            NextEmptyStack.Push(fact.Id);
            //Facts.RemoveAt(fact.Id);
            Facts.Remove(Facts.Find(x => x.Id == fact.Id));
            CommunicationEvents.RemoveFactEvent.Invoke(fact);
        }
    }

    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)
    {
        //We need to do this somehwere...
        CommunicationEvents.ActiveToolMode = 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)
    {
        
        switch (ActiveToolMode)
        {
            //If Left-Mouse-Button was pressed in MarkPointMode
            case ToolMode.MarkPointMode:
                CommunicationEvents.AddFactEvent.Invoke(this.AddPointFact(hit, this.GetFirstEmptyID()));
                break;
            //If Left-Mouse-Button was pressed in CreateLineMode
            case ToolMode.CreateLineMode:
                //Check if an existing Point was hit
                if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Point"))
                {
                    Fact tempFact = Facts[hit.transform.GetComponent<FactObject>().Id];

                    if (this.lineModeFirstPointSelected)
                    {
                        //Event for end of line-rendering in "ShinyThings"
                        CommunicationEvents.StopLineRendererEvent.Invoke(null);
                        //Create LineFact
                        CommunicationEvents.AddFactEvent.Invoke(this.AddLineFact(this.firstPointSelected.Id, tempFact.Id, this.GetFirstEmptyID()));
                        this.lineModeFirstPointSelected = false;
                        this.firstPointSelected = null;
                    }
                    else {
                        //Activate LineRenderer for preview
                        this.lineModeFirstPointSelected = true;
                        this.firstPointSelected = tempFact;
                        //Event for start line-rendering in "ShinyThings"
                        CommunicationEvents.StartLineRendererEvent.Invoke(this.firstPointSelected);
                    }
                }
                //If no Point was hit
                else {
                    if (this.lineModeFirstPointSelected)
                    {
                        //Deactivate LineRendering and first point selection
                        this.lineModeFirstPointSelected = false;
                        this.firstPointSelected = null;
                        //Event for end of line-rendering in "ShinyThings"
                        CommunicationEvents.StopLineRendererEvent.Invoke(null);
                    }

                    //TODO: Hint that only a line can be drawn between already existing points
                }
                break;
            //If Left-Mouse-Button was pressed in CreateAngleMode
            case ToolMode.CreateAngleMode:
                break;
            //If Left-Mouse-Button was pressed in DeleteMode
            case ToolMode.DeleteMode:
                //Search for the Fact that was hit
                //If the hit GameObject was a Point/Line/Angle
                if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Point") || hit.transform.gameObject.layer == LayerMask.NameToLayer("Line") || hit.transform.gameObject.layer == LayerMask.NameToLayer("Angle")){
                    //Search for the suitable fact from the List
                    this.DeleteFact(Facts.Find(x => x.Id == hit.transform.GetComponent<FactObject>().Id));
                }
                break;
            //If Left-Mouse-Button was pressed in ExtraMode
            case ToolMode.ExtraMode:
                if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Point")) {
                    var menu = GameObject.Instantiate(SmartMenu);
                    menu.GetComponent<SmartMenu>().FactManager = this;
                    menu.GetComponent<Canvas>().worldCamera = Camera.main;
                    menu.transform.SetParent(hit.transform);
                    menu.transform.localPosition = Vector3.up - Camera.main.transform.forward;
                }
                else
                {
                    PointFact fact = AddPointFact(hit, GetFirstEmptyID());
                    CommunicationEvents.AddFactEvent.Invoke(fact);
                }
                break;

        }
    }
}