using UnityEngine;

[RequireComponent(typeof(LineRenderer))]
public class CircleLineRenderer : MonoBehaviour
{

    private const float DOTTED_LINE_TICKNESS_RATIO = 2.5f;
    private const int NUM_CAP_VERTICES = 6;

    [Tooltip("The radius of the circle perimeter")]
    public float radius = 2;

    [Tooltip("The width of the line for the circle perimeter")]
    public float lineWidth = 0.05f;

    [Tooltip("Check this to use the dotted line material for the circle perimeter line")]
    public bool isDotted = false;

    [Tooltip("The material for the plain line")]
    public Material plainMaterial;

    [Tooltip("The material for the dotted line")]
    public Material dottedMaterial;

    private LineRenderer lineRenderer;

    void Start()
    {
        lineRenderer = GetComponent<LineRenderer>();
        lineRenderer.useWorldSpace = false;
        lineRenderer.numCapVertices = NUM_CAP_VERTICES;
        SetRadius(radius);
    }

    void Update()
    {
        //While testing in-editor, refresh the circle each frame so we can test the circle by changing the fields in the inspector.
        if (Application.isEditor) SetRadius(radius);
    }

    //Call this method from other scripts to adjust the radius at runtime
    public void SetRadius(float pRadius)
    {
        radius = pRadius;

        if (radius <= 0.1f)
        {
            lineRenderer.positionCount = 0;
            return;
        }

        float tickness = lineWidth;
        if (isDotted) tickness *= DOTTED_LINE_TICKNESS_RATIO;
        lineRenderer.startWidth = tickness;
        lineRenderer.endWidth = tickness;

        //Calculate the number of vertices needed depending on radius so it always looks round.
        //For instance, huge circles need proportionaly less vertices than smaller ones to look good.
        //Source : http://stackoverflow.com/questions/11774038/how-to-render-a-circle-with-as-few-vertices-as-possible
        float e = 0.01f; //constant ratio to adjust, reduce this value for more vertices
        float th = Mathf.Acos(2 * Mathf.Pow(1 - e / radius, 2) - 1); //th is in radian
        int numberOfVertices = Mathf.CeilToInt(2 * Mathf.PI / th);

        lineRenderer.positionCount = numberOfVertices + 1;
        for (int i = 0; i < numberOfVertices + 1; i++)
        {
            float angle = (360f / (float)numberOfVertices) * (float)i;
            lineRenderer.SetPosition(i, radius * new Vector3(Mathf.Cos(Mathf.Deg2Rad * angle), Mathf.Sin(Mathf.Deg2Rad * angle), 0));
        }

        //Update material depending if its a dotted line or a plain one.
        if (isDotted)
        {
            lineRenderer.material = dottedMaterial;
            lineRenderer.materials[0].mainTextureScale = new Vector3(2 * Mathf.PI * radius * (1 / tickness), 1, 1);
        }
        else
        {
            lineRenderer.material = plainMaterial;
            lineRenderer.materials[0].mainTextureScale = Vector3.one;
        }
    }

    //Call this method from other scripts to adjust the width of the line at runtime
    public void SetWidth(float pWidth)
    {
        lineWidth = pWidth;
        SetRadius(radius);
    }

    //Call this method from other scripts to switch between plain and dotted line at runtime
    public void SetIsDotted(bool pIsDotted)
    {
        if (isDotted != pIsDotted)
        {
            isDotted = pIsDotted;
            SetRadius(radius);
        }
    }
}