using UnityEngine;

namespace AR51.HeadsetCalibration.Samples
{
    /// <summary>
    /// One checkbox for see-through. Passthrough on Quest needs three things at once: the runtime feature
    /// on <see cref="OVRManager"/>, an <see cref="OVRPassthroughLayer"/> composited under the scene, and the
    /// eye camera clearing to a transparent colour so the camera image shows through. This component owns
    /// all three and can be flipped at runtime (inspector or code). Over Link it also needs
    /// "Pass-through over Meta Horizon Link" enabled in the Link app's developer settings; standalone needs
    /// passthrough support in the Meta project config.
    /// </summary>
    public sealed class PassthroughToggle : MonoBehaviour
    {
        private OVRPassthroughLayer _layer;
        private Camera _camera;
        private bool _applied;
        private bool _appliedValue;
        private CameraClearFlags _originalClearFlags;
        private Color _originalBackground;
        private bool _hasOriginalCamera;

        [Tooltip("See the room through the headset cameras instead of the virtual floor.")]
        public bool Passthrough = false;

        [Tooltip("Objects to hide while passthrough is on (the virtual floor); shown again when it is off.")]
        public GameObject[] HideWhilePassthrough = new GameObject[0];

        [Tooltip("Passthrough image opacity, 0..1.")]
        [Range(0f, 1f)]
        public float Opacity = 1f;

        private void OnEnable()
        {
            _applied = false;
        }

        private void OnDisable()
        {
            if (_applied) Apply(false);
            _applied = false;
        }

        private void Update()
        {
            if (!_applied || _appliedValue != Passthrough) Apply(Passthrough);
            if (_layer != null && !Mathf.Approximately(_layer.textureOpacity, Opacity)) _layer.textureOpacity = Opacity;
        }

        /// <summary>Turns passthrough on or off now; the inspector checkbox follows.</summary>
        public void Set(bool passthrough)
        {
            Passthrough = passthrough;
            Apply(passthrough);
        }

        private void Apply(bool on)
        {
            if (!TryResolve()) return;

            OVRManager.instance.isInsightPassthroughEnabled = on;
            _layer.hidden = !on;
            _layer.textureOpacity = Opacity;

            if (on)
            {
                _camera.clearFlags = CameraClearFlags.SolidColor;
                _camera.backgroundColor = new Color(0f, 0f, 0f, 0f);
            }
            else if (_hasOriginalCamera)
            {
                _camera.clearFlags = _originalClearFlags;
                _camera.backgroundColor = _originalBackground;
            }

            foreach (var target in HideWhilePassthrough)
            {
                if (target != null) target.SetActive(!on);
            }

            _applied = true;
            _appliedValue = on;
            Debug.Log($"{nameof(PassthroughToggle)}: passthrough {(on ? "on" : "off")} (supported by runtime: {OVRManager.IsInsightPassthroughSupported()}).");
        }

        /// <summary>Finds the rig, the eye camera and the passthrough layer, creating the layer once.</summary>
        private bool TryResolve()
        {
            if (OVRManager.instance == null)
            {
                Debug.LogWarning($"{nameof(PassthroughToggle)}: no {nameof(OVRManager)} in the scene yet; waiting.");
                return false;
            }

            if (_camera == null)
            {
                _camera = Camera.main;
                if (_camera == null)
                {
                    Debug.LogWarning($"{nameof(PassthroughToggle)}: no camera tagged MainCamera; waiting.");
                    return false;
                }
                _originalClearFlags = _camera.clearFlags;
                _originalBackground = _camera.backgroundColor;
                _hasOriginalCamera = true;
            }

            if (_layer == null)
            {
                var rig = OVRManager.instance.gameObject;
                _layer = rig.GetComponent<OVRPassthroughLayer>();
                if (_layer == null)
                {
                    _layer = rig.AddComponent<OVRPassthroughLayer>();
                    _layer.projectionSurfaceType = OVRPassthroughLayer.ProjectionSurfaceType.Reconstructed;
                    _layer.overlayType = OVROverlay.OverlayType.Underlay;
                    _layer.compositionDepth = 0;
                }
            }

            return true;
        }
    }
}
