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

namespace Unity.VisualScripting
{
    [Canvas(typeof(StateGraph))]
    public sealed class StateCanvas : VisualScriptingCanvas<StateGraph>
    {
        public StateCanvas(StateGraph graph) : base(graph) { }


        #region View

        protected override bool shouldEdgePan => base.shouldEdgePan || isCreatingTransition;

        #endregion


        #region Drawing

        protected override void DrawBackground()
        {
            base.DrawBackground();

            if (isCreatingTransition)
            {
                var startRect = this.Widget(transitionSource).position;
                var end = mousePosition;

                Edge startEdge, endEdge;

                GraphGUI.GetConnectionEdge
                    (
                        startRect.center,
                        end,
                        out startEdge,
                        out endEdge
                    );

                var start = startRect.GetEdgeCenter(startEdge);

                GraphGUI.DrawConnectionArrow(Color.white, start, end, startEdge, endEdge);
            }
        }

        #endregion


        #region Clipboard

        public override void ShrinkCopyGroup(HashSet<IGraphElement> copyGroup)
        {
            copyGroup.RemoveWhere(element =>
            {
                if (element is IStateTransition)
                {
                    var transition = (IStateTransition)element;

                    if (!copyGroup.Contains(transition.source) ||
                        !copyGroup.Contains(transition.destination))
                    {
                        return true;
                    }
                }

                return false;
            });
        }

        #endregion


        #region Window

        public override void OnToolbarGUI()
        {
            if (graph.states.Any(u => u.GetException(reference) != null) || graph.transitions.Any(t => t.GetException(reference) != null))
            {
                if (GUILayout.Button("Clear Errors", LudiqStyles.toolbarButton))
                {
                    foreach (var state in graph.states)
                    {
                        state.SetException(reference, null);
                    }

                    foreach (var transition in graph.transitions)
                    {
                        transition.SetException(reference, null);
                    }
                }
            }

            EditorGUI.BeginChangeCheck();

            BoltCore.Configuration.dimInactiveNodes = GUILayout.Toggle(BoltCore.Configuration.dimInactiveNodes, "Dim", LudiqStyles.toolbarButton);

            if (EditorGUI.EndChangeCheck())
            {
                BoltCore.Configuration.Save();
            }

            base.OnToolbarGUI();
        }

        #endregion


        #region Context

        protected override void OnContext()
        {
            if (isCreatingTransition)
            {
                CancelTransition();
            }
            else
            {
                base.OnContext();
            }
        }

        protected override IEnumerable<DropdownOption> GetContextOptions()
        {
            yield return new DropdownOption((Action<Vector2>)CreateFlowState, "Create Script State");
            yield return new DropdownOption((Action<Vector2>)CreateSuperState, "Create Super State");
            yield return new DropdownOption((Action<Vector2>)CreateAnyState, "Create Any State");

            foreach (var baseOption in base.GetContextOptions())
            {
                yield return baseOption;
            }
        }

        private void CreateFlowState(Vector2 position)
        {
            var flowState = FlowState.WithEnterUpdateExit();

            if (!graph.states.Any())
            {
                flowState.isStart = true;
                flowState.nest.embed.title = "Start";
            }

            AddState(flowState, position);
        }

        private void CreateSuperState(Vector2 position)
        {
            var superState = SuperState.WithStart();

            if (!graph.states.Any())
            {
                superState.isStart = true;
                superState.nest.embed.title = "Start";
            }

            AddState(superState, position);
        }

        private void CreateAnyState(Vector2 position)
        {
            AddState(new AnyState(), position);
        }

        public void AddState(IState state, Vector2 position)
        {
            UndoUtility.RecordEditedObject("Create State");
            state.position = position;
            graph.states.Add(state);
            state.position -= this.Widget(state).position.size / 2;
            state.position = state.position.PixelPerfect();
            this.Widget(state).Reposition();
            selection.Select(state);
            GUI.changed = true;
        }

        #endregion


        #region Lifecycle

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

            CancelTransition();
        }

        protected override void HandleHighPriorityInput()
        {
            if (isCreatingTransition)
            {
                if (e.IsMouseDrag(MouseButton.Left))
                {
                    // Priority over lasso
                    e.Use();
                }
                else if (e.IsKeyDown(KeyCode.Escape))
                {
                    CancelTransition();
                    e.Use();
                }
                if (e.IsMouseDown(MouseButton.Left) || e.IsMouseUp(MouseButton.Left))
                {
                    CompleteTransitionToNewState();
                    e.Use();
                }
            }

            base.HandleHighPriorityInput();
        }

        public void CompleteTransitionToNewState()
        {
            var startRect = this.Widget(transitionSource).position;
            var end = mousePosition;

            GraphGUI.GetConnectionEdge
                (
                    startRect.center,
                    end,
                    out var startEdge,
                    out var endEdge
                );

            var destination = FlowState.WithEnterUpdateExit();
            graph.states.Add(destination);

            Vector2 offset;

            var size = this.Widget(destination).position.size;

            switch (endEdge)
            {
                case Edge.Left:
                    offset = new Vector2(0, -size.y / 2);
                    break;
                case Edge.Right:
                    offset = new Vector2(-size.x, -size.y / 2);
                    break;
                case Edge.Top:
                    offset = new Vector2(-size.x / 2, 0);
                    break;
                case Edge.Bottom:
                    offset = new Vector2(-size.x / 2, -size.y);
                    break;
                default:
                    throw new UnexpectedEnumValueException<Edge>(endEdge);
            }

            destination.position = mousePosition + offset;

            destination.position = destination.position.PixelPerfect();

            EndTransition(destination);
        }

        #endregion


        #region Drag & Drop

        public override bool AcceptsDragAndDrop()
        {
            return DragAndDropUtility.Is<ScriptGraphAsset>() || DragAndDropUtility.Is<StateGraphAsset>();
        }

        public override void PerformDragAndDrop()
        {
            if (DragAndDropUtility.Is<ScriptGraphAsset>())
            {
                var flowMacro = DragAndDropUtility.Get<ScriptGraphAsset>();
                var flowState = new FlowState(flowMacro);
                AddState(flowState, DragAndDropUtility.position);
            }
            else if (DragAndDropUtility.Is<StateGraphAsset>())
            {
                var asset = DragAndDropUtility.Get<StateGraphAsset>();
                var superState = new SuperState(asset);
                AddState(superState, DragAndDropUtility.position);
            }
        }

        public override void DrawDragAndDropPreview()
        {
            if (DragAndDropUtility.Is<ScriptGraphAsset>())
            {
                GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, DragAndDropUtility.Get<ScriptGraphAsset>().name, typeof(ScriptGraphAsset).Icon());
            }
            else if (DragAndDropUtility.Is<StateGraphAsset>())
            {
                GraphGUI.DrawDragAndDropPreviewLabel(DragAndDropUtility.offsetedPosition, DragAndDropUtility.Get<StateGraphAsset>().name, typeof(StateGraphAsset).Icon());
            }
        }

        #endregion


        #region Transition Creation

        public IState transitionSource { get; set; }

        public bool isCreatingTransition => transitionSource != null;

        public void StartTransition(IState source)
        {
            transitionSource = source;
            window.Focus();
        }

        public void EndTransition(IState destination)
        {
            UndoUtility.RecordEditedObject("Create State Transition");

            var transition = FlowStateTransition.WithDefaultTrigger(transitionSource, destination);
            graph.transitions.Add(transition);
            transitionSource = null;
            this.Widget(transition).BringToFront();
            selection.Select(transition);
            GUI.changed = true;
        }

        public void CancelTransition()
        {
            transitionSource = null;
        }

        #endregion
    }
}
