namespace Dreamteck.Splines.Editor
{
    using UnityEngine;
    using UnityEditor;
    using System.Text.RegularExpressions;


    public static class SplineEditorGUI
    {
        public static readonly GUIStyle leftButtonStyle, midButtonStyle, rightButtonStyle, boxStyle;

        private static Color previousContentColor, previousBackgroundColor, highLightBGColor, highlightContentColor;


        public static readonly GUIStyle defaultButton = null;
        public static readonly GUIStyle defaultEditorButton = null;
        public static readonly GUIStyle defaultEditorButtonSelected = null;
        public static readonly GUIStyle dropdownItem = null;
        public static readonly GUIStyle bigButton = null;
        public static readonly GUIStyle bigButtonSelected = null;
        public static readonly GUIStyle labelText = null;
        public static GUIStyle whiteBox
        {
            get
            {
                if (_whiteBox.normal.background == null) _whiteBox.normal.background = white;
                return _whiteBox;
            }
        }
        private static readonly GUIStyle _whiteBox = null;
        public static GUIStyle defaultField
        {
            get
            {
                if (_defaultField.normal.background == null) _defaultField.normal.background = white;
                return _defaultField;
            }
        }
        private static GUIStyle _defaultField = null;
        public static GUIStyle smallField
        {
            get
            {
                if (_smallField.normal.background == null) _smallField.normal.background = white;
                return _smallField;
            }
        }
        private static GUIStyle _smallField = null;
        public static readonly Color inactiveColor = new Color(0.7f, 0.7f, 0.7f, 0.3f);
        public static readonly Color textColor = new Color(0.2f, 0.2f, 0.2f, 1f);
        public static readonly Color activeColor = new Color(1f, 1f, 1f, 1f);
        public static readonly Color blackColor = new Color(0, 0, 0, 0.7f);
        public static readonly Color buttonContentColor = Color.black;
        private static bool[] controlStates = new bool[0];
        private static string[] floatFieldContents = new string[0];
        private static int controlIndex = 0;

        public static float scale = 1f;
        public static Texture2D white
        {
            get
            {
                if (_white == null)
                {
                    _white = new Texture2D(1, 1);
                    _white.SetPixel(0, 0, Color.white);
                    _white.Apply();
                }
                return _white;
            }
        }
        private static Texture2D _white = null;

        public static void Update()
        {
            controlStates = new bool[0];
            floatFieldContents = new string[0];
        }

        public static void Reset()
        {
            controlIndex = 0;
        }

        static SplineEditorGUI()
        {
            midButtonStyle = new GUIStyle(GUI.skin.button);
            midButtonStyle.margin = new RectOffset(0, 0, midButtonStyle.margin.top, midButtonStyle.margin.bottom);
            midButtonStyle.padding = new RectOffset(3, 3, midButtonStyle.padding.top, midButtonStyle.padding.bottom);

            leftButtonStyle = new GUIStyle(midButtonStyle);
            leftButtonStyle.contentOffset = new Vector2(-leftButtonStyle.border.left * 0.5f, 0f);
            rightButtonStyle = new GUIStyle(midButtonStyle);
            rightButtonStyle.contentOffset = new Vector2(rightButtonStyle.border.right * 0.5f, 0f);

            boxStyle = new GUIStyle(GUI.skin.GetStyle("box"));
            boxStyle.normal.background = DreamteckEditorGUI.blankImage;
            boxStyle.margin = new RectOffset(0, 0, 0, 2);

            defaultButton = new GUIStyle(GUI.skin.GetStyle("button"));
            _whiteBox = new GUIStyle(GUI.skin.GetStyle("box"));
            _whiteBox.normal.background = white;
            _defaultField = new GUIStyle(GUI.skin.GetStyle("textfield"));
            _defaultField.normal.background = white;
            _defaultField.normal.textColor = Color.white;
            defaultField.alignment = TextAnchor.MiddleLeft;
            _smallField = new GUIStyle(GUI.skin.GetStyle("textfield"));
            _smallField.normal.background = white;
            _smallField.normal.textColor = Color.white;
            _smallField.alignment = TextAnchor.MiddleLeft;
            _smallField.clipping = TextClipping.Clip;
            labelText = new GUIStyle(GUI.skin.GetStyle("label"));
            labelText.fontStyle = FontStyle.Bold;
            labelText.alignment = TextAnchor.MiddleRight;
            labelText.normal.textColor = Color.white;
            dropdownItem = new GUIStyle(GUI.skin.GetStyle("button"));
            dropdownItem.normal.background = white;
            dropdownItem.normal.textColor = Color.white;
            dropdownItem.alignment = TextAnchor.MiddleLeft;
            bigButton = new GUIStyle(GUI.skin.GetStyle("button"));
            bigButton.fontStyle = FontStyle.Bold;
            bigButton.normal.textColor = buttonContentColor;
            bigButtonSelected = new GUIStyle(GUI.skin.GetStyle("button"));
            bigButtonSelected.fontStyle = FontStyle.Bold;
            buttonContentColor = defaultButton.normal.textColor;
            //If the button text color is too dark, generate a brightened version
            float avg = (buttonContentColor.r + buttonContentColor.g + buttonContentColor.b) / 3f;
            if (avg <= 0.2f) buttonContentColor = new Color(0.2f, 0.2f, 0.2f);
            Rescale();
        }

        public static void SetHighlightColors(Color background, Color content)
        {
            highLightBGColor = background;
            highlightContentColor = content;
        }

        public static bool LeftButton(GUIContent content, bool selected)
        {
            bool clicked = false;
            Rect rect = ButtonBegin(selected, leftButtonStyle);
            if (GUI.Button(new Rect(0, 0, rect.width + leftButtonStyle.border.right, rect.height), content, leftButtonStyle)) clicked = true;
            ButtonEnd();
            return clicked;
        }

        public static bool MidButton(GUIContent content, bool selected)
        {
            bool clicked = false;
            Rect rect = ButtonBegin(selected, midButtonStyle);
            if (GUI.Button(new Rect(-midButtonStyle.border.left, 0, rect.width + midButtonStyle.border.left + midButtonStyle.border.right, rect.height), content, midButtonStyle)) clicked = true;
            ButtonEnd();
            return clicked;
        }

        public static bool RightButton(GUIContent content, bool selected)
        {
            bool clicked = false;
            Rect rect = ButtonBegin(selected, rightButtonStyle);
            if (GUI.Button(new Rect(-rightButtonStyle.border.left, 0, rect.width + rightButtonStyle.border.left, rect.height), content, rightButtonStyle)) clicked = true;
            ButtonEnd();
            return clicked;
        }

        static Rect ButtonBegin(bool selected, GUIStyle style)
        {
            previousContentColor = GUI.contentColor;
            previousBackgroundColor = GUI.backgroundColor;
            GUI.contentColor = style.normal.textColor;
            if (selected)
            {
                GUI.backgroundColor = highLightBGColor;
                GUI.contentColor = highlightContentColor;
            }
            Rect rect = GUILayoutUtility.GetRect(30f, 22f);
            GUI.BeginGroup(rect);
            return rect;
        }

        public static void ButtonEnd()
        {
            GUI.EndGroup();
            GUI.contentColor = previousContentColor;
            GUI.backgroundColor = previousBackgroundColor;
        }

        public static int ButtonRibbon(GUIContent[] contents, float buttonWidth, int highLighted = -1)
        {
            int selected = -1;
            float width = contents.Length * buttonWidth;
            EditorGUILayout.BeginHorizontal(GUILayout.Width(width));
            for (int i = 0; i < contents.Length; i++)
            {
                if (i == 0)
                {
                    if (LeftButton(contents[i], highLighted == i)) selected = i;
                }
                else if (i == contents.Length - 1)
                {
                    if(RightButton(contents[i], highLighted == i)) selected = i;
                }
                else
                {
                    if(MidButton(contents[i], highLighted == i)) selected = i;
                }
            }
            EditorGUILayout.EndHorizontal();
            return selected;
        }


        public static void BeginContainerBox(ref bool open, string name)
        {
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            EditorGUI.indentLevel++;
            GUI.color = new Color(1f, 1f, 1f, 0.7f);
            open = Foldout(open, name, true);
            GUI.color = Color.white;
            EditorGUI.indentLevel--;
            EditorGUILayout.Space();
        }

        public static void EndContainerBox()
        {
            EditorGUILayout.EndVertical();
        }

        public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick)
        {
            Rect position = GUILayoutUtility.GetRect(40f, 40f, 16f, 16f, EditorStyles.foldout);
            return EditorGUI.Foldout(position, foldout, content, toggleOnLabelClick, EditorStyles.foldout);
        }

        public static bool Foldout(bool foldout, string content, bool toggleOnLabelClick)
        {
            return Foldout(foldout, new GUIContent(content), toggleOnLabelClick);
        }

        private static void Rescale()
        {
            defaultField.padding = new RectOffset(Mathf.RoundToInt(5 * scale), Mathf.RoundToInt(5 * scale), Mathf.RoundToInt(5 * scale), Mathf.RoundToInt(5 * scale));
            smallField.padding = new RectOffset(Mathf.RoundToInt(2 * scale), Mathf.RoundToInt(2 * scale), Mathf.RoundToInt(2 * scale), Mathf.RoundToInt(2 * scale));
            dropdownItem.padding = new RectOffset(Mathf.RoundToInt(10 * scale), 0, 0, 0);
            bigButton.padding = new RectOffset(Mathf.RoundToInt(3*scale), Mathf.RoundToInt(3 * scale), Mathf.RoundToInt(3 * scale), Mathf.RoundToInt(3 * scale));
            bigButtonSelected.normal.textColor = new Color(0.95f, 0.95f, 0.95f);
            bigButton.padding = new RectOffset(Mathf.RoundToInt(4 * scale), Mathf.RoundToInt(4 * scale), Mathf.RoundToInt(4 * scale), Mathf.RoundToInt(4 * scale));
            bigButton.fontSize = Mathf.RoundToInt(30 * scale);
            bigButtonSelected.fontSize = Mathf.RoundToInt(30 * scale);
            defaultButton.fontSize = Mathf.RoundToInt(14 * scale);
            dropdownItem.fontSize = Mathf.RoundToInt(12 * scale);
            labelText.fontSize = Mathf.RoundToInt(12 * scale);
            defaultField.fontSize = Mathf.RoundToInt(14 * scale);
            smallField.fontSize = Mathf.RoundToInt(11 * scale);
        }

        public static void SetScale(float s)
        {
            if(s != scale)
            {
                scale = s;
                Rescale();
            } scale = s;
        }

        public static bool EditorLayoutSelectableButton(GUIContent content, bool active = true, bool selected = false, params GUILayoutOption[] options)
        {
            Color prevColor = GUI.color;
            Color prevContentColor = GUI.contentColor;
            Color prevBackgroundColor = GUI.backgroundColor;
            GUIStyle selectedStyle = GUI.skin.button;
            if (!active) GUI.color = inactiveColor;
            else
            {
                GUI.color = activeColor;
                if (selected)
                {
                    GUI.backgroundColor = highLightBGColor;
                    GUI.contentColor = highlightContentColor;
                    selectedStyle = new GUIStyle(selectedStyle);
                    selectedStyle.normal.textColor = Color.white;
                    selectedStyle.hover.textColor = Color.white;
                    selectedStyle.active.textColor = Color.white;
                } else GUI.contentColor = buttonContentColor;
            }
            bool clicked = GUILayout.Button(content, selectedStyle, options);
            GUI.color = prevColor;
            GUI.contentColor = prevContentColor;
            GUI.backgroundColor = prevBackgroundColor;
            return clicked && active;
        }
        private static string CleanStringForFloat(string input)
        {
            if (Regex.Match(input, @"^-?[0-9]*(?:\.[0-9]*)?$").Success)
                return input;
            else
            {
                return "0";
            }
        }

       private static void HandleControlsCount() {
            if (controlIndex >= controlStates.Length)
            {
                bool[] newStates = new bool[controlStates.Length + 1];
                controlStates.CopyTo(newStates, 0);
                controlStates = newStates;

                string[] newContents = new string[controlStates.Length + 1];
                floatFieldContents.CopyTo(newContents, 0);
                floatFieldContents = newContents;
            }
        }

#if DREAMTECK_SPLINES
        public static double ScreenPointToSplinePercent(SplineComputer computer, Vector2 screenPoint)
        {
            SplinePoint[] points = computer.GetPoints();
            float closestDistance = (screenPoint - HandleUtility.WorldToGUIPoint(points[0].position)).sqrMagnitude;
            double closestPercent = 0.0;
            double add = computer.moveStep;
            if (computer.type == Spline.Type.Linear) add /= 2f;
            int count = 0;
            for (double i = add; i < 1.0; i += add)
            {
                SplineSample result = computer.Evaluate(i);
                Vector2 point = HandleUtility.WorldToGUIPoint(result.position);
                float dist = (point - screenPoint).sqrMagnitude;
                if (dist < closestDistance)
                {
                    closestDistance = dist;
                    closestPercent = i;
                }
                count++;
            }
            return closestPercent;
        }
#endif
    }
}