using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;

namespace AIStudio.Wpf.DiagramDesigner
{
    public static partial class BlockDesignerItemViewModelHelper
    {
        /// <summary>
        /// type=0最近且没有依附；=1
        /// </summary>
        /// <param name="blockDesignerItemViewModel"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Tuple<FullyCreatedConnectorInfo, FullyCreatedConnectorInfo> FindNearPortToAttachTo(this IDiagramViewModel diagramViewModel, BlockDesignerItemTempLink blockDesignerItemTempLink)
        {
            if (blockDesignerItemTempLink == null || blockDesignerItemTempLink.LinkList == null || blockDesignerItemTempLink.LinkList.Count == 0)
                return new Tuple<FullyCreatedConnectorInfo, FullyCreatedConnectorInfo>(null, null);

            List<BlockDesignerItemViewModel> items = diagramViewModel.Items.OfType<BlockDesignerItemViewModel>().Where(p => !blockDesignerItemTempLink.LinkList.Contains(p)).ToList();

            return diagramViewModel.FindNearPortToAttachTo(items, blockDesignerItemTempLink);
        }

        public static Tuple<FullyCreatedConnectorInfo, FullyCreatedConnectorInfo> FindNearPortToAttachTo(this IDiagramViewModel diagramViewModel, List<BlockDesignerItemViewModel> items, BlockDesignerItemTempLink blockDesignerItemTempLink)
        {
            if (blockDesignerItemTempLink == null || blockDesignerItemTempLink.LinkList == null || blockDesignerItemTempLink.LinkList.Count == 0)
                return new Tuple<FullyCreatedConnectorInfo, FullyCreatedConnectorInfo>(null, null);

            FullyCreatedConnectorInfo parent = null;
            FullyCreatedConnectorInfo next = null;


            foreach (var port2 in blockDesignerItemTempLink.Connectors.OfType<BlockConnectorInfo>())
            {
                bool success = false;
                foreach (var item in items)
                {
                    foreach (var port in item.Connectors.OfType<BlockConnectorInfo>())
                    {
                        //已经被连接的不允许在顶部吸附了
                        if ((port.Orientation == ConnectorOrientation.Top || port.Orientation == ConnectorOrientation.Left) && port.DataItem.LinkNode.Previous != null)
                        {
                            continue;
                        }

                        //parent
                        if (parent == null)
                        {
                            if ((port.Orientation == ConnectorOrientation.Right && port2.Orientation == ConnectorOrientation.Left)
                                || (port.Orientation == ConnectorOrientation.Bottom && port2.Orientation == ConnectorOrientation.Top))
                            {
                                var dis = port.DistanceTo(port2);
                                //Debug.WriteLine($"{port.DataItem.Name}-{port2.DataItem.Name}:{dis}");
                                if (dis < diagramViewModel.DiagramOption.SnappingOption.BlockSnappingRadius)
                                {
                                    port.DataItem.ShowConnectors = true;
                                    if (port.CanAttachTo(port2) == true)
                                    {
                                        diagramViewModel.AddAttachTo(port, true);
                                        parent = port;
                                        success = true;
                                        break;
                                    }
                                    else
                                    {
                                        diagramViewModel.AddAttachTo(port, false);
                                    }

                                }
                            }
                        }

                        //next
                        if (next == null)
                        {
                            if ((port.Orientation == ConnectorOrientation.Left && port2.Orientation == ConnectorOrientation.Right)
                           || (port.Orientation == ConnectorOrientation.Top && port2.Orientation == ConnectorOrientation.Bottom))
                            {
                                var dis = port.DistanceTo(port2);
                                //Debug.WriteLine($"{port.DataItem.Name}-{port2.DataItem.Name}:{dis}");
                                if (dis < diagramViewModel.DiagramOption.SnappingOption.BlockSnappingRadius)
                                {
                                    port.DataItem.ShowConnectors = true;
                                    if (port.CanAttachTo(port2) == true)
                                    {
                                        diagramViewModel.AddAttachTo(port, true);
                                        next = port;
                                        success = true;
                                        break;
                                    }
                                    else
                                    {
                                        diagramViewModel.AddAttachTo(port, false);
                                    }

                                }
                            }
                        }
                    }

                    if (success)
                    {
                        break;
                    }
                }

                if (success)
                {
                    break;
                }
            }

            return new Tuple<FullyCreatedConnectorInfo, FullyCreatedConnectorInfo>(parent, next);
        }

        public static BlockItemsContainerInfo FindNearContainerToAttachTo(this IDiagramViewModel diagramViewModel, BlockDesignerItemTempLink blockDesignerItemTempLink)
        {
            if (blockDesignerItemTempLink == null || blockDesignerItemTempLink.LinkList == null || blockDesignerItemTempLink.LinkList.Count == 0)
                return null;

            List<BlockDesignerItemViewModel> items;

            items = diagramViewModel.Items.OfType<BlockDesignerItemViewModel>().Where(p => !blockDesignerItemTempLink.LinkList.Contains(p)).ToList();

            foreach (var container in items.SelectMany(n => n.Containers))
            {
                if (container.GetBounds().IntersectsWith(blockDesignerItemTempLink.GetBounds())) //如果两个位置相交
                {
                    var innerport = container.GetAllContainers(container.Children, false).Where(p => p.GetBounds().IntersectsWith(blockDesignerItemTempLink.GetBounds())).OrderByDescending(p => p.ContainerLevel).FirstOrDefault();
                    if (innerport != null)
                    {
                        if (innerport.CanAttachTo(blockDesignerItemTempLink.LinkList.FirstOrDefault()) == true)
                        {
                            innerport.DataItem.ShowConnectors = true;
                            if (innerport.OnlyOneChild || innerport.Children.Count == 0)
                            {
                                diagramViewModel.AddAttachTo(innerport, true);
                            }
                            else
                            {
                                diagramViewModel.FindNearPortToAttachTo(innerport.Children.ToList(), blockDesignerItemTempLink);
                            }
                            return innerport;
                        }
                        //else
                        //{
                        //    innerport.DisableAttachTo = true;
                        //    return null;
                        //}

                    }
                    else
                    {

                        if (container.CanAttachTo(blockDesignerItemTempLink.LinkList.FirstOrDefault()) == true)
                        {
                            container.DataItem.ShowConnectors = true;
                            if (container.OnlyOneChild || container.Children.Count == 0)
                            {
                                diagramViewModel.AddAttachTo(container, true);
                            }
                            else
                            {
                                diagramViewModel.FindNearPortToAttachTo(container.Children.ToList(), blockDesignerItemTempLink);
                            }
                            return container;
                        }
                        //else
                        //{
                        //    port.DisableAttachTo = true;
                        //    return null;
                        //}
                    }
                }
            }

            return null;
        }

        #region Block拖拽预览-拖拽完成
        public static void PreviewNearBlock(this IBlockDiagramViewModel diagramViewModel, List<BlockDesignerItemViewModel> blocks)
        {
            if (diagramViewModel == null)
                return;

            if (blocks.Any())
            {
                diagramViewModel.ClearAttachTo();
                var links = BlockDesignerItemTempLink.Build(blocks);

                blocks.ToList().ForEach(p => {
                    if (p.ZIndex != int.MaxValue)
                    {
                        p.SetOldValue(p.ZIndex, "ZIndex");
                    }
                    p.ZIndex = int.MaxValue;
                });

                foreach (BlockDesignerItemTempLink item in links)
                {
                    var container = diagramViewModel.FindNearContainerToAttachTo(item);
                    if (container != null)
                    {
                        continue;
                    }
                    diagramViewModel.FindNearPortToAttachTo(item);
                }
            }
        }



        public static void FinishNearBlock(this IBlockDiagramViewModel diagramViewModel, List<BlockDesignerItemViewModel> blocks)
        {
            if (diagramViewModel == null)
                return;

            if (blocks.Any())
            {
                var links = BlockDesignerItemTempLink.Build(blocks);

                blocks.ToList().ForEach(p => {
                    if (p.HasOldValue("ZIndex"))
                    {
                        p.ZIndex = p.GetOldValue<int>("ZIndex");
                    }
                });

                foreach (BlockDesignerItemTempLink item in links)
                {
                    var container = diagramViewModel.FindNearContainerToAttachTo(item);
                    if (container != null)
                    {
                        int index = 0;
                        var child = container.Children.FirstOrDefault(p => p.Connectors.Any(q => q.BeAttachTo == true));
                        if (child != null)
                        {
                            index = container.Children.IndexOf(child);
                            if (child.RightConnector?.BeAttachTo == true || child.BottomConnector?.BeAttachTo == true)
                            {
                                index++;
                            }
                        }
                        diagramViewModel.InsertChildCommand.Execute(new BlockContainerPara() { Item = container.DataItem, Child = item.LinkList.FirstOrDefault(), Container = container, Index = index });
                        continue;
                    }

                    var portTuple = diagramViewModel.FindNearPortToAttachTo(item);
                    var portParent = portTuple.Item1;
                    var portNext = portTuple.Item2;

                    if (portParent != null)
                    {
                        diagramViewModel.AddNextCommand.Execute(new BlockNextPara() { Item = portParent.DataItem as BlockDesignerItemViewModel, Next = item.LinkList.FirstOrDefault() });
                        portParent.BeAttachTo = false;
                        portParent.DisableAttachTo = false;
                    }
                    else if (portNext != null)
                    {
                        diagramViewModel.AddNextCommand.Execute(new BlockNextPara() { First = item.LinkList.FirstOrDefault(), Item = item.LinkList.LastOrDefault(), Next = portNext.DataItem as BlockDesignerItemViewModel });
                        portNext.BeAttachTo = false;
                        portNext.DisableAttachTo = false;
                    }
                    else
                    {
                        if (item.LinkList.FirstOrDefault().LinkNode.Previous != null)
                        {
                            diagramViewModel.RemoveNextCommand.Execute(new BlockNextPara() { Item = item.LinkList.FirstOrDefault().LinkNode.Previous.Value, Next = item.LinkList.FirstOrDefault().LinkNode.Value });
                        }
                    }
                }

                diagramViewModel.ClearAttachTo();
            }
        }


        #endregion
    }

    public static class LinkedListExtensions
    {
        public static void InsertRange(this LinkedList<BlockDesignerItemViewModel> source, LinkedListNode<BlockDesignerItemViewModel> node, IEnumerable<BlockDesignerItemViewModel> items)
        {
            if (node == null || items.Count() == 0)
            {
                return;
            }

            BlockDesignerItemViewModel last = null;
            foreach (var item in items)
            {
                item.LinkNode = source.AddAfter(node, item);
                item.ParentId = item.LinkNode.Previous.Value.Id;
                node = item.LinkNode;

                last = item;
            }

            //把后面的接上
            var lastnode = source.Find(last);
            if (lastnode.Next != null)
            {
                lastnode.Next.Value.ParentId = last.Id;
            }
        }

        public static void AppendRange(this LinkedList<BlockDesignerItemViewModel> source, IEnumerable<BlockDesignerItemViewModel> items)
        {
            foreach (var item in items)
            {
                item.LinkNode = source.AddLast(item);
                item.ParentId = item.LinkNode.Previous?.Value.Id ?? new Guid();
            }
        }

        public static void PrependRange(this LinkedList<BlockDesignerItemViewModel> source, IEnumerable<BlockDesignerItemViewModel> items)
        {
            var first = source.First; // If the list is empty, we can just append everything.
            if (first is null)
            {
                AppendRange(source, items); return;
            } // Otherwise, add each item in turn just before the original first item
            foreach (var item in items)
            {
                item.LinkNode = source.AddBefore(first, item);
                item.ParentId = item.LinkNode.Previous.Value.Id;
            }
        }
    }
}
