using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
#if UNITY_6000_2_OR_NEWER
using TreeView = UnityEditor.IMGUI.Controls.TreeView<int>;
#endif

namespace Unity.PlasticSCM.Editor.UI.Tree
{
    internal static class DrawTreeViewItem
    {
        internal enum TextTrimming
        {
            None,
            Path,
        }
        internal static void InitializeStyles()
        {
            if (EditorStyles.label == null)
                return;

            TreeView.DefaultStyles.label = UnityStyles.Tree.Label;
            TreeView.DefaultStyles.boldLabel = UnityStyles.Tree.BoldLabel;
        }

        internal static void ForIndentedItem(
            Rect rowRect,
            int depth,
            string label,
            string infoLabel,
            bool isSelected,
            bool isFocused)
        {
            float indent = GetIndent(depth);

            rowRect.x += indent;
            rowRect.width -= indent;

            //add a little indentation
            rowRect.x += 5;
            rowRect.width -= 5;

            TreeView.DefaultGUI.Label(rowRect, label, isSelected, isFocused);

            if (!string.IsNullOrEmpty(infoLabel))
                DrawInfolabel(rowRect, label, infoLabel);
        }

        internal static void ForIndentedItemWithIcon(
            Rect rowRect,
            float rowHeight,
            int depth,
            string label,
            string infoLabel,
            Texture icon,
            bool isSelected,
            bool isFocused)
        {
            float indent = GetIndent(depth);

            rowRect.x += indent;
            rowRect.width -= indent;

            rowRect = DrawIconLeft(
                rowRect, rowHeight, icon, null, null);

            TreeView.DefaultGUI.Label(rowRect, label, isSelected, isFocused);

            if (!string.IsNullOrEmpty(infoLabel))
                DrawInfolabel(rowRect, label, infoLabel);
        }

        internal static bool ForCheckableIndentedItem(
            Rect rowRect,
            int depth,
            string label,
            string infoLabel,
            bool isSelected,
            bool isFocused,
            bool wasChecked,
            bool hadCheckedChildren,
            bool hadPartiallyCheckedChildren)
        {
            float indent = GetIndent(depth);

            rowRect.x += indent;
            rowRect.width -= indent;

            Rect checkRect = GetCheckboxRect(rowRect);

            if (!wasChecked && (hadCheckedChildren || hadPartiallyCheckedChildren))
                EditorGUI.showMixedValue = true;

            bool isChecked = EditorGUI.Toggle(checkRect, wasChecked);
            EditorGUI.showMixedValue = false;

            rowRect.x = checkRect.xMax - 4;
            rowRect.width -= checkRect.width;

            //add a little indentation
            rowRect.x += 5;
            rowRect.width -= 5;

            TreeView.DefaultGUI.Label(rowRect, label, isSelected, isFocused);

            if (!string.IsNullOrEmpty(infoLabel))
                DrawInfolabel(rowRect, label, infoLabel);

            return isChecked;
        }

        internal static void ForItemCell(
            Rect rect,
            float rowHeight,
            int depth,
            Texture icon,
            string iconTooltip,
            Texture overlayIcon,
            string label,
            bool isSelected,
            bool isFocused,
            bool isBoldText,
            bool isSecondaryLabel,
            TextTrimming textTrimming = TextTrimming.None)
        {
            float indent = GetIndent(depth);

            rect.x += indent;
            rect.width -= indent;

            rect = DrawIconLeft(
               rect, rowHeight, icon, iconTooltip, overlayIcon);

            if (isSecondaryLabel)
            {
                ForSecondaryLabel(rect, label, isSelected, isFocused, isBoldText, textTrimming);
                return;
            }

            ForLabel(rect, label, isSelected, isFocused, isBoldText, textTrimming);
        }

        internal static bool ForCheckableItemCell(
            Rect rect,
            float rowHeight,
            int depth,
            Texture icon,
            string iconTooltip,
            Texture overlayIcon,
            string label,
            bool isSelected,
            bool isFocused,
            bool isHighlighted,
            bool wasChecked,
            DrawTreeViewItem.TextTrimming textTrimming = TextTrimming.None)
        {
            float indent = GetIndent(depth);

            rect.x += indent;
            rect.width -= indent;

            Rect checkRect = GetCheckboxRect(rect);

            bool isChecked = EditorGUI.Toggle(checkRect, wasChecked);

            rect.x = checkRect.xMax;
            rect.width -= checkRect.width;

            rect = DrawIconLeft(
                rect, rowHeight, icon, iconTooltip, overlayIcon);

            GUIStyle style = isHighlighted ?
                UnityStyles.Tree.BoldLabel :
                UnityStyles.Tree.Label;

            if (Event.current.type != UnityEngine.EventType.Repaint)
                return isChecked;

            GUIContent content = GetGUIContent(label, style, rect, textTrimming);

            DrawLabel(
                content,
                style,
                rect,
                false,
                true,
                isSelected,
                isFocused);

            return isChecked;
        }

        internal static Rect DrawIconLeft(
            Rect rect,
            float rowHeight,
            Texture icon,
            string iconTooltip,
            Texture overlayIcon)
        {
            if (icon == null)
                return rect;

            float iconWidth = UnityConstants.TREEVIEW_ICON_SIZE * ((float)icon.width / icon.height);;

            Rect iconRect = new Rect(rect.x, rect.y, iconWidth, rowHeight);

            EditorGUI.LabelField(iconRect, new GUIContent(icon, iconTooltip), UnityStyles.Tree.IconStyle);

            if (overlayIcon != null)
            {
                Rect overlayIconRect = GetOverlayRect.ForPendingChanges(iconRect);

                GUI.DrawTexture(
                    overlayIconRect, overlayIcon,
                    ScaleMode.ScaleToFit);
            }

            rect.x += iconRect.width;
            rect.width -= iconRect.width;

            return rect;
        }

        static void DrawInfolabel(
            Rect rect,
            string label,
            string infoLabel)
        {
            Vector2 labelSize = ((GUIStyle)UnityStyles.Tree.Label)
                .CalcSize(new GUIContent(label));

            rect.x += labelSize.x;

            GUI.Label(rect, infoLabel, UnityStyles.Tree.InfoLabel);
        }

        static Rect GetCheckboxRect(Rect rect)
        {
            return new Rect(
                rect.x,
                rect.y + UnityConstants.TREEVIEW_CHECKBOX_Y_OFFSET,
                UnityConstants.TREEVIEW_CHECKBOX_SIZE,
                rect.height);
        }

        static float GetIndent(int depth)
        {
            return 16 * (depth + 1);
        }

        internal static void ForSecondaryLabelRightAligned(
            Rect rect,
            string label,
            bool isSelected,
            bool isFocused,
            bool isBoldText,
            TextTrimming textTrimming = TextTrimming.None)
        {
            if (Event.current.type != EventType.Repaint)
                return;

            GUIStyle style = isBoldText ?
                UnityStyles.Tree.SecondaryLabelBoldRightAligned :
                UnityStyles.Tree.SecondaryLabelRightAligned;
            GUIContent content = GetGUIContent(label, style, rect, textTrimming);

            DrawLabel(
                content,
                style,
                rect,
                false,
                true,
                isSelected,
                isFocused);
        }

        internal static void ForSecondaryLabel(
            Rect rect,
            string label,
            bool isSelected,
            bool isFocused,
            bool isBoldText,
            TextTrimming textTrimming = TextTrimming.None)
        {
            if (Event.current.type != EventType.Repaint)
                return;

            GUIStyle style = isBoldText ?
                UnityStyles.Tree.SecondaryBoldLabel :
                UnityStyles.Tree.SecondaryLabel;

            if (isBoldText)
                style.normal.textColor = Color.red;

            GUIContent content = GetGUIContent(label, style, rect, textTrimming);

            DrawLabel(
                content,
                style,
                rect,
                false,
                true,
                isSelected,
                isFocused);
        }

        internal static void ForLabel(
            Rect rect,
            GUIContent content,
            bool isSelected,
            bool isFocused,
            bool isBoldText)
        {
            if (Event.current.type != EventType.Repaint)
                return;

            GUIStyle style = isBoldText ? UnityStyles.Tree.BoldLabel: UnityStyles.Tree.Label;

            DrawLabel(
                content,
                style,
                rect,
                false,
                true,
                isSelected,
                isFocused);
        }

        internal static void ForLabel(
            Rect rect,
            string label,
            bool isSelected,
            bool isFocused,
            bool isBoldText,
            TextTrimming textTrimming = TextTrimming.None)
        {
            if (Event.current.type != EventType.Repaint)
                return;

            label = TruncateLabelIfNeeded(label);

            GUIStyle style = isBoldText ? UnityStyles.Tree.BoldLabel: UnityStyles.Tree.Label;
            GUIContent content = GetGUIContent(label, style, rect, textTrimming);

            DrawLabel(
                content,
                style,
                rect,
                false,
                true,
                isSelected,
                isFocused);
        }

        static void DrawLabel(
            GUIContent content,
            GUIStyle style,
            Rect rect,
            bool isHover,
            bool isActive,
            bool isSelected,
            bool isFocused)
        {
            style.Draw(
                rect, content, isHover, isActive, isSelected, isFocused);
        }

        static GUIContent GetGUIContent(string label, GUIStyle style, Rect rect, TextTrimming textTrimming)
        {
            if (textTrimming == TextTrimming.Path)
            {
                string truncatedLabel = PathTrimming.TruncatePath(
                    label,
                    rect.width,
                    CalcTextSize.FromStyle(style),
                    out var wasTrimmed);

                return new GUIContent(truncatedLabel, wasTrimmed ? label : null);
            }

            return new GUIContent(label);
        }

        static string TruncateLabelIfNeeded(string label)
        {
            if (string.IsNullOrEmpty(label))
                return label;

            if (label.Length <= MAX_LABEL_LENGTH)
                return label;

            return label.Substring(0, MAX_LABEL_LENGTH) + "...";
        }

        const int MAX_LABEL_LENGTH = 1024;
    }
}