// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;

namespace HoloToolkit.Unity.SharingWithUNET
{
    /// <summary>
    /// Controls a scrollable list of sessions.
    /// </summary>
    public class ScrollingSessionListUIController : SingleInstance<ScrollingSessionListUIController>
    {
        /// <summary>
        /// Object which controls finding sessions so we know what to put on our buttons.
        /// </summary>
        private NetworkDiscoveryWithAnchors networkDiscovery;

        /// <summary>
        /// Current list of sessions.
        /// TODO: Currently these don't clean up if a session goes away...
        /// </summary>
        private Dictionary<string, NetworkDiscoveryWithAnchors.SessionInfo> sessionList;

        /// <summary>
        /// Keeps track of the current index that is the 'top' of the UI list
        /// to enable scrolling.
        /// </summary>
        private int SessionIndex = 0;

        /// <summary>
        /// List of session controls that will have the session info on them.
        /// </summary>
        public SessionListButton[] SessionControls;

        /// <summary>
        /// Keeps track of the session the user has currently selected.
        /// </summary>
        public NetworkDiscoveryWithAnchors.SessionInfo SelectedSession { get; private set; }

        private void Start()
        {
            // On the immersive device the UI is best placed a little closer than on a HoloLens
#if UNITY_WSA && UNITY_2017_2_OR_NEWER
            if (UnityEngine.XR.WSA.HolographicSettings.IsDisplayOpaque)
            {
                gameObject.GetComponent<SimpleTagalong>().TagalongDistance = 1;
            }
#endif

            // Register for events when sessions are found / joined.
            networkDiscovery = NetworkDiscoveryWithAnchors.Instance;
            networkDiscovery.SessionListChanged += NetworkDiscovery_SessionListChanged;
            networkDiscovery.ConnectionStatusChanged += NetworkDiscovery_ConnectionStatusChanged;
            ScrollSessions(0);
        }

        /// <summary>
        /// When we are connected we want to disable the UI
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">the event data</param>
        private void NetworkDiscovery_ConnectionStatusChanged(object sender, EventArgs e)
        {
            gameObject.SetActive(networkDiscovery.running && !networkDiscovery.isServer);
            // sets the UI to be active when not connected and disabled when connected.
            //SetChildren(networkDiscovery.running && !networkDiscovery.isServer);
        }

        /// <summary>
        /// Called when a session is discovered
        /// </summary>
        /// <param name="sender">The sender of the event</param>
        /// <param name="e">the event data</param>
        private void NetworkDiscovery_SessionListChanged(object sender, EventArgs e)
        {
            sessionList = networkDiscovery.remoteSessions;
            SessionIndex = Mathf.Min(SessionIndex, sessionList.Count);

            // this will force a recalculation of the buttons.
            ScrollSessions(0);
        }

        /// <summary>
        /// Sometimes it is useful to disable rendering 
        /// </summary>
        /// <param name="Enabled"></param>
        void SetChildren(bool Enabled)
        {
            foreach (Renderer mr in GetComponentsInChildren<Renderer>())
            {
                mr.enabled = Enabled;
            }

            foreach (BoxCollider bc in GetComponentsInChildren<BoxCollider>())
            {
                bc.enabled = Enabled;
            }
        }

        /// <summary>
        /// Updates which session is the 'top' session in the list, and sets the 
        /// session buttons accordingly
        /// </summary>
        /// <param name="Direction">are we scrolling up, down, or not scrolling</param>
        public void ScrollSessions(int Direction)
        {
            int sessionCount = sessionList == null ? 0 : sessionList.Count;
            // Update the session index
            SessionIndex = Mathf.Clamp(SessionIndex + Direction, 0, Mathf.Max(0, sessionCount - SessionControls.Length));

            // Update all of the controls
            for (int index = 0; index < SessionControls.Length; index++)
            {
                if (SessionIndex + index < sessionCount)
                {
                    SessionControls[index].gameObject.SetActive(true);
                    NetworkDiscoveryWithAnchors.SessionInfo sessionInfo = sessionList.Values.ElementAt(SessionIndex + index);
                    SessionControls[index].SetSessionInfo(sessionInfo);
                }
                else
                {
                    SessionControls[index].gameObject.SetActive(false);
                }
            }
        }

        /// <summary>
        /// Sets the selected session
        /// </summary>
        /// <param name="sessionInfo">The session to set as selected</param>
        public void SetSelectedSession(NetworkDiscoveryWithAnchors.SessionInfo sessionInfo)
        {
            SelectedSession = sessionInfo;
            // Recalculating the session list so we can update the text colors.  
            ScrollSessions(0);
        }

        /// <summary>
        /// Joins the selected session if there is a selected session.
        /// </summary>
        public void JoinSelectedSession()
        {
            if (SelectedSession != null && networkDiscovery.running)
            {
                networkDiscovery.JoinSession(SelectedSession);
            }
        }
    }
}