using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Jovian.InspectorTools {
    public class UniformVector3Attribute : PropertyAttribute {
        public UniformVector3Attribute() {
        }
    }
}

#if UNITY_EDITOR
namespace Jovian.InspectorTools.Internal {
    [CustomPropertyDrawer(typeof(UniformVector3Attribute))]
    public class UniformVector3AttributeDrawer : PropertyDrawer {

        private GUIContent linkedIconContent;
        private GUIContent unlinkedIconContent;
        private GUIStyle linkButtonStyle;

        private const int CHILD_COUNT = 3;
        private const float LABEL_WIDTH = 13f;
        private const float COMBINED_LABEL_WIDTH = 26f;
        private const float INDENT_WIDTH = 15f;
        private const float CHILD_HORIZONTAL_SPACING = 4f;
        private static readonly string[] childPropertyNames = new string[] { "x", "y", "z" };
        private static bool IsCompactLayout => Screen.width <= 333f;
        private static bool IsNoLabelLayout => Screen.width <= 240f;

        private enum LinkState {
            Unknown,
            Linked,
            Unlinked
        }

        private LinkState linkState = LinkState.Unknown;

        public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
            if(IsCompactLayout) { // Unity Vector2 + Vector3 go multi-line at this point
                return EditorGUIUtility.singleLineHeight * 2f + EditorGUIUtility.standardVerticalSpacing;
            }
            return EditorGUIUtility.singleLineHeight;
        }

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
            if(property.propertyType != SerializedPropertyType.Vector3) {
                GUI.Label(position, "Error, SerializedProperty must be 'Vector3' type");
                return;
            }

            if(linkedIconContent == null || unlinkedIconContent == null || linkButtonStyle == null) {
                linkedIconContent = EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_Linked" : "Linked");
                unlinkedIconContent = EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_Unlinked" : "Unlinked");
                linkButtonStyle = new GUIStyle(GUI.skin.button);
                linkButtonStyle.padding = new RectOffset(1, 1, 0, 0);
                linkButtonStyle.imagePosition = ImagePosition.ImageOnly;
            }

            if(linkState == LinkState.Unknown) {
                linkState = GetLinkState(property);
            }
            
            EditorGUI.BeginProperty(position, label, property);

            if(IsCompactLayout) {
                EditorGUIUtility.labelWidth = position.width;
            }
            Rect labelRect = new Rect(position) { height = EditorGUIUtility.singleLineHeight };
            Rect fieldRect = EditorGUI.PrefixLabel(labelRect, label);
            fieldRect.height = EditorGUIUtility.singleLineHeight;

            float height = Mathf.Min(20f, position.height) - 2f;
            Rect buttonRect = new Rect(fieldRect);
            buttonRect.width = 18f;
            buttonRect.height = height;
            buttonRect.y += 1f;
            buttonRect.x -= buttonRect.width + 2f;

            if(GUI.Button(buttonRect, linkState == LinkState.Linked ? linkedIconContent : unlinkedIconContent, linkButtonStyle)) {
                linkState = linkState == LinkState.Linked ? LinkState.Unlinked : LinkState.Linked;
            }
            
            if(IsCompactLayout) {
                fieldRect.y = labelRect.yMax + EditorGUIUtility.standardVerticalSpacing;
                fieldRect.x = labelRect.x + (IsNoLabelLayout ? 0f : INDENT_WIDTH); // 15 = single indent
                fieldRect.width = position.xMax - fieldRect.xMin;
            }


            if(linkState == LinkState.Linked) {
                EditorGUIUtility.labelWidth = IsNoLabelLayout ? 0.01f : COMBINED_LABEL_WIDTH;
                SerializedProperty childProperty = property.FindPropertyRelative(childPropertyNames[0]);
                EditorGUI.BeginChangeCheck();
                float newValue = EditorGUI.FloatField(fieldRect, "XYZ", childProperty.floatValue);
                if(EditorGUI.EndChangeCheck()) {
                    for(int i = 0; i < CHILD_COUNT; i++) {
                        SerializedProperty otherChildProperty = property.FindPropertyRelative(childPropertyNames[i]);
                        otherChildProperty.floatValue = newValue;
                    }
                }
            }
            else {
                EditorGUIUtility.labelWidth = IsNoLabelLayout ? 0.01f : LABEL_WIDTH;
                float childSpacing = IsNoLabelLayout ? CHILD_HORIZONTAL_SPACING / 2f : CHILD_HORIZONTAL_SPACING;
                float childWidth = ((fieldRect.width + childSpacing) / CHILD_COUNT) - childSpacing;

                for(int i = 0; i < CHILD_COUNT; i++) {
                    SerializedProperty childProperty = property.FindPropertyRelative(childPropertyNames[i]);
                    Rect childRect = new Rect(fieldRect);
                    childRect.width = childWidth; // add spacing
                    childRect.x += i * (childWidth + childSpacing);
                    EditorGUI.PropertyField(childRect, childProperty);
                }
            }

            EditorGUI.EndProperty();
        }

        private LinkState GetLinkState(SerializedProperty property) {
            float value0 = property.FindPropertyRelative(childPropertyNames[0]).floatValue;
            for(int i = 1; i < CHILD_COUNT; i++) {
                SerializedProperty childProperty = property.FindPropertyRelative(childPropertyNames[i]);
                if(Mathf.Approximately(value0, childProperty.floatValue) == false) {
                    return LinkState.Unlinked;
                }
            }
            return LinkState.Linked;
        }
    }
}
#endif