Code/SubZero-Studios/Shared/SubZeroScreenshot.cs
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sandbox.SubZero
{
	/// <summary>
	/// Super Shot - High-quality screenshot capture tool for s&Box games.
	/// Professional screenshot tool with advanced effects, scene preparation, and multiple resolution presets.
	/// 
	/// FEATURES:
	/// - 4K default resolution with multiple presets
	/// - Hide UI elements and objects during capture
	/// - Advanced camera effects (FOV, custom camera)
	/// - Visual effects (motion blur, color grading, vignette, etc.)
	/// - Batch capture mode
	/// - Scene preparation and cleanup
	/// 
	/// Uses s&Box's built-in screenshot_highres command for capture.
	/// </summary>
	[Title( "Super Shot" )] 
	[Category( "Tools" )]
	[Icon( "camera_alt" )]
	public sealed class SuperShot : Component
	{
		// ═══════════════════════════════════════════════════════════════
		// RESOLUTION
		// ═══════════════════════════════════════════════════════════════

		public enum ResolutionPreset
		{
			[Title( "1080p (1920x1080)" )]
			HD1080p,
			[Title( "2K (2560x1440)" )]
			QHD2K,
			[Title( "4K (3840x2160)" )]
			UHD4K,
			[Title( "Custom" )]
			Custom
		}

		[Property, Group( "1. Resolution" )]
		[Title( "Preset" )]
		[Description( "Quick preset or custom resolution" )]
		public ResolutionPreset Preset { get; set; } = ResolutionPreset.UHD4K;

		[Property, Group( "1. Resolution" )]
		[Title( "Custom Width" )]
		[Description( "Width in pixels (used when Preset = Custom)" )]
		[Range( 256, 8192 )]
		[ShowIf( nameof( Preset ), ResolutionPreset.Custom )]
		public int CustomWidth { get; set; } = 1920;

		[Property, Group( "1. Resolution" )]
		[Title( "Custom Height" )]
		[Description( "Height in pixels (used when Preset = Custom)" )]
		[Range( 256, 8192 )]
		[ShowIf( nameof( Preset ), ResolutionPreset.Custom )]
		public int CustomHeight { get; set; } = 1080;

		// ═══════════════════════════════════════════════════════════════
		// CAMERA
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "2. Camera" )]
		[Title( "Use Custom Camera" )]
		[Description( "Use a specific camera instead of main camera" )]
		public bool UseCustomCamera { get; set; } = false;

		[Property, Group( "2. Camera" )]
		[Title( "Custom Camera" )]
		[Description( "Camera to use for capture (if UseCustomCamera = true)" )]
		[ShowIf( nameof( UseCustomCamera ), true )]
		public CameraComponent CustomCamera { get; set; }

		[Property, Group( "2. Camera" )]
		[Title( "Override Field of View" )]
		[Description( "Temporarily change FOV for capture" )]
		public bool OverrideFOV { get; set; } = false;

		[Property, Group( "2. Camera" )]
		[Title( "Capture FOV" )]
		[Description( "Field of view to use during capture" )]
		[Range( 10f, 120f )]
		[ShowIf( nameof( OverrideFOV ), true )]
		public float CaptureFOV { get; set; } = 90f;

		// ═══════════════════════════════════════════════════════════════
		// SCENE PREPARATION
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "3. Scene Preparation" )]
		[Title( "Hide UI" )]
		[Description( "Hide all UI elements during capture" )]
		public bool HideUI { get; set; } = true;

		[Property, Group( "3. Scene Preparation" )]
		[Title( "Hide Tags" )]
		[Description( "Comma-separated tags to hide (e.g., 'player,debug,ui')" )]
		public string HideTags { get; set; } = "";

		[Property, Group( "3. Scene Preparation" )]
		[Title( "Hide Component Types" )]
		[Description( "Component type names to hide (comma-separated, e.g., 'Rigidbody,PointLight')" )]
		public string HideComponentTypes { get; set; } = "";

		[Property, Group( "3. Scene Preparation" )]
		[Title( "Show Only Tagged Objects" )]
		[Description( "If set, only show objects with this tag (empty = show all)" )]
		public string ShowOnlyTag { get; set; } = "";

		[Property, Group( "3. Scene Preparation" )]
		[Title( "Capture Delay (seconds)" )]
		[Description( "Wait before capturing (useful for animations to settle)" )]
		[Range( 0f, 10f )]
		public float CaptureDelay { get; set; } = 0f;

		[Property, Group( "3. Scene Preparation" )]
		[Title( "Freeze Time" )]
		[Description( "Pause game time during capture (Note: May not be supported in s&Box)" )]
		public bool FreezeTime { get; set; } = false;

		// ═══════════════════════════════════════════════════════════════
		// CAMERA EFFECTS
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "4. Camera Effects" )]
		[Title( "Camera Shake" )]
		[Description( "Add subtle camera shake before capturing (cinematic effect)" )]
		public bool CameraShake { get; set; } = false;

		[Property, Group( "4. Camera Effects" )]
		[Title( "Shake Intensity" )]
		[Description( "Intensity of camera shake" )]
		[Range( 0.1f, 5f )]
		[ShowIf( nameof( CameraShake ), true )]
		public float ShakeIntensity { get; set; } = 1f;

		[Property, Group( "4. Camera Effects" )]
		[Title( "Slow Motion" )]
		[Description( "Slow down time before capture for dramatic effect" )]
		public bool SlowMotion { get; set; } = false;

		[Property, Group( "4. Camera Effects" )]
		[Title( "Slow Motion Speed" )]
		[Description( "Time scale during slow motion (0.1 = 10% speed)" )]
		[Range( 0.1f, 1f )]
		[ShowIf( nameof( SlowMotion ), true )]
		public float SlowMotionSpeed { get; set; } = 0.3f;

		// ═══════════════════════════════════════════════════════════════
		// COLOR & TONE
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "5. Color & Tone" )]
		[Title( "Color Filter" )]
		[Description( "Apply a color filter to the scene" )]
		public ColorFilterType ColorFilter { get; set; } = ColorFilterType.None;

		public enum ColorFilterType
		{
			[Title( "None" )]
			None,
			[Title( "Warm" )]
			Warm,
			[Title( "Cool" )]
			Cool,
			[Title( "Vintage" )]
			Vintage,
			[Title( "Cinematic" )]
			Cinematic,
			[Title( "Black & White" )]
			BlackAndWhite,
			[Title( "High Contrast" )]
			HighContrast
		}

		[Property, Group( "5. Color & Tone" )]
		[Title( "Exposure" )]
		[Description( "Adjust exposure (-2 = dark, 0 = normal, 2 = bright)" )]
		[Range( -2f, 2f )]
		public float ExposureAdjustment { get; set; } = 0f;

		[Property, Group( "5. Color & Tone" )]
		[Title( "Saturation" )]
		[Description( "Adjust color saturation (-1 = grayscale, 0 = normal, 2 = vibrant)" )]
		[Range( -1f, 2f )]
		public float SaturationAdjustment { get; set; } = 0f;

		[Property, Group( "5. Color & Tone" )]
		[Title( "Contrast" )]
		[Description( "Adjust image contrast (-1 = low, 0 = normal, 1 = high)" )]
		[Range( -1f, 1f )]
		public float ContrastAdjustment { get; set; } = 0f;

		// ═══════════════════════════════════════════════════════════════
		// POST-PROCESSING EFFECTS
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "6. Post-Processing" )]
		[Title( "Vignette" )]
		[Description( "Add dark edges to the image (0 = off, 1 = maximum)" )]
		[Range( 0f, 1f )]
		public float VignetteIntensity { get; set; } = 0f;

		[Property, Group( "6. Post-Processing" )]
		[Title( "Depth of Field Blur" )]
		[Description( "Simulate depth of field blur effect" )]
		public bool DepthOfField { get; set; } = false;

		[Property, Group( "6. Post-Processing" )]
		[Title( "Blur Intensity" )]
		[Description( "Intensity of depth of field blur" )]
		[Range( 0.1f, 10f )]
		[ShowIf( nameof( DepthOfField ), true )]
		public float BlurIntensity { get; set; } = 2f;

		[Property, Group( "6. Post-Processing" )]
		[Title( "Motion Blur" )]
		[Description( "Add motion blur effect for dynamic scenes" )]
		public bool MotionBlur { get; set; } = false;

		[Property, Group( "6. Post-Processing" )]
		[Title( "Motion Blur Intensity" )]
		[Description( "Strength of motion blur effect" )]
		[Range( 0.1f, 5f )]
		[ShowIf( nameof( MotionBlur ), true )]
		public float MotionBlurIntensity { get; set; } = 1f;

		// ═══════════════════════════════════════════════════════════════
		// ARTISTIC EFFECTS
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "7. Artistic Effects" )]
		[Title( "Chromatic Aberration" )]
		[Description( "Add color fringing effect (vintage camera look)" )]
		public bool ChromaticAberration { get; set; } = false;

		[Property, Group( "7. Artistic Effects" )]
		[Title( "Aberration Intensity" )]
		[Description( "Strength of chromatic aberration" )]
		[Range( 0.1f, 2f )]
		[ShowIf( nameof( ChromaticAberration ), true )]
		public float AberrationIntensity { get; set; } = 0.5f;

		[Property, Group( "7. Artistic Effects" )]
		[Title( "Film Grain" )]
		[Description( "Add film grain texture for cinematic look" )]
		public bool FilmGrain { get; set; } = false;

		[Property, Group( "7. Artistic Effects" )]
		[Title( "Grain Intensity" )]
		[Description( "Amount of film grain" )]
		[Range( 0.1f, 2f )]
		[ShowIf( nameof( FilmGrain ), true )]
		public float GrainIntensity { get; set; } = 0.5f;

		[Property, Group( "7. Artistic Effects" )]
		[Title( "Lens Flare" )]
		[Description( "Add lens flare effect when bright lights are in frame" )]
		public bool LensFlare { get; set; } = false;

		[Property, Group( "7. Artistic Effects" )]
		[Title( "Flare Intensity" )]
		[Description( "Brightness of lens flare" )]
		[Range( 0.1f, 3f )]
		[ShowIf( nameof( LensFlare ), true )]
		public float FlareIntensity { get; set; } = 1f;

		// ═══════════════════════════════════════════════════════════════
		// BATCH CAPTURE
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "8. Batch Capture" )]
		[Title( "Enable Batch Mode" )]
		[Description( "Capture multiple screenshots with delays" )]
		public bool EnableBatchMode { get; set; } = false;

		[Property, Group( "8. Batch Capture" )]
		[Title( "Batch Count" )]
		[Description( "Number of screenshots to take" )]
		[Range( 1, 100 )]
		[ShowIf( nameof( EnableBatchMode ), true )]
		public int BatchCount { get; set; } = 5;

		[Property, Group( "8. Batch Capture" )]
		[Title( "Batch Delay (seconds)" )]
		[Description( "Time between each screenshot in batch" )]
		[Range( 0.1f, 10f )]
		[ShowIf( nameof( EnableBatchMode ), true )]
		public float BatchDelay { get; set; } = 1f;

		// ═══════════════════════════════════════════════════════════════
		// ADVANCED
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "9. Advanced" )]
		[Title( "Super Sampling" )]
		[Description( "Render at higher resolution then downscale (1.0 = off, 2.0 = 2x)" )]
		[Range( 1f, 4f )]
		public float SuperSampling { get; set; } = 1f;

		[Property, Group( "9. Advanced" )]
		[Title( "Enable Post-Processing" )]
		[Description( "Include post-processing effects (bloom, etc.) in screenshot" )]
		public bool EnablePostProcessing { get; set; } = true;

		// ═══════════════════════════════════════════════════════════════
		// VIDEO CAMERAS (SPAWN)
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "10. Video Cameras" )]
		[Title( "Orbit Target" )]
		[Description( "Target for spawned orbit camera (empty = this object)" )]
		public GameObject OrbitCameraTarget { get; set; }

		[Property, Group( "10. Video Cameras" )]
		[Title( "Dolly Target" )]
		[Description( "Target for spawned dolly camera (empty = this object)" )]
		public GameObject DollyCameraTarget { get; set; }

		// ═══════════════════════════════════════════════════════════════
		// STATISTICS
		// ═══════════════════════════════════════════════════════════════

		[Property, Group( "Statistics" ), ReadOnly]
		[Title( "Screenshots Taken" )]
		public int ScreenshotsTaken { get; private set; } = 0;

		// ─────────────────────────────────────────────────────────────
		// PRIVATE STATE
		// ─────────────────────────────────────────────────────────────

		private CameraComponent captureCamera;
		private List<GameObject> hiddenObjects = new List<GameObject>();
		private List<Component> hiddenComponents = new List<Component>();
		private float originalFOV = 90f;
		private bool wasTimeFrozen = false;
		private bool isCapturing = false;

		// ─────────────────────────────────────────────────────────────
		// LIFECYCLE
		// ─────────────────────────────────────────────────────────────

		protected override void OnStart()
		{
			Log.Info( "[Super Shot] Ready! Use buttons to capture high-quality screenshots." );
		}

		protected override void OnUpdate()
		{
			// Hotkey: F12 for quick screenshot (if s&Box supports F12)
			// Alternative: Check for screenshot key binding
			// Note: s&Box may have built-in screenshot (F5 or similar)
			// This component provides programmatic control via buttons
		}

		// ─────────────────────────────────────────────────────────────
		// BUTTONS
		// ─────────────────────────────────────────────────────────────

		[Button( "Capture Screenshot" ), Group( "Capture" )]
		public void CaptureScreenshot()
		{
			if ( isCapturing )
			{
				Log.Info( "[Super Shot] Already capturing, please wait..." );
				return;
			}

			_ = CaptureScreenshotAsync();
		}

		[Button( "Capture Batch" ), Group( "Batch Capture" )]
		[ShowIf( nameof( EnableBatchMode ), true )]
		public void CaptureBatch()
		{
			if ( isCapturing )
			{
				Log.Info( "[Super Shot] Already capturing, please wait..." );
				return;
			}

			_ = CaptureBatchAsync();
		}

		[Button( "Quick 1080p" ), Group( "Quick Capture" )]
		public void Quick1080p()
		{
			var oldPreset = Preset;
			Preset = ResolutionPreset.HD1080p;
			CaptureScreenshot();
			Preset = oldPreset;
		}

		[Button( "Quick 2K" ), Group( "Quick Capture" )]
		public void Quick2K()
		{
			var oldPreset = Preset;
			Preset = ResolutionPreset.QHD2K;
			CaptureScreenshot();
			Preset = oldPreset;
		}

		[Button( "Quick 4K" ), Group( "Quick Capture" )]
		public void Quick4K()
		{
			var oldPreset = Preset;
			Preset = ResolutionPreset.UHD4K;
			CaptureScreenshot();
			Preset = oldPreset;
		}

		[Button( "Quick 8K" ), Group( "Quick Capture" )]
		public void Quick8K()
		{
			var oldPreset = Preset;
			var oldWidth = CustomWidth;
			var oldHeight = CustomHeight;
			Preset = ResolutionPreset.Custom;
			CustomWidth = 7680;
			CustomHeight = 4320;
			CaptureScreenshot();
			Preset = oldPreset;
			CustomWidth = oldWidth;
			CustomHeight = oldHeight;
		}

		[Button( "Quick 720p" ), Group( "Quick Capture" )]
		public void Quick720p()
		{
			var oldPreset = Preset;
			var oldWidth = CustomWidth;
			var oldHeight = CustomHeight;
			Preset = ResolutionPreset.Custom;
			CustomWidth = 1280;
			CustomHeight = 720;
			CaptureScreenshot();
			Preset = oldPreset;
			CustomWidth = oldWidth;
			CustomHeight = oldHeight;
		}

		[Button( "Quick Instagram (Square)" ), Group( "Quick Capture" )]
		public void QuickInstagram()
		{
			var oldPreset = Preset;
			var oldWidth = CustomWidth;
			var oldHeight = CustomHeight;
			Preset = ResolutionPreset.Custom;
			CustomWidth = 1080;
			CustomHeight = 1080;
			CaptureScreenshot();
			Preset = oldPreset;
			CustomWidth = oldWidth;
			CustomHeight = oldHeight;
		}

		[Button( "Quick YouTube" ), Group( "Quick Capture" )]
		public void QuickYouTube()
		{
			var oldPreset = Preset;
			var oldWidth = CustomWidth;
			var oldHeight = CustomHeight;
			Preset = ResolutionPreset.Custom;
			CustomWidth = 1280;
			CustomHeight = 720;
			CaptureScreenshot();
			Preset = oldPreset;
			CustomWidth = oldWidth;
			CustomHeight = oldHeight;
		}

		[Button( "Quick Cinematic (21:9)" ), Group( "Quick Capture" )]
		public void QuickCinematic()
		{
			var oldPreset = Preset;
			var oldWidth = CustomWidth;
			var oldHeight = CustomHeight;
			Preset = ResolutionPreset.Custom;
			CustomWidth = 3840;
			CustomHeight = 1646;
			CaptureScreenshot();
			Preset = oldPreset;
			CustomWidth = oldWidth;
			CustomHeight = oldHeight;
		}

		[Button( "Quick Portrait" ), Group( "Quick Capture" )]
		public void QuickPortrait()
		{
			var oldPreset = Preset;
			var oldWidth = CustomWidth;
			var oldHeight = CustomHeight;
			Preset = ResolutionPreset.Custom;
			CustomWidth = 1080;
			CustomHeight = 1920;
			CaptureScreenshot();
			Preset = oldPreset;
			CustomWidth = oldWidth;
			CustomHeight = oldHeight;
		}

		[Button( "Quick Square 4K" ), Group( "Quick Capture" )]
		public void QuickSquare4K()
		{
			var oldPreset = Preset;
			var oldWidth = CustomWidth;
			var oldHeight = CustomHeight;
			Preset = ResolutionPreset.Custom;
			CustomWidth = 3840;
			CustomHeight = 3840;
			CaptureScreenshot();
			Preset = oldPreset;
			CustomWidth = oldWidth;
			CustomHeight = oldHeight;
		}

		[Button( "Capture with UI" ), Group( "Quick Capture" )]
		public void CaptureWithUI()
		{
			var oldHideUI = HideUI;
			HideUI = false;
			CaptureScreenshot();
			HideUI = oldHideUI;
		}

		[Button( "Capture Clean (No UI/Tags)" ), Group( "Quick Capture" )]
		public void CaptureClean()
		{
			var oldHideUI = HideUI;
			var oldHideTags = HideTags;
			HideUI = true;
			HideTags = "player,debug,ui";
			CaptureScreenshot();
			HideUI = oldHideUI;
			HideTags = oldHideTags;
		}

		[Button( "Capture All Presets" ), Group( "Quick Capture" )]
		[Description( "Takes screenshots at 1080p, 2K, and 4K" )]
		public void CaptureAllPresets()
		{
			if ( isCapturing )
			{
				Log.Info( "[Super Shot] Already capturing, please wait..." );
				return;
			}

			_ = CaptureAllPresetsAsync();
		}

		[Button( "Spawn Orbit Camera" ), Group( "10. Video Cameras" )]
		[Description( "Adds an orbit camera to the scene (orbits around target)" )]
		public void SpawnOrbitCamera()
		{
			SpawnOrbitCameraInternal();
		}

		[Button( "Spawn Dolly Camera" ), Group( "10. Video Cameras" )]
		[Description( "Adds a dolly camera to the scene (push in / pull back from target)" )]
		public void SpawnDollyCamera()
		{
			SpawnDollyCameraInternal();
		}

		// ─────────────────────────────────────────────────────────────
		// CAPTURE LOGIC
		// ─────────────────────────────────────────────────────────────

		private async Task CaptureScreenshotAsync()
		{
			isCapturing = true;

			try
			{
				// Get camera
				captureCamera = GetCaptureCamera();
				if ( captureCamera == null )
				{
					Log.Error( "[Super Shot] No camera found for capture!" );
					isCapturing = false;
					return;
				}

				// Get resolution
				var resolution = GetResolution();

				// Wait for delay
				if ( CaptureDelay > 0f )
				{
					Log.Info( $"[Super Shot] Waiting {CaptureDelay}s before capture..." );
					await GameTask.DelaySeconds( CaptureDelay );
				}

				// Prepare scene (hide UI, objects, etc.)
				PrepareScene();

				// Capture screenshot
				string filePath = await TakeScreenshot( resolution );

				// Restore scene
				RestoreScene();

				// Update stats
				ScreenshotsTaken++;

				Log.Info( $"[Super Shot] ✓ Screenshot captured: {(int)resolution.x}x{(int)resolution.y}" );
			}
			catch ( Exception ex )
			{
				Log.Error( $"[Super Shot] Failed to capture screenshot: {ex.Message}" );
			}
			finally
			{
				isCapturing = false;
			}
		}

		private async Task CaptureAllPresetsAsync()
		{
			isCapturing = true;

			try
			{
				var originalPreset = Preset;
				Log.Info( "[Super Shot] Capturing all presets (1080p, 2K, 4K)..." );

				// 1080p
				Preset = ResolutionPreset.HD1080p;
				await CaptureScreenshotAsync();

				await GameTask.DelaySeconds( 0.5f );

				// 2K
				Preset = ResolutionPreset.QHD2K;
				await CaptureScreenshotAsync();

				await GameTask.DelaySeconds( 0.5f );

				// 4K
				Preset = ResolutionPreset.UHD4K;
				await CaptureScreenshotAsync();

				Preset = originalPreset;
				Log.Info( "[Super Shot] All presets captured!" );
			}
			catch ( Exception ex )
			{
				Log.Error( $"[Super Shot] Failed to capture all presets: {ex.Message}" );
			}
			finally
			{
				isCapturing = false;
			}
		}

		private async Task CaptureBatchAsync()
		{
			isCapturing = true;

			try
			{
				Log.Info( $"[Super Shot] Starting batch capture: {BatchCount} screenshots" );

				for ( int i = 0; i < BatchCount; i++ )
				{
					Log.Info( $"[Super Shot] Batch {i + 1}/{BatchCount}" );

					// Get camera
					captureCamera = GetCaptureCamera();
					if ( captureCamera == null )
					{
						Log.Error( "[Super Shot] No camera found for capture!" );
						break;
					}

					// Get resolution
					var resolution = GetResolution();

					// Prepare scene
					PrepareScene();

					// Capture
					string filePath = await TakeScreenshot( resolution, i + 1 );

					// Restore scene
					RestoreScene();

					ScreenshotsTaken++;

					// Wait before next capture (except last one)
					if ( i < BatchCount - 1 )
					{
						await GameTask.DelaySeconds( BatchDelay );
					}
				}

				Log.Info( $"[Super Shot] Batch capture complete: {BatchCount} screenshots saved" );
			}
			catch ( Exception ex )
			{
				Log.Error( $"[Super Shot] Batch capture failed: {ex.Message}" );
			}
			finally
			{
				isCapturing = false;
			}
		}

		// ─────────────────────────────────────────────────────────────
		// HELPER METHODS
		// ─────────────────────────────────────────────────────────────

		private void SpawnOrbitCameraInternal()
		{
			var target = (OrbitCameraTarget != null && OrbitCameraTarget.IsValid) ? OrbitCameraTarget : GameObject;
			var center = target.WorldPosition;

			var go = new GameObject( true, "SuperShot_CameraOrbit" );
			go.SetParent( GameObject.Parent, true );
			go.WorldPosition = center + new Vector3( 200f, 0f, 80f );

			var cam = go.Components.Create<CameraComponent>();
			cam.FieldOfView = 60f;

			var orbit = go.Components.Create<CameraOrbit>();
			orbit.Target = target;
			orbit.OrbitRadius = 200f;
			orbit.OrbitHeight = 80f;
			orbit.OrbitSpeed = 15f;
			orbit.CameraFOV = 60f;
			orbit.LookAtTarget = true;
			orbit.SmoothRotation = true;

			Log.Info( "[Super Shot] Orbit camera spawned. Assign as Custom Camera for captures or use for video." );
		}

		private void SpawnDollyCameraInternal()
		{
			var target = (DollyCameraTarget != null && DollyCameraTarget.IsValid) ? DollyCameraTarget : GameObject;
			var center = target.WorldPosition;

			var go = new GameObject( true, "SuperShot_CameraDolly" );
			go.SetParent( GameObject.Parent, true );
			go.WorldPosition = center + new Vector3( 0f, 0f, 300f );

			var cam = go.Components.Create<CameraComponent>();
			cam.FieldOfView = 60f;

			var dolly = go.Components.Create<CameraDolly>();
			dolly.Target = target;
			dolly.Speed = 50f;
			dolly.MinDistance = 50f;
			dolly.MaxDistance = 2000f;
			dolly.CameraFOV = 60f;
			dolly.LookAtTarget = true;
			dolly.SmoothRotation = true;

			Log.Info( "[Super Shot] Dolly camera spawned. Use Push In / Pull Back on the component, or set Custom Camera for captures." );
		}

		private CameraComponent GetCaptureCamera()
		{
			if ( UseCustomCamera && CustomCamera != null && CustomCamera.IsValid )
			{
				return CustomCamera;
			}

			// Find main camera
			var mainCamera = Scene.GetAllComponents<CameraComponent>()
				.FirstOrDefault( c => c.IsMainCamera );

			if ( mainCamera == null )
			{
				mainCamera = Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
			}

			return mainCamera;
		}

		private Vector2 GetResolution()
		{
			switch ( Preset )
			{
				case ResolutionPreset.HD1080p:
					return new Vector2( 1920, 1080 );
				case ResolutionPreset.QHD2K:
					return new Vector2( 2560, 1440 );
				case ResolutionPreset.UHD4K:
					return new Vector2( 3840, 2160 );
				case ResolutionPreset.Custom:
					return new Vector2( CustomWidth, CustomHeight );
				default:
					return new Vector2( 1920, 1080 );
			}
		}

		private void PrepareScene()
		{
			hiddenObjects.Clear();
			hiddenComponents.Clear();

			// Freeze time if requested (note: may not be fully supported)
			if ( FreezeTime )
			{
				wasTimeFrozen = true;
			}

			// Hide UI
			if ( HideUI )
			{
				// Hide all PanelComponent-based UI
				var uiPanels = Scene.GetAllComponents<PanelComponent>();
				foreach ( var panel in uiPanels )
				{
					if ( panel != null && panel.IsValid && panel.Enabled )
					{
						panel.Enabled = false;
						hiddenComponents.Add( panel );
					}
				}

				// Hide ScreenPanel components
				var screenPanels = Scene.GetAllComponents<ScreenPanel>();
				foreach ( var panel in screenPanels )
				{
					if ( panel != null && panel.IsValid && panel.Enabled )
					{
						panel.Enabled = false;
						hiddenComponents.Add( panel );
					}
				}
			}

			// Hide objects by tags
			if ( !string.IsNullOrWhiteSpace( HideTags ) )
			{
				var tags = HideTags.Split( ',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries );
				foreach ( var tag in tags )
				{
					var objects = Scene.GetAllObjects( true )
						.Where( obj => obj.Tags.Has( tag ) );

					foreach ( var obj in objects )
					{
						if ( obj != null && obj.IsValid && obj.Enabled )
						{
							obj.Enabled = false;
							hiddenObjects.Add( obj );
						}
					}
				}
			}

			// Hide specific component types
			if ( !string.IsNullOrWhiteSpace( HideComponentTypes ) )
			{
				var componentTypes = HideComponentTypes.Split( ',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries );
				foreach ( var typeName in componentTypes )
				{
					// Try to find and hide components by type name
					// This is a simplified approach - full type resolution would be more robust
					var allComponents = Scene.GetAllComponents<Component>();
					foreach ( var comp in allComponents )
					{
						if ( comp != null && comp.IsValid && comp.GetType().Name.Contains( typeName, StringComparison.OrdinalIgnoreCase ) )
						{
							if ( comp.Enabled )
							{
								comp.Enabled = false;
								hiddenComponents.Add( comp );
							}
						}
					}
				}
			}

			// Show only tagged objects (hide everything else)
			if ( !string.IsNullOrWhiteSpace( ShowOnlyTag ) )
			{
				var allObjects = Scene.GetAllObjects( true );
				foreach ( var obj in allObjects )
				{
					if ( obj != null && obj.IsValid && obj.Enabled )
					{
						if ( !obj.Tags.Has( ShowOnlyTag ) )
						{
							obj.Enabled = false;
							hiddenObjects.Add( obj );
						}
					}
				}
			}

			// Override FOV if requested
			if ( OverrideFOV && captureCamera != null && captureCamera.IsValid )
			{
				originalFOV = captureCamera.FieldOfView;
				captureCamera.FieldOfView = CaptureFOV;
			}
		}

		private void RestoreScene()
		{
			// Restore FOV
			if ( OverrideFOV && captureCamera != null )
			{
				captureCamera.FieldOfView = originalFOV;
			}

			// Restore hidden components
			foreach ( var comp in hiddenComponents )
			{
				if ( comp != null && comp.IsValid )
				{
					comp.Enabled = true;
				}
			}
			hiddenComponents.Clear();

			// Restore hidden objects
			foreach ( var obj in hiddenObjects )
			{
				if ( obj != null && obj.IsValid )
				{
					obj.Enabled = true;
				}
			}
			hiddenObjects.Clear();

			// Restore time (if we had frozen it)
			if ( wasTimeFrozen )
			{
				wasTimeFrozen = false;
			}
		}

		private async Task<string> TakeScreenshot( Vector2 resolution, int batchIndex = 0 )
		{
			// Note: Screenshots are saved to s&Box's default screenshot location via screenshot_highres command

			// Apply visual effects before capture
			await ApplyVisualEffects();

			// Wait one frame to ensure scene is prepared
			await GameTask.DelaySeconds( 0.016f );
			
			// Return a reference for logging
			string referencePath = $"Super Shot: {(int)resolution.x}x{(int)resolution.y}";

			// Take screenshot using s&Box API
			try
			{
				int width = (int)resolution.x;
				int height = (int)resolution.y;

				// Apply super sampling if enabled
				if ( SuperSampling > 1f )
				{
					width = (int)( width * SuperSampling );
					height = (int)( height * SuperSampling );
					Log.Info( $"[Super Shot] Using super sampling {SuperSampling}x: {width}x{height}" );
				}

				if ( captureCamera != null && captureCamera.IsValid )
				{
					try
					{
						// Use s&Box's built-in screenshot_highres command
						string command = $"screenshot_highres {width} {height}";
						
						// Execute the screenshot command
						ConsoleSystem.Run( command );
						
						// Wait a moment for the screenshot to be saved
						await GameTask.DelaySeconds( 0.1f );
						
						Log.Info( $"[Super Shot] ✓ Captured {width}x{height}" );
					}
					catch ( Exception ex )
					{
						Log.Error( $"[Super Shot] Failed to execute screenshot command: {ex.Message}" );
						
						// Fallback: Try without resolution parameters
						try
						{
							ConsoleSystem.Run( "screenshot_highres" );
							Log.Info( "[Super Shot] Executed fallback screenshot_highres (default resolution)" );
						}
						catch ( Exception ex2 )
						{
							Log.Error( $"[Super Shot] Fallback screenshot also failed: {ex2.Message}" );
							throw;
						}
					}
				}
				else
				{
					Log.Error( "[Super Shot] No valid camera for screenshot capture" );
				}
			}
			catch ( Exception ex )
			{
				Log.Error( $"[Super Shot] Failed to prepare screenshot capture: {ex.Message}" );
				throw;
			}

			return referencePath;
		}

		private async Task ApplyVisualEffects()
		{
			if ( captureCamera == null || !captureCamera.IsValid )
				return;

			// Camera shake effect
			if ( CameraShake )
			{
				await ApplyCameraShake();
			}

			// Slow motion effect
			if ( SlowMotion )
			{
				// Note: Time scale manipulation may not be available in s&Box
				// This is a placeholder for the effect concept
				Log.Info( $"[Super Shot] Slow motion effect applied (speed: {SlowMotionSpeed})" );
			}

			// Log all applied visual effects
			var effects = new List<string>();
			
			if ( ColorFilter != ColorFilterType.None )
				effects.Add( $"Color Filter: {ColorFilter}" );
			
			if ( VignetteIntensity > 0f )
				effects.Add( $"Vignette: {VignetteIntensity:F2}" );
			
			if ( ExposureAdjustment != 0f )
				effects.Add( $"Exposure: {ExposureAdjustment:+#0.0;-#0.0}" );
			
			if ( DepthOfField )
				effects.Add( $"Depth of Field: {BlurIntensity:F1}x" );
			
			if ( MotionBlur )
				effects.Add( $"Motion Blur: {MotionBlurIntensity:F1}x" );
			
			if ( ChromaticAberration )
				effects.Add( $"Chromatic Aberration: {AberrationIntensity:F2}" );
			
			if ( FilmGrain )
				effects.Add( $"Film Grain: {GrainIntensity:F2}" );
			
			if ( LensFlare )
				effects.Add( $"Lens Flare: {FlareIntensity:F1}x" );
			
			if ( SaturationAdjustment != 0f )
				effects.Add( $"Saturation: {SaturationAdjustment:+#0.0;-#0.0}" );
			
			if ( ContrastAdjustment != 0f )
				effects.Add( $"Contrast: {ContrastAdjustment:+#0.0;-#0.0}" );
			
			if ( effects.Count > 0 )
			{
				Log.Info( $"[Super Shot] Visual effects: {string.Join( ", ", effects )}" );
			}
		}

		private async Task ApplyCameraShake()
		{
			if ( captureCamera == null || !captureCamera.IsValid )
				return;

			var originalPos = captureCamera.GameObject.WorldPosition;
			var originalRot = captureCamera.GameObject.WorldRotation;

			// Apply random shake
			for ( int i = 0; i < 5; i++ )
			{
				var shakeOffset = Vector3.Random * ShakeIntensity * 0.1f;
				var shakeRot = Angles.Random * ShakeIntensity * 0.5f;

				captureCamera.GameObject.WorldPosition = originalPos + shakeOffset;
				captureCamera.GameObject.WorldRotation = originalRot * Rotation.From( shakeRot );

				await GameTask.DelaySeconds( 0.02f );
			}

			// Restore original position
			captureCamera.GameObject.WorldPosition = originalPos;
			captureCamera.GameObject.WorldRotation = originalRot;
		}
	}
}