using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;
using UnityEngine.UI;
using System.Linq;

namespace SimplifyXR
{

    public interface IMenuItem
    {
        public string GetName();
        public Sprite GetIcon();
        public void MenuAction();
        public string GetPath();
        public void SetMenuAction(System.Action action);
    }


    [System.Serializable]
    public class MainMenuItem : IMenuItem, IModuleOrSceneReference
    {
        public string sceneKey;
        public bool isModule;
        public string title;
        public Sprite sceneIcon;
        public string menuPath;
        public Object scene;

        public Sprite GetIcon()
        {
            return sceneIcon;
        }

        public string GetName()
        {
            return title;
        }

        public string GetPath()
        {
            return menuPath + "/";
        }

        public bool IsModule()
        {
            return isModule;
        }

        public SceneOrModuleLoadType LoadType()
        {
            return SceneOrModuleLoadType.ReplaceCurrentScene;
        }

        public void MenuAction()
        {
            StandardAssetsHelpers.HandleModuleSceneLoading(this);
        }

        public string SceneKey()
        {
            return sceneKey;
        }

        public void SetMenuAction(System.Action action)
        {
            throw new System.NotImplementedException();
        }
    }

    public enum MenuType
    {
        singleLevel,
        multiLevel
    }

    [System.Serializable]
    public class Category : IMenuItem
    {
        public string categoryName;
        public Sprite categoryIcon;
        public List<Category> levelCategories;
        [HideInInspector]
        public string menuPath;

        public System.Action menuAction;

        public Sprite GetIcon()
        {
            return categoryIcon;
        }

        public string GetName()
        {
            return categoryName;
        }

        public string GetPath()
        {
            return menuPath;
        }

        public void MenuAction()
        {
            menuAction?.Invoke();
        }

        public void SetMenuAction(System.Action action)
        {
            menuAction = action;
        }
    }

    public class MainMenuFeatures : BasePanelFeatures
    {

        [Header("Main Menu Features")]
        [Tooltip("Customize the behavior of the Main Menu Panel features.")]
        public bool customizeMainMenuFeatures;

        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals), 
            Tooltip("Auto configure pagination for menu options.")]
        public bool autoPagination = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals),
            Tooltip("Auto generate menu items from scenes and modules listed.")]
        public bool autoMenuItems = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals), 
            Tooltip("Auto configure the left and right nav paging buttons.")]
        public bool autoNav = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals),
            Tooltip("Auto configure the menu search buttons and logic.")]
        public bool autoSearch = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals),
            Tooltip("Automatically populate the main menu title.")]
        public bool autoTitle = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals),
            Tooltip("Automatically populate the main menu subtitle.")]
        public bool autoSubtitle = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals), 
            Tooltip("Auto configure the back button for menu level navigation.")]
        public bool autoBackLevel = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals),
            Tooltip("Automatically displays a Splash Screen on enable.")]
        public bool showSplashScreenOnEnable = true;
        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals),
           Tooltip("Automatically displays version text that matches app version in the Splash Screen.")]
        public bool autoVersionText = true;

        [Conditional("customizeMainMenuFeatures", true, ComparisonType.Equals),
          Tooltip("Automatically persists menu state for category levels and and menu depth.")]
        public bool autoPersistMenuState = true;

        [Header("Menu Configuration")]
        [Tooltip("Select whether the menu should have a singe or multiple layers of navigation")]
        public MenuType menuType;

        [Conditional("menuType", MenuType.multiLevel, ComparisonType.Equals),
                    Tooltip("Select a multi-level menu map scriptable object that specifies the level hierarchy.")]
        public MenuMap menuMap;

        [Tooltip("Specify the grid size per page for scene and category options.")]
        public GridProperties gridSize;


        [HideInInspector]
        public List<MainMenuItem> menuItems;

        public Button rightNavButton => baseRoot.GetElementAsType<Button>("Right Nav");
        public Button leftNavButton => baseRoot.GetElementAsType<Button>("Left Nav");
        public Button searchButton => baseRoot.GetElementAsType<Button>("Search Button");
        public Button backButton => baseRoot.GetElementAsType<Button>("Back Button");

        public TextMeshProUGUI menuSubTitle => baseRoot.GetElementAsType<TextMeshProUGUI>("Menu SubTitle");
        public TextMeshProUGUI appVersionText => baseRoot.GetElementAsType<TextMeshProUGUI>("Version Text");

        public Image companyLogo => baseRoot.GetElementAsType<Image>("Company Logo");
        public Image productLogo => baseRoot.GetElementAsType<Image>("Product Logo");

        public PagingNav pagingNav => baseRoot.GetElementAsType<PagingNav>("Pagination");
        public MenuGrid menuGrid => baseRoot.GetElementAsType<MenuGrid>("Menu Grid");

        public TMP_InputField searchInput => baseRoot.GetElementAsType<TMP_InputField>("Search Input");

        public GameObject mainMenuUI => baseRoot.GetElementAsType<GameObject>("Main Menu UI");
        public GameObject splashUI => baseRoot.GetElementAsType<GameObject>("Splash Elements");



        private Dictionary<string, List<IMenuItem>> categoryDictionary;
        public Dictionary<int, List<IMenuItem>> pagingDictionary;
        
        private int activePage;
        private string currentPath = "";
        public string CurrentPath { get { return currentPath; } set { currentPath = value; } } // is this required yet?


        private string filterString;

        private int gridTotal => gridSize.maxColumns* gridSize.maxRows;
        private int pagesAtLevel => pagingDictionary.Count;

        public List<IMenuItem> levelItems;

        public List<IMenuItem> filteredItems => !string.IsNullOrEmpty(filterString) ?
            menuItems.Where(
            e => e.title.ToLower().Contains(filterString))?.Cast<IMenuItem>().ToList() :
            levelItems?.Cast<IMenuItem>()?.ToList();       


        public new void Awake()
        {
            base.Awake();

            //Let's get currentPath and showSplashScreenOnEnable (currently in PlayerPrefs)
            if (autoPersistMenuState) GetPersistentData();

            InitializeMenu();          

            if (autoNav) rightNavButton.onClick.AddListener(PageNext);
            if (autoNav) leftNavButton.onClick.AddListener(PagePrevious);
            if(autoSearch) searchButton.onClick.AddListener(Search);
            backButton.onClick.AddListener(GoBackLevel);

            if(autoVersionText) UpdateAppVersion();
        }

        private void UpdateAppVersion()
        {
            appVersionText.text = "version " + Application.version;
        }

        public new void OnDestroy()
        {
            base.OnDestroy();
            if (autoNav) rightNavButton.onClick.RemoveAllListeners();
            if (autoNav) leftNavButton.onClick.RemoveAllListeners();
            if (autoSearch) searchButton.onClick.RemoveAllListeners();
            backButton.onClick.RemoveAllListeners();

            if (autoPersistMenuState)
            {
                if (_onApplicationQuit)
                    // Restore the currentPath and showSplashScreenOnEnable to original values. Caveat is that this method will not be called
                    // when a module scene is loaded or the main menu is closed prior to quitting. This could be a benefit in that if we
                    // allow quiting the application while in a module scene the user would simply return to the main menu level they left.
                    RestorePersistentData();
                else
                    // Assuming the main menu scene is replaced with a module scene, set the currentPath and showSplashScreenOnEnable
                    // to false, since we do not need to re-enable the splash screen when the main menu scene is reloaded.
                    SetPersistentData();
            }           

        }


        private void PageNext()
        {
            if(activePage < pagesAtLevel)
            {
                activePage++;
                UpdateMenuItemsOnPage();
                HandleNavVisibility();
            }
        }

        private void PagePrevious()
        {
            if (activePage > 0)
            {
                activePage--;
                UpdateMenuItemsOnPage();
                HandleNavVisibility();
            }
            
        }

        private void Search()
        {
            filterString = searchInput.text.ToLower();
            InitializeMenu();
        }

        private void HandleNavVisibility()
        {
            leftNavButton.interactable = activePage > 0;
            rightNavButton.interactable = activePage < pagesAtLevel - 1;

        }

        public void UpdateMenuItemsOnPage()
        {
            var items =
                pagingDictionary.ContainsKey(activePage) ?
                pagingDictionary[activePage] : null;
            menuGrid.UpdateCards(items);
            menuGrid.gameObject.SetActive(false);
            menuGrid.gameObject.SetActive(true);
        }

        private void UpdatePagingButtons()
        {
            pagingNav.ResetPagingButtons();      

            if(pagesAtLevel > 1)
            {
                for (int i = 0; i < pagesAtLevel; i++)
                {

                    var id = i;

                    pagingNav.AddPageButton(i,
                        buttonAction: () =>
                        {
                            activePage = id;
                            UpdateMenuItemsOnPage();
                            pagingNav.SetActivePage(id);
                            HandleNavVisibility();
                        });
                }            
            }

        }

        public void OnEnable()
        {
            if (showSplashScreenOnEnable)
                StartCoroutine(SplashFadeIntro());
            else
                splashUI.SetActive(false);
        }


        public IEnumerator SplashFadeIntro()
        {

            var titleCanvas = titleText.GetComponentInParent<CanvasGroup>();
            var mainCanvas = mainMenuUI.GetComponent<CanvasGroup>();
            var splashCanvas = splashUI.GetComponent<CanvasGroup>();
            var closeCanvas = closeButton.GetComponent<CanvasGroup>();
            splashCanvas.gameObject.SetActive(true);

            if (titleCanvas) titleCanvas.alpha = 0;
            if (mainCanvas) mainCanvas.alpha = 0;
            if (closeCanvas) closeCanvas.alpha = 0;

            for (float i = 0; i < 0.5f; i += Time.deltaTime)
            {
                splashCanvas.alpha = i / 0.5f;
                yield return null;
            }
            splashCanvas.alpha = 1;

            yield return new WaitForSeconds(1f);

            for (float i = 0; i < 0.5f; i += Time.deltaTime)
            {
                mainCanvas.alpha = i / 0.5f;
                splashCanvas.alpha = 1 - (i / 0.5f);
                titleCanvas.alpha = i / 0.5f;
                closeCanvas.alpha = i / 0.5f; 
                yield return null;
            }

            titleCanvas.alpha = 1f;
            splashCanvas.alpha = 0f;
            mainCanvas.alpha = 1f;
            closeCanvas.alpha = 1f;
            splashCanvas.gameObject.SetActive(false);
        }


        public void InitializeMenu()
        {
            GenerateMenuItemDictionary();
            UpdateMenu();
          
        }

        private void UpdateMenu()
        {
            SetupLevelItems();
            UpdateMenuHeader();
            GeneratePagingDictionary(filteredItems);
            if(autoMenuItems) UpdateMenuItemsOnPage();
            if(autoNav) HandleNavVisibility();
            if(autoPagination) UpdatePagingButtons();
        }

        private void SetupLevelItems()
        {
            if (menuType.Equals(MenuType.singleLevel))
            {
                levelItems = menuItems.Cast<IMenuItem>().ToList();
            }
            else
            {
                levelItems = categoryDictionary[currentPath];
            }
        }

        private void UpdateMenuHeader()
        {
            if (menuType.Equals(MenuType.singleLevel))
            {
                backButton.gameObject.SetActive(false);
                if (autoSubtitle) menuSubTitle.gameObject.SetActive(false);
                if (autoTitle) titleText.text = "Welcome";
            }
            else
            {
                backButton.gameObject.SetActive(currentPath != "");
                var pathToDisplay = currentPath == "" ?
                    "Welcome" :
                    currentPath;
                if (autoTitle) titleText.text = pathToDisplay;
            }
        }

        private void GoBackLevel()
        {
            if(currentPath != "")
            {
                var splitPath = currentPath.Trim('/').Split('/');
                var lastIndex = currentPath.IndexOf(splitPath.Last());
                currentPath = currentPath.Substring(0, lastIndex);
                UpdateMenu();
            }      
        }

        public int NumberOfPages(int totalItems)
        {
            var total = totalItems / gridTotal;
            total += totalItems % gridTotal > 0 ?
                 1 : 0;
            return total;
        }

        private void GeneratePagingDictionary(List<IMenuItem> items)
        {
            pagingDictionary = new Dictionary<int, List<IMenuItem>>();
           
            int pageIndex = 0;
            int itemIndex = 0;
            items.ForEach(item =>
            {
                if (pagingDictionary.ContainsKey(pageIndex))
                {
                    pagingDictionary[pageIndex].Add(item);
                }
                else
                {
                    var list = new List<IMenuItem>();
                    list.Add(item);
                    pagingDictionary.Add(pageIndex, list);
                }              

                itemIndex += 1;
                if (itemIndex == gridTotal)
                {
                    pageIndex++; itemIndex = 0;
                }
            });
        }

        private void SetMenuActions(List<IMenuItem> categories)
        {
            categories.ForEach(c =>
            {
                var name = c.GetName();
                c.SetMenuAction(() =>
                    {
                        var newPath = currentPath + name + "/";

                        currentPath = 
                        categoryDictionary.ContainsKey(newPath) ?
                        newPath :
                        currentPath;
                        UpdateMenu();
                    }
                );
            });
        }

        private void GenerateMenuItemDictionary()
        {
            if (menuType.Equals(MenuType.multiLevel))
            {
                var items = menuItems.Cast<IMenuItem>().ToList();
                var mapItems = menuMap.GetMenuItemsFromCategories();
                SetMenuActions(mapItems);
                var combined = items.Concat(mapItems).ToList();


                categoryDictionary = new Dictionary<string, List<IMenuItem>>();
                combined.ForEach(item =>
                {
                    bool keyExists = categoryDictionary.ContainsKey(item.GetPath());
                    if (keyExists)
                    {
                        categoryDictionary[item.GetPath()].Add(item);
                    }
                    else
                    {
                        var menuList = new List<IMenuItem>();
                        menuList.Add(item);
                        categoryDictionary.Add(
                            item.GetPath(),
                            menuList
                            );
                    }

                });
            }
        }

        #region Persistent Data Methods

        private bool _onApplicationQuit = false;
        private bool _showSplashScreenOnEnable = false;
        private void GetPersistentData()
        {
            //Save the showSplashScreenOnEnable, since we will want to restore this later
            _showSplashScreenOnEnable = showSplashScreenOnEnable;

            currentPath = PlayerPrefs.GetString("MainMenuFeatures.currentPath", "");
            //showSplashScreenOnEnable = (PlayerPrefs.GetInt("MainMenuFeatures.showSplashScreenOnEnable", 1) != 0);
            Debug.Log("GetPersistentData: " + currentPath +", " + showSplashScreenOnEnable);
        }
        private void SetPersistentData()
        {
            PlayerPrefs.SetString("MainMenuFeatures.currentPath", currentPath);
            PlayerPrefs.SetInt("MainMenuFeatures.showSplashScreenOnEnable", 0);
            Debug.Log("SetPersistentData: " + currentPath + ", " + showSplashScreenOnEnable);
        }

        private void RestorePersistentData()
        {
            PlayerPrefs.SetString("MainMenuFeatures.currentPath", "");
            PlayerPrefs.SetInt("MainMenuFeatures.showSplashScreenOnEnable", _showSplashScreenOnEnable ? 1 : 0);
            Debug.Log("RestorePersistentData");
        }

        private void OnApplicationQuit()
        {
            _onApplicationQuit = true;
        }

        #endregion



    }

}

