using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace Unity.VisualScripting
{
    [Widget(typeof(StickyNote))]
    public class StickyNoteWidget : GraphElementWidget<ICanvas, StickyNote>
    {
        public StickyNoteWidget(ICanvas canvas, StickyNote stickyNote) : base(canvas, stickyNote)
        {
            minResizeSize = new Vector2(64, 64);
            resizeInnerOffset.top = 0;
        }

        #region Model

        private StickyNote sticky => element;

        public override void CacheItem()
        {
            base.CacheItem();

            labelContent.text = sticky.title;
        }

        #endregion


        #region Lifecycle

        public override bool foregroundRequiresInput => true;

        #endregion


        #region Contents

        private static readonly GUIContent maxHeadLabelSizeContent = new GUIContent("M");

        private readonly GUIContent labelContent = new GUIContent();

        private Vector2 scrollPosition = Vector2.zero;

        private static Color _transparentWhite = new Color(1, 1, 1, .5f);

        #endregion


        #region Positioning

        protected override bool snapToGrid => BoltCore.Configuration.snapToGrid;

        public override float zIndex
        {
            get { return float.MaxValue; }
            set { }
        }

        public override Rect position
        {
            get { return sticky.position; }
            set { sticky.position = value; }
        }

        public Rect titlePosition { get; private set; }

        public Rect bodyPosition { get; private set; }

        public override Rect hotArea => headerPosition;

        public override void CachePosition()
        {
            AdjustLabelFontSize();

            //HeaderPosition is the draggable area, so the entire Sticky Note in this case.
            headerPosition = position;

            titlePosition = new Rect
            (
                position.x + Styles.title.margin.left,
                position.y + Styles.title.margin.top,
                position.width - Styles.body.margin.left,
                Styles.sticky.border.top
            );

            bodyPosition = new Rect
            (
                position.x + Styles.body.margin.left,
                position.y + Styles.body.margin.top + Styles.headerHeight,
                position.width - Styles.body.margin.left,
                position.height - Styles.body.margin.bottom - Styles.headerHeight
            );
        }

        public override void OnViewportChange()
        {
            base.OnViewportChange();

            Reposition(); // The label position is dependant on the zoom factor
        }

        private void AdjustLabelFontSize()
        {
            Styles.title.fontSize = Mathf.RoundToInt(Styles.headerFontSize / graph.zoom);
        }

        #endregion


        #region Drawing

        static void DrawBox(Rect rect, int thickness, Color color)
        {
            var left = new Rect(rect.x, rect.y, thickness, rect.height);
            var right = new Rect(rect.x + rect.width - thickness, rect.y, thickness, rect.height);
            var top = new Rect(rect.x, rect.y, rect.width, thickness);
            var bottom = new Rect(rect.x, rect.y + rect.height - thickness, rect.width, thickness);

            EditorGUI.DrawRect(left, color);
            EditorGUI.DrawRect(right, color);
            EditorGUI.DrawRect(top, color);
            EditorGUI.DrawRect(bottom, color);
        }

        public override void DrawForeground()
        {
            AdjustLabelFontSize();

            var displayMoveArrowCursor = true;

            var selected = selection.Contains(sticky);
            if (!selected)
            {
                focusOnBody = false;
                focusOnTitle = false;
            }

            //Sticky background
            if (e.IsRepaint)
            {
                using (LudiqGUI.color.Override(StickyNote.GetStickyColor(sticky.colorTheme)))

                {
                    Styles.sticky.Draw(position, false, false, selected, false);
                }


                int borderThickness = 0;
                //Focus border
                if (position.Contains(mousePosition))
                {
                    borderThickness++;
                }

                if (selected)
                {
                    borderThickness++;
                }

                if (borderThickness > 0)
                {
                    DrawBox(position, borderThickness, Color.cyan);
                }
            }

            //Title section
            using (new EditorGUI.DisabledScope(!focusOnTitle))
            {
                GUI.SetNextControlName(item.guid + "title");

                var color = StickyNote.GetFontColor(sticky.colorTheme);

                var titleBanner = titlePosition.position;
                var titleSize = Styles.title.CalcSize(new GUIContent(sticky.title));

                titleSize.y -= Styles.title.padding.top;
                titleBanner.y += Styles.title.padding.top;
                var titleBannerRect = new Rect(titleBanner, titleSize);

                //Add semi transparent text background for Pro due to dark on dark background
                if (EditorGUIUtility.isProSkin && color == Color.black)
                {
                    //Enable when title is longer than width of the sticky
                    if (bodyPosition.width < titleSize.x)
                    {
                        EditorGUI.DrawRect(titleBannerRect, _transparentWhite);
                    }
                }

                //Draw focus border when mouse over text
                if (bodyPosition.width > titleSize.x)
                {
                    //Use the title area width if the text is under the sticky box width
                    titleBannerRect.width = titlePosition.width - Styles.title.margin.left;
                }

                if (titleBannerRect.Contains(mousePosition))
                {
                    if (focusOnTitle)
                    {
                        displayMoveArrowCursor = false;
                        window.AddCursorRect(titleBannerRect, MouseCursor.Text);
                    }
                    DrawBox(titleBannerRect, 1, Color.cyan);
                }

                if (!focusOnTitle)
                    color.a += 1; //Offset the disable color
                using (LudiqGUI.color.Override(color))
                {
                    if (focusOnTitle)
                    {
                        sticky.title = EditorGUI.TextField(titlePosition, GUIContent.none, sticky.title,
                            Styles.title);
                    }
                    else
                    {
                        EditorGUI.LabelField(titlePosition, sticky.title, Styles.title);
                    }
                }
            }

            //Body section
            var bodyBox = bodyPosition;
            bodyBox.width -= Styles.title.margin.left;

            var scrollBox = bodyBox;

            var bodyContentHeight = Styles.body.CalcHeight(new GUIContent(sticky.body), bodyBox.width);
            var needScrollbar = bodyContentHeight > bodyBox.height;

            if (needScrollbar)
                bodyBox.width -= 12;

            var innerScrollboxRect = bodyBox;
            innerScrollboxRect.height = needScrollbar ?
                Styles.body.CalcHeight(new GUIContent(sticky.body), bodyBox.width) :
                bodyContentHeight;
            innerScrollboxRect.width -= 1;

            //Body mouse over focus box
            if (bodyBox.Contains(mousePosition))
            {
                if (focusOnBody)
                {
                    displayMoveArrowCursor = false;
                    window.AddCursorRect(bodyBox, MouseCursor.Text);
                }
                DrawBox(bodyBox, 1, Color.cyan);
            }
            else
            {
                if (needScrollbar && bodyPosition.Contains(mousePosition))
                {
                    //Cursor on scrollbar
                    displayMoveArrowCursor = false;
                }
            }

            scrollPosition = GUI.BeginScrollView(scrollBox, scrollPosition, innerScrollboxRect);
            using (new EditorGUI.DisabledScope(!focusOnBody))
            {
                GUI.SetNextControlName(item.guid + "body");
                var color = StickyNote.GetFontColor(sticky.colorTheme);

                if (!focusOnBody)
                    color.a += 1f; //Offset the disable color
                using (LudiqGUI.color.Override(color))
                {
                    if (focusOnBody)
                    {
                        sticky.body = GUI.TextArea(innerScrollboxRect, sticky.body, Styles.body);
                    }
                    else
                    {
                        EditorGUI.LabelField(bodyBox, sticky.body, Styles.body);
                    }
                }
            }

            GUI.EndScrollView();

            if (displayMoveArrowCursor && isMouseOverHeaderArea)
            {
                window.AddCursorRect(hotArea, MouseCursor.MoveArrow);
            }
        }
        #endregion


        #region Deleting

        public override bool canDelete => true;

        #endregion


        #region Layouting

        public override bool canAlignAndDistribute => false;

        #endregion


        #region Resizing

        public override bool canResizeHorizontal => true;

        public override bool canResizeVertical => true;

        #endregion


        #region Selecting

        private bool focusOnTitle;

        private bool focusOnBody;
        public override bool canSelect => true;

        protected override void OnDoubleClick()
        {
            focusOnTitle = false;
            focusOnBody = false;

            if (titlePosition.Contains(mousePosition))
            {
                focusOnTitle = true;
                EditorGUI.FocusTextInControl(item.guid + "title");
            }

            if (bodyPosition.Contains(mousePosition))
            {
                focusOnBody = true;
                EditorGUI.FocusTextInControl(item.guid + "body");
            }
        }

        #endregion


        #region Dragging

        public override bool canDrag => true;

        #endregion


        public static class Styles
        {
            static Styles()
            {
                sticky = new GUIStyle();
                sticky.normal.background = Texture2D.whiteTexture;
                sticky.onNormal.background = sticky.normal.background;

                sticky.onActive.background = Texture2D.grayTexture;
                sticky.border = new RectOffset(16, 16, 25, 16);

                title = new GUIStyle();
                title.normal.textColor = new Color(1, 1, 1, 0.75f);
                title.alignment = TextAnchor.UpperLeft;
                title.padding = new RectOffset(0, 5, 5, 0);
                title.margin = new RectOffset(10, 0, 0, 0);

                body = new GUIStyle();
                body.normal.textColor = new Color(1, 1, 1, 0.75f);
                body.alignment = TextAnchor.UpperLeft;
                body.wordWrap = true;
                body.padding = new RectOffset(0, 5, 0, 0);
                body.margin = new RectOffset(10, 0, 0, 0);
                body.focused = new GUIStyleState() { };
                body.focused.textColor = Color.cyan;
            }

            public static readonly float headerFontSize = 14;

            public static readonly GUIStyle sticky;

            public static readonly GUIStyle title;

            public static readonly GUIStyle body;

            public static float headerHeight => sticky.border.top;
        }
    }
}