﻿using AsmodeeDigital.Common.Plugin.UI;
using AsmodeeDigital.PlayReal.Plugin.Manager;
using AsmodeeDigital.PlayReal.Samples.Logic;
using AsmodeeDigital.PlayReal.Samples.UI.LobbyPlayers;
using com.daysofwonder;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace AsmodeeDigital.PlayReal.Samples.Views
{
    /// <summary>
    /// View displayed for the lobby players
    /// </summary>
    public class LobbyPlayersView : MonoBehaviour
    {
        /// <summary>
        /// UI references of LobbyPlayersView
        /// </summary>
        [Serializable]
        public class UI
        {
            [Header("Panels")]
            public Transform ChatPanel;
            public Transform PlayersPanel;

            [Header("Tabs")]
            public TabToggle PlayersTab;
            public TabToggle FriendsTab;

            [Header("Chat")]
            public ScrollRect ChatItemsParent;
            public InputField InputField;
            public List<ChatItem> ChatItems;

            [Header("Players")]
            public ScrollRect LobbyPlayerItemsParent;
            public List<LobbyPlayerItem> LobbyPlayerItems;

            [Header("Popup")]
            public PopupBuddy PopupBudddy;
            public Transform Blocker;
        }

        private TypePlayerList _typePlayerList = TypePlayerList.Friends;

        public ChatItem ChatItemPrefab;
        public LobbyPlayerItem LobbyPlayerItemPrefab;
        public Image PlayerPortraitPrefab;

        public static LobbyPlayersView Instance;

        /// <summary>
        /// UI instance
        /// </summary>
        public UI ui;

        /// <summary>
        /// Lobby logic of the Lobby view
        /// </summary>
        private LobbyPlayersLogic _lobbyPlayersLogic;

        /// <summary>
        /// Chat logic
        /// </summary>
        private ChatLogic _chatLogic;

        private void OnEnable()
        {
            Instance = this;
        }

        private void Start()
        {
            Init();
        }

        #region Private methods
        /// <summary>
        /// Initialize the view - Creating logic - Creating .Net events
        /// </summary>
        private void Init()
        {
            _chatLogic = new ChatLogic(PlayRealManager.Instance.ServerConnection, PlayRealManager.Instance.Persistence);
            _lobbyPlayersLogic = new LobbyPlayersLogic(PlayRealManager.Instance.ServerConnection, PlayRealManager.Instance.Persistence);

            _chatLogic.LogEnabled = PlayRealManager.Instance.LogLogicEnabled;
            _lobbyPlayersLogic.LogEnabled = PlayRealManager.Instance.LogLogicEnabled;

            _chatLogic.ClientChatEvent += ChatLogic_ClientChatEvent;
            _lobbyPlayersLogic.RefreshLobbyPlayerListEvent += LobbyPlayersLogic_RefreshLobbyPlayerListEvent;

            _lobbyPlayersLogic.Init();
            _chatLogic.Init();

            //--- Set focus to the input field
            //ui.InputField.Select();
            //ui.InputField.ActivateInputField();
            //---

            Canvas.ForceUpdateCanvases();
        }

        /// <summary>
        /// Refresh the player list depending on the current Player Tab
        /// </summary>
        private void RefreshLobbyPlayerList()
        {
            List<IPlayer> players = null;

            switch (_typePlayerList)
            {
                case TypePlayerList.Online:
                    players = _lobbyPlayersLogic.GetOnlinePlayers();
                    break;
                case TypePlayerList.Friends:
                    players = _lobbyPlayersLogic.GetFriendsPlayers();
                    break;
                case TypePlayerList.Ignored:
                    players = _lobbyPlayersLogic.GetIgnoredPlayers();
                    break;
                default:
                    break;
            }

            RefreshLobbyPlayerList(players);
        }

        /// <summary>
        /// Show the given player list
        /// </summary>
        /// <param name="players"></param>
        private void RefreshLobbyPlayerList(List<IPlayer> players)
        {
            //---> Deactivate all the items, in order to activate only those to the connected players (in RefreshLobbyPlayerItem)
            ui.LobbyPlayerItems.ForEach(pl => pl.IsActive = false);

            if (players != null)
            {
                for (int i = 0; i < players.Count; i++)
                {
                    RefreshLobbyPlayerItem(players[i]);
                }
            }

            //---> Hide the deactivated items and show the activated
            ui.LobbyPlayerItems.ForEach(pl => pl.gameObject.SetActive(pl.IsActive));
        }

        /// <summary>
        /// Refresh a player item on lobby UI
        /// </summary>
        private void RefreshLobbyPlayerItem(IPlayer player)
        {
            //---> Search LobbyPlayerItem in the list
            LobbyPlayerItem lobbyPlayerItem = ui.LobbyPlayerItems.Find(pl => pl.Player.w_w_w_id == player.w_w_w_id);

            //---> Instantiate if not found
            if (lobbyPlayerItem == null)
            {
                lobbyPlayerItem = ui.LobbyPlayerItems.Find(pl => pl.IsActive == false);

                if (lobbyPlayerItem == null)
                    lobbyPlayerItem = Instantiate<LobbyPlayerItem>(LobbyPlayerItemPrefab);

                lobbyPlayerItem.IsActive = true;

                lobbyPlayerItem.ui.PlayerName.text = player.name;
                lobbyPlayerItem.ui.BuddyListButton.gameObject.SetActive(player.w_w_w_id != PlayRealManager.Instance.Persistence.ConnectedPlayer.w_w_w_id);

                lobbyPlayerItem.transform.SetParent(ui.LobbyPlayerItemsParent.content);
                lobbyPlayerItem.transform.localScale = Vector3.one;

                ui.LobbyPlayerItems.Add(lobbyPlayerItem);
            }
            //---> Active it if found
            else
            {
                lobbyPlayerItem.IsActive = true;
                lobbyPlayerItem.ui.PlayerName.text = player.name;
            }

            lobbyPlayerItem.Player = player;

            //---> Enable the draggable only for the friends list
            lobbyPlayerItem.ui.Draggable.enabled = (_typePlayerList == TypePlayerList.Friends);
        }

        /// <summary>
        /// Add a chat entry in the chat room
        /// </summary>
        /// <param name="chatEntry"></param>
        private void AddChatItem(ChatEntry chatEntry)
        {
            bool isScrollAtBottom = ui.ChatItemsParent.verticalNormalizedPosition < 0.01f;

            ChatItem chatItem = Instantiate<ChatItem>(ChatItemPrefab);

            chatItem.Init(chatEntry, ui.ChatItemsParent.content);

            ui.ChatItems.Add(chatItem);

            //---> Force scroll to bottom if the scroll was already at the bottom (keep the new chat entry visible)
            if (isScrollAtBottom)
            {
                Canvas.ForceUpdateCanvases();
                ui.ChatItemsParent.verticalNormalizedPosition = 0;
            }
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Show the popup buddy
        /// </summary>
        /// <param name="dowId">DoW Id of a player (www_id)</param>
        public void ShowPopupBudddy(int dowId)
        {
            ui.PopupBudddy.Show(dowId);
        }

        /// <summary>
        /// Add a player to the buddy list
        /// </summary>
        /// <param name="DowId">DoW Id of a player (www_id)</param>
        public void AddToBuddyList(int DowId)
        {
            _lobbyPlayersLogic.AddToBuddyList(DowId);
        }

        /// <summary>
        /// Add a player to the ignore list
        /// </summary>
        /// <param name="DowId">DoW Id of a player (www_id)</param>
        public void AddToIgnoreList(int DowId)
        {
            _lobbyPlayersLogic.AddToIgnoreList(DowId);
        }

        /// <summary>
        /// Remove a playerfrom the buddy list
        /// </summary>
        /// <param name="DowId">DoW Id of a player (www_id)</param>
        public void RemoveFromBuddyList(int DowId)
        {
            _lobbyPlayersLogic.RemoveFromBuddyList(DowId);
        }

        /// <summary>
        /// Remove a player from the ignore list
        /// </summary>
        /// <param name="DowId">DoW Id of a player (www_id)</param>
        public void RemoveFromIgnoreList(int DowId)
        {
            _lobbyPlayersLogic.RemoveFromIgnoreList(DowId);
        }

        /// <summary>
        /// Select the friends tab (when INVTATION tab is selected in the lobby game view)
        /// </summary>
        public void SelectFriendsTab()
        {
            ui.PlayersTab.isOn = true;
            ui.PlayersTab.group.NotifyToggleOn(ui.PlayersTab);

            ui.FriendsTab.isOn = true;
            ui.FriendsTab.group.NotifyToggleOn(ui.FriendsTab);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="DoWId"></param>
        /// <returns></returns>
        public IPlayer GetBuddy(int DoWId)
        {
            return _lobbyPlayersLogic.GetBuddy(DoWId);
        }

        public BuddyStatus GetBuddyStatus(int DoWId)
        {
            return _lobbyPlayersLogic.GetBuddyStatus(DoWId);
        }
        #endregion

        #region UI events
        /// <summary>
        /// Send chat when the user validate his message
        /// </summary>
        /// <param name="value"></param>
        public void ChatInput_OnEndEdit(string value)
        {
            if (!String.IsNullOrEmpty(ui.InputField.text))
            {
                _chatLogic.SendChatToLobby(ui.InputField.text);

                ui.InputField.text = string.Empty;
                ui.InputField.Select();
                ui.InputField.ActivateInputField();
            }
        }

        /// <summary>
        /// Show the chat panel
        /// </summary>
        /// <param name="value">Selected state of the tab</param>
        public void ShowChatPanel(bool isOn)
        {
            //---> Show the panel only if the tab is selected
            if (!isOn)
                return;

            ui.ChatPanel.gameObject.SetActive(true);
            ui.PlayersPanel.gameObject.SetActive(false);
        }

        /// <summary>
        /// Show the players panel
        /// </summary>
        /// <param name="value">Selected state of the tab</param>
        public void ShowPlayersPanel(bool isOn)
        {
            //---> Show the panel only if the tab is selected
            if (!isOn)
                return;

            ui.ChatPanel.gameObject.SetActive(false);
            ui.PlayersPanel.gameObject.SetActive(true);
        }

        /// <summary>
        /// Show the online players panel
        /// </summary>
        /// <param name="value">Selected state of the tab</param>
        public void ShowPlayersOnline(bool isOn)
        {
            //---> Show the panel only if the tab is selected
            if (!isOn)
                return;

            _typePlayerList = TypePlayerList.Online;
            RefreshLobbyPlayerList();
        }

        /// <summary>
        /// Show the friends panel
        /// </summary>
        /// <param name="value">Selected state of the tab</param>
        public void ShowPlayersFriends(bool isOn)
        {
            //---> Show the panel only if the tab is selected
            if (!isOn)
                return;

            _typePlayerList = TypePlayerList.Friends;
            RefreshLobbyPlayerList();
        }

        /// <summary>
        /// Show the ignored players panel
        /// </summary>
        /// <param name="value">Selected state of the tab</param>
        public void ShowPlayersIgnored(bool isOn)
        {
            if (!isOn)
                return;

            _typePlayerList = TypePlayerList.Ignored;
            RefreshLobbyPlayerList();
        }
        #endregion

        #region Logic events callbacks
        /// <summary>
        /// Event fired when a chat entry is coming
        /// </summary>
        /// <param name="chatEntry"></param>
        private void ChatLogic_ClientChatEvent(ChatEntry chatEntry)
        {
            AddChatItem(chatEntry);
        }

        /// <summary>
        /// Event fired when the lobby player list must be refreshed
        /// </summary>
        private void LobbyPlayersLogic_RefreshLobbyPlayerListEvent()
        {
            RefreshLobbyPlayerList();
        }
        #endregion

        /// <summary>
        /// Dispose events and logic
        /// </summary>
        private void OnDestroy()
        {
            _chatLogic.ClientChatEvent -= ChatLogic_ClientChatEvent;
            _lobbyPlayersLogic.RefreshLobbyPlayerListEvent -= LobbyPlayersLogic_RefreshLobbyPlayerListEvent;

            _lobbyPlayersLogic.Dispose();
        }

        public enum TypePlayerList
        {
            Online,
            Friends,
            Ignored
        }
    }
}
