using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using Cinemachine;
using TMPro;
using UnityEngine.Rendering.PostProcessing;

public class WaitingSceneManager : MonoBehaviour
{
    // References to scene objects
    [Header("Scene References")]
    public Transform spawnPoint;
    public CinemachineDollyCart dollyCart;
    public CinemachineVirtualCamera dollyCamera;
    public Button skipButton;
    public TextMeshProUGUI countdownText;
    public GameObject loadingOverlay;
    public PostProcessVolume postProcessVolume;

    // Configuration
    [Header("Configuration")]
    public float entranceDuration = 10f;
    public float walkSpeed = 1.5f;
    public float dollySpeed = 2f;
    [Range(0, 1)]
    public float cameraShakeIntensity = 0.1f;
    
    [Header("Enhanced Cinematics")]
    public Light[] cinematicLights;
    public GameObject[] atmosphericEffects;
    public AudioClip dramaticMusic;
    public AudioClip crowdCheering;
    [Range(0, 1)]
    public float musicVolume = 0.7f;
    [Range(0, 1)]
    public float crowdVolume = 0.5f;
    [Range(0, 5)]
    public float dramaticPauseTime = 1.5f;
    
    // Private variables
    private GameObject playerInstance;
    private Animator playerAnimator;
    private AudioSource footstepAudio;
    private AudioSource musicAudioSource;
    private AudioSource crowdAudioSource;
    private bool isTransitioning = false;
    private float remainingTime;
    private Vector3 walkDirection = Vector3.forward;
    private Vector3 walkTarget;
    private bool isPlayerMoving = false;
    private CinemachineBasicMultiChannelPerlin noise;
    private DepthOfField depthOfField;
    private Vignette vignette;
    private ChromaticAberration chromaticAberration;
    private ColorGrading colorGrading;
    private Grain filmGrain;
    
    // Camera shot state
    private enum CameraShot { Establishing, CloseUp, MidShot, DramaticTilt, Final }
    private CameraShot currentShot = CameraShot.Establishing;
    private float shotTimer = 0f;
    private float shotDuration = 2.5f;

    private void Start()
    {
        // Initialize
        remainingTime = entranceDuration;
        
        if (countdownText != null)
        {
            countdownText.text = Mathf.Ceil(remainingTime).ToString();
        }
        
        if (skipButton != null)
        {
            skipButton.onClick.AddListener(SkipWaitingScene);
        }
        
        // Set up audio sources
        SetupAudioSources();
        
        // Set up post-processing if not already set
        SetupPostProcessing();
        
        // Set up and start the entrance sequence
        SetupDollyCamera();
        StartCoroutine(SpawnPlayerAndStartWalking());
        StartCoroutine(CountdownTimer());
        
        // Start camera shot sequence
        StartCoroutine(CinematicShotSequence());
        
        // Start dynamic lighting sequence
        StartCoroutine(DynamicLightingSequence());
        
        // Start audio synchronization with camera
        StartCoroutine(SyncAudioWithCameraShots());
    }

    private void SetupAudioSources()
    {
        // Setup music audio source
        if (dramaticMusic != null)
        {
            var musicObj = new GameObject("MusicSource");
            musicObj.transform.SetParent(transform);
            musicAudioSource = musicObj.AddComponent<AudioSource>();
            musicAudioSource.clip = dramaticMusic;
            musicAudioSource.volume = musicVolume;
            musicAudioSource.loop = true;
            musicAudioSource.playOnAwake = false;
            musicAudioSource.spatialBlend = 0f; // 2D sound
            musicAudioSource.Play();
        }
        
        // Setup crowd cheering audio source
        if (crowdCheering != null)
        {
            var crowdObj = new GameObject("CrowdSource");
            crowdObj.transform.SetParent(transform);
            crowdAudioSource = crowdObj.AddComponent<AudioSource>();
            crowdAudioSource.clip = crowdCheering;
            crowdAudioSource.volume = 0f; // Start silent and fade in
            crowdAudioSource.loop = true;
            crowdAudioSource.playOnAwake = false;
            crowdAudioSource.spatialBlend = 0f; // 2D sound
            crowdAudioSource.Play();
            
            // Start with very low volume and gradually increase
            StartCoroutine(FadeCrowdAudio());
        }
    }

    private void SetupPostProcessing()
    {
        // If post-process volume doesn't exist, create one
        if (postProcessVolume == null)
        {
            var ppVolumeObject = GameObject.Find("Post Process Volume");
            if (ppVolumeObject == null)
            {
                ppVolumeObject = new GameObject("Post Process Volume");
                postProcessVolume = ppVolumeObject.AddComponent<PostProcessVolume>();
                ppVolumeObject.layer = LayerMask.NameToLayer("PostProcessing");
                
                // If post-processing layer doesn't exist, use Default
                if (ppVolumeObject.layer == -1)
                {
                    ppVolumeObject.layer = 0;
                }
                
                postProcessVolume.isGlobal = true;
                postProcessVolume.priority = 1;
                postProcessVolume.profile = ScriptableObject.CreateInstance<PostProcessProfile>();
            }
            else
            {
                postProcessVolume = ppVolumeObject.GetComponent<PostProcessVolume>();
                if (postProcessVolume == null)
                {
                    postProcessVolume = ppVolumeObject.AddComponent<PostProcessVolume>();
                    postProcessVolume.isGlobal = true;
                    postProcessVolume.priority = 1;
                    postProcessVolume.profile = ScriptableObject.CreateInstance<PostProcessProfile>();
                }
            }
        }
        
        // Setup effects if they don't exist
        if (postProcessVolume.profile != null)
        {
            // Depth of Field
            if (!postProcessVolume.profile.TryGetSettings(out depthOfField))
            {
                depthOfField = postProcessVolume.profile.AddSettings<DepthOfField>();
                depthOfField.enabled.Override(true);
                depthOfField.focusDistance.Override(4.5f);
                depthOfField.aperture.Override(5.6f);
                depthOfField.focalLength.Override(50f);
            }
            
            // Vignette
            if (!postProcessVolume.profile.TryGetSettings(out vignette))
            {
                vignette = postProcessVolume.profile.AddSettings<Vignette>();
                vignette.enabled.Override(true);
                vignette.intensity.Override(0.25f);
                vignette.smoothness.Override(0.4f);
            }
            
            // Chromatic Aberration
            if (!postProcessVolume.profile.TryGetSettings(out chromaticAberration))
            {
                chromaticAberration = postProcessVolume.profile.AddSettings<ChromaticAberration>();
                chromaticAberration.enabled.Override(true);
                chromaticAberration.intensity.Override(0.1f);
            }
            
            // Color Grading
            if (!postProcessVolume.profile.TryGetSettings(out colorGrading))
            {
                colorGrading = postProcessVolume.profile.AddSettings<ColorGrading>();
                colorGrading.enabled.Override(true);
                colorGrading.contrast.Override(10f);
                colorGrading.saturation.Override(10f);
                colorGrading.temperature.Override(5f);
            }
            
            // Film Grain
            if (!postProcessVolume.profile.TryGetSettings(out filmGrain))
            {
                filmGrain = postProcessVolume.profile.AddSettings<Grain>();
                filmGrain.enabled.Override(true);
                filmGrain.intensity.Override(0.2f);
                filmGrain.size.Override(1.5f);
            }
        }
        
        // Make sure camera can see post-processing effects
        var mainCamera = Camera.main;
        if (mainCamera != null)
        {
            var layer = LayerMask.NameToLayer("PostProcessing");
            if (layer != -1)
            {
                mainCamera.cullingMask |= (1 << layer);
            }
            
            // Add post-process layer component if missing
            if (mainCamera.GetComponent<PostProcessLayer>() == null)
            {
                var ppLayer = mainCamera.gameObject.AddComponent<PostProcessLayer>();
                ppLayer.volumeLayer = LayerMask.GetMask("Default");
                ppLayer.antialiasingMode = PostProcessLayer.Antialiasing.FastApproximateAntialiasing;
            }
        }
        
        Debug.Log("Post-processing setup complete");
    }

    private IEnumerator DynamicLightingSequence()
    {
        if (cinematicLights == null || cinematicLights.Length == 0)
        {
            yield break;
        }
        
        // Fade in lights at start
        foreach (var light in cinematicLights)
        {
            if (light == null) continue;
            
            float originalIntensity = light.intensity;
            light.intensity = 0;
            
            // Gradually increase intensity
            float elapsed = 0;
            float duration = 2.0f;
            while (elapsed < duration)
            {
                light.intensity = Mathf.Lerp(0, originalIntensity, elapsed / duration);
                elapsed += Time.deltaTime;
                yield return null;
            }
            
            light.intensity = originalIntensity;
        }
        
        while (!isTransitioning)
        {
            // Create dramatic light pulses timed with camera changes
            if (shotTimer > shotDuration - 0.5f && shotTimer < shotDuration - 0.3f)
            {
                // Brief flash of higher intensity before camera change
                foreach (var light in cinematicLights)
                {
                    if (light == null) continue;
                    
                    float originalIntensity = light.intensity;
                    light.intensity = originalIntensity * 1.5f;
                    
                    // Return to normal intensity
                    yield return new WaitForSeconds(0.2f);
                    light.intensity = originalIntensity;
                }
            }
            
            yield return null;
        }
        
        // Fade out lights at end
        if (isTransitioning)
        {
            foreach (var light in cinematicLights)
            {
                if (light == null) continue;
                
                float originalIntensity = light.intensity;
                
                // Gradually decrease intensity
                float elapsed = 0;
                float duration = 1.5f;
                while (elapsed < duration)
                {
                    light.intensity = Mathf.Lerp(originalIntensity, 0, elapsed / duration);
                    elapsed += Time.deltaTime;
                    yield return null;
                }
                
                light.intensity = 0;
            }
        }
    }

    private IEnumerator CinematicShotSequence()
    {
        // Wait for player to be initialized
        while (playerInstance == null)
            yield return null;
            
        // Initial shot timing
        shotTimer = 0f;
        currentShot = CameraShot.Establishing;
        
        // Apply initial shot settings
        ApplyCameraShotSettings();
        
        // Dramatic pause at start
        if (dramaticPauseTime > 0)
        {
            Time.timeScale = 0.3f; // Slow motion effect
            yield return new WaitForSecondsRealtime(dramaticPauseTime);
            Time.timeScale = 1.0f; // Back to normal
            
            // Initial camera shake
            if (noise != null)
            {
                float originalAmplitude = noise.m_AmplitudeGain;
                noise.m_AmplitudeGain = cameraShakeIntensity * 2;
                yield return new WaitForSeconds(0.3f);
                noise.m_AmplitudeGain = originalAmplitude;
            }
        }
        
        while (!isTransitioning)
        {
            shotTimer += Time.deltaTime;
            
            // Change shots based on timer
            if (shotTimer > shotDuration)
            {
                shotTimer = 0f;
                
                // Optional dramatic pause between shots
                Time.timeScale = 0.5f; // Slight slow motion during transition
                yield return new WaitForSecondsRealtime(0.2f);
                Time.timeScale = 1.0f;
                
                // Cycle through shot types with better pacing
                switch (currentShot)
                {
                    case CameraShot.Establishing:
                        currentShot = CameraShot.MidShot;
                        shotDuration = 3.0f; // Longer for mid shot
                        break;
                    case CameraShot.MidShot:
                        currentShot = CameraShot.CloseUp;
                        shotDuration = 2.0f; // Shorter for close-up
                        break;
                    case CameraShot.CloseUp:
                        currentShot = CameraShot.DramaticTilt;
                        shotDuration = 1.5f; // Even shorter for dramatic tilt
                        break;
                    case CameraShot.DramaticTilt:
                        currentShot = CameraShot.Final;
                        shotDuration = 3.5f; // Longer for final shot
                        break;
                    case CameraShot.Final:
                        // Stay in final shot
                        break;
                }
                
                // Apply new shot settings with transition effect
                StartCoroutine(TransitionToNewShot());
            }
            
            yield return null;
        }
    }
    
    private IEnumerator TransitionToNewShot()
    {
        // Quick camera shake during transition
        if (noise != null)
        {
            float originalAmplitude = noise.m_AmplitudeGain;
            noise.m_AmplitudeGain = cameraShakeIntensity * 2;
            
            // Increase chromatic aberration briefly
            if (chromaticAberration != null)
            {
                float originalIntensity = chromaticAberration.intensity.value;
                chromaticAberration.intensity.Override(originalIntensity * 2f);
                
                yield return new WaitForSeconds(0.2f);
                
                chromaticAberration.intensity.Override(originalIntensity);
            }
            else
            {
                yield return new WaitForSeconds(0.2f);
            }
            
            noise.m_AmplitudeGain = originalAmplitude;
        }
        
        // Apply the actual camera settings
        ApplyCameraShotSettings();
    }
    
    private void ApplyCameraShotSettings()
    {
        if (dollyCamera == null || playerInstance == null) return;
        
        CinemachineComposer composer = dollyCamera.GetCinemachineComponent<CinemachineComposer>();
        if (composer == null)
        {
            composer = dollyCamera.AddCinemachineComponent<CinemachineComposer>();
        }
        
        switch (currentShot)
        {
            case CameraShot.Establishing:
                // Wide establishing shot from a slightly elevated angle
                dollyCamera.m_Lens.FieldOfView = 42f;
                composer.m_TrackedObjectOffset = new Vector3(0, 0.7f, 0);
                composer.m_DeadZoneWidth = 0.1f;
                composer.m_DeadZoneHeight = 0.1f;
                dollyCamera.m_Lens.Dutch = 0f;
                
                // Set composer framing for widescreen cinematic effect
                composer.m_ScreenX = 0.5f;
                composer.m_ScreenY = 0.45f; // Slightly lower to show more headroom
                composer.m_SoftZoneWidth = 0.8f;
                composer.m_SoftZoneHeight = 0.7f;
                
                // Subtle depth of field
                if (depthOfField != null)
                {
                    depthOfField.focusDistance.Override(10f);
                    depthOfField.aperture.Override(5.6f);
                    depthOfField.focalLength.Override(50f);
                }
                
                // Minimal noise for establishing shot
                if (noise != null)
                {
                    noise.m_AmplitudeGain = 0.1f;
                    noise.m_FrequencyGain = 0.1f;
                }
                
                // Subtle post-processing
                if (vignette != null)
                {
                    vignette.intensity.Override(0.2f);
                }
                
                if (chromaticAberration != null)
                {
                    chromaticAberration.intensity.Override(0.1f);
                }
                
                if (colorGrading != null)
                {
                    colorGrading.contrast.Override(5f);
                    colorGrading.saturation.Override(5f);
                }
                break;
                
            case CameraShot.MidShot:
                // Mid shot - shows upper body
                dollyCamera.m_Lens.FieldOfView = 32f;
                composer.m_TrackedObjectOffset = new Vector3(0, 1.0f, 0);
                composer.m_DeadZoneWidth = 0.15f;
                composer.m_DeadZoneHeight = 0.15f;
                dollyCamera.m_Lens.Dutch = 2f; // Slight tilt
                
                // Rule of thirds framing
                composer.m_ScreenX = 0.33f; // Position player at 1/3 of screen
                composer.m_ScreenY = 0.5f;
                composer.m_SoftZoneWidth = 0.6f;
                composer.m_SoftZoneHeight = 0.6f;
                
                // Mid-shot depth of field
                if (depthOfField != null)
                {
                    depthOfField.focusDistance.Override(6f);
                    depthOfField.aperture.Override(4.0f);
                    depthOfField.focalLength.Override(75f);
                }
                
                if (noise != null)
                {
                    noise.m_AmplitudeGain = 0.2f;
                    noise.m_FrequencyGain = 0.2f;
                }
                
                // Medium vignette
                if (vignette != null)
                {
                    vignette.intensity.Override(0.3f);
                }
                
                if (chromaticAberration != null)
                {
                    chromaticAberration.intensity.Override(0.15f);
                }
                
                if (colorGrading != null)
                {
                    colorGrading.contrast.Override(8f);
                    colorGrading.saturation.Override(8f);
                    colorGrading.temperature.Override(3f); // Slightly warmer
                }
                break;
                
            case CameraShot.CloseUp:
                // Close-up on character's face/upper body
                dollyCamera.m_Lens.FieldOfView = 28f;
                composer.m_TrackedObjectOffset = new Vector3(0, 1.4f, 0); // Higher to frame face
                composer.m_DeadZoneWidth = 0.05f;
                composer.m_DeadZoneHeight = 0.05f;
                dollyCamera.m_Lens.Dutch = -3f; // Opposite tilt for visual interest
                
                // Dramatic close-up framing
                composer.m_ScreenX = 0.55f; // Slightly off-center
                composer.m_ScreenY = 0.45f;
                composer.m_SoftZoneWidth = 0.4f; // Tighter framing
                composer.m_SoftZoneHeight = 0.4f;
                
                // Shallow depth of field for close-up
                if (depthOfField != null)
                {
                    depthOfField.focusDistance.Override(3f);
                    depthOfField.aperture.Override(2.8f); // Very shallow DOF
                    depthOfField.focalLength.Override(100f);
                }
                
                if (noise != null)
                {
                    noise.m_AmplitudeGain = 0.3f; // More shake for tension
                    noise.m_FrequencyGain = 0.3f;
                }
                
                // Stronger vignette for close-up
                if (vignette != null)
                {
                    vignette.intensity.Override(0.35f);
                }
                
                if (chromaticAberration != null)
                {
                    chromaticAberration.intensity.Override(0.2f);
                }
                
                if (colorGrading != null)
                {
                    colorGrading.contrast.Override(12f); // Higher contrast
                    colorGrading.saturation.Override(10f);
                    colorGrading.temperature.Override(5f); // Even warmer
                }
                break;
                
            case CameraShot.DramaticTilt:
                // Low angle dramatic shot
                dollyCamera.m_Lens.FieldOfView = 36f;
                composer.m_TrackedObjectOffset = new Vector3(0, 0.3f, 0); // Lower offset to look up
                composer.m_DeadZoneWidth = 0.2f;
                composer.m_DeadZoneHeight = 0.2f;
                dollyCamera.m_Lens.Dutch = 15f; // Extreme Dutch angle
                
                // Low angle hero shot framing
                composer.m_ScreenX = 0.5f; // Center framing
                composer.m_ScreenY = 0.4f; // Lower in frame to emphasize height
                composer.m_SoftZoneWidth = 0.5f;
                composer.m_SoftZoneHeight = 0.5f;
                
                // Dramatic depth of field
                if (depthOfField != null)
                {
                    depthOfField.focusDistance.Override(4f);
                    depthOfField.aperture.Override(3.5f);
                    depthOfField.focalLength.Override(85f);
                }
                
                if (noise != null)
                {
                    noise.m_AmplitudeGain = 0.4f; // Most shake for dramatic shot
                    noise.m_FrequencyGain = 0.4f;
                }
                
                // Heavy vignette for dramatic shot
                if (vignette != null)
                {
                    vignette.intensity.Override(0.45f);
                    vignette.smoothness.Override(0.3f);
                }
                
                if (chromaticAberration != null)
                {
                    chromaticAberration.intensity.Override(0.3f); // Strong aberration
                }
                
                if (colorGrading != null)
                {
                    colorGrading.contrast.Override(15f); // Highest contrast
                    colorGrading.saturation.Override(15f); // Highest saturation
                    colorGrading.temperature.Override(-5f); // Switch to cool tone for drama
                }
                
                if (filmGrain != null)
                {
                    filmGrain.intensity.Override(0.4f); // Add more grain for grit
                }
                break;
                
            case CameraShot.Final:
                // Final hero shot - wide, powerful framing
                dollyCamera.m_Lens.FieldOfView = 38f;
                composer.m_TrackedObjectOffset = new Vector3(0, 0.8f, 0);
                composer.m_DeadZoneWidth = 0.1f;
                composer.m_DeadZoneHeight = 0.1f;
                dollyCamera.m_Lens.Dutch = 0f; // Level out for final shot
                
                // Epic final framing
                composer.m_ScreenX = 0.5f; // Center
                composer.m_ScreenY = 0.5f; // Center
                composer.m_SoftZoneWidth = 0.6f;
                composer.m_SoftZoneHeight = 0.6f;
                
                // Clear depth of field for final shot
                if (depthOfField != null)
                {
                    depthOfField.focusDistance.Override(7f);
                    depthOfField.aperture.Override(5.6f); // Deeper DOF
                    depthOfField.focalLength.Override(50f);
                }
                
                if (noise != null)
                {
                    noise.m_AmplitudeGain = 0.15f; // Settle down for final pose
                    noise.m_FrequencyGain = 0.15f;
                }
                
                // Balanced post-processing for final shot
                if (vignette != null)
                {
                    vignette.intensity.Override(0.25f);
                }
                
                if (chromaticAberration != null)
                {
                    chromaticAberration.intensity.Override(0.12f);
                }
                
                if (colorGrading != null)
                {
                    colorGrading.contrast.Override(10f);
                    colorGrading.saturation.Override(12f);
                    colorGrading.temperature.Override(2f); // Slightly warm
                }
                
                if (filmGrain != null)
                {
                    filmGrain.intensity.Override(0.2f);
                }
                break;
        }
        
        Debug.Log("Applied camera shot: " + currentShot);
    }

    private void Update()
    {
        // Move the player if walking animation is active
        if (isPlayerMoving && playerInstance != null && !isTransitioning)
        {
            // Calculate the distance to move this frame
            float step = walkSpeed * Time.deltaTime;
            
            // Move the player
            playerInstance.transform.position = Vector3.MoveTowards(
                playerInstance.transform.position, 
                walkTarget, 
                step
            );
            
            // Check if we've reached the target
            if (Vector3.Distance(playerInstance.transform.position, walkTarget) < 0.001f)
            {
                // Stop moving if we've reached the walk target
                isPlayerMoving = false;
                
                // Slow down to idle animation
                if (playerAnimator != null)
                {
                    StartCoroutine(SlowToIdle());
                }
            }
        }
    }

    private IEnumerator SlowToIdle()
    {
        // Gradually slow down the animation from walking to idle
        float currentSpeed = 0.2f;
        float targetSpeed = 0f;
        float transitionTime = 1.0f;
        float elapsedTime = 0f;
        
        while (elapsedTime < transitionTime)
        {
            elapsedTime += Time.deltaTime;
            float t = elapsedTime / transitionTime;
            float newSpeed = Mathf.Lerp(currentSpeed, targetSpeed, t);
            
            if (playerAnimator != null)
            {
                playerAnimator.SetFloat("Speed", newSpeed);
            }
            
            yield return null;
        }
        
        // Ensure we end at exactly idle
        if (playerAnimator != null)
        {
            playerAnimator.SetFloat("Speed", 0f);
        }
        
        // Maybe also play an idle pose animation here
        if (playerAnimator != null && playerAnimator.parameters.Length > 0)
        {
            // Look for an "IdlePose" trigger parameter
            for (int i = 0; i < playerAnimator.parameters.Length; i++)
            {
                if (playerAnimator.parameters[i].name == "IdlePose" && 
                    playerAnimator.parameters[i].type == AnimatorControllerParameterType.Trigger)
                {
                    playerAnimator.SetTrigger("IdlePose");
                    break;
                }
            }
        }
    }

    private void SetupDollyCamera()
    {
        if (dollyCart != null)
        {
            // Set the dolly cart speed to match the player walk speed
            dollyCart.m_Speed = 0f; // Initially stopped - we'll control it manually
            
            // We'll manually move the dolly cart to follow the player
            StartCoroutine(ControlDollyMovement());
        }
        
        // Set camera to face player with cinematic settings
        if (dollyCamera != null)
        {
            // Set camera to look at player's face level with cinematic settings
            dollyCamera.m_Lens.FieldOfView = 35f; // Narrower FOV for more cinematic look
            
            // Add noise for subtle camera shake (handheld effect)
            noise = dollyCamera.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
            if (noise == null)
            {
                noise = dollyCamera.AddCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
            }
            noise.m_AmplitudeGain = cameraShakeIntensity;
            noise.m_FrequencyGain = 0.3f;
            
            // Add Composer component for proper framing
            var composer = dollyCamera.GetCinemachineComponent<CinemachineComposer>();
            if (composer == null)
            {
                composer = dollyCamera.AddCinemachineComponent<CinemachineComposer>();
            }
            composer.m_TrackedObjectOffset = new Vector3(0, 0.5f, 0); // Offset for proper framing
            composer.m_LookaheadTime = 0.2f;  // Slight lookahead
            composer.m_DeadZoneWidth = 0.1f;  // Small dead zone
            composer.m_DeadZoneHeight = 0.1f;
            composer.m_SoftZoneWidth = 0.6f;  // Soft zone for smoother transitions
            composer.m_SoftZoneHeight = 0.6f;
            
            // Add a slight Dutch angle for more dramatic effect
            dollyCamera.m_Lens.Dutch = 2f; // Slight tilt
        }
    }

    private IEnumerator ControlDollyMovement()
    {
        if (dollyCart == null || playerInstance == null)
            yield break;
        
        // Wait until player is initialized
        while (playerInstance == null)
            yield return null;
        
        // Get the dolly path
        var dollyPath = dollyCart.m_Path as CinemachineSmoothPath;
        if (dollyPath == null)
            yield break;
        
        // Reset the dolly cart to the beginning of the path
        dollyCart.m_Position = 0;
        
        // Position the camera in front of the player
        PositionCameraInFrontOfPlayer(dollyPath);
        
        // Wait a frame to ensure everything is initialized
        yield return null;
        
        float initialPosition = dollyCart.m_Position;
        Vector3 initialPlayerPos = playerInstance.transform.position;
        
        // While the player is moving
        while (isPlayerMoving && !isTransitioning)
        {
            // Calculate how far the player has moved from start
            float playerProgress = Vector3.Distance(initialPlayerPos, playerInstance.transform.position);
            
            // Convert player progress to path position (full path length)
            float pathLength = dollyPath.PathLength;
            float targetPosition = initialPosition + (playerProgress * (pathLength / 15f)); // 15 is the walk distance
            
            // Update the dolly cart position to match player progress with cinematic easing
            dollyCart.m_Position = Mathf.SmoothStep(
                dollyCart.m_Position, 
                targetPosition, 
                Time.deltaTime * 2.5f
            );
            
            yield return null;
        }
    }

    private void PositionCameraInFrontOfPlayer(CinemachineSmoothPath path)
    {
        if (path == null || playerInstance == null) return;
        
        // Create a new path that starts in front of the player and moves backward
        path.m_Waypoints = new CinemachineSmoothPath.Waypoint[5]; // Added one more point for smoother path
        
        // Get the player's position and forward direction (which is now facing toward camera)
        Vector3 playerPos = playerInstance.transform.position;
        Vector3 playerForward = playerInstance.transform.forward; // This is now pointing toward camera
        
        // Since player is facing toward camera now, we position camera in front of them along that direction
        Vector3 frontPosition = playerPos + (playerForward * 5f); // 5 units in front in direction they face
        frontPosition.y = playerPos.y + 1.7f; // Slightly above eye level
        
        // Create a path that camera follows as player walks toward it
        path.m_Waypoints[0] = new CinemachineSmoothPath.Waypoint { 
            position = frontPosition, 
            roll = 0 
        };
        
        // Second point - slightly farther ahead of player
        path.m_Waypoints[1] = new CinemachineSmoothPath.Waypoint { 
            position = frontPosition + (playerForward * 3f) + new Vector3(0.3f, 0.2f, 0), 
            roll = 2 // Slight roll
        };
        
        // Third point - even farther ahead of player
        path.m_Waypoints[2] = new CinemachineSmoothPath.Waypoint { 
            position = frontPosition + (playerForward * 6f) + new Vector3(-0.3f, 0.5f, 0), 
            roll = -2 // Opposite roll
        };
        
        // Fourth point - farther ahead, wider angle 
        path.m_Waypoints[3] = new CinemachineSmoothPath.Waypoint { 
            position = frontPosition + (playerForward * 9f) + new Vector3(0.5f, 0.8f, 0), 
            roll = 3 // Roll again
        };
        
        // Fifth point - final camera position
        path.m_Waypoints[4] = new CinemachineSmoothPath.Waypoint { 
            position = frontPosition + (playerForward * 12f) + new Vector3(0, 1f, 0), 
            roll = 0 // Level out at the end
        };
        
        Debug.Log("Camera path positioned in front of player (player facing camera): " + playerPos + " -> " + frontPosition);
    }

    private IEnumerator SpawnPlayerAndStartWalking()
    {
        // Wait one frame to ensure SelectionOptions is initialized
        yield return null;
        
        // Get the selected player info from SelectionOptions
        SpawnSelectedPlayer();
        
        if (playerInstance != null)
        {
            // Make the player face toward the camera (opposite of default forward)
            walkDirection = Vector3.forward * -1; // Reversed - walk toward the beginning (and camera)
            playerInstance.transform.rotation = Quaternion.LookRotation(walkDirection);
            
            // Set the walk target - a point ahead of where the player spawned, toward the camera
            walkTarget = spawnPoint.position + (walkDirection * 15f);
            
            // Configure the player for walking animation
            ConfigurePlayerForEntrance();
            
            // Make the dolly camera follow the player if needed
            if (dollyCamera != null && playerInstance != null)
            {
                SetDollyCameraTarget();
            }
            
            // Create ground check setup to avoid GroundCheck errors
            SetupGroundCheck();
            
            // Start walking
            isPlayerMoving = true;
        }
        else
        {
            Debug.LogError("Failed to spawn player in WaitingScene");
            SkipWaitingScene(); // Skip if something went wrong
        }
    }

    private void SetupGroundCheck()
    {
        if (playerInstance == null) return;
        
        // Check if player has a GroundCheck component and set it up
        GroundCheck groundCheck = playerInstance.GetComponent<GroundCheck>();
        if (groundCheck != null)
        {
            // We need to properly create the floor_limit as a child of the player
            // First check if floor_limit already exists as a child
            Transform existingFloorLimit = playerInstance.transform.Find("floor_limit");
            
            if (existingFloorLimit == null)
            {
                // Create a floor limit game object as a child of the player
                GameObject floorLimit = new GameObject("floor_limit");
                floorLimit.transform.SetParent(playerInstance.transform);
                floorLimit.transform.localPosition = new Vector3(0, 0.1f, 0);
                existingFloorLimit = floorLimit.transform;
                
                Debug.Log("Created new floor_limit for player: " + playerInstance.name);
            }
            
            // Assign the floor limit to the ground check
            groundCheck.groundCheckTransform = existingFloorLimit;
            
            // Set ground layer mask
            groundCheck.groundLayer = LayerMask.GetMask("Ground");
            if (groundCheck.groundLayer == 0)
            {
                // Fallback to default layer if Ground layer doesn't exist
                groundCheck.groundLayer = 1; // Default layer
                Debug.LogWarning("Ground layer not found. Add a 'Ground' layer in your project settings for best results.");
            }
            
            groundCheck.groundDistance = 0.2f;
            groundCheck.isGrounded = true;
            
            // Force the Start method to run to initialize properly
            groundCheck.enabled = false;
            groundCheck.enabled = true;
            
            Debug.Log("GroundCheck setup complete. groundCheckTransform: " + 
                     (groundCheck.groundCheckTransform != null ? groundCheck.groundCheckTransform.name : "null"));
        }
        else
        {
            Debug.LogWarning("No GroundCheck component found on player: " + playerInstance.name);
        }
        
        // Similar check for FallCheck
        FallCheck fallCheck = playerInstance.GetComponent<FallCheck>();
        if (fallCheck != null)
        {
            // Safe initialization for FallCheck if needed
            fallCheck.enabled = false; // Disable during the waiting scene
        }
    }

    private void SpawnSelectedPlayer()
    {
        if (SelectionOptions.Instance == null)
        {
            Debug.LogError("SelectionOptions.Instance is null");
            return;
        }

        // Check if we're in AI mode or Player mode
        if (SelectionOptions.Instance.isAISelected)
        {
            SpawnAIPlayer();
        }
        else
        {
            SpawnLocalPlayer();
        }
        
        // Rotate player to face the walk direction
        if (playerInstance != null)
        {
            // Set player's forward direction to match the walk direction
            playerInstance.transform.rotation = Quaternion.LookRotation(walkDirection);
        }
    }

    private void SpawnAIPlayer()
    {
        if (SelectionOptions.Instance.selectedPlayersAI == null || SelectionOptions.Instance.selectedPlayersAI.Count == 0)
        {
            Debug.LogError("No AI players selected");
            return;
        }

        // Find the player character (not the AI opponent)
        foreach (var kvp in SelectionOptions.Instance.selectedPlayersAI)
        {
            if (kvp.Value == "PLAYER")
            {
                string playerName = kvp.Key;
                GameObject prefab = CharacterPrefabManager.Instance.GetCharacterPrefab(playerName, false);
                
                if (prefab != null)
                {
                    playerInstance = Instantiate(prefab, spawnPoint.position, spawnPoint.rotation);
                    playerInstance.tag = "Player";
                    break;
                }
            }
        }
    }

    private void SpawnLocalPlayer()
    {
        if (SelectionOptions.Instance.selectedPlayerDevice == null || SelectionOptions.Instance.selectedPlayerDevice.Count == 0)
        {
            Debug.LogError("No players selected");
            return;
        }

        // For local multiplayer, just spawn the first player for the entrance
        foreach (var kvp in SelectionOptions.Instance.selectedPlayerDevice)
        {
            string playerName = kvp.Key;
            GameObject prefab = CharacterPrefabManager.Instance.GetCharacterPrefab(playerName, false);
            
            if (prefab != null)
            {
                playerInstance = Instantiate(prefab, spawnPoint.position, spawnPoint.rotation);
                playerInstance.tag = "Player";
                break; // Just spawn the first player for the entrance
            }
        }
    }

    private void ConfigurePlayerForEntrance()
    {
        if (playerInstance == null) return;

        // Get or add required components
        playerAnimator = playerInstance.GetComponent<Animator>();
        if (playerAnimator == null)
        {
            playerAnimator = playerInstance.AddComponent<Animator>();
        }

        // Play walking animation
        if (playerAnimator != null)
        {
            playerAnimator.SetFloat("Speed", 0.2f); // Set to walking speed in the blend tree
        }

        // Set up walking sound
        footstepAudio = playerInstance.GetComponent<AudioSource>();
        if (footstepAudio == null)
        {
            footstepAudio = playerInstance.AddComponent<AudioSource>();
        }

        // Find walking sound clip from CharacterMovement component if available
        CharacterMovement characterMovement = playerInstance.GetComponent<CharacterMovement>();
        if (characterMovement != null && characterMovement.walkSoundEffect != null)
        {
            footstepAudio.clip = characterMovement.walkSoundEffect;
            footstepAudio.loop = true;
            footstepAudio.volume = 0.7f;
            footstepAudio.Play();
        }
        else
        {
            // Try to find walking sound from resources as fallback
            AudioClip walkSound = Resources.Load<AudioClip>("Sounds/FootstepWalk");
            if (walkSound != null)
            {
                footstepAudio.clip = walkSound;
                footstepAudio.loop = true;
                footstepAudio.volume = 0.7f;
                footstepAudio.Play();
            }
        }

        // Disable player input/controls during entrance
        DisablePlayerControls();
    }

    private void DisablePlayerControls()
    {
        // Disable any scripts that would allow player control
        if (playerInstance == null) return;

        // Disable PlayerScript if it exists
        PlayerScript playerScript = playerInstance.GetComponent<PlayerScript>();
        if (playerScript != null)
        {
            playerScript.enabled = false;
        }

        // Disable PlayerInput if it exists
        UnityEngine.InputSystem.PlayerInput playerInput = playerInstance.GetComponent<UnityEngine.InputSystem.PlayerInput>();
        if (playerInput != null)
        {
            playerInput.enabled = false;
        }
    }

    private void SetDollyCameraTarget()
    {
        if (dollyCamera == null || playerInstance == null) return;

        // Find a good target on the player (head bone or similar)
        Transform targetBone = FindCharacterHead(playerInstance);
        if (targetBone != null)
        {
            dollyCamera.Follow = targetBone;
            dollyCamera.LookAt = targetBone;
        }
        else
        {
            // Fallback to the player's transform if no head bone is found
            dollyCamera.Follow = playerInstance.transform;
            dollyCamera.LookAt = playerInstance.transform;
        }
        
        // Adjust camera to look at player's face
        dollyCamera.m_Lens.Dutch = 0;  // No tilt
        dollyCamera.GetCinemachineComponent<CinemachineTransposer>().m_XDamping = 1f;
        dollyCamera.GetCinemachineComponent<CinemachineTransposer>().m_YDamping = 1f;
        dollyCamera.GetCinemachineComponent<CinemachineTransposer>().m_ZDamping = 1f;
    }

    private Transform FindCharacterHead(GameObject character)
    {
        if (character == null) return null;

        string characterName = character.name;
        Transform headTransform = null;

        try
        {
            // This logic is copied from SelectionOptions.FindCharacterHead
            switch (characterName)
            {
                case var _ when characterName.Contains("Cheetah"):
                    headTransform = character.transform.GetChild(9)?.GetChild(6)?.GetChild(1);
                    break;
                case var _ when characterName.Contains("Rabbit"):
                case var _ when characterName.Contains("Eagle"):
                case var _ when characterName.Contains("Hyena"):
                case var _ when characterName.Contains("HM"):
                case var _ when characterName.Contains("Cat"):
                case var _ when characterName.Contains("CAT N"):
                    headTransform = character.transform.GetChild(7)?.GetChild(6)?.GetChild(1);
                    break;
            }
        }
        catch (System.Exception e)
        {
            Debug.LogWarning($"Error finding head for character {characterName}: {e.Message}");
        }

        return headTransform ?? character.transform;
    }

    private IEnumerator CountdownTimer()
    {
        while (remainingTime > 0 && !isTransitioning)
        {
            remainingTime -= Time.deltaTime;
            
            if (countdownText != null)
            {
                countdownText.text = Mathf.Ceil(remainingTime).ToString();
            }
            
            yield return null;
        }
        
        if (!isTransitioning)
        {
            LoadGameScene();
        }
    }

    public void SkipWaitingScene()
    {
        if (!isTransitioning)
        {
            isTransitioning = true;
            LoadGameScene();
        }
    }

    private void LoadGameScene()
    {
        isTransitioning = true;
        StartCoroutine(TransitionToGameScene());
    }

    private IEnumerator TransitionToGameScene()
    {
        // Show loading UI
        if (loadingOverlay != null)
        {
            loadingOverlay.SetActive(true);
        }
        else if (GameManager.Instance != null)
        {
            GameManager.Instance.ShowLoadingUI();
        }
        
        // Give the loading UI time to appear
        yield return new WaitForSeconds(0.5f);

        // Load Game scene (Editor: built-in, Android: custom AssetBundle)
        // SceneLoader now handles everything automatically!
        var asyncLoad = SceneLoader.LoadSceneAsyncOnce("Game", LoadSceneMode.Single, allowSceneActivation: true);
        if (asyncLoad == null)
        {
            // Another load already in progress; wait for it
            while (SceneLoader.IsLoading)
                yield return null;
            yield break;
        }
        
        // Wait for the scene to finish loading (custom loader handles activation internally)
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
    }

    private IEnumerator FadeCrowdAudio()
    {
        // Wait until player is initialized
        while (playerInstance == null)
            yield return null;
        
        if (crowdAudioSource == null)
            yield break;
        
        // Start with low volume
        crowdAudioSource.volume = 0.05f;
        
        // Gradually increase the crowd volume over time
        float elapsed = 0f;
        float fadeDuration = entranceDuration * 0.6f; // Fade in over 60% of the entrance time
        
        while (elapsed < fadeDuration && !isTransitioning)
        {
            elapsed += Time.deltaTime;
            float t = elapsed / fadeDuration;
            
            // Ease-in function for smoother volume increase
            float easedT = 1f - Mathf.Cos(t * Mathf.PI * 0.5f);
            crowdAudioSource.volume = Mathf.Lerp(0.05f, crowdVolume, easedT);
            
            yield return null;
        }
        
        // Ensure we reach the target volume
        if (!isTransitioning)
        {
            crowdAudioSource.volume = crowdVolume;
        }
    }

    private IEnumerator SyncAudioWithCameraShots()
    {
        // Wait for player to be initialized
        while (playerInstance == null)
            yield return null;
            
        if (musicAudioSource == null)
            yield break;
            
        // Initial volume
        musicAudioSource.volume = musicVolume * 0.5f;
        
        while (!isTransitioning)
        {
            // Adjust music based on current camera shot
            switch (currentShot)
            {
                case CameraShot.Establishing:
                    // Softer, establishing music
                    musicAudioSource.volume = Mathf.Lerp(musicAudioSource.volume, musicVolume * 0.6f, Time.deltaTime * 2f);
                    musicAudioSource.pitch = Mathf.Lerp(musicAudioSource.pitch, 0.95f, Time.deltaTime * 2f);
                    
                    // Quieter crowd for establishing shot
                    if (crowdAudioSource != null)
                    {
                        crowdAudioSource.volume = Mathf.Lerp(crowdAudioSource.volume, crowdVolume * 0.3f, Time.deltaTime * 2f);
                    }
                    break;
                    
                case CameraShot.MidShot:
                    // Medium intensity
                    musicAudioSource.volume = Mathf.Lerp(musicAudioSource.volume, musicVolume * 0.7f, Time.deltaTime * 2f);
                    musicAudioSource.pitch = Mathf.Lerp(musicAudioSource.pitch, 1.0f, Time.deltaTime * 2f);
                    
                    // Medium crowd
                    if (crowdAudioSource != null)
                    {
                        crowdAudioSource.volume = Mathf.Lerp(crowdAudioSource.volume, crowdVolume * 0.6f, Time.deltaTime * 2f);
                    }
                    break;
                    
                case CameraShot.CloseUp:
                    // More intense for close-up
                    musicAudioSource.volume = Mathf.Lerp(musicAudioSource.volume, musicVolume * 0.8f, Time.deltaTime * 2f);
                    musicAudioSource.pitch = Mathf.Lerp(musicAudioSource.pitch, 1.05f, Time.deltaTime * 2f);
                    
                    // Growing crowd excitement
                    if (crowdAudioSource != null)
                    {
                        crowdAudioSource.volume = Mathf.Lerp(crowdAudioSource.volume, crowdVolume * 0.8f, Time.deltaTime * 2f);
                    }
                    break;
                    
                case CameraShot.DramaticTilt:
                    // Peak intensity for dramatic tilt
                    musicAudioSource.volume = Mathf.Lerp(musicAudioSource.volume, musicVolume * 1.0f, Time.deltaTime * 2f);
                    musicAudioSource.pitch = Mathf.Lerp(musicAudioSource.pitch, 1.1f, Time.deltaTime * 2f);
                    
                    // Full crowd
                    if (crowdAudioSource != null)
                    {
                        crowdAudioSource.volume = Mathf.Lerp(crowdAudioSource.volume, crowdVolume * 1.0f, Time.deltaTime * 3f);
                    }
                    break;
                    
                case CameraShot.Final:
                    // Resolving, heroic feel for final shot
                    musicAudioSource.volume = Mathf.Lerp(musicAudioSource.volume, musicVolume * 0.9f, Time.deltaTime * 2f);
                    musicAudioSource.pitch = Mathf.Lerp(musicAudioSource.pitch, 1.0f, Time.deltaTime * 2f);
                    
                    // Maintain full crowd volume
                    if (crowdAudioSource != null)
                    {
                        crowdAudioSource.volume = Mathf.Lerp(crowdAudioSource.volume, crowdVolume * 1.0f, Time.deltaTime * 2f);
                    }
                    break;
            }
            
            // Add audio effects during shot transitions
            if (shotTimer > shotDuration - 0.5f && shotTimer < shotDuration - 0.3f)
            {
                // Brief volume swell before camera change
                musicAudioSource.volume = Mathf.Lerp(musicAudioSource.volume, musicVolume * 1.1f, Time.deltaTime * 5f);
                
                // Brief crowd swell at transition
                if (crowdAudioSource != null)
                {
                    crowdAudioSource.volume = Mathf.Lerp(crowdAudioSource.volume, crowdVolume * 1.2f, Time.deltaTime * 5f);
                }
            }
            
            // If footstep audio exists, adjust it based on player movement
            if (footstepAudio != null && playerAnimator != null)
            {
                // Sync footstep volume with animation speed
                float animSpeed = playerAnimator.GetFloat("Speed");
                if (animSpeed > 0.05f && !footstepAudio.isPlaying)
                {
                    footstepAudio.Play();
                }
                else if (animSpeed < 0.05f && footstepAudio.isPlaying)
                {
                    footstepAudio.Pause();
                }
                
                // Adjust footstep volume based on animation speed
                footstepAudio.volume = animSpeed * 0.7f;
            }
            
            yield return null;
        }
        
        // Fade out music when transitioning
        if (isTransitioning)
        {
            float duration = 1.5f;
            float startMusicVolume = musicAudioSource.volume;
            float startCrowdVolume = crowdAudioSource != null ? crowdAudioSource.volume : 0f;
            float elapsed = 0f;
            
            while (elapsed < duration)
            {
                musicAudioSource.volume = Mathf.Lerp(startMusicVolume, 0f, elapsed / duration);
                
                if (crowdAudioSource != null)
                {
                    crowdAudioSource.volume = Mathf.Lerp(startCrowdVolume, 0f, elapsed / duration);
                }
                
                elapsed += Time.deltaTime;
                yield return null;
            }
            
            musicAudioSource.volume = 0f;
            if (crowdAudioSource != null)
            {
                crowdAudioSource.volume = 0f;
            }
        }
    }
}