using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using Photon.Pun;

// Custom class used in Leaderboard functions
public class ButtonInfo
{
    public TextMeshProUGUI name;        // Name text holder on the place button
    public TextMeshProUGUI placeNumber; // Place text holder on the place button
    public Vector3 goalPosition;        // Stores where our button is on screen
    public RectTransform rectTransform; // The anchored position of the button
    public RacerCore kart;              // Kart associated with this button
    public int position;                // The current stored position of this racer
    public bool active;                 // Whether or not the racer is active
    public bool moving;                 // Whether or not the button is moving
    public bool completed;              // Whether or not the racer has completed the race

    public ButtonInfo(TextMeshProUGUI name, TextMeshProUGUI placeNumber, Vector3 goalPosition, RectTransform rectTransform, RacerCore kart = null, int position = 0, bool active = false, bool moving = false, bool completed = false)
    {
        this.name = name;
        this.placeNumber = placeNumber;
        this.goalPosition = goalPosition;
        this.rectTransform = rectTransform;
        this.kart = kart;
        this.position = position;
        this.active = active;
        this.moving = moving;
        this.completed = completed;
    }
}

public class LeaderboardDisplay : MonoBehaviour
{
    [Header("Spectator Info")]
    
    [SerializeField] SpectatorController m_spectatorController;
    public SpectatorController SpectatorController { get { return m_spectatorController; } }

    [Header("Button Data")]
    [SerializeField] LeaderboardButton m_buttonPrefab;

    [Header("Leaderboard Data")]
    [SerializeField] SceneField m_lobbyScene;
    [SerializeField] Dictionary<RacerCore, LeaderboardButton> m_racerButtons = new Dictionary<RacerCore, LeaderboardButton>();

    [Header("Leaderboard Settings")]
    [SerializeField] List<Color> m_placeColors;
    public Color[] PlaceColors { get { return m_placeColors.ToArray(); } }
    [SerializeField] float m_buttonMoveTime = 0.2f;
    public float ButtonMoveTime { get { return m_buttonMoveTime; } }
    [SerializeField] float m_buttonSpacing = 28f;
    public float ButtonSpacing { get { return m_buttonSpacing; } }
    [SerializeField] Vector2 m_screenOffset = new Vector2(0, 140f);
    public Vector3 ScreenOffset { get { return m_screenOffset; } }

    [Header("Debug")]
    [SerializeField] DebugChannelSO m_debugChannel;

    // 
    public bool LeaderboardActive 
    {
        get 
        {
            return transform.childCount > 0 ? gameObject.activeInHierarchy : transform.GetChild(0).gameObject.activeInHierarchy;
        }
        set 
        { 
            foreach (LeaderboardButton _button in m_racerButtons.Values)
            {
                _button.gameObject.SetActive(value);
            }
        } 
    }

    void Start()
    {
        //InitializeButtons();
        //InitializeKartList();
        LeaderboardActive = false;  // The leaderboard is not active at the start of the game
    }

    private void Update()
    {
        if (m_buttonPrefab == null)
            return;

        RacerCore[] _racers = FindObjectsOfType<RacerCore>();

        if (_racers.Length != m_racerButtons.Count)
        {
            foreach (RacerCore _racer in _racers)
                if (!m_racerButtons.ContainsKey(_racer))
                    CreateButtonForRacer(_racer);
        }
    }

    void CreateButtonForRacer(RacerCore _racer)
    {
        LeaderboardButton _newButton = Instantiate(m_buttonPrefab.gameObject, transform).GetComponent<LeaderboardButton>();
        _newButton.transform.position = new Vector3(ScreenOffset.x, 471 - ScreenOffset.y - (FindObjectsOfType<RacerCore>().Length * m_buttonSpacing));

        m_racerButtons.Add(_racer, _newButton);

        _newButton.SetTarget(_racer);
    }

    void OnEnable()
    {
        //if (!spectatorController)   // If we don't have a spectator controller...
        //{
        //    // ...try to get one
        //    spectatorController = FindObjectOfType<SpectatorController>();

        //    // If we still can't find one, disable the leaderboard
        //    if (!spectatorController) 
        //    { 
        //        if (m_debugChannel)
        //            m_debugChannel.Raise(this, "Clicked on a button, but can't find the spectator controller!", DebugChannelSO.Severity.Warning); 
                
        //        return;
        //    }
        //}

        //// Start our Leaderboard Update routine whenever the leaderboard is enabled
        //StartCoroutine("UpdateLeaderboard");
    }
    
    void OnDisable()
    {
        //StopCoroutine("UpdateLeaderboard");     // Shut down the routine
        //LeaderboardActive = false;              // Set the active value to false
    }
    
    // Set up our button list and info
    //void InitializeButtons()
    //{
    //    m_buttonIndex.Clear();    // Clear the list of buttons
    //    m_buttonList.Clear();     // Clear the list of button information

    //    for (int i = 0; i < transform.childCount; i++)                      // Loop through each button in our child list
    //    {
    //        //if (i > buttonIndex.Count) { Debug.LogError("Too many buttons!"); return; }    // Alert us if there are too many buttons in the canvas

    //        m_buttonIndex.Add(transform.GetChild(i).GetComponent<Button>());  // Add each button to our index

    //        if (m_buttonIndex[i])   // If a button was successfully found...
    //        {
    //            TextMeshProUGUI name = transform.GetChild(i).Find("Name").GetComponent<TextMeshProUGUI>();          // Get its name field
    //            TextMeshProUGUI placeNumber = transform.GetChild(i).Find("Place").GetComponent<TextMeshProUGUI>();  // Get its place field
    //            RectTransform rectTransform = m_buttonIndex[i].GetComponent<RectTransform>();

    //            if (name && placeNumber && rectTransform)                   // If we found both fields...
    //            {
    //                name.text = "No Racer";                                 // ...set the default text for the name field
    //                placeNumber.text = "-";                                 // Set the default text for the place field

    //                ButtonInfo newButtonInfo = new ButtonInfo(name, placeNumber, new Vector3(72.4f, 471f), rectTransform);   // Create a new set of button information for this button

    //                m_buttonList.Add(m_buttonIndex[i], newButtonInfo);          // Add the new button info to the list, associated with our button
    //                m_buttonIndex[i].interactable = false;                    // Set the button to inactive by default
    //            }
    //            else                                                        // If we were missing one or both of the two fields...
    //            {
    //                Debug.LogError("Button #" + i + " failed to initialize. Does it have a Name and Place?");   // Let us know there's a problem
    //            }
    //        }
    //    }
    //}
    
    // Assigns a look target to our spectator
    //void ButtonClick(ButtonInfo thisButtonInfo)
    //{
    //    if (!m_spectatorController)                                                               // If we don't have a spectator controller...
    //    {
    //        m_spectatorController = FindObjectOfType<SpectatorController>();  // ...try to find one

    //        // If we still don't have one, log an error and return
    //        if (!m_spectatorController)
    //        {
    //            if (m_debugChannel)
    //                m_debugChannel.Raise(this, "Clicked on a button, but can't find the spectator controller!", DebugChannelSO.Severity.Warning); 

    //            return; 
    //        }
    //    }

    //    m_spectatorController.SetCurrentTarget(thisButtonInfo.kart.transform);                    // Set the current spectator target to the kart
    //}

    // Checks our buttons for updated values and positions
    //void CheckButtons()
    //{
    //    int noPlaces = 0;                                                                           // Tracks the number of racers with no place

    //    for (int i = 0; i < m_buttonIndex.Count; i++)                                                // For each button...
    //    {
    //        int buttonNumber = i + 1;                                                               // ...get a button number for debugging purposes
    //        Button button = m_buttonIndex[i];                                                         // Get the button
    //        ButtonInfo buttonInfo = m_buttonList[button];                                             // Get the button info

    //         !!! Change this later to recursively create button information for any buttons missing them
    //        if (buttonInfo == null)
    //        {
    //            if (m_debugChannel)
    //                m_debugChannel.Raise(this, "No button info found for button #" + buttonNumber, DebugChannelSO.Severity.Warning); 

    //            continue; 
    //        }

    //         If this button is marked as having completed the race, skip it!
    //        if (buttonInfo.completed)
    //            continue;

    //         If this button has no kart associated with it, continue
    //        if (!buttonInfo.kart)
    //            continue;

    //         If our button isn't interactable, make it so!
    //        if (!button.interactable) 
    //            button.interactable = true; 

    //         MOVE PRE-RACE BUTTONS INTO POSITION //
    //        if (SceneManager.GetActiveScene().name == m_lobbyScene.SceneName)
    //        {
    //             ...and the button hasn't been activated yet, set the buttons in a default order
    //            if (!buttonInfo.active)
    //                SetButtonToDefault(button, i);
                
    //            continue;   // Move on to the next button
    //        }


    //         SET ALL RACERS TO ACTIVE IF WE HAVE A KART AND ARE IN A RACE //
    //        if (!buttonInfo.active)
    //            buttonInfo.active = true;


    //         MOVE LIVE BUTTONS INTO POSITION //
    //        if (buttonInfo.position != buttonInfo.kart.PositionInRace)  // If our button's position does not match the kart's place value...
    //        {
    //            if (buttonInfo.kart.PositionInRace < 1) // If our button's position is less than 1...
    //                buttonInfo.position = kartIndex.Count - noPlaces++;  // ...set it to the bottom of the list
    //            else
    //                buttonInfo.position = buttonInfo.kart.PositionInRace;   // Otherwise, set it to the racer's position

    //            buttonInfo.goalPosition = GetCorrectScreenPosition(button); // During a race, we derive our button's position from their place in the race

    //            buttonInfo.placeNumber.text = buttonInfo.position.ToString();   // Update the place text to be our position value
    //            StartCoroutine(MoveButton(button, buttonInfo.goalPosition));    // Move the button into position
    //        }
    //        else
    //        {
    //            if (!buttonInfo.moving) // If the button isn't moving...
    //            {
    //                buttonInfo.rectTransform.anchoredPosition = buttonInfo.goalPosition;    // ...snap the button to its desired position
    //            }
    //        }

    //        if (buttonInfo.kart.RaceCompleted)                                  // If our kart has completed the race...
    //            buttonInfo.completed = true;                                    // Lock our button so that it will no longer move
    //    }
    //}

    // Returns where the button should be on our screen given its position
    //Vector3 GetCorrectScreenPosition(Button button)
    //{
    //    ButtonInfo buttonInfo = m_buttonList[button];
        
    //    float correctVertical;

    //    // If the button's position is not zero...
    //    if (buttonInfo.position != 0)
    //    {
    //        // Set our goal to be the minimum height, plus the remainder of our racer count and the kart's position multiplied by a spacing value
    //        correctVertical = 471 - ScreenOffset.y - (buttonInfo.position * buttonSpacing);
    //    }
    //    else
    //    {
    //        correctVertical = 471;
    //    }

    //    return new Vector3(ScreenOffset.x, correctVertical);
    //}

    // Deactivates a button
    //void SetButtonToInactive(Button button)
    //{
    //    ButtonInfo buttonInfo = m_buttonList[button];                                 // Get the button's info

    //    button.interactable = false;                                                // Make it uninteractable
    //    buttonInfo.position = 0;                                                    // Set its position to zero
    //    buttonInfo.name.text = "No Racer";                                          // Change its name text
    //    buttonInfo.placeNumber.text = "-";                                          // Change its place text
    //    buttonInfo.active = false;                                                  // Set the button as inactive

    //    buttonInfo.goalPosition = GetCorrectScreenPosition(button);                 // During a race, we derive our button's position from their place in the race

    //    StartCoroutine(MoveButton(button, buttonInfo.goalPosition));                // Move the button off-screen
    //}

    // Sets a button to a generic value (mostly for offline racers)
    //void SetButtonToDefault(Button button, int i)
    //{
    //    ButtonInfo buttonInfo = m_buttonList[button];                                 // Get the button's info
    //    buttonInfo.active = true;                                                   // Set the button as active

    //    if (buttonInfo.position != kartIndex.Count - i)                                      // Reset our button position to its default position
    //    {
    //        buttonInfo.position = kartIndex.Count - i;                                       // Set the button position value to the number of racers minus our loop value
    //        buttonInfo.goalPosition = GetCorrectScreenPosition(button);             // During a race, we derive our button's position from their place in the race

    //        buttonInfo.placeNumber.text = m_buttonList[button].position.ToString();   // Update the position text of the button
    //        StartCoroutine(MoveButton(button, buttonInfo.goalPosition));            // Move the button
    //    }
    //}

    //void InitializeKartList()
    //{
    //    kartIndex.Clear();
    //}

    // Updates our kart button assignments when the number of racers in the scene changes
    //void UpdateKarts()
    //{
    //    int place = 0;  // Track the default places of our racers

    //    List<RacerCore> newList = new List<RacerCore>();

    //    for (int i = kartIndex.Count - 1 ; i > 0; i--)
    //    {
    //        if (!kartIndex[i])
    //        {
    //            kartIndex.RemoveAt(i);
    //            //Debug.Log("Removed missing kart #" + (i + 1));
    //            continue;
    //        }
    //        newList.Add(kartIndex[i]);
    //    }

    //    kartIndex = newList;

    //    for (int i = 0; i < m_buttonList.Count; i++)  // For each button in the game...
    //    {

    //        place++;    // Incriment our default place value
    //        Button button = m_buttonIndex[i]; // ...get the button...
    //        ButtonInfo buttonInfo = m_buttonList[button]; // ...and its button info
    //        //Debug.Log("Checking " + button.transform.name + "!");

    //        if (buttonInfo.kart) { place++; buttonInfo.placeNumber.text = (place).ToString(); continue; }  // If this button still has a kart, skip it

    //        RacerCore newKart = FindNewKart(); // Find a new kart

    //        if (!newKart)   // If a new kart cannot be found...
    //        {
    //            //Debug.Log("No new karts to assign! Resetting button values.");
    //            buttonInfo.kart = null;     // ...set the button to have no kart
    //            buttonInfo.position = 0;    // Set its place value to 0
    //            continue;   // Go to the next button
    //        }

    //        buttonInfo.kart = newKart;  // Update our button info
    //        kartIndex.Add(newKart); // Add the kart to our index

    //        string racerName = "Racer"; // Set the default name for each racer

    //        if (buttonInfo.kart.photonView) // If our kart is connected to Photon...
    //        {
    //            //Debug.Log("Kart is connected to Photon");
    //            object prop;                                    // ...get the Display Name property of the player
    //            buttonInfo.kart.photonView.Owner.CustomProperties.TryGetValue("DisplayName", out prop);
    //            if (prop != null) { racerName = prop as string; }   // If we found one, return the property as a string and overwrite the racer name
    //        }


    //        buttonInfo.name.text = racerName;   // Set the button text to the racer's name
    //        buttonInfo.placeNumber.text = (place).ToString();   // Set their place (by default) to the current racer count

    //        button.onClick.RemoveAllListeners();    // Remove any existing listener events from the button
    //        button.onClick.AddListener(() => ButtonClick(buttonInfo));  // Add a new listener using our button's kart
    //        button.GetComponent<RectTransform>().anchoredPosition = new Vector3(72.4f, 471f);  // Move the button to its default location
    //    }
    //}

    //RacerCore FindNewKart()
    //{
    //    RacerCore[] currentKarts = FindObjectsOfType<RacerCore>(); // Get all of the karts in our scene

    //    foreach (RacerCore kart in currentKarts)    // For each kart in our game...
    //    {
    //        if (kartIndex.Contains(kart)) { continue; } // If we already have the kart recorded, skip it

    //        return kart;
    //    }
    //    return null;
    //}

    // This coroutine updates our leaderboard once every second while active
    //IEnumerator UpdateLeaderboard()
    //{
    //    // This makes sure that only one Leaderboard routine is running at any given time
    //    if (LeaderboardActive) { Debug.LogError("Leaderboard is already active!"); yield break; }
    //    LeaderboardActive = true;                   // Let us know that the leaderboard is active

    //    //int count = 0;                              // Keep track of how many times we've looped (for debugging purposes only)

    //    while (true)                                // Loop forevar
    //    {
    //        if (allowLeaderboardUpdates)            // If our leaderboard can update...
    //        {
    //            // Print how many times we've looped to the console

    //            // ...get all karts currently in our scene
    //            KartController[] kartControllers = KartController.FindObjectsOfType<KartController>();

    //            if (kartControllers.Length != kartIndex.Count)   // If the number of karts has changed since we last checked...
    //            {
    //                if (kartControllers.Length < kartIndex.Count)
    //                {
    //                    Debug.Log("Player has left the game...");
    //                }
    //                else
    //                {
    //                    Debug.Log("Player has joined the game!");
    //                }

    //                UpdateKarts();       // ...update the karts list
    //            }

    //            CheckButtons(); // Check if any buttons need to be updated
    //        }
    //        yield return new WaitForSeconds(updateFrequency);    // Suspent for the value of our update frequency
    //    }
    //}
    
    // Moves a button to a new position on the leaderboard
    //IEnumerator MoveButton(Button button, Vector3 endPosition)
    //{
    //    ButtonInfo buttonInfo = m_buttonList[button];
        
    //    if (buttonInfo.moving) { yield break; }                             // Prevent the button from attempting to move if it is already in motion

    //    buttonInfo.moving = true;                                           // Set ourselves to moving
 
    //    float currentTime = 0;                                              // Keeps track of the amount of time we've been moving
    //    Vector3 startPosition = buttonInfo.rectTransform.anchoredPosition;  // Our starting position

    //    while (currentTime <= buttonMoveTime)                               // While our motion is not completed...
    //    {
    //        currentTime += Time.deltaTime;                                  // Add the amount of time between our frames 
    //        float normalizedValue = currentTime / buttonMoveTime;           // Get the percentage of time that has passed over our max move time
    //        Mathf.Clamp(normalizedValue, 0f, buttonMoveTime);               // Clamp the completion value between 0 and 1

    //        buttonInfo.rectTransform.anchoredPosition = Vector3.Lerp(startPosition, endPosition, normalizedValue); // Lerp the position of our button according to our normalized value

    //        yield return null;                                              // Return to the top of the While loop
    //    }

    //    buttonInfo.rectTransform.anchoredPosition = endPosition;            // Snap our button position to the endPosition at the end of its movement to correct for float imperfections
    //    buttonInfo.moving = false;                                          // Set our button to be no longer moving
    //    yield return null;
    //}

    /*
    // Jigs a button if it is currently selected
    IEnumerator JigPlace (Button button)
    {
        float currentTime = 0f;
        float pulseRate = 64f;
        float sizeMultiplier = 1.5f;

        while (true)
        {
            if (buttonList[button].position < 4)
            {
                currentTime += Time.deltaTime;
                float timeRemaining = (pulseRate / buttonList[button].position) - currentTime;
                if (timeRemaining <= 0)
                {
                    currentTime = -timeRemaining;
                }

                buttonList[button].placeNumber.transform.localScale = Vector3.one + (Vector3.one * sizeMultiplier) * Mathf.Sin(currentTime/( pulseRate / buttonList[button].position));
            }
            else
            {
                currentTime = 0f;
                buttonList[button].placeNumber.transform.localScale = Vector3.one;
            }
        }
    }
    */
}
