using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using AIStudio.Wpf.DiagramDesigner.Geometrys;

namespace AIStudio.Wpf.DiagramDesigner.Controls
{
    public class ResizeThumb : Thumb
    {
        public ResizeThumb()
        {
            base.DragDelta += new DragDeltaEventHandler(ResizeThumb_DragDelta);
            base.DragStarted += ResizeThumb_DragStarted;
            base.DragCompleted += ResizeThumb_DragCompleted;
        }

        public IDiagramViewModel DiagramViewModel
        {
            get
            {
                return (this.DataContext as SelectableDesignerItemViewModelBase)?.Root;
            }
        }

        private List<SelectableDesignerItemViewModelBase> designerItems;

        private void ResizeThumb_DragStarted(object sender, DragStartedEventArgs e)
        {
            DesignerItemViewModelBase designerItem = this.DataContext as DesignerItemViewModelBase;
            if (designerItem != null && designerItem.IsSelected)
            {
                designerItems = designerItem.Root.SelectedItems.ToList();

                Interlocked.Increment(ref DiagramViewModel.DoCommandManager.BeginDo);
                foreach (DesignerItemViewModelBase item in designerItems.OfType<DesignerItemViewModelBase>())
                {
                    item.SetOldValue(item.Size, nameof(item.Size));
                }

                e.Handled = true;
            }
            else
            {
                designerItems = null;
            }
        }

        private void ResizeThumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            if (designerItems != null)
            {
                Dictionary<DesignerItemViewModelBase, Tuple<SizeBase, SizeBase>> infos =
                  designerItems.OfType<DesignerItemViewModelBase>().ToDictionary(p => p,
                  p => new Tuple<SizeBase, SizeBase>(p.GetOldValue<SizeBase>(nameof(p.Size)), p.Size));

                Interlocked.Decrement(ref DiagramViewModel.DoCommandManager.BeginDo);
                DiagramViewModel.DoCommandManager.DoNewCommand(this.ToString(),
                  () => {
                      foreach (var info in infos)
                      {
                          info.Key.Size = info.Value.Item2;
                      }
                  },
                  () => {
                      foreach (var info in infos)
                      {
                          info.Key.Size = info.Value.Item1;
                      }
                  });
                e.Handled = true;
            }
        }


        void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            if (designerItems != null)
            {
                double minLeft, minTop, minDeltaHorizontal, minDeltaVertical;
                double dragDeltaVertical, dragDeltaHorizontal, scale;

                CalculateDragLimits(designerItems.OfType<DesignerItemViewModelBase>(), out minLeft, out minTop,
                                    out minDeltaHorizontal, out minDeltaVertical);

                foreach (DesignerItemViewModelBase item in designerItems.OfType<DesignerItemViewModelBase>())
                {
                    if (item != null)
                    {
                        switch (base.VerticalAlignment)
                        {
                            case VerticalAlignment.Bottom:
                                dragDeltaVertical = Math.Min(-e.VerticalChange, minDeltaVertical);
                                scale = (item.ItemHeight - dragDeltaVertical) / item.ItemHeight;
                                DragBottom(scale, item, DiagramViewModel.SelectionService);
                                break;
                            case VerticalAlignment.Top:
                                double top = item.Top;
                                dragDeltaVertical = Math.Min(Math.Max(-minTop, e.VerticalChange), minDeltaVertical);
                                scale = (item.ItemHeight - dragDeltaVertical) / item.ItemHeight;
                                DragTop(scale, item, DiagramViewModel.SelectionService);
                                break;
                            default:
                                break;
                        }

                        switch (base.HorizontalAlignment)
                        {
                            case HorizontalAlignment.Left:
                                double left = item.Left;
                                dragDeltaHorizontal = Math.Min(Math.Max(-minLeft, e.HorizontalChange), minDeltaHorizontal);
                                scale = (item.ItemWidth - dragDeltaHorizontal) / item.ItemWidth;
                                DragLeft(scale, item, DiagramViewModel.SelectionService);
                                break;
                            case HorizontalAlignment.Right:
                                dragDeltaHorizontal = Math.Min(-e.HorizontalChange, minDeltaHorizontal);
                                scale = (item.ItemWidth - dragDeltaHorizontal) / item.ItemWidth;
                                DragRight(scale, item, DiagramViewModel.SelectionService);
                                break;
                            default:
                                break;
                        }
                    }
                }

                e.Handled = true;
            }
        }

        #region Helper methods

        private void DragLeft(double scale, DesignerItemViewModelBase item, SelectionService selectionService)
        {
            IEnumerable<DesignerItemViewModelBase> groupItems = selectionService.GetGroupMembers(item, false).Cast<DesignerItemViewModelBase>();

            double groupLeft = item.Left + item.ItemWidth;
            foreach (DesignerItemViewModelBase groupItem in groupItems)
            {
                double groupItemLeft = groupItem.Left;
                double delta = (groupLeft - groupItemLeft) * (scale - 1);
                groupItem.Left = groupItemLeft - delta;
                groupItem.ItemWidth = groupItem.ItemWidth * scale;
            }
        }

        private void DragTop(double scale, DesignerItemViewModelBase item, SelectionService selectionService)
        {
            IEnumerable<DesignerItemViewModelBase> groupItems = selectionService.GetGroupMembers(item, false).Cast<DesignerItemViewModelBase>();
            double groupBottom = item.Top + item.ItemHeight;
            foreach (DesignerItemViewModelBase groupItem in groupItems)
            {
                double groupItemTop = groupItem.Top;
                double delta = (groupBottom - groupItemTop) * (scale - 1);
                groupItem.Top = groupItemTop - delta;
                groupItem.ItemHeight = groupItem.ItemHeight * scale;
            }
        }

        private void DragRight(double scale, DesignerItemViewModelBase item, SelectionService selectionService)
        {
            IEnumerable<DesignerItemViewModelBase> groupItems = selectionService.GetGroupMembers(item, false).Cast<DesignerItemViewModelBase>();

            double groupLeft = item.Left;
            foreach (DesignerItemViewModelBase groupItem in groupItems)
            {
                double groupItemLeft = groupItem.Left;
                double delta = (groupItemLeft - groupLeft) * (scale - 1);

                groupItem.Left = groupItemLeft + delta;
                groupItem.ItemWidth = groupItem.ItemWidth * scale;
            }
        }

        private void DragBottom(double scale, DesignerItemViewModelBase item, SelectionService selectionService)
        {
            IEnumerable<DesignerItemViewModelBase> groupItems = selectionService.GetGroupMembers(item, false).Cast<DesignerItemViewModelBase>();
            double groupTop = item.Top;
            foreach (DesignerItemViewModelBase groupItem in groupItems)
            {
                double groupItemTop = groupItem.Top;
                double delta = (groupItemTop - groupTop) * (scale - 1);

                groupItem.Top = groupItemTop + delta;
                groupItem.ItemHeight = groupItem.ItemHeight * scale;
            }
        }

        private void CalculateDragLimits(IEnumerable<DesignerItemViewModelBase> selectedItems, out double minLeft, out double minTop, out double minDeltaHorizontal, out double minDeltaVertical)
        {
            minLeft = double.MaxValue;
            minTop = double.MaxValue;
            minDeltaHorizontal = double.MaxValue;
            minDeltaVertical = double.MaxValue;

            // drag limits are set by these parameters: canvas top, canvas left, minHeight, minWidth
            // calculate min value for each parameter for each item
            foreach (DesignerItemViewModelBase item in selectedItems)
            {
                double left = item.Left;
                double top = item.Top;

                minLeft = double.IsNaN(left) ? 0 : Math.Min(left, minLeft);
                minTop = double.IsNaN(top) ? 0 : Math.Min(top, minTop);

                minDeltaVertical = Math.Min(minDeltaVertical, 10);
                minDeltaHorizontal = Math.Min(minDeltaHorizontal, 10);
            }
        }

        #endregion
    }
}