using TMPro;
using UnityEngine;

public class Billboard : MonoBehaviour
{
    private GameObject _parent;
    public string defaultText = "Sample Text";
    public TMP_Text tmpText;
    public Canvas canvas;
    public string prefix = "<mark=#000000BB>";
    private Camera _camera;

    public LineRenderer lineRenderer;
    public Transform lineStart;
    
    private GameObject _lineEndObject;
    private MeshFilter _lineEndMeshFilter;

    private void Start()
    {
        _camera = Camera.main;
        lineRenderer = GetComponent<LineRenderer>();
        _parent = transform.parent.gameObject;
        transform.SetParent(null);
        tmpText.text = defaultText;
    }

    private void LateUpdate()
    {
        if (canvas.enabled) transform.position = _parent.transform.position + (_camera.transform.up * 0.1f);
        if (!lineRenderer.enabled) return;
        
        lineRenderer.SetPosition(0, lineStart.position);
        lineRenderer.SetPosition(1, CalculateLineEndPosition());
    }

    private void SetText(string txt)
    {
        tmpText.text = prefix + txt;
    }


    private void ShowCanvas()
    {
        canvas.enabled = true;
        lineRenderer.enabled = true;
    }

    private void HideCanvas()
    {
        canvas.enabled = false;
        lineRenderer.enabled = false;
    }

    public void ShowHint(string txt)
    {
        ShowCanvas();
        SetText(txt);
    }

    public void HideHint()
    {
        HideCanvas();
    }

    public void SetLineTarget(GameObject newGM)
    {
        _lineEndObject = newGM;
        _lineEndMeshFilter = _lineEndObject.GetComponent<MeshFilter>();
    }

    private Vector3 CalculateLineEndPosition()
    {
        if (_lineEndObject == null)
        {
            lineRenderer.enabled = false;
            return Vector3.zero;
        }
        return _lineEndMeshFilter == null ? _lineEndObject.transform.position : FindClosestVertexToTargetPoint(lineStart.position, _lineEndMeshFilter);
    }

    private Vector3 FindClosestVertexToTargetPoint(Vector3 start, MeshFilter filter)
    {
        // Get the Mesh object from the MeshFilter component
        Mesh mesh = filter.mesh;
        // Get the world position of the point you want to find the closest point on the mesh to
        // Transform the point into local space relative to the MeshFilter's transform
        Vector3 localPoint = filter.transform.InverseTransformPoint(start);

        // Find the closest vertex to the point
        float closestDistance = Mathf.Infinity;
        Vector3 closestVertex = Vector3.zero;
        for (int i = 0; i < mesh.vertexCount; i++)
        {
            Vector3 vertex = mesh.vertices[i];
            float distance = Vector3.Distance(localPoint, vertex);
            if (distance < closestDistance)
            {
                closestDistance = distance;
                closestVertex = vertex;
            }
        }

        // Transform the closest vertex back into world space
        return filter.transform.TransformPoint(closestVertex);
    }
}