// 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.Collections.Generic;
using System.Reflection;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace HoloToolkit.Unity
{
    // Displays a drop-down list of available material properties from the material supplied in a named member
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public sealed class MaterialPropertyAttribute : DrawOverrideAttribute
    {
        public enum PropertyTypeEnum
        {
            Color,
            Float,
            Range,
            Vector,
        }

        public string Property { get; private set; }
        public PropertyTypeEnum PropertyType { get; private set; }
        public string MaterialMemberName { get; private set; }
        public bool AllowNone { get; private set; }
        public string DefaultProperty { get; private set; }
        public string CustomLabel { get; private set; }

        public MaterialPropertyAttribute(PropertyTypeEnum propertyType, string materialMemberName, bool allowNone = true, string defaultProperty = "_Color", string customLabel = null)
        {
            PropertyType = propertyType;
            MaterialMemberName = materialMemberName;
            AllowNone = allowNone;
            DefaultProperty = defaultProperty;
            CustomLabel = customLabel;
        }

#if UNITY_EDITOR
        public override void DrawEditor(UnityEngine.Object target, FieldInfo field, SerializedProperty property)
        {
            Material mat = GetMaterial(target);

            string fieldValue = MaterialPropertyName(
               (string)field.GetValue(target),
               mat,
               PropertyType,
               AllowNone,
               DefaultProperty,
               (string.IsNullOrEmpty(CustomLabel) ? SplitCamelCase(field.Name) : CustomLabel));

            field.SetValue(target, fieldValue);
        }

        public override void DrawEditor(UnityEngine.Object target, PropertyInfo prop)
        {
            Material mat = GetMaterial(target);

            string propValue = MaterialPropertyName(
               (string)prop.GetValue(target, null),
               mat,
               PropertyType,
               AllowNone,
               DefaultProperty,
               (string.IsNullOrEmpty(CustomLabel) ? SplitCamelCase(prop.Name) : CustomLabel));

            prop.SetValue(target, propValue, null);
        }

        private Material GetMaterial(object target)
        {
            MemberInfo[] members = target.GetType().GetMember(MaterialMemberName);
            if (members.Length == 0)
            {
                Debug.LogError("Couldn't find material member " + MaterialMemberName);
                return null;
            }

            Material mat = null;

            switch (members[0].MemberType)
            {
                case MemberTypes.Field:
                    FieldInfo matField = target.GetType().GetField(MaterialMemberName);
                    mat = matField.GetValue(target) as Material;
                    break;

                case MemberTypes.Property:
                    PropertyInfo matProp = target.GetType().GetProperty(MaterialMemberName);
                    mat = matProp.GetValue(target, null) as Material;
                    break;

                default:
                    Debug.LogError("Couldn't find material member " + MaterialMemberName);
                    break;
            }

            return mat;
        }

        private static string MaterialPropertyName(string property, Material mat, PropertyTypeEnum type, bool allowNone, string defaultProperty, string labelName)
        {
            Color tColor = GUI.color;
            // Create a list of available color and value properties
            List<string> props = new List<string>();

            int selectedPropIndex = 0;

            if (allowNone)
            {
                props.Add("(None)");
            }

            if (mat != null)
            {
                int propertyCount = ShaderUtil.GetPropertyCount(mat.shader);
                string propName = string.Empty;
                for (int i = 0; i < propertyCount; i++)
                {
                    if (ShaderUtil.GetPropertyType(mat.shader, i).ToString() == type.ToString())
                    {
                        propName = ShaderUtil.GetPropertyName(mat.shader, i);
                        if (propName == property)
                        {
                            // We've found our current property
                            selectedPropIndex = props.Count;
                        }
                        props.Add(propName);
                    }
                }

                if (string.IsNullOrEmpty(labelName))
                {
                    labelName = type.ToString();
                }
                int newPropIndex = EditorGUILayout.Popup(labelName, selectedPropIndex, props.ToArray());
                if (allowNone)
                {
                    property = (newPropIndex > 0 ? props[newPropIndex] : string.Empty);
                }
                else
                {
                    if (props.Count > 0)
                    {
                        property = props[newPropIndex];
                    }
                    else
                    {
                        property = defaultProperty;
                    }
                }
                return property;
            }
            else
            {
                GUI.color = Color.Lerp(tColor, Color.gray, 0.5f);
                // Draw an empty property
                EditorGUILayout.Popup(labelName, selectedPropIndex, props.ToArray());
                GUI.color = tColor;
                return string.Empty;
            }
        }
#endif

    }
}