//--------------------------------------------------------------------------------------------------------------------------------
// Cartoon FX
// (c) 2012-2025 Jean Moreno
//--------------------------------------------------------------------------------------------------------------------------------


//--------------------------------------------------------------------------------------------------------------------------------

// Use the defines below to globally disable features:

// #define DISABLE_CAMERA_SHAKE
// #define DISABLE_LIGHTS
// #define DISABLE_LIGHTS_LINEAR_REMAPPING
// #define DISABLE_CLEAR_BEHAVIOR

//--------------------------------------------------------------------------------------------------------------------------------

using UnityEngine;
using UnityEngine.Rendering;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace CartoonFX
{
	[RequireComponent(typeof(ParticleSystem))]
	[DisallowMultipleComponent]
	public partial class CFXR_Effect : MonoBehaviour
	{
		// Change this value to easily tune the camera shake strength for all effects
		const float GLOBAL_CAMERA_SHAKE_MULTIPLIER = 1.0f;

		static readonly int _GameObjectWorldPosition = Shader.PropertyToID("_GameObjectWorldPosition");

#if UNITY_EDITOR
		[InitializeOnLoadMethod]
		static void InitGlobalOptions()
		{
			AnimatedLight.editorPreview = EditorPrefs.GetBool("CFXR Light EditorPreview", true);
	#if !DISABLE_CAMERA_SHAKE
			CameraShake.editorPreview = EditorPrefs.GetBool("CFXR CameraShake EditorPreview", true);
	#endif
		}
#endif

		public enum ClearBehavior
		{
			None,
			Disable,
			Destroy
		}

		[System.Serializable]
		public class AnimatedLight
		{
			static public bool editorPreview = true;

			public Light light;

			public bool loop;

			public bool animateIntensity;
			public float intensityStart = 8f;
			public float intensityEnd = 0f;
			public float intensityDuration = 0.5f;
			public AnimationCurve intensityCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f);
			public bool perlinIntensity;
			public float perlinIntensitySpeed = 1f;
			public bool fadeIn;
			public float fadeInDuration = 0.5f;
			public bool fadeOut;
			public float fadeOutDuration = 0.5f;

			public bool animateRange;
			public float rangeStart = 8f;
			public float rangeEnd = 0f;
			public float rangeDuration = 0.5f;
			public AnimationCurve rangeCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f);
			public bool perlinRange;
			public float perlinRangeSpeed = 1f;

			public bool animateColor;
			public Gradient colorGradient;
			public float colorDuration = 0.5f;
			public AnimationCurve colorCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 0f);
			public bool perlinColor;
			public float perlinColorSpeed = 1f;

			public void animate(float time)
			{
#if UNITY_EDITOR
				if (!editorPreview && !EditorApplication.isPlaying)
				{
					return;
				}
#endif

				if (light != null)
				{
					if (animateIntensity)
					{
						float delta = loop ? Mathf.Clamp01((time % intensityDuration)/intensityDuration) : Mathf.Clamp01(time/intensityDuration);
						delta = perlinIntensity ? Mathf.PerlinNoise(Time.time * perlinIntensitySpeed, 0f) : intensityCurve.Evaluate(delta);
						light.intensity = Mathf.LerpUnclamped(intensityEnd, intensityStart, delta);

						if (fadeIn && time < fadeInDuration)
						{
							light.intensity *= Mathf.Clamp01(time / fadeInDuration);
						}
					}

					if (animateRange)
					{
						float delta = loop ? Mathf.Clamp01((time % rangeDuration)/rangeDuration) : Mathf.Clamp01(time/rangeDuration);
						delta = perlinRange ? Mathf.PerlinNoise(Time.time * perlinRangeSpeed, 10f) : rangeCurve.Evaluate(delta);
						light.range = Mathf.LerpUnclamped(rangeEnd, rangeStart, delta);
					}

					if (animateColor)
					{
						float delta = loop ? Mathf.Clamp01((time % colorDuration)/colorDuration) : Mathf.Clamp01(time/colorDuration);
						delta = perlinColor ? Mathf.PerlinNoise(Time.time * perlinColorSpeed, 0f) : colorCurve.Evaluate(delta);
						light.color = colorGradient.Evaluate(delta);
					}
				}
			}

			public void animateFadeOut(float time)
			{
				if (fadeOut && light != null)
				{
					light.intensity *= 1.0f - Mathf.Clamp01(time / fadeOutDuration);
				}
			}

			public void reset()
			{
				if (light != null)
				{
					if (animateIntensity)
					{
						light.intensity = (fadeIn || fadeOut) ? 0 : intensityEnd;
					}

					if (animateRange)
					{
						light.range = rangeEnd;
					}

					if (animateColor)
					{
						light.color = colorGradient.Evaluate(1f);
					}
				}
			}

			#region Animated Light Property Drawer
#if UNITY_EDITOR
			[CustomPropertyDrawer(typeof(AnimatedLight))]
			public class AnimatedLightDrawer : PropertyDrawer
			{
				SerializedProperty light;

				SerializedProperty loop;

				SerializedProperty animateIntensity;
				SerializedProperty intensityStart;
				SerializedProperty intensityEnd;
				SerializedProperty intensityDuration;
				SerializedProperty intensityCurve;
				SerializedProperty perlinIntensity;
				SerializedProperty perlinIntensitySpeed;
				SerializedProperty fadeIn;
				SerializedProperty fadeInDuration;
				SerializedProperty fadeOut;
				SerializedProperty fadeOutDuration;

				SerializedProperty animateRange;
				SerializedProperty rangeStart;
				SerializedProperty rangeEnd;
				SerializedProperty rangeDuration;
				SerializedProperty rangeCurve;
				SerializedProperty perlinRange;
				SerializedProperty perlinRangeSpeed;

				SerializedProperty animateColor;
				SerializedProperty colorGradient;
				SerializedProperty colorDuration;
				SerializedProperty colorCurve;
				SerializedProperty perlinColor;
				SerializedProperty perlinColorSpeed;

				void fetchProperties(SerializedProperty property)
				{
					light                 = property.FindPropertyRelative("light");

					loop                  = property.FindPropertyRelative("loop");

					animateIntensity      = property.FindPropertyRelative("animateIntensity");
					intensityStart        = property.FindPropertyRelative("intensityStart");
					intensityEnd          = property.FindPropertyRelative("intensityEnd");
					intensityDuration     = property.FindPropertyRelative("intensityDuration");
					intensityCurve        = property.FindPropertyRelative("intensityCurve");
					perlinIntensity       = property.FindPropertyRelative("perlinIntensity");
					perlinIntensitySpeed  = property.FindPropertyRelative("perlinIntensitySpeed");
					fadeIn                = property.FindPropertyRelative("fadeIn");
					fadeInDuration        = property.FindPropertyRelative("fadeInDuration");
					fadeOut               = property.FindPropertyRelative("fadeOut");
					fadeOutDuration       = property.FindPropertyRelative("fadeOutDuration");

					animateRange          = property.FindPropertyRelative("animateRange");
					rangeStart            = property.FindPropertyRelative("rangeStart");
					rangeEnd              = property.FindPropertyRelative("rangeEnd");
					rangeDuration         = property.FindPropertyRelative("rangeDuration");
					rangeCurve            = property.FindPropertyRelative("rangeCurve");
					perlinRange           = property.FindPropertyRelative("perlinRange");
					perlinRangeSpeed      = property.FindPropertyRelative("perlinRangeSpeed");

					animateColor          = property.FindPropertyRelative("animateColor");
					colorGradient         = property.FindPropertyRelative("colorGradient");
					colorDuration         = property.FindPropertyRelative("colorDuration");
					colorCurve            = property.FindPropertyRelative("colorCurve");
					perlinColor           = property.FindPropertyRelative("perlinColor");
					perlinColorSpeed      = property.FindPropertyRelative("perlinColorSpeed");
				}

				static GUIContent[] ModePopupLabels = new GUIContent[] { new GUIContent("Curve"), new GUIContent("Perlin Noise") };
				static GUIContent IntensityModeLabel = new GUIContent("Intensity Mode");
				static GUIContent RangeModeLabel = new GUIContent("Range Mode");
				static GUIContent ColorModeLabel = new GUIContent("Color Mode");

				const float INDENT_WIDTH = 15f;
				const float PADDING = 4f;

				void startIndent(ref Rect rect)
				{
					EditorGUIUtility.labelWidth -= INDENT_WIDTH;
					rect.xMin += INDENT_WIDTH;
				}

				void endIndent(ref Rect rect)
				{
					EditorGUIUtility.labelWidth += INDENT_WIDTH;
					rect.xMin -= INDENT_WIDTH;
				}

				public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
				{
					fetchProperties(property);

					Rect rect = EditorGUI.IndentedRect(position);

					//Rect lineRect = rect;
					//lineRect.height = 1;
					//lineRect.y -= 2;
					//EditorGUI.DrawRect(lineRect, Color.gray);

					if (Event.current.type == EventType.Repaint)
					{
						EditorStyles.helpBox.Draw(rect, GUIContent.none, 0);
					}

					EditorGUIUtility.labelWidth -= INDENT_WIDTH;

					rect.height = EditorGUIUtility.singleLineHeight;
					rect.xMax -= PADDING;
					rect.y += PADDING;
					float propSpace = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;

					EditorGUI.PropertyField(rect, light); rect.y += propSpace;
					EditorGUI.PropertyField(rect, loop); rect.y += propSpace;

					EditorGUI.PropertyField(rect, animateIntensity); rect.y += propSpace;
					if (animateIntensity.boolValue)
					{
						startIndent(ref rect);
						{

							EditorGUI.PropertyField(rect, intensityStart); rect.y += propSpace;
							EditorGUI.PropertyField(rect, intensityEnd); rect.y += propSpace;

							int val = EditorGUI.Popup(rect, IntensityModeLabel, perlinIntensity.boolValue ? 1 : 0, ModePopupLabels); rect.y += propSpace;
							if (val == 1 && !perlinIntensity.boolValue)
							{
								perlinIntensity.boolValue = true;
							}
							else if (val == 0 && perlinIntensity.boolValue)
							{
								perlinIntensity.boolValue = false;
							}

							startIndent(ref rect);
							{
								if (perlinIntensity.boolValue)
								{
									EditorGUI.PropertyField(rect, perlinIntensitySpeed); rect.y += propSpace;
								}
								else
								{
									EditorGUI.PropertyField(rect, intensityDuration); rect.y += propSpace;
									EditorGUI.PropertyField(rect, intensityCurve); rect.y += propSpace;
								}
							}
							endIndent(ref rect);

							EditorGUI.PropertyField(rect, fadeIn); rect.y += propSpace;
							if (fadeIn.boolValue)
							{
								startIndent(ref rect);
								EditorGUI.PropertyField(rect, fadeInDuration); rect.y += propSpace;
								endIndent(ref rect);
							}

							EditorGUI.PropertyField(rect, fadeOut); rect.y += propSpace;
							if (fadeOut.boolValue)
							{
								startIndent(ref rect);
								EditorGUI.PropertyField(rect, fadeOutDuration); rect.y += propSpace;
								endIndent(ref rect);
							}

						}
						endIndent(ref rect);
					}

					EditorGUI.PropertyField(rect, animateRange); rect.y += propSpace;
					if (animateRange.boolValue)
					{
						startIndent(ref rect);
						{
							EditorGUI.PropertyField(rect, rangeStart); rect.y += propSpace;
							EditorGUI.PropertyField(rect, rangeEnd); rect.y += propSpace;

							int val = EditorGUI.Popup(rect, RangeModeLabel, perlinRange.boolValue ? 1 : 0, ModePopupLabels); rect.y += propSpace;
							if (val == 1 && !perlinRange.boolValue)
							{
								perlinRange.boolValue = true;
							}
							else if (val == 0 && perlinRange.boolValue)
							{
								perlinRange.boolValue = false;
							}

							startIndent(ref rect);
							{
								if (perlinRange.boolValue)
								{
									EditorGUI.PropertyField(rect, perlinRangeSpeed); rect.y += propSpace;
								}
								else
								{
									EditorGUI.PropertyField(rect, rangeDuration); rect.y += propSpace;
									EditorGUI.PropertyField(rect, rangeCurve); rect.y += propSpace;
								}
							}
							endIndent(ref rect);
						}
						endIndent(ref rect);
					}

					EditorGUI.PropertyField(rect, animateColor); rect.y += propSpace;
					if (animateColor.boolValue)
					{
						startIndent(ref rect);
						{

							EditorGUI.PropertyField(rect, colorGradient); rect.y += propSpace;

							int val = EditorGUI.Popup(rect, ColorModeLabel, perlinColor.boolValue ? 1 : 0, ModePopupLabels); rect.y += propSpace;
							if (val == 1 && !perlinColor.boolValue)
							{
								perlinColor.boolValue = true;
							}
							else if (val == 0 && perlinColor.boolValue)
							{
								perlinColor.boolValue = false;
							}

							startIndent(ref rect);
							{

								if (perlinColor.boolValue)
								{
									EditorGUI.PropertyField(rect, perlinColorSpeed); rect.y += propSpace;
								}
								else
								{
									EditorGUI.PropertyField(rect, colorDuration); rect.y += propSpace;
									EditorGUI.PropertyField(rect, colorCurve); rect.y += propSpace;
								}
							}
							endIndent(ref rect);
						}
						endIndent(ref rect);
					}

					EditorGUIUtility.labelWidth += INDENT_WIDTH;

					if (GUI.changed)
					{
						property.serializedObject.ApplyModifiedProperties();
					}
				}

				public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
				{
					fetchProperties(property);

					float propSpace = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
					int count = 5;

					if (animateIntensity.boolValue)
					{
						count += 3;
						count += perlinIntensity.boolValue ? 1 : 2;
						count += 1;
						count += fadeIn.boolValue ? 1 : 0;
						count += 1;
						count += fadeOut.boolValue ? 1 : 0;
					}

					if (animateRange.boolValue)
					{
						count += 3;
						count += perlinRange.boolValue ? 1 : 2;
					}

					if (animateColor.boolValue)
					{
						count += 2;
						count += perlinColor.boolValue ? 1 : 2;
					}

					return count * propSpace + PADDING * 2;
				}
			}
#endif
			#endregion

		}

		// ================================================================================================================================

		// Globally disable features
		public static bool GlobalDisableCameraShake;
		public static bool GlobalDisableLights;

		// ================================================================================================================================

		[Tooltip("Defines an action to execute when the Particle System has completely finished playing and emitting particles.")]
		public ClearBehavior clearBehavior = ClearBehavior.Destroy;
		[Space]
		public CameraShake cameraShake;
		[Space]
		public AnimatedLight[] animatedLights;
		[Tooltip("Defines which Particle System to track to trigger light fading out.\nLeave empty if not using fading out.")]
		public ParticleSystem fadeOutReference;

#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
		float time;
#endif
		ParticleSystem rootParticleSystem;
		[System.NonSerialized] MaterialPropertyBlock materialPropertyBlock;
		[System.NonSerialized] Renderer particleRenderer;

		// ================================================================================================================================

		public void ResetState()
		{
#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
			time = 0f;
#endif

#if !DISABLE_LIGHTS
			fadingOutStartTime = 0f;
			isFadingOut = false;
			if (animatedLights != null)
			{
				foreach (var animLight in animatedLights)
				{
					animLight.reset();
				}
			}
#endif

#if !DISABLE_CAMERA_SHAKE
			if (cameraShake != null && cameraShake.enabled)
			{
				cameraShake.StopShake();
			}
#endif
		}

#if !DISABLE_CAMERA_SHAKE || !DISABLE_CLEAR_BEHAVIOR
		void Awake()
		{
	#if !DISABLE_CAMERA_SHAKE
			if (cameraShake != null && cameraShake.enabled)
			{
				cameraShake.fetchCameras();
			}
	#endif
	#if !DISABLE_CLEAR_BEHAVIOR
			startFrameOffset = GlobalStartFrameOffset++;
	#endif
			// Detect if world position needs to be passed to the shader
			particleRenderer = this.GetComponent<ParticleSystemRenderer>();
			if (particleRenderer.sharedMaterial != null && particleRenderer.sharedMaterial.IsKeywordEnabled("_CFXR_LIGHTING_WPOS_OFFSET"))
			{
				materialPropertyBlock = new MaterialPropertyBlock();
			}

	#if !DISABLE_LIGHTS && !DISABLE_LIGHTS_LINEAR_REMAPPING
			// Lights were calibrated using the built-in rendering pipeline, but
			// when using URP their intensities are transmitted using linear values.
			// This internal Unity flag defines that behavior, so we compensate here
			// such that original values remain.
			if (!GraphicsSettings.lightsUseLinearIntensity && animatedLights != null)
			{
				foreach (var animLight in animatedLights)
				{
					animLight.intensityStart = Mathf.LinearToGammaSpace(animLight.intensityStart);
					animLight.intensityEnd = Mathf.LinearToGammaSpace(animLight.intensityEnd);
				}
			}
	#endif
		}
#endif

		void OnEnable()
		{
			foreach (var animLight in animatedLights)
			{
				if (animLight.light != null)
				{
#if !DISABLE_LIGHTS
					animLight.light.enabled = !GlobalDisableLights;
#else
					animLight.light.enabled = false;
#endif
				}
			}
		}

		void OnDisable()
		{
			ResetState();
		}

#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE || !DISABLE_CLEAR_BEHAVIOR
		const int CHECK_EVERY_N_FRAME = 20;
#if !DISABLE_CLEAR_BEHAVIOR
		static int GlobalStartFrameOffset;
#endif
		int startFrameOffset;
		void Update()
		{
#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
			time += Time.deltaTime;

			Animate(time);

#if !DISABLE_LIGHTS
			if (fadeOutReference != null && !fadeOutReference.isEmitting && (fadeOutReference.isPlaying || isFadingOut))
			{
				FadeOut(time);
			}
#endif
#endif
#if !DISABLE_CLEAR_BEHAVIOR
			if (clearBehavior != ClearBehavior.None)
			{
				if (rootParticleSystem == null)
				{
					rootParticleSystem = this.GetComponent<ParticleSystem>();
				}

				// Check isAlive every N frame, with an offset so that all active effects aren't checked at once
				if ((Time.renderedFrameCount + startFrameOffset) % CHECK_EVERY_N_FRAME == 0)
				{
					if (!rootParticleSystem.IsAlive(true))
					{
						if (clearBehavior == ClearBehavior.Destroy)
						{
							GameObject.Destroy(this.gameObject);
						}
						else
						{
							this.gameObject.SetActive(false);
						}
					}
				}
			}
#endif
			if (materialPropertyBlock != null)
			{
				particleRenderer.GetPropertyBlock(materialPropertyBlock);
				materialPropertyBlock.SetVector(_GameObjectWorldPosition, this.transform.position);
				particleRenderer.SetPropertyBlock(materialPropertyBlock);
			}
		}
#endif

#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
		public void Animate(float _time)
		{
#if !DISABLE_LIGHTS
			if (animatedLights != null && !GlobalDisableLights)
			{
				foreach (var animLight in animatedLights)
				{
					animLight.animate(_time);
				}
			}
#endif

#if !DISABLE_CAMERA_SHAKE
			if (cameraShake != null && cameraShake.enabled && !GlobalDisableCameraShake)
			{
#if UNITY_EDITOR
				if (!cameraShake.isShaking)
				{
					cameraShake.fetchCameras();
				}
#endif
				cameraShake.animate(_time);
			}
#endif
		}
#endif

#if !DISABLE_LIGHTS
		bool isFadingOut;
		float fadingOutStartTime;
		public void FadeOut(float _time)
		{
			if (animatedLights == null)
			{
				return;
			}

			if (!isFadingOut)
			{
				isFadingOut = true;
				fadingOutStartTime = _time;
			}

			foreach (var animLight in animatedLights)
			{
				animLight.animateFadeOut(_time - fadingOutStartTime);
			}
		}
#endif

#if UNITY_EDITOR
		// Editor preview
		// Detect when the Particle System is previewing and trigger this animation too

		[System.NonSerialized] ParticleSystem _parentParticle;
		ParticleSystem parentParticle
		{
			get
			{
				if (_parentParticle == null)
				{
					_parentParticle = this.GetComponent<ParticleSystem>();
				}
				return _parentParticle;
			}
		}
		[System.NonSerialized] public bool editorUpdateRegistered;

		[System.NonSerialized] bool particleWasStopped;
		[System.NonSerialized] float particleTime;
#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
		[System.NonSerialized] float particleTimeUnwrapped;
#endif

		void OnDestroy()
		{
			UnregisterEditorUpdate();
		}

		public void RegisterEditorUpdate()
		{
			var type = PrefabUtility.GetPrefabAssetType(this.gameObject);
			var status = PrefabUtility.GetPrefabInstanceStatus(this.gameObject);

			// Prefab in Project window
			if (type is PrefabAssetType.Regular or PrefabAssetType.Variant && status == PrefabInstanceStatus.NotAPrefab)
			{
				return;
			}

			if (!editorUpdateRegistered)
			{
				EditorApplication.update += onEditorUpdate;
				editorUpdateRegistered = true;
			}
		}

		public void UnregisterEditorUpdate()
		{
			if (editorUpdateRegistered)
			{
				editorUpdateRegistered = false;
				EditorApplication.update -= onEditorUpdate;
			}
			ResetState();
		}

		void onEditorUpdate()
		{
			if (EditorApplication.isPlayingOrWillChangePlaymode)
			{
				return;
			}

			if (this == null)
			{
				return;
			}

			var rend = this.GetComponent<ParticleSystemRenderer>();
			if (rend.sharedMaterial != null && rend.sharedMaterial.IsKeywordEnabled("_CFXR_LIGHTING_WPOS_OFFSET"))
			{
				if (materialPropertyBlock == null)
				{
					materialPropertyBlock = new MaterialPropertyBlock();
				}

				rend.GetPropertyBlock(materialPropertyBlock);
				materialPropertyBlock.SetVector(_GameObjectWorldPosition, this.transform.position);
				rend.SetPropertyBlock(materialPropertyBlock);
			}

			// Need to track unwrapped time when playing back from Editor
			// because the parentParticle.time will be reset at each loop
			float delta = parentParticle.time - particleTime;

			if (delta < 0 && parentParticle.isPlaying)
			{
				delta = parentParticle.main.duration + delta;
				if (delta > 0.1 || delta < 0)
				{
					// try to detect when "Restart" is pressed
					ResetState();
#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
					particleTimeUnwrapped = 0;
#endif
					delta = 0;
				}
			}
#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
			particleTimeUnwrapped += delta;
#endif

			if (particleTime != parentParticle.time)
			{
#if !DISABLE_CAMERA_SHAKE
				if (cameraShake != null && cameraShake.enabled && parentParticle.time < particleTime && parentParticle.time < 0.05f)
				{
					cameraShake.StartShake();
				}
#endif
#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
				Animate(particleTimeUnwrapped);

#if !DISABLE_LIGHTS
				if (!parentParticle.isEmitting)
				{
					FadeOut(particleTimeUnwrapped);
				}
#endif
#endif
			}

			if (particleWasStopped != parentParticle.isStopped)
			{
				if (parentParticle.isStopped)
				{
					ResetState();
				}
#if !DISABLE_LIGHTS || !DISABLE_CAMERA_SHAKE
				particleTimeUnwrapped = 0;
#endif
			}

			particleWasStopped = parentParticle.isStopped;
			particleTime = parentParticle.time;
		}
#endif
	}

#if UNITY_EDITOR
	[CustomEditor(typeof(CFXR_Effect))]
	[CanEditMultipleObjects]
	public class CFXR_Effect_Editor : Editor
	{
		bool? lightEditorPreview;
		bool? shakeEditorPreview;

		GUIStyle _PaddedRoundedRect;
		GUIStyle PaddedRoundedRect
		{
			get
			{
				if (_PaddedRoundedRect == null)
				{
					_PaddedRoundedRect = new GUIStyle(EditorStyles.helpBox);
					_PaddedRoundedRect.padding = new RectOffset(4, 4, 4, 4);
				}
				return _PaddedRoundedRect;
			}
		}

		public override void OnInspectorGUI()
		{
			GlobalOptionsGUI();

#if DISABLE_CAMERA_SHAKE
			EditorGUILayout.HelpBox("Camera Shake has been globally disabled in the code.\nThe properties remain to avoid data loss but the shaking won't be applied for any effect.", MessageType.Info);
#endif

			base.OnInspectorGUI();
		}

		void GlobalOptionsGUI()
		{
			EditorGUILayout.BeginVertical(PaddedRoundedRect);
			{
				GUILayout.Label("Editor Preview:", EditorStyles.boldLabel);

				if (lightEditorPreview == null)
				{
					lightEditorPreview = EditorPrefs.GetBool("CFXR Light EditorPreview", true);
				}
				bool lightPreview = EditorGUILayout.Toggle("Light Animations", lightEditorPreview.Value);
				if (lightPreview != lightEditorPreview.Value)
				{
					lightEditorPreview = lightPreview;
					EditorPrefs.SetBool("CFXR Light EditorPreview", lightPreview);
					CFXR_Effect.AnimatedLight.editorPreview = lightPreview;
				}

#if !DISABLE_CAMERA_SHAKE
				if (shakeEditorPreview == null)
				{
					shakeEditorPreview = EditorPrefs.GetBool("CFXR CameraShake EditorPreview", true);
				}
				bool shakePreview = EditorGUILayout.Toggle("Camera Shake", shakeEditorPreview.Value);
				if (shakePreview != shakeEditorPreview.Value)
				{
					shakeEditorPreview = shakePreview;
					EditorPrefs.SetBool("CFXR CameraShake EditorPreview", shakePreview);
					CFXR_Effect.CameraShake.editorPreview = shakePreview;
				}
#endif
			}
			EditorGUILayout.EndVertical();
		}

		void OnEnable()
		{
			if (this.targets == null)
			{
				return;
			}

			foreach (var t in this.targets)
			{
				var cfxr_effect = t as CFXR_Effect;
				if (cfxr_effect != null)
				{
					if (isPrefabSource(cfxr_effect.gameObject))
					{
						return;
					}
					cfxr_effect.RegisterEditorUpdate();
				}
			}
		}

		void OnDisable()
		{
			if (this.targets == null)
			{
				return;
			}

			foreach (var t in this.targets)
			{
				// Can be null if GameObject has been destroyed
				var cfxr_effect = t as CFXR_Effect;
				if (cfxr_effect != null)
				{
					if (isPrefabSource(cfxr_effect.gameObject))
					{
						return;
					}
					cfxr_effect.UnregisterEditorUpdate();
				}
			}
		}

		static bool isPrefabSource(GameObject gameObject)
		{
			var assetType = PrefabUtility.GetPrefabAssetType(gameObject);
			var prefabType = PrefabUtility.GetPrefabInstanceStatus(gameObject);
			return ((assetType == PrefabAssetType.Regular || assetType == PrefabAssetType.Variant) && prefabType == PrefabInstanceStatus.NotAPrefab);
		}
	}
#endif
}
;