// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace HoloToolkit.Unity
{
    public class SceneLauncher : Singleton<SceneLauncher>
    {
        [Serializable]
        private class SceneMapping
        {
            public string ScenePath = string.Empty;

            [Tooltip("This toggle enables or disables the generation of a button for this specific scene.")]
            public bool IsButtonEnabled;
        }

        [SerializeField]
        [Tooltip("The button scene mapping to keep track of which scenes are enabled in the scene launcher.  This list of scenes is generated from the build window's active scenes.")]
        private SceneMapping[] sceneMapping;

        [Tooltip("Location of the center of the grid of buttons in Unity space.")]
        public GameObject ButtonSpawnLocation;

        [Tooltip("Prefab used as a button for each scene.")]
        public SceneLauncherButton SceneButtonPrefab;

        [Tooltip("Number of rows in the grid of buttons. As more scenes are added, they will spread out horizontally using this number of rows.")]
        public int MaxRows = 5;

        private int SceneLauncherBuildIndex { get; set; }
        private Vector3 sceneButtonSize = Vector3.one;

        private void OnValidate()
        {
            Debug.Assert(SceneButtonPrefab != null, "SceneLauncher.SceneButtonPrefab is not set.");
        }

        protected override void Awake()
        {
            // If we have already initialized,
            // then we've created a second one.
            if (IsInitialized)
            {
                Destroy(gameObject);
            }
            else
            {
                base.Awake();
            }
        }

        private void Start()
        {
            if (SceneButtonPrefab == null)
            {
                return;
            }

            SceneLauncherBuildIndex = SceneManager.GetActiveScene().buildIndex;

            // Determine the size of the buttons. Instantiate one of them so that we can check its bounds.
            SceneLauncherButton sceneButtonForSize = Instantiate(SceneButtonPrefab);
            var sceneButtonForSizeCollider = sceneButtonForSize.GetComponent<Collider>();

            if (sceneButtonForSizeCollider != null)
            {
                sceneButtonSize = sceneButtonForSizeCollider.bounds.size;
            }

            for (int sceneIndex = 0; sceneIndex < sceneMapping.Length; ++sceneIndex)
            {
                if (sceneMapping[sceneIndex].IsButtonEnabled)
                {
                    CreateSceneButton(ButtonSpawnLocation, sceneIndex);
                }
            }

            Destroy(sceneButtonForSize.gameObject);
        }

        private void CreateSceneButton(GameObject buttonParent, int sceneIndex)
        {
            string sceneName = sceneMapping[sceneIndex].ScenePath;
            sceneName = sceneName.Substring(sceneName.LastIndexOf("/", StringComparison.Ordinal) + 1);
            sceneName = sceneName.Replace(".unity", "");
            var scene = SceneManager.GetSceneByBuildIndex(sceneIndex);
            Debug.Assert(SceneManager.GetSceneByName(sceneName) == scene);

            SceneLauncherButton sceneButton = Instantiate(SceneButtonPrefab, GetButtonPosition(sceneIndex, sceneMapping.Length), Quaternion.identity, buttonParent.transform);
            sceneButton.SceneIndex = sceneIndex;
            sceneButton.SceneName = sceneName;
            sceneButton.MenuReference = ButtonSpawnLocation;
        }

        private Vector3 GetButtonPosition(int sceneIndex, int numberOfScenes)
        {
            int yCount = Mathf.Min(numberOfScenes, MaxRows);
            int xCount = (numberOfScenes - 1) / yCount + 1;
            int x = sceneIndex % xCount;
            int y = sceneIndex / xCount;
            Debug.Assert(x < xCount && y < yCount);

            // Center a grid of cells in a grid.
            // The top-left corner is shifted .5 cell widths for every row/column after the first one.
            var topLeft = new Vector3((xCount - 1) * -0.5f, (yCount - 1) * 0.5f, 0.0f);
            var cellFromTopLeft = new Vector3(x, -y, 0.0f);
            // Scale by size of the button.
            var positionOffset = Vector3.Scale(topLeft + cellFromTopLeft, new Vector3(sceneButtonSize.x, sceneButtonSize.y, 1.0f));

            return ButtonSpawnLocation.transform.position + positionOffset;
        }

        public void LaunchSceneLoader()
        {
            Debug.LogFormat("SceneLauncher: Returning to SceneLauncher scene {0}.", SceneLauncherBuildIndex);
            SceneManager.LoadSceneAsync(SceneLauncherBuildIndex, LoadSceneMode.Single);
            ButtonSpawnLocation.SetActive(true);
        }
    }
}
