using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;
using Photon.Pun;
using Cinemachine;
using UnityEngine.Rendering.PostProcessing;

public class CameraControllerV2 : MonoBehaviourPunCallbacks
{
    [Header("Camera Components")]
    public CinemachineVirtualCamera cinacam;
    public SpectatorController spectatorController;

    [Header("Visual Components")]

    [Header("Kart Control Data")]
    [ReadOnly]
    public RacerCore currentRacer;
    [Tooltip("Toggles usage of Cinemacam rather than custom camera behaviors")]
    public bool toggleCinemaCam;

    [Header("Audio")]
    public AudioManager audioManager;
    public AudioSource music;
    public AudioSource lapSounds;
    public AudioClip countNormal;
    public AudioClip countEnd;
    
    [Header("UI")]
    public Transform countdownCanvas;
    public GameObject playAgainButton;
    public TextMeshProUGUI timer;
    public TimeDisplayHandler timeDisplay;
    public TextMeshProUGUI lapCountText;
    public TextMeshProUGUI positionDisplay;

    [Header("Follow Behaviors")]
    //[HideInInspector]
    [SerializeField] RacerCore m_localPlayer;
    public bool spectatorOverride = false;
    public bool forceDisableFollow = false;
    public bool enableFollow;

    [Header("Follow Settings")]
    //[HideInInspector]
    public Vector3 camFollowOffset = new Vector3(0f, 2f, -10f);
    public Vector3 camLookOffset = new Vector3(0f, 0f, 5f);
    public bool snapCam;
    [SerializeField] float m_camPivotSpeed = 5f;
    [SerializeField] float m_camSmoothTime = 0.15f;
    [SerializeField] float m_camFollowSpeed = 5000f;
    [SerializeField] float m_standaloneMultiplier;

    [Header("Debug")]
    [SerializeField] bool m_enableDebugMessages = false;

    Vector3 camVelocity = Vector3.zero;

    private void OnValidate()
    {
        SetDefaults();
    }
    private void Start()
    {
        #if UNITY_EDITOR
        return;
        #endif
        //m_camPivotSpeed *= m_standaloneMultiplier;
        //m_camSmoothTime /= m_standaloneMultiplier;
        //m_camFollowSpeed *= m_standaloneMultiplier;
    }
    void LateUpdate()
    {
        // Only enables movement behaviors if our override and spectator methods are disabled
        if (!forceDisableFollow && !spectatorController.isActiveAndEnabled)
        {
            if (!currentRacer)
            {
                SetTarget();
            }
            else if (!toggleCinemaCam)
            {
                UpdateCameraPositionAndRotation(currentRacer, snapCam);
            }
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(transform.position, 0.2f);
    }

    void SetDefaults()
    {
        if (!cinacam) { cinacam = FindObjectOfType<CinemachineVirtualCamera>(); }
        if (!spectatorController) { spectatorController = transform.parent.parent.GetComponentInChildren<SpectatorController>(); }
        if (!audioManager) { audioManager = FindObjectOfType<AudioManager>(); }
    }

    /// <summary>
    /// Finds a Racer to follow, preferring locally controlled karts
    /// </summary>
    /// <returns></returns>
    public bool FindLegalFollowTarget()
    {
        RacerCore[] racers = FindObjectsOfType<RacerCore>();
        if (racers.Length > 0)
        {
            foreach (RacerCore r in racers)
            {
                if (r.photonView && r.photonView.IsMine && PhotonNetwork.IsConnected)
                {
                    currentRacer = r;
                    return true;
                }
                else if (!r.photonView || !PhotonNetwork.IsConnected)
                {
                    currentRacer = r;
                    return true;
                }
            }
        }
        return false;
    }

    /// <summary>
    /// Sets the camera's target
    /// </summary>
    void SetTarget()
    {
        RacerCore[] racers = FindObjectsOfType<RacerCore>();
        RacerCore me = null;
        if (racers.Length > 0)
        {
            foreach (RacerCore r in racers)
            {
                if (r.photonView && r.photonView.IsMine && PhotonNetwork.IsConnected)
                {
                    me = r;
                    break;
                }
                else if (!r.photonView || !PhotonNetwork.IsConnected)
                {
                    me = r;
                    break;
                }
            }
        }

        if (me)
        {
            currentRacer = me;
            if (toggleCinemaCam)
            {
                cinacam.Follow = me.transform.GetChild(0);
                cinacam.LookAt = me.transform;
            }
        }

        // DEPRECATED AUTO-DISABLE BEHAVIOR
        #region
        /*
        else
        {
            //Debug.LogError("No player was found. Did you forget to add one to the scene?");
            forceDisableFollow = true;
        }
        */
        #endregion
    }

    /// <summary>
    /// Moves to the specified Racer's position
    /// </summary>
    /// <param name="me"></param>
    public void MoveToSpecificTarget(RacerCore me)
    {
        currentRacer = me;

        if (toggleCinemaCam)
        {
            cinacam.Follow = currentRacer.transform.GetChild(0);
            cinacam.LookAt = currentRacer.transform;
        }

        // DEPRECATED PIVOT BEHAVIORS
        #region
        //kController.camPivot = GameObject.FindGameObjectWithTag("Camera Pivot").transform;
        //target = pBase.camPivot;
        /*
		if (!collision.myCam)
		{
			collision.Initialize(GetComponent<Camera>());
			collision.UpdateCameraClipPoints(transform.position, transform.rotation, ref collision.adjustedCameraClipPoints);
			collision.UpdateCameraClipPoints(destination, transform.rotation, ref collision.desiredCameraClipPoints);
		}
		*/
        #endregion
    }

    /// <summary>
    /// Returns a position from a target transform using an given offset vector
    /// </summary>
    /// <param name="targetTransform"></param>
    /// <param name="targetOffset"></param>
    /// <returns></returns>
    public Vector3 ReturnOffsetPositionAtVector(Transform targetTransform, Vector3 targetOffset)
    {
        // Derive a return position by adding the product of each directional vector and their respective offset values to the target position
        Vector3 returnPosition = targetTransform.position + (targetTransform.right * targetOffset.x) + (targetTransform.up * targetOffset.y) + (targetTransform.forward * targetOffset.z);

        // Return the derived position
        return returnPosition;
    }

    /// <summary>
    /// Updates the camera's position and rotation each frame
    /// </summary>
    /// <param name="target"></param>
    /// <param name="snap"></param>
    public void UpdateCameraPositionAndRotation(RacerCore target, bool snap)
    {
        // Disables the following behaviors if we were unable to find a legal target
        if (forceDisableFollow)
        {
            // Recursively checks for a new target if we have none
            if (!FindLegalFollowTarget())
            {
                return;
            }
        }

        if (enableFollow)
        {
            //float followDistance = camFollowOffset.magnitude;
            
            DeriveLookPosition(currentRacer.transform, snap);
            DeriveLookRotation(currentRacer.Animator.Player, snap);
        }
    }

    void DeriveLookPosition(Transform target, bool snap)
    {
        Vector3 targetPosition = ReturnOffsetPositionAtVector(target, camFollowOffset);

        // Only move if we are not currently on target
        if (transform.position != targetPosition)
        {
            // Only move smoothly if we are not currently snapping to target
            if (!snap)
            {
                // Move the camera smoothly into position
                //transform.position = Vector3.SmoothDamp(transform.position, targetPosition, ref camVelocity, m_camSmoothTime, m_camFollowSpeed * (Vector3.Distance(target.position, targetPosition) / camFollowOffset.magnitude) * Time.unscaledDeltaTime);
                transform.position = Vector3.MoveTowards(transform.position, targetPosition, m_camFollowSpeed * (Vector3.Distance(transform.position, targetPosition) / camFollowOffset.magnitude) * Time.unscaledDeltaTime);
            }
            else
            {
                // Otherwise, snap to the target
                transform.position = targetPosition;
            }
        }
    }

    void DeriveLookRotation(Transform target, bool snap)
    {
        Vector3 lookPosition = ReturnOffsetPositionAtVector(target, camLookOffset);

        // Store a reference to our current rotation in Euler
        Vector3 currentRotation = new Vector3(transform.rotation.x, transform.rotation.y, transform.rotation.z);
        
        // Check if the angle between our current and target look position is
        // different from our current camera rotation
        if (Vector3.Angle((target.position - transform.position).normalized, currentRotation.normalized) != 0)
        {
            // Only lerp the camera to the ourrect rotation if we are not snapping to target
            if (!snap)
            {
                Quaternion adjustQuaternion = transform.rotation;

                // Use Spherical Interpolation to move from our current rotation to the target rotation over time
                transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(lookPosition - transform.position, target.up), m_camPivotSpeed * Time.unscaledDeltaTime);
            }
            else
            {
                // Otherwise snap to target
                transform.LookAt(lookPosition, currentRacer.transform.up);
            }
        }
    }
}
