using UnityEngine;
using UnityEngine.Rendering;

using Unity.Cinemachine;

namespace Streamingle.StreamingleControl.Extensions
{
    /// <summary>
    /// Cinemachine Extension for controlling Beautify post-processing effects.
    /// This extension allows each Virtual Camera to have its own Beautify settings
    /// that will be applied when the camera becomes active, similar to CinemachineVolumeSettings.
    /// </summary>
    [ExecuteAlways]
    [AddComponentMenu("Cinemachine/Procedural/Extensions/Cinemachine Beautify Volume Settings")]
    [SaveDuringPlay]
    [DisallowMultipleComponent]
    public class CinemachineBeautifyVolumeSettings : CinemachineExtension
    {
        /// <summary>
        /// This is the priority for the Beautify volume. It's set to a high number 
        /// to ensure that it overrides other volumes for the active vcam.
        /// </summary>
        public static float s_VolumePriority = 1001f;

        /// <summary>
        /// The weight that the Beautify profile will have when the camera is fully active.
        /// It will blend to and from 0 along with the camera.
        /// </summary>
        [Tooltip("This weight will be applied to the Volume when this camera is active")]
        public float Weight = 1f;

        /// <summary>The reference object for focus tracking</summary>
        public enum FocusTrackingMode
        {
            /// <summary>No focus tracking</summary>
            None,
            /// <summary>Focus offset is relative to the LookAt target</summary>
            LookAtTarget,
            /// <summary>Focus offset is relative to the Follow target</summary>
            FollowTarget,
            /// <summary>Focus offset is relative to the Custom target set here</summary>
            CustomTarget,
            /// <summary>Focus offset is relative to the camera</summary>
            Camera
        }

        /// <summary>If DOF is enabled, will set the focus distance to be the distance 
        /// from the selected target to the camera. The Focus Offset field will then modify that distance</summary>
        [Tooltip("If DOF is enabled, will set the focus distance to be the distance from the selected target to the camera. The Focus Offset field will then modify that distance.")]
        public FocusTrackingMode FocusTracking = FocusTrackingMode.None;

        /// <summary>The target to use if Focus Tracks Target is set to Custom Target</summary>
        [Tooltip("The target to use if Focus Tracks Target is set to Custom Target")]
        public Transform FocusTarget;

        /// <summary>Offset from target distance, to be used with Focus Tracks Target. 
        /// Offsets the sharpest point away from the focus target</summary>
        [Tooltip("Offset from target distance, to be used with Focus Tracks Target. Offsets the sharpest point away from the focus target.")]
        public float FocusOffset = 0f;

        /// <summary>
        /// If Focus tracking is enabled, this will return the calculated focus distance
        /// </summary>
        public float CalculatedFocusDistance { get; private set; }

        /// <summary>
        /// This profile will be applied whenever this virtual camera is live.
        /// If null, a default profile will be created automatically.
        /// </summary>
        [Tooltip("This Beautify profile will be applied whenever this virtual camera is live")]
        public VolumeProfile Profile;

        [Header("DOF Settings")]
        [Tooltip("Enable or disable DOF effect")]
        public bool EnableDOF = false;

        [Tooltip("DOF distance value")]
        public float DOFDistance = 1f;

        [Tooltip("DOF focal length")]
        public float FocalLength = 0.3f;

        [Tooltip("DOF aperture value")]
        public float Aperture = 5.6f;

        [Tooltip("Enable bokeh effect")]
        public bool EnableBokeh = true;

        [Tooltip("Bokeh threshold")]
        public float BokehThreshold = 1f;

        [Tooltip("Bokeh intensity")]
        public float BokehIntensity = 2f;

        [Tooltip("Enable foreground blur")]
        public bool EnableForegroundBlur = true;

        [Tooltip("Foreground blur distance")]
        public float ForegroundDistance = 0.5f;

        // Private fields for profile management
        class VcamExtraState : VcamExtraStateBase
        {
            public VolumeProfile ProfileCopy;

            public void CreateProfileCopy(VolumeProfile source)
            {
                DestroyProfileCopy();
                if (source == null) return;
                
                VolumeProfile profile = ScriptableObject.CreateInstance<VolumeProfile>();
                profile.name = source.name + " (Copy)";
                
                try
                {
                    for (int i = 0; i < source.components.Count; ++i)
                    {
                        if (source.components[i] != null)
                        {
                            var itemCopy = UnityEngine.Object.Instantiate(source.components[i]);
                            if (itemCopy != null)
                            {
                                itemCopy.hideFlags = HideFlags.DontSave;
                                profile.components.Add(itemCopy);
                            }
                        }
                    }
                    profile.isDirty = true;
                    ProfileCopy = profile;
                }
                catch (System.Exception ex)
                {
                    UnityEngine.Debug.LogWarning($"[CinemachineBeautifyVolumeSettings] Failed to create profile copy: {ex.Message}");
                    if (profile != null)
                    {
                        if (Application.isPlaying)
                            UnityEngine.Object.Destroy(profile);
                        else
                            UnityEngine.Object.DestroyImmediate(profile);
                    }
                }
            }

            public void DestroyProfileCopy()
            {
                if (ProfileCopy != null)
                {
                    try
                    {
                        // First destroy all components to avoid reference issues
                        for (int i = ProfileCopy.components.Count - 1; i >= 0; i--)
                        {
                            if (ProfileCopy.components[i] != null)
                            {
                                if (Application.isPlaying)
                                    UnityEngine.Object.Destroy(ProfileCopy.components[i]);
                                else
                                    UnityEngine.Object.DestroyImmediate(ProfileCopy.components[i]);
                            }
                        }
                        ProfileCopy.components.Clear();
                        
                        // Then destroy the profile itself
                        if (Application.isPlaying)
                            UnityEngine.Object.Destroy(ProfileCopy);
                        else
                            UnityEngine.Object.DestroyImmediate(ProfileCopy);
                    }
                    catch (System.Exception ex)
                    {
                        UnityEngine.Debug.LogWarning($"[CinemachineBeautifyVolumeSettings] Error destroying profile copy: {ex.Message}");
                    }
                    finally
                    {
                        ProfileCopy = null;
                    }
                }
            }
        }

        private System.Type m_BeautifyType;
        private object m_BeautifyOverride;

        /// <summary>True if the profile is enabled and nontrivial</summary>
        public bool IsValid => Profile != null && Profile.components.Count > 0;

        protected override void Awake()
        {
            base.Awake();
            InitializeBeautify();
        }

        protected override void OnEnable()
        {
            InvalidateCachedProfile();
        }

        protected override void OnDestroy()
        {
            InvalidateCachedProfile();
            base.OnDestroy();
        }

        /// <summary>Called by the editor when the shared asset has been edited</summary>
        public void InvalidateCachedProfile()
        {
            var extraStateCache = new System.Collections.Generic.List<VcamExtraState>();
            GetAllExtraStates(extraStateCache);
            for (int i = 0; i < extraStateCache.Count; ++i)
                extraStateCache[i].DestroyProfileCopy();
        }

        protected override void PostPipelineStageCallback(
            CinemachineVirtualCameraBase vcam,
            CinemachineCore.Stage stage,
            ref CameraState state,
            float deltaTime)
        {
            // Set the focus after the camera has been fully positioned.
            if (stage == CinemachineCore.Stage.Finalize)
            {
                var extra = GetExtraState<VcamExtraState>(vcam);
                
                // Only apply volume settings if this camera is live (active)
                bool isLive = CinemachineCore.IsLive(vcam);
                
                if (!IsValid || !isLive)
                    extra.DestroyProfileCopy();
                else
                {
                    var profile = Profile;

                    // Handle Focus Tracking
                    if (FocusTracking == FocusTrackingMode.None)
                        extra.DestroyProfileCopy();
                    else
                    {
                        if (extra.ProfileCopy == null)
                            extra.CreateProfileCopy(Profile);
                        profile = extra.ProfileCopy;
                    }

                    // Calculate and apply focus distance for DOF (always if Focus Tracking is not None)
                    if (FocusTracking != FocusTrackingMode.None && profile != null)
                    {
                        var beautifyOverride = FindBeautifyOverride(profile);
                        if (beautifyOverride != null)
                        {
                            float focusDistance = FocusOffset;
                            if (FocusTracking == FocusTrackingMode.LookAtTarget)
                                focusDistance += (state.GetFinalPosition() - state.ReferenceLookAt).magnitude;
                            else
                            {
                                Transform focusTarget = null;
                                switch (FocusTracking)
                                {
                                    default: break;
                                    case FocusTrackingMode.FollowTarget: focusTarget = vcam.Follow; break;
                                    case FocusTrackingMode.CustomTarget: focusTarget = FocusTarget; break;
                                }
                                if (focusTarget != null)
                                    focusDistance += (state.GetFinalPosition() - focusTarget.position).magnitude;
                            }
                            CalculatedFocusDistance = focusDistance = Mathf.Max(0, focusDistance);
                            SetBeautifyParameter(beautifyOverride, "depthOfFieldDistance", focusDistance);
                            state.Lens.PhysicalProperties.FocusDistance = focusDistance;
                            profile.isDirty = true;
                        }
                    }

                    // Update all Beautify parameters in the profile
                    UpdateBeautifyParameters(profile);
                    
                    // Apply the post-processing
                    state.AddCustomBlendable(new CameraState.CustomBlendableItems.Item { Custom = profile, Weight = Weight });
                }
            }
        }

        private void InitializeBeautify()
        {
            // Find Beautify type
            m_BeautifyType = System.Type.GetType("Beautify.Universal.Beautify, Unity.RenderPipelines.Universal.Runtime");
            
            if (m_BeautifyType == null)
            {
                UnityEngine.Debug.LogError("[CinemachineBeautifyVolumeSettings] Beautify 타입을 찾을 수 없습니다. Beautify 에셋이 설치되어 있는지 확인하세요.");
                return;
            }

            UnityEngine.Debug.Log("[CinemachineBeautifyVolumeSettings] Beautify type found successfully");
        }

        private void UpdateBeautifyParameters(VolumeProfile profile)
        {
            if (profile == null || m_BeautifyType == null) return;

            // Find or create Beautify override in the profile
            object beautifyOverride = FindBeautifyOverride(profile);
            
            if (beautifyOverride != null)
            {
                // Update all Beautify parameters
                SetBeautifyParameter(beautifyOverride, "depthOfField", EnableDOF);
                
                // Use calculated focus distance if Focus Tracking is active, otherwise use DOFDistance
                float finalDOFDistance = (FocusTracking != FocusTrackingMode.None) ? CalculatedFocusDistance : DOFDistance;
                SetBeautifyParameter(beautifyOverride, "depthOfFieldDistance", finalDOFDistance);
                
                SetBeautifyParameter(beautifyOverride, "depthOfFieldFocalLength", FocalLength);
                SetBeautifyParameter(beautifyOverride, "depthOfFieldAperture", Aperture);
                SetBeautifyParameter(beautifyOverride, "depthOfFieldBokeh", EnableBokeh);
                SetBeautifyParameter(beautifyOverride, "depthOfFieldBokehThreshold", BokehThreshold);
                SetBeautifyParameter(beautifyOverride, "depthOfFieldBokehIntensity", BokehIntensity);
                SetBeautifyParameter(beautifyOverride, "depthOfFieldForegroundBlur", EnableForegroundBlur);
                SetBeautifyParameter(beautifyOverride, "depthOfFieldForegroundDistance", ForegroundDistance);
                
                profile.isDirty = true;
            }
        }

        private object FindBeautifyOverride(VolumeProfile profile)
        {
            if (profile == null || m_BeautifyType == null) return null;

            // Try to find existing Beautify override in the profile
            for (int i = 0; i < profile.components.Count; i++)
            {
                if (profile.components[i] != null && profile.components[i].GetType() == m_BeautifyType)
                {
                    return profile.components[i];
                }
            }

            return null;
        }





        private void SetBeautifyParameter(object beautifyOverride, string fieldName, object value)
        {
            if (beautifyOverride == null || m_BeautifyType == null) return;

            var field = m_BeautifyType.GetField(fieldName);
            if (field != null)
            {
                var fieldValue = field.GetValue(beautifyOverride);
                if (fieldValue != null)
                {
                    var valueProperty = fieldValue.GetType().GetProperty("value");
                    var overrideProperty = fieldValue.GetType().GetProperty("overrideState");

                    if (valueProperty != null && overrideProperty != null)
                    {
                        // Enable override state
                        overrideProperty.SetValue(fieldValue, true);
                        
                        // Set value with type checking
                        if (valueProperty.PropertyType == value.GetType() ||
                            (valueProperty.PropertyType == typeof(bool) && value is bool) ||
                            (valueProperty.PropertyType == typeof(float) && value is float))
                        {
                            valueProperty.SetValue(fieldValue, value);
                        }
                    }
                }
            }
        }


        void OnValidate()
        {
            Weight = Mathf.Max(0, Weight);
        }

        void Reset()
        {
            Weight = 1;
            FocusTracking = FocusTrackingMode.None;
            FocusTarget = null;
            FocusOffset = 0;
            Profile = null;
            EnableDOF = false;
            DOFDistance = 1f;
            FocalLength = 0.3f;
            Aperture = 5.6f;
            EnableBokeh = true;
            BokehThreshold = 1f;
            BokehIntensity = 2f;
            EnableForegroundBlur = true;
            ForegroundDistance = 0.5f;
        }
    }
}