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

using System;
using System.Globalization;
using UnityEditor;
using UnityEngine;

namespace HoloToolkit.Unity
{
    /// <summary>
    /// Extensions for the UnityEnditor.EditorGUI class.
    /// </summary>
    public class EditorGUIExtensions : MonoBehaviour
    {
        public static float Indent
        {
            get
            {
                return EditorGUI.IndentedRect(new Rect()).x;
            }
        }

        /// <summary>
        /// Space to place between adjacent horizontal elements.
        /// Matches value used in Unity source for EditorGUILayout.Space().
        /// </summary>
        public const float HorizontalSpacing = 6.0f;

        public static bool Button(Rect position, string text)
        {
            return Button(position, new GUIContent(text));
        }

        public static bool Button(Rect position, GUIContent content)
        {
            float indent = Indent;
            position.x += indent;
            position.width -= indent;
            return GUI.Button(position, content);
        }

        public static void Label(Rect position, string text)
        {
            float indent = Indent;
            position.x += indent;
            position.width -= indent;
            GUI.Label(position, text);
        }

        // Based on logic in Unity source function EditorGUI.GetSinglePropertyHeight().
        // Doesn't handle serialized types with nested serialized children.
        public static float GetTypeHeight(bool hasLabel, Type valueType)
        {
            if (valueType == typeof(Vector3) || valueType == typeof(Vector2))
            {
                return (!hasLabel || EditorGUIUtility.wideMode ? 0f : EditorGUIUtility.singleLineHeight) + EditorGUIUtility.singleLineHeight;
            }

            if (valueType == typeof(Rect))
            {
                return (!hasLabel || EditorGUIUtility.wideMode ? 0f : EditorGUIUtility.singleLineHeight) + EditorGUIUtility.singleLineHeight * 2;
            }

            if (valueType == typeof(Bounds))
            {
                return (!hasLabel ? 0f : EditorGUIUtility.singleLineHeight) + EditorGUIUtility.singleLineHeight * 2;
            }

            return EditorGUIUtility.singleLineHeight;
        }

        /// <summary>
        /// Shows an object field editor for object types that do no derive from UnityEngine.Object.
        /// </summary>
        /// <typeparam name="T">Type of the object to modify.</typeparam>
        /// <param name="position">The region to show the UI.</param>
        /// <param name="label">Label to show.</param>
        /// <param name="value">Current value to show.</param>
        /// <param name="allowSceneObjects">Whether scene objects should be allowed in the set of field choices.</param>
        /// <returns>The new value.</returns>
        public static T ObjectField<T>(Rect position, GUIContent label, T value, bool allowSceneObjects)
        {
            object objValue = value;

            Type valueType = objValue.GetType();
            if (valueType == typeof(Bounds))
            {
                objValue = EditorGUI.BoundsField(position, label, (Bounds)objValue);
            }
            else if (valueType == typeof(Color))
            {
                objValue = EditorGUI.ColorField(position, label, (Color)objValue);
            }
            else if (valueType == typeof(Material))
            {
                objValue = EditorGUI.ObjectField(position, (Material)objValue, typeof(Material), allowSceneObjects);
            }
            else if (valueType == typeof(AnimationCurve))
            {
                objValue = EditorGUI.CurveField(position, label, (AnimationCurve)objValue);
            }
            else if (valueType == typeof(float))
            {
                objValue = EditorGUI.FloatField(position, label, (float)objValue);
            }
            else if (valueType == typeof(int))
            {
                objValue = EditorGUI.IntField(position, label, (int)objValue);
            }
            else if (valueType == typeof(LayerMask))
            {
                objValue = EditorGUI.MaskField(position, label, (LayerMask)objValue, LayerMaskExtensions.LayerMaskNames);
            }
            else if (valueType.IsEnum)
            {
                if (valueType.GetCustomAttributes(typeof(FlagsAttribute), true).Length > 0)
                {
                    objValue = EditorGUI.EnumMaskField(position, label, (Enum)objValue);
                }
                else
                {
                    objValue = EditorGUI.EnumPopup(position, label, (Enum)objValue);
                }
            }
            else if (valueType == typeof(Rect))
            {
                objValue = EditorGUI.RectField(position, label, (Rect)objValue);
            }
            else if (valueType == typeof(string))
            {
                objValue = EditorGUI.TextField(position, label, (string)objValue);
            }
            else if (valueType == typeof(Vector2))
            {
                objValue = EditorGUI.Vector2Field(position, new GUIContent(), (Vector2)objValue);
            }
            else if (valueType == typeof(Vector3))
            {
                objValue = EditorGUI.Vector3Field(position, new GUIContent(), (Vector3)objValue);
            }
            else if (valueType == typeof(Vector4))
            {
                if (label.image != null)
                {
                    throw new ArgumentException("Images not supported for labels of Vector4 fields.", "label");
                }

                if (!string.IsNullOrEmpty(label.tooltip))
                {
                    throw new ArgumentException("Tool-tips not supported for labels of Vector4 fields.", "label");
                }

                objValue = EditorGUI.Vector4Field(position, label.text, (Vector4)objValue);
            }
            else if (Equals(objValue, typeof(SceneAsset)))
            {
                objValue = EditorGUI.ObjectField(position, (SceneAsset)objValue, typeof(SceneAsset), allowSceneObjects);
            }
            else if (objValue is UnityEngine.Object)
            {
                objValue = EditorGUI.ObjectField(position, label, (UnityEngine.Object)objValue, valueType, allowSceneObjects);
            }
            else
            {
                throw new ArgumentException(
                    string.Format(
                        CultureInfo.InvariantCulture,
                        "Unimplemented value type: {0}.",
                        valueType),
                    "value");
            }

            return (T)objValue;
        }
    }
}