using System.Reflection;

using UnityEditor;
using UnityEngine;

using PlasticGui;
using Unity.PlasticSCM.Editor.UI;

namespace Unity.PlasticSCM.Editor.Views.PendingChanges
{
    internal static class DrawCommentTextArea
    {
        internal static void For(
            PendingChangesTab pendingChangesTab,
            float width,
            bool isOperationRunning)
        {
            using (new GuiEnabled(!isOperationRunning))
            {
                EditorGUILayout.BeginHorizontal();

                Rect textAreaRect = BuildTextAreaRect(
                    pendingChangesTab.CommentText, width);

                EditorGUI.BeginChangeCheck();

                pendingChangesTab.CommentText = DoTextArea(
                    pendingChangesTab.CommentText ?? string.Empty,
                    pendingChangesTab.ForceToShowComment,
                    textAreaRect);

                pendingChangesTab.ForceToShowComment = false;

                if (EditorGUI.EndChangeCheck())
                    OnTextAreaChanged(pendingChangesTab);

                if (string.IsNullOrEmpty(pendingChangesTab.CommentText))
                {
                    DoPlaceholderIfNeeded(PlasticLocalization.GetString(
                        PlasticLocalization.Name.CheckinComment),
                        textAreaRect);
                }

                EditorGUILayout.EndHorizontal();
            }
        }

        static void OnTextAreaChanged(PendingChangesTab pendingChangesTab)
        {
            pendingChangesTab.ClearIsCommentWarningNeeded();
        }

        static string DoTextArea(string text, bool forceToShowText, Rect textAreaRect)
        {
            // while the text area has the focus, the changes to 
            // the source string will not be picked up by the text editor. 
            // so, when we want to change the text programmatically
            // we have to remove the focus, set the text and then reset the focus.

            TextEditor textEditor = typeof(EditorGUI)
                .GetField("activeEditor", BindingFlags.Static | BindingFlags.NonPublic)
                .GetValue(null) as TextEditor;

            bool shouldBeFocusFixed = forceToShowText && textEditor != null;

            if (shouldBeFocusFixed)
                EditorGUIUtility.keyboardControl = 0;

            text = EditorGUI.TextArea(textAreaRect, text, EditorStyles.textArea);

            if (shouldBeFocusFixed)
                EditorGUIUtility.keyboardControl = textEditor.controlID;

            return text;
        }

        static void DoPlaceholderIfNeeded(string placeholder, Rect textAreaRect)
        {
            int textAreaControlId = GUIUtility.GetControlID(FocusType.Passive) - 1;

            if (EditorGUIUtility.keyboardControl == textAreaControlId)
                return;

            Rect hintRect = textAreaRect;
            hintRect.height = EditorStyles.textArea.lineHeight;

            GUI.Label(hintRect, placeholder,
                UnityStyles.PendingChangesTab.CommentPlaceHolder);
        }

        static Rect BuildTextAreaRect(string text, float width)
        {
            GUIStyle commentTextAreaStyle =
                UnityStyles.PendingChangesTab.CommentTextArea;

            float contentWidth = width -
                commentTextAreaStyle.margin.left -
                commentTextAreaStyle.margin.right;

            float requiredHeight = commentTextAreaStyle
                .CalcHeight(new GUIContent(text), contentWidth);

            Rect result = GUILayoutUtility.GetRect(
                contentWidth, Mathf.Max(requiredHeight, 42));
            result.x += commentTextAreaStyle.margin.left;
            result.width = contentWidth;
            result.height = Mathf.Max(result.height, 42);

            return result;
        }
    }
}
