using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

namespace Streamingle.Gaze
{
    /// <summary>
    /// Describes a camera/target gaze request on a Timeline track.
    /// The track mixer combines requests and submits the result to a
    /// <see cref="BlendshapeGazeDriver"/>; clips never write blendshapes directly.
    /// </summary>
    public sealed class BlendshapeGazeClip : PlayableAsset, ITimelineClipAsset
    {
        [SerializeField]
        [Tooltip("Optional world-space target. When empty, the Driver Target Camera is used before Camera.main.")]
        private ExposedReference<Transform> target;

        [SerializeField, Range(0f, 1f)]
        [Tooltip("Maximum gaze influence contributed by this clip.")]
        private float strength = 1f;

        [SerializeField]
        [Tooltip("Allow optional eyelid/corrective channels from the calibration profile.")]
        private bool enableCorrectives;

        [SerializeField]
        [Tooltip("Override the bound driver's camera-cut response while this clip is dominant.")]
        private bool overrideCutResponse;

        [SerializeField]
        private GazeCutResponse cutResponse;

        [SerializeField, Min(0f)]
        [Tooltip("Transition duration used by cut-response modes that smooth between cameras.")]
        private float cutTransitionDuration = 0.1f;

        public ExposedReference<Transform> Target
        {
            get => target;
            set => target = value;
        }

        public float Strength
        {
            get => strength;
            set => strength = SanitizeStrength(value);
        }

        public bool EnableCorrectives
        {
            get => enableCorrectives;
            set => enableCorrectives = value;
        }

        public bool OverrideCutResponse
        {
            get => overrideCutResponse;
            set => overrideCutResponse = value;
        }

        public GazeCutResponse CutResponse
        {
            get => cutResponse;
            set => cutResponse = value;
        }

        public float CutTransitionDuration
        {
            get => cutTransitionDuration;
            set => cutTransitionDuration = SanitizeDuration(value);
        }

        public ClipCaps clipCaps => ClipCaps.Blending;

        public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
        {
            var playable = ScriptPlayable<BlendshapeGazeBehaviour>.Create(graph);
            var behaviour = playable.GetBehaviour();

            behaviour.Target = target.Resolve(graph.GetResolver());
            behaviour.Strength = SanitizeStrength(strength);
            behaviour.EnableCorrectives = enableCorrectives;
            behaviour.OverrideCutResponse = overrideCutResponse;
            behaviour.CutResponse = cutResponse;
            behaviour.CutTransitionDuration = SanitizeDuration(cutTransitionDuration);

            return playable;
        }

        private static float SanitizeStrength(float value)
        {
            return float.IsNaN(value) || float.IsInfinity(value)
                ? 0f
                : Mathf.Clamp01(value);
        }

        private static float SanitizeDuration(float value)
        {
            return float.IsNaN(value) || float.IsInfinity(value)
                ? 0f
                : Mathf.Max(0f, value);
        }
    }
}
