//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: Example menu using OnGUI with SteamVR_Camera's overlay support
//
//=============================================================================

using UnityEngine;
using Valve.VR;

namespace Valve.VR
{
    public class SteamVR_Menu : MonoBehaviour
    {
        public Texture cursor, background, logo;
        public float logoHeight, menuOffset;

        public Vector2 scaleLimits = new Vector2(0.1f, 5.0f);
        public float scaleRate = 0.5f;

        SteamVR_Overlay overlay;
        Camera overlayCam;
        Vector4 uvOffset;
        float distance;

        public RenderTexture texture { get { return overlay ? overlay.texture as RenderTexture : null; } }
        public float scale { get; private set; }

        string scaleLimitX, scaleLimitY, scaleRateText;

        CursorLockMode savedCursorLockState;
        bool savedCursorVisible;

        void Awake()
        {
            scaleLimitX = string.Format("{0:N1}", scaleLimits.x);
            scaleLimitY = string.Format("{0:N1}", scaleLimits.y);
            scaleRateText = string.Format("{0:N1}", scaleRate);

            var overlay = SteamVR_Overlay.instance;
            if (overlay != null)
            {
                uvOffset = overlay.uvOffset;
                distance = overlay.distance;
            }
        }

        void OnGUI()
        {
            if (overlay == null)
                return;

            var texture = overlay.texture as RenderTexture;

            var prevActive = RenderTexture.active;
            RenderTexture.active = texture;

            if (Event.current.type == EventType.Repaint)
                GL.Clear(false, true, Color.clear);

            var area = new Rect(0, 0, texture.width, texture.height);

            // Account for screen smaller than texture (since mouse position gets clamped)
            if (Screen.width < texture.width)
            {
                area.width = Screen.width;
                overlay.uvOffset.x = -(float)(texture.width - Screen.width) / (2 * texture.width);
            }
            if (Screen.height < texture.height)
            {
                area.height = Screen.height;
                overlay.uvOffset.y = (float)(texture.height - Screen.height) / (2 * texture.height);
            }

            GUILayout.BeginArea(area);

            if (background != null)
            {
                GUI.DrawTexture(new Rect(
                    (area.width - background.width) / 2,
                    (area.height - background.height) / 2,
                    background.width, background.height), background);
            }

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            GUILayout.BeginVertical();

            if (logo != null)
            {
                GUILayout.Space(area.height / 2 - logoHeight);
                GUILayout.Box(logo);
            }

            GUILayout.Space(menuOffset);

            bool bHideMenu = GUILayout.Button("[Esc] - Close menu");

            GUILayout.BeginHorizontal();
            GUILayout.Label(string.Format("Scale: {0:N4}", scale));
            {
                var result = GUILayout.HorizontalSlider(scale, scaleLimits.x, scaleLimits.y);
                if (result != scale)
                {
                    SetScale(result);
                }
            }
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            GUILayout.Label(string.Format("Scale limits:"));
            {
                var result = GUILayout.TextField(scaleLimitX);
                if (result != scaleLimitX)
                {
                    if (float.TryParse(result, out scaleLimits.x))
                        scaleLimitX = result;
                }
            }
            {
                var result = GUILayout.TextField(scaleLimitY);
                if (result != scaleLimitY)
                {
                    if (float.TryParse(result, out scaleLimits.y))
                        scaleLimitY = result;
                }
            }
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            GUILayout.Label(string.Format("Scale rate:"));
            {
                var result = GUILayout.TextField(scaleRateText);
                if (result != scaleRateText)
                {
                    if (float.TryParse(result, out scaleRate))
                        scaleRateText = result;
                }
            }
            GUILayout.EndHorizontal();

            if (SteamVR.active)
            {
                var vr = SteamVR.instance;

                GUILayout.BeginHorizontal();
                {
                    var t = SteamVR_Camera.sceneResolutionScale;
                    int w = (int)(vr.sceneWidth * t);
                    int h = (int)(vr.sceneHeight * t);
                    int pct = (int)(100.0f * t);
                    GUILayout.Label(string.Format("Scene quality: {0}x{1} ({2}%)", w, h, pct));
                    var result = Mathf.RoundToInt(GUILayout.HorizontalSlider(pct, 50, 200));
                    if (result != pct)
                    {
                        SteamVR_Camera.sceneResolutionScale = (float)result / 100.0f;
                    }
                }
                GUILayout.EndHorizontal();
            }

            var tracker = SteamVR_Render.Top();
            if (tracker != null)
            {
                tracker.wireframe = GUILayout.Toggle(tracker.wireframe, "Wireframe");

                if (SteamVR.settings.trackingSpace == ETrackingUniverseOrigin.TrackingUniverseSeated)
                {
                    if (GUILayout.Button("Switch to Standing"))
                        SteamVR.settings.trackingSpace = ETrackingUniverseOrigin.TrackingUniverseStanding;
                    if (GUILayout.Button("Center View"))
                    {
                        var system = OpenVR.System;
                        if (system != null)
                            system.ResetSeatedZeroPose();
                    }
                }
                else
                {
                    if (GUILayout.Button("Switch to Seated"))
                        SteamVR.settings.trackingSpace = ETrackingUniverseOrigin.TrackingUniverseSeated;
                }
            }

#if !UNITY_EDITOR
		if (GUILayout.Button("Exit"))
			Application.Quit();
#endif
            GUILayout.Space(menuOffset);

            var env = System.Environment.GetEnvironmentVariable("VR_OVERRIDE");
            if (env != null)
            {
                GUILayout.Label("VR_OVERRIDE=" + env);
            }

            GUILayout.Label("Graphics device: " + SystemInfo.graphicsDeviceVersion);

            GUILayout.EndVertical();
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            GUILayout.EndArea();

            if (cursor != null)
            {
                float x = Input.mousePosition.x, y = Screen.height - Input.mousePosition.y;
                float w = cursor.width, h = cursor.height;
                GUI.DrawTexture(new Rect(x, y, w, h), cursor);
            }

            RenderTexture.active = prevActive;

            if (bHideMenu)
                HideMenu();
        }

        public void ShowMenu()
        {
            var overlay = SteamVR_Overlay.instance;
            if (overlay == null)
                return;

            var texture = overlay.texture as RenderTexture;
            if (texture == null)
            {
                Debug.LogError("<b>[SteamVR]</b> Menu requires overlay texture to be a render texture.");
                return;
            }

            SaveCursorState();

            Cursor.visible = true;
            Cursor.lockState = CursorLockMode.None;

            this.overlay = overlay;
            uvOffset = overlay.uvOffset;
            distance = overlay.distance;

            // If an existing camera is rendering into the overlay texture, we need
            // to temporarily disable it to keep it from clearing the texture on us.
            var cameras = Object.FindObjectsOfType(typeof(Camera)) as Camera[];
            foreach (var cam in cameras)
            {
                if (cam.enabled && cam.targetTexture == texture)
                {
                    overlayCam = cam;
                    overlayCam.enabled = false;
                    break;
                }
            }

            var tracker = SteamVR_Render.Top();
            if (tracker != null)
                scale = tracker.origin.localScale.x;
        }

        public void HideMenu()
        {
            RestoreCursorState();

            if (overlayCam != null)
                overlayCam.enabled = true;

            if (overlay != null)
            {
                overlay.uvOffset = uvOffset;
                overlay.distance = distance;
                overlay = null;
            }
        }

        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Escape) || Input.GetKeyDown(KeyCode.Joystick1Button7))
            {
                if (overlay == null)
                {
                    ShowMenu();
                }
                else
                {
                    HideMenu();
                }
            }
            else if (Input.GetKeyDown(KeyCode.Home))
            {
                SetScale(1.0f);
            }
            else if (Input.GetKey(KeyCode.PageUp))
            {
                SetScale(Mathf.Clamp(scale + scaleRate * Time.deltaTime, scaleLimits.x, scaleLimits.y));
            }
            else if (Input.GetKey(KeyCode.PageDown))
            {
                SetScale(Mathf.Clamp(scale - scaleRate * Time.deltaTime, scaleLimits.x, scaleLimits.y));
            }
        }

        void SetScale(float scale)
        {
            this.scale = scale;

            var tracker = SteamVR_Render.Top();
            if (tracker != null)
                tracker.origin.localScale = new Vector3(scale, scale, scale);
        }

        void SaveCursorState()
        {
            savedCursorVisible = Cursor.visible;
            savedCursorLockState = Cursor.lockState;
        }

        void RestoreCursorState()
        {
            Cursor.visible = savedCursorVisible;
            Cursor.lockState = savedCursorLockState;
        }
    }
}