using System;
using UnityEngine;

namespace pdinklag
{
    /// <summary>
    /// Helper for 2D fields of view.
    /// 
    /// Often, we are in the situation where we have a base direction B and a test direction A,
    /// and we want to make sure the A is within a field of view around B on the plane that they span.
    /// 
    /// This helper struct defines such a field of view by an FOV angle.
    /// The test can then be performed by comparing A.normalized dot B.normalized against the dot threshold.
    /// The dot threshold is also serialized, so it doesn't have to be computed on the fly.
    /// 
    /// However, because of this, changing a threshold's FOV angle at runtime will invalidate the dot threshold value and is thus not supported.
    /// If you need a different value, construct a new threshold instead.
    /// </summary>
    [Serializable]
    public struct FieldOfView2D
    {
        private const float Deg2RadHalf = 0.5f * Mathf.Deg2Rad;

        /// <summary>
        /// Computes the dot threshold for the given FOV angle.
        /// </summary>
        /// <param name="fov">the FOV angle in degrees</param>
        /// <returns></returns>
        public static float ComputeDotThreshold(float fov) => Mathf.Cos(Deg2RadHalf * fov);

        /// <summary>
        /// Constructs a 2D FOV from a given dot threshold.
        /// </summary>
        /// <param name="dotThreshold">the dot threshold</param>
        /// <returns></returns>
        public static FieldOfView2D FromDotThreshold(float dotThreshold) => 2.0f * Mathf.Rad2Deg * Mathf.Acos(dotThreshold);

        public static implicit operator FieldOfView2D(float fov) => new FieldOfView2D(fov);

        [Tooltip("The field of view in degrees.")]
        public float Angle;

        [HideInInspector]
        public float DotThreshold;

        /// <summary>
        /// Constructs a 2D FOV.
        /// </summary>
        /// <param name="fov">the FOV angle in degrees</param>
        public FieldOfView2D(float fov)
        {
            Angle = fov;
            DotThreshold = ComputeDotThreshold(fov);
        }
    }
}
