#if VISUAL_EFFECT_GRAPH_0_0_1_OR_NEWER
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace UnityEngine.VFX.Utility
{
    /// 
    /// Camera parameter binding helper class.
    /// 
    [VFXBinder("URP/URP Camera")]
    public class URPCameraBinder : VFXBinderBase
    {
        /// 
        /// Camera URP additional data.
        /// 
        public UniversalAdditionalCameraData AdditionalData;
        Camera m_Camera;
        [VFXPropertyBinding("UnityEditor.VFX.CameraType"), SerializeField]
        ExposedProperty CameraProperty = "Camera";
        ExposedProperty m_Position;
        ExposedProperty m_Angles;
        ExposedProperty m_Scale;
        ExposedProperty m_FieldOfView;
        ExposedProperty m_NearPlane;
        ExposedProperty m_FarPlane;
        ExposedProperty m_AspectRatio;
        ExposedProperty m_Dimensions;
        ExposedProperty m_ScaledDimensions;
        ExposedProperty m_DepthBuffer;
        ExposedProperty m_ColorBuffer;
        ExposedProperty m_Orthographic;
        ExposedProperty m_OrthographicSize;
        ExposedProperty m_LensShift;
        /// 
        /// Set a camera property.
        /// 
        /// Property name.
        public void SetCameraProperty(string name)
        {
            CameraProperty = name;
            UpdateSubProperties();
        }
        void UpdateSubProperties()
        {
            // Get Camera component from URP additional data
            if (AdditionalData != null)
            {
                m_Camera = AdditionalData.GetComponent();
            }
            // Update VFX Sub Properties
            m_Position = CameraProperty + "_transform_position";
            m_Angles = CameraProperty + "_transform_angles";
            m_Scale = CameraProperty + "_transform_scale";
            m_Orthographic = CameraProperty + "_orthographic";
            m_FieldOfView = CameraProperty + "_fieldOfView";
            m_NearPlane = CameraProperty + "_nearPlane";
            m_FarPlane = CameraProperty + "_farPlane";
            m_OrthographicSize = CameraProperty + "_orthographicSize";
            m_AspectRatio = CameraProperty + "_aspectRatio";
            m_Dimensions = CameraProperty + "_pixelDimensions";
            m_LensShift = CameraProperty + "_lensShift";
            m_DepthBuffer = CameraProperty + "_depthBuffer";
            m_ColorBuffer = CameraProperty + "_colorBuffer";
            m_ScaledDimensions = CameraProperty + "_scaledPixelDimensions";
        }
        static void RequestHistoryAccess(IPerFrameHistoryAccessTracker access)
        {
            access?.RequestAccess();
            access?.RequestAccess();
        }
        /// 
        /// OnEnable implementation.
        /// 
        protected override void OnEnable()
        {
            base.OnEnable();
            if (AdditionalData != null)
                AdditionalData.history.OnGatherHistoryRequests += RequestHistoryAccess;
            UpdateSubProperties();
        }
        /// 
        /// OnDisable implementation.
        /// 
        protected override void OnDisable()
        {
            base.OnDisable();
            if (AdditionalData != null)
                AdditionalData.history.OnGatherHistoryRequests -= RequestHistoryAccess;
        }
        private void OnValidate()
        {
            UpdateSubProperties();
            if (AdditionalData != null)
                AdditionalData.history.OnGatherHistoryRequests += RequestHistoryAccess;
        }
        /// 
        /// Returns true if the Visual Effect and the configuration of the binder are valid to perform the binding.
        /// 
        /// Component to be tested.
        /// True if the Visual Effect and the configuration of the binder are valid to perform the binding.
        public override bool IsValid(VisualEffect component)
        {
            return AdditionalData != null
                && m_Camera != null
                && component.HasVector3(m_Position)
                && component.HasVector3(m_Angles)
                && component.HasVector3(m_Scale)
                && component.HasBool(m_Orthographic)
                && component.HasFloat(m_FieldOfView)
                && component.HasFloat(m_NearPlane)
                && component.HasFloat(m_FarPlane)
                && component.HasFloat(m_OrthographicSize)
                && component.HasFloat(m_AspectRatio)
                && component.HasVector2(m_Dimensions)
                && component.HasVector2(m_LensShift)
                && component.HasTexture(m_DepthBuffer)
                && component.HasTexture(m_ColorBuffer)
                && component.HasVector2(m_ScaledDimensions);
        }
        /// 
        /// Update bindings for a visual effect.
        /// 
        /// Component to update.
        public override void UpdateBinding(VisualEffect component)
        {
            var asset = UniversalRenderPipeline.asset;
            if (AdditionalData == null || asset == null)
                return;
            var targetSpace = component.visualEffectAsset.GetExposedSpace(m_Position);
            Matrix4x4 readTransform;
            if (targetSpace == VFXSpace.Local)
            {
                readTransform = component.transform.worldToLocalMatrix * AdditionalData.transform.localToWorldMatrix;
            }
            else
            {
                readTransform = AdditionalData.transform.localToWorldMatrix;
            }
            component.SetVector3(m_Position, readTransform.GetPosition());
            component.SetVector3(m_Angles, readTransform.rotation.eulerAngles);
            component.SetVector3(m_Scale, readTransform.lossyScale);
            component.SetBool(m_Orthographic, m_Camera.orthographic);
            component.SetFloat(m_OrthographicSize, m_Camera.orthographicSize);
            // While field of View is set in degrees for the camera, it is expected in radians in VFX
            component.SetFloat(m_FieldOfView, Mathf.Deg2Rad * m_Camera.fieldOfView);
            component.SetFloat(m_NearPlane, m_Camera.nearClipPlane);
            component.SetFloat(m_FarPlane, m_Camera.farClipPlane);
            component.SetVector2(m_LensShift, m_Camera.lensShift);
            component.SetFloat(m_AspectRatio, m_Camera.aspect);
            var cameraSize = new Vector2(m_Camera.pixelWidth, m_Camera.pixelHeight);
            component.SetVector2(m_Dimensions, cameraSize);
            var scaledSize = new Vector2(m_Camera.scaledPixelWidth, m_Camera.scaledPixelHeight) * asset.renderScale;
            component.SetVector2(m_ScaledDimensions, scaledSize);
            var depth = AdditionalData.history.GetHistoryForRead()?.GetCurrentTexture();
            var color = AdditionalData.history.GetHistoryForRead()?.GetCurrentTexture();
            if (depth != null)
                component.SetTexture(m_DepthBuffer, depth);
            if (color != null)
                component.SetTexture(m_ColorBuffer, color);
        }
        /// 
        /// To string implementation.
        /// 
        /// String containing the binder information.
        public override string ToString()
        {
            return string.Format($"URP Camera : '{(AdditionalData == null ? "null" : AdditionalData.gameObject.name)}' -> {CameraProperty}");
        }
    }
}
#endif