using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using AIStudio.Wpf.DiagramDesigner.Helpers;
using AIStudio.Wpf.DiagramDesigner.Models;
using Newtonsoft.Json;

namespace AIStudio.Wpf.DiagramDesigner
{
    public class DiagramViewModel : BindableBase, IDiagramViewModel
    {
        #region 属性
        private bool _isReadOnly;
        public bool IsReadOnly
        {
            get
            {
                return _isReadOnly;
            }
            set
            {
                SetProperty(ref _isReadOnly, value);
            }
        }

        public bool IsLoading
        {
            get; set;
        }

        public IDrawModeViewModel DrawModeViewModel
        {
            get; set;
        }

        public IColorViewModel ColorViewModel
        {
            get; set;
        }

        private PageSizeType _pageSizeType = PageSizeType.A4;
        public PageSizeType PageSizeType
        {
            get
            {
                return _pageSizeType;
            }
            set
            {
                SetProperty(ref _pageSizeType, value);
                RaisePropertyChanged(nameof(PageSize));
            }
        }

        private Size _pageSize = new Size(1000, 600);

        public Size PageSize
        {
            get
            {
                if (PageSizeOrientation == PageSizeOrientation.Vertical)
                {
                    return GetPageSize();
                }
                else
                {
                    return new Size(GetPageSize().Height, GetPageSize().Width);
                }
            }
            set
            {
                SetProperty(ref _pageSize, value);
            }
        }

        public Size GetPageSize()
        {
            switch (PageSizeType)
            {
                case PageSizeType.Letter: return new Size(612, 792);
                case PageSizeType.Folio: return new Size(612, 936);
                case PageSizeType.Legal: return new Size(612, 1008);
                case PageSizeType.Executive: return new Size(522, 756);
                case PageSizeType.Statement: return new Size(396, 612);
                case PageSizeType.Envelope: return new Size(297, 684);
                case PageSizeType.MonarchEnvelope: return new Size(279, 540);
                case PageSizeType.Tabloid: return new Size(792, 1224);
                case PageSizeType.LetterSmall: return new Size(612, 792);
                case PageSizeType.CSheet: return new Size(1224, 1584);
                case PageSizeType.DSheet: return new Size(1584, 2448);
                case PageSizeType.ESheet: return new Size(2448, 3168);
                case PageSizeType.A3: return new Size(842, 1191);
                case PageSizeType.A4: return new Size(595, 842);
                case PageSizeType.A5: return new Size(420, 595);
                case PageSizeType.B4: return new Size(709, 1003);
                case PageSizeType.B5: return new Size(516, 729);
                case PageSizeType.DLEnvelope: return new Size(312, 624);
                case PageSizeType.C5Envelope: return new Size(459, 649);
                case PageSizeType.Quarto: return new Size(609, 780);
                case PageSizeType.C6Quarto: return new Size(323, 459);
                case PageSizeType.B5Quarto: return new Size(499, 709);
                case PageSizeType.ItalyQuarto: return new Size(312, 652);
                case PageSizeType.A4small: return new Size(595, 842);
                case PageSizeType.GermanStdFanfold: return new Size(612, 864);
                case PageSizeType.GermanLegalFanfold: return new Size(576, 936);
                case PageSizeType.PRC16K: return new Size(414, 609);
                case PageSizeType.PRC32K: return new Size(275, 428);
                default: return _pageSize;
            }
        }

        private PageSizeOrientation _pageSizeOrientation;
        public PageSizeOrientation PageSizeOrientation
        {
            get
            {
                return _pageSizeOrientation;
            }
            set
            {
                SetProperty(ref _pageSizeOrientation, value);
                RaisePropertyChanged(nameof(PageSize));
            }
        }

        private PageUnit _pageUnit = PageUnit.cm;
        [Browsable(false)]
        public PageUnit PageUnit
        {
            get
            {
                return _pageUnit;
            }
            set
            {
                if (value != PageUnit.cm && value != PageUnit.inch)
                {
                    return;
                }
                SetProperty(ref _pageUnit, value);
            }
        }

        private Size _gridCellSize = new Size(100, 100);
        public Size GridCellSize
        {
            get
            {
                return _gridCellSize;
            }
            set
            {
                SetProperty(ref _gridCellSize, value);
            }
        }

        public double GridCellWidth
        {
            get
            {
                return _gridCellSize.Width;
            }
            set
            {
                _gridCellSize.Width = value;
                RaisePropertyChanged(nameof(GridCellSize));
            }
        }

        public double GridCellHeight
        {
            get
            {
                return _gridCellSize.Height;
            }
            set
            {
                _gridCellSize.Height = value;
                RaisePropertyChanged(nameof(GridCellSize));
            }
        }

        private Color _pageBackground = Colors.White;
        public Color PageBackground
        {
            get
            {
                return _pageBackground;
            }
            set
            {
                SetProperty(ref _pageBackground, value);
            }
        }

        private bool _showGrid;
        public bool ShowGrid
        {
            get
            {
                return _showGrid;
            }
            set
            {
                SetProperty(ref _showGrid, value);
            }
        }

        private Color _gridColor = Colors.LightGray;
        public Color GridColor
        {
            get
            {
                return _gridColor;
            }
            set
            {
                SetProperty(ref _gridColor, value);
            }
        }

        private double _gridMargin = 28d;
        public double GridMargin
        {
            get
            {
                return _gridMargin;
            }
            set
            {
                SetProperty(ref _gridMargin, value);
            }
        }

        private double _zoomValue = 1;
        [Browsable(false)]
        public double ZoomValue
        {
            get
            {
                return _zoomValue;
            }
            set
            {
                SetProperty(ref _zoomValue, value);
            }
        }

        private string _name;
        [Browsable(false)]
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                SetProperty(ref _name, value);
            }
        }

        private DiagramType _diagramType;
        [Browsable(false)]
        public DiagramType DiagramType
        {
            get
            {
                return _diagramType;
            }
            set
            {
                SetProperty(ref _diagramType, value);
            }
        }

        private CellHorizontalAlignment _cellHorizontalAlignment;
        [Browsable(false)]
        public CellHorizontalAlignment CellHorizontalAlignment
        {
            get
            {
                return _cellHorizontalAlignment;
            }
            set
            {
                SetProperty(ref _cellHorizontalAlignment, value);
            }
        }

        private CellVerticalAlignment _cellVerticalAlignment;
        [Browsable(false)]
        public CellVerticalAlignment CellVerticalAlignment
        {
            get
            {
                return _cellVerticalAlignment;
            }
            set
            {
                SetProperty(ref _cellVerticalAlignment, value);
            }
        }


        private bool _isEditName;
        [Browsable(false)]
        public bool IsEditName
        {
            get
            {
                return _isEditName;
            }
            set
            {
                SetProperty(ref _isEditName, value);
            }
        }

        private Point _currentPoint;
        [Browsable(false)]
        public Point CurrentPoint
        {
            get
            {
                return _currentPoint;
            }
            set
            {
                SetProperty(ref _currentPoint, value);
            }
        }

        private Color _currentColor;
        [Browsable(false)]
        public Color CurrentColor
        {
            get
            {
                return _currentColor;
            }
            set
            {
                SetProperty(ref _currentColor, value);
            }
        }

        /// <summary>
        /// 用于wpf大小与物理像素之间转换
        /// </summary>
        public double ScreenScale { get; set; } = 1;

        private double OffsetX = 10;
        private double OffsetY = 10;
        #endregion

        private DoCommandManager DoCommandManager = new DoCommandManager();
        public DiagramViewModel()
        {
            CreateNewDiagramCommand = new SimpleCommand(ExecuteEnable, ExecuteCreateNewDiagramCommand);
            AddItemCommand = new SimpleCommand(ExecuteEnable, ExecuteAddItemCommand);
            DirectAddItemCommand = new SimpleCommand(ExecuteEnable, ExecuteDirectAddItemCommand);
            RemoveItemCommand = new SimpleCommand(ExecuteEnable, ExecuteRemoveItemCommand);
            DirectRemoveItemCommand = new SimpleCommand(ExecuteEnable, ExecuteDirectRemoveItemCommand);
            ClearSelectedItemsCommand = new SimpleCommand(ExecuteEnable, ExecuteClearSelectedItemsCommand);

            AlignTopCommand = new SimpleCommand(ExecuteEnable, ExecuteAlignTopCommand);
            AlignVerticalCentersCommand = new SimpleCommand(ExecuteEnable, ExecuteAlignVerticalCentersCommand);
            AlignBottomCommand = new SimpleCommand(ExecuteEnable, ExecuteAlignBottomCommand);
            AlignLeftCommand = new SimpleCommand(ExecuteEnable, ExecuteAlignLeftCommand);
            AlignHorizontalCentersCommand = new SimpleCommand(ExecuteEnable, ExecuteAlignHorizontalCentersCommand);
            AlignRightCommand = new SimpleCommand(ExecuteEnable, ExecuteAlignRightCommand);
            BringForwardCommand = new SimpleCommand(ExecuteEnable, ExecuteBringForwardCommand);
            BringToFrontCommand = new SimpleCommand(ExecuteEnable, ExecuteBringToFrontCommand);
            SendBackwardCommand = new SimpleCommand(ExecuteEnable, ExecuteSendBackwardCommand);
            SendToBackCommand = new SimpleCommand(ExecuteEnable, ExecuteSendToBackCommand);
            DistributeHorizontalCommand = new SimpleCommand(ExecuteEnable, ExecuteDistributeHorizontalCommand);
            DistributeVerticalCommand = new SimpleCommand(ExecuteEnable, ExecuteDistributeVerticalCommand);
            SelectAllCommand = new SimpleCommand(ExecuteEnable, ExecuteSelectAllCommand);
            CopyCommand = new SimpleCommand(ExecuteEnable, ExecuteCopyCommand);
            PasteCommand = new SimpleCommand(ExecuteEnable, ExecutePasteCommand);
            CutCommand = new SimpleCommand(ExecuteEnable, ExecuteCutCommand);
            DeleteCommand = new SimpleCommand(ExecuteEnable, ExecuteDeleteCommand);
            LeftMoveCommand = new SimpleCommand(ExecuteEnable, ExecuteLeftMoveCommand);
            RightMoveCommand = new SimpleCommand(ExecuteEnable, ExecuteRightMoveCommand);
            UpMoveCommand = new SimpleCommand(ExecuteEnable, ExecuteUpMoveCommand);
            DownMoveCommand = new SimpleCommand(ExecuteEnable, ExecuteDownMoveCommand);
            CenterMoveCommand = new SimpleCommand(ExecuteEnable, ExecuteCenterMoveCommand);
            SameSizeCommand = new SimpleCommand(ExecuteEnable, ExecuteSameSizeCommand);
            SameWidthCommand = new SimpleCommand(ExecuteEnable, ExecuteSameWidthCommand);
            SameHeightCommand = new SimpleCommand(ExecuteEnable, ExecuteSameHeightCommand);
            SameAngleCommand = new SimpleCommand(ExecuteEnable, ExecuteSameAngleCommand);
            GroupCommand = new SimpleCommand(ExecuteEnable, ExecuteGroupCommand);
            UngroupCommand = new SimpleCommand(ExecuteEnable, ExecuteUngroupCommand);
            LockCommand = new SimpleCommand(ExecuteEnable, ExecuteLockCommand);
            UnlockCommand = new SimpleCommand(ExecuteEnable, ExecuteUnlockCommand);
            Mediator.Instance.Register(this);

            Items.CollectionChanged += Items_CollectionChanged;
        }

        public bool ExecuteEnable(object para)
        {
            return IsReadOnly == false;
        }

        #region UnDo ReDo

        private void Do(object sender, string propertyName, object newvalue)
        {
            sender.SetPropertyValue(propertyName, newvalue);
        }

        private void UnDo(object sender, string propertyName, object oldvalue)
        {
            sender.SetPropertyValue(propertyName, oldvalue);
        }

        private bool _undoing;
        private void UndoExecuted(object para)
        {
            _undoing = true;
            DoCommandManager.UnDo();
            _undoing = false;
        }
        private void RedoExecuted(object para)
        {
            _undoing = true;
            DoCommandManager.ReDo();
            _undoing = false;
        }
        private bool Undo_Enabled(object para)
        {
            return DoCommandManager.CanUnDo;
        }
        private bool Redo_Enabled(object para)
        {
            return DoCommandManager.CanReDo;
        }

        #endregion

        private void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (var item in e.OldItems.OfType<SelectableDesignerItemViewModelBase>())
                {
                    item.PropertyChanged -= Item_PropertyChanged;
                    item.Dispose();
                }
            }
            if (e.NewItems != null)
            {
                foreach (var item in e.NewItems.OfType<SelectableDesignerItemViewModelBase>())
                {
                    item.PropertyChanged += Item_PropertyChanged;
                }
            }

            RaisePropertyChanged("Items");
        }

        private void Item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            RaisePropertyChanged(sender, e.PropertyName);

            if (_undoing == true) return;

            //连续改变，需要特殊处理，不单独触发属性改变ReDo
            if (sender is DesignerItemViewModelBase designer)
            {
                if (designer.BeginDo) return;
            }

            if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
            {
                var property = sender.GetType().GetProperty(e.PropertyName);
                var attr = property.GetCustomAttributes(typeof(CanDoAttribute), true);
                if (attr != null && attr.Length != 0)
                {
                    DoCommandManager.DoNewCommand(sender.ToString() + e.PropertyName, () => Do(sender, e.PropertyName, valuePropertyChangedEventArgs.NewValue), () => UnDo(sender, e.PropertyName, valuePropertyChangedEventArgs.OldValue), null, false);
                }
            }
        }

        [MediatorMessageSink("DoneDrawingMessage")]
        public void OnDoneDrawingMessage(bool dummy)
        {
            foreach (var item in Items.OfType<DesignerItemViewModelBase>())
            {
                item.ShowConnectors = false;
            }
        }

        public SimpleCommand CreateNewDiagramCommand
        {
            get; private set;
        }
        public SimpleCommand DirectAddItemCommand
        {
            get; private set;
        }
        public SimpleCommand AddItemCommand
        {
            get; private set;
        }
        public SimpleCommand DirectRemoveItemCommand
        {
            get; private set;
        }
        public SimpleCommand RemoveItemCommand
        {
            get; private set;
        }
        public SimpleCommand ClearSelectedItemsCommand
        {
            get; private set;
        }
        public SimpleCommand AlignTopCommand
        {
            get; private set;
        }
        public SimpleCommand AlignVerticalCentersCommand
        {
            get; private set;
        }
        public SimpleCommand AlignBottomCommand
        {
            get; private set;
        }
        public SimpleCommand AlignLeftCommand
        {
            get; private set;
        }
        public SimpleCommand AlignHorizontalCentersCommand
        {
            get; private set;
        }
        public SimpleCommand AlignRightCommand
        {
            get; private set;
        }
        public SimpleCommand BringForwardCommand
        {
            get; private set;
        }
        public SimpleCommand BringToFrontCommand
        {
            get; private set;
        }
        public SimpleCommand SendBackwardCommand
        {
            get; private set;
        }
        public SimpleCommand SendToBackCommand
        {
            get; private set;
        }

        public SimpleCommand DistributeHorizontalCommand
        {
            get; private set;
        }
        public SimpleCommand DistributeVerticalCommand
        {
            get; private set;
        }
        public SimpleCommand SelectAllCommand
        {
            get; private set;
        }

        public SimpleCommand CopyCommand
        {
            get; private set;
        }

        public SimpleCommand PasteCommand
        {
            get; private set;
        }

        public SimpleCommand CutCommand
        {
            get; private set;
        }

        public SimpleCommand DeleteCommand
        {
            get; private set;
        }

        public SimpleCommand LeftMoveCommand
        {
            get; private set;
        }

        public SimpleCommand RightMoveCommand
        {
            get; private set;
        }

        public SimpleCommand UpMoveCommand
        {
            get; private set;
        }

        public SimpleCommand DownMoveCommand
        {
            get; private set;
        }

        public SimpleCommand CenterMoveCommand
        {
            get; private set;
        }
        public SimpleCommand SameSizeCommand
        {
            get; private set;
        }
        public SimpleCommand SameWidthCommand
        {
            get; private set;
        }
        public SimpleCommand SameHeightCommand
        {
            get; private set;
        }
        public SimpleCommand SameAngleCommand
        {
            get; private set;
        }
        public SimpleCommand GroupCommand
        {
            get; private set;
        }
        public SimpleCommand UngroupCommand
        {
            get; private set;
        }
        public SimpleCommand LockCommand
        {
            get; private set;
        }
        public SimpleCommand UnlockCommand
        {
            get; private set;
        }

        private SimpleCommand _undoCommand;
        public SimpleCommand UndoCommand
        {
            get
            {
                return this._undoCommand ?? (this._undoCommand = new SimpleCommand(Undo_Enabled, this.UndoExecuted));
            }
        }

        private SimpleCommand _redoCommand;
        public SimpleCommand RedoCommand
        {
            get
            {
                return this._redoCommand ?? (this._redoCommand = new SimpleCommand(Redo_Enabled, this.RedoExecuted));
            }
        }


        public ObservableCollection<SelectableDesignerItemViewModelBase> Items { get; set; } = new ObservableCollection<SelectableDesignerItemViewModelBase>();

        public List<SelectableDesignerItemViewModelBase> SelectedItems
        {
            get
            {
                return Items.Where(x => x.IsSelected).ToList();
            }
        }


        private SelectionService selectionService;
        public SelectionService SelectionService
        {
            get
            {
                if (selectionService == null)
                    selectionService = new SelectionService(this);

                return selectionService;
            }
        }

        public Func<SelectableDesignerItemViewModelBase, bool> OutAddVerify
        {
            get; set;
        }


        public bool AddVerify(SelectableDesignerItemViewModelBase item)
        {
            if (item.InitData() == false)
                return false;

            if (OutAddVerify != null && OutAddVerify(item) != true)
                return false;

            return true;
        }

        private void ExecuteCreateNewDiagramCommand(object parameter)
        {
            this.Items.Clear();
        }

        private void ExecuteDirectAddItemCommand(object parameter)
        {
            if (parameter is SelectableDesignerItemViewModelBase ite)
            {
                if (AddVerify(ite) != true) return;

                ClearSelectedItems();
                Add(ite);
            }
            else if (parameter is List<SelectableDesignerItemViewModelBase> items)
            {
                if (items.Select(p => AddVerify(p)).Any() != true) return;

                ClearSelectedItems();
                foreach (var item in items)
                {
                    Add(item);
                }
            }
        }

        private void Add(SelectableDesignerItemViewModelBase item)
        {
            item.Parent = this;
            item.ZIndex = Items.Count;
            if (item.Id == Guid.Empty)
            {
                item.Id = Guid.NewGuid();
            }
            //item.LineColor = this.LineColor;
            //item.FillColor = this.FillColor;
            var logical = item as LogicalGateItemViewModelBase;
            if (logical != null && logical.LogicalType > 0)
            {
                logical.OrderNumber = Items.OfType<LogicalGateItemViewModelBase>().Count(p => (int)p.LogicalType > 0) + 1;
            }

            var designerItemViewModelBase = item as DesignerItemViewModelBase;
            if (designerItemViewModelBase != null)
            {
                designerItemViewModelBase.SetCellAlignment();
            }
            Items.Add(item);
            item.IsSelected = true;
        }

        private void ExecuteAddItemCommand(object parameter)
        {
            if (parameter is SelectableDesignerItemViewModelBase ite)
            {
                if (AddVerify(ite) != true) return;

                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    ClearSelectedItems();
                    Add(ite);
                },
                () => {
                    Items.Remove(ite);
                });
            }
            else if (parameter is List<SelectableDesignerItemViewModelBase> items)
            {
                if (items.Select(p => AddVerify(p)).Any() != true) return;

                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    ClearSelectedItems();
                    foreach (var item in items)
                    {
                        Add(item);
                    }
                },
                () => {
                    items.ForEach(item => Items.Remove(item));
                });
            }
        }

        private void ExecuteDirectRemoveItemCommand(object parameter)
        {
            if (parameter is SelectableDesignerItemViewModelBase ite)
            {
                ite.IsSelected = false;
                Items.Remove(ite);
                if (ite.OutTextItem != null)
                {
                    Items.Remove(ite.OutTextItem);
                }
            }
            else if (parameter is List<SelectableDesignerItemViewModelBase> items)
            {
                foreach (var item in items)
                {
                    item.IsSelected = false;
                    Items.Remove(item);
                    if (item.OutTextItem != null)
                    {
                        Items.Remove(item.OutTextItem);
                    }
                }
            }
        }
        private void ExecuteRemoveItemCommand(object parameter)
        {
            if (parameter is SelectableDesignerItemViewModelBase ite)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    ite.IsSelected = false;
                    Items.Remove(ite);
                    if (ite.OutTextItem != null)
                    {
                        Items.Remove(ite.OutTextItem);
                    }

                },
                () => {
                    Items.Add(ite);
                });
            }
            else if (parameter is List<SelectableDesignerItemViewModelBase> items)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    foreach (var item in items)
                    {
                        item.IsSelected = false;
                        Items.Remove(item);
                        if (item.OutTextItem != null)
                        {
                            Items.Remove(item.OutTextItem);
                        }
                    }

                },
                () => {
                    foreach (var item in items)
                    {
                        Items.Add(item);
                        if (item.OutTextItem != null)
                        {
                            Items.Add(item.OutTextItem);
                        }
                    }

                });
            }
        }
        private void ExecuteClearSelectedItemsCommand(object parameter)
        {
            ClearSelectedItems();
        }

        public void ClearSelectedItems()
        {
            foreach (var item in this.Items.ToList())
            {
                item.IsSelected = false;
            }
        }

        #region 布局
        private void ExecuteAlignTopCommand(object parameter)
        {
            var selectedItems = this.SelectedItems.OfType<DesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double top = selectedItems.OrderBy(p => p.Top).Select(p => p.Top).FirstOrDefault();

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.SetOldValue(item.Top, nameof(item.Top), guid.ToString());
                        item.Top = top;
                    }
                },
                 () => {
                     foreach (DesignerItemViewModelBase item in selectedItems)
                     {
                         item.Top = item.GetOldValue<double>(nameof(item.Top), guid.ToString());
                     }
                 },
                 () => {
                     foreach (DesignerItemViewModelBase item in selectedItems)
                     {
                         item.ClearOldValue<double>(nameof(item.Top), guid.ToString());
                     }
                 });
            }
        }

        private void ExecuteAlignVerticalCentersCommand(object parameter)
        {
            var selectedItems = this.SelectedItems.OfType<DesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double mid = selectedItems.Select(p => p.Top + p.ItemHeight / 2).Average();

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.SetOldValue(item.Top, nameof(item.Top), guid.ToString());
                        item.Top = mid - item.ItemHeight / 2;
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.Top = item.GetOldValue<double>(nameof(item.Top), guid.ToString());
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.ClearOldValue<double>(nameof(item.Top), guid.ToString());
                    }
                });
            }
        }

        private void ExecuteAlignBottomCommand(object parameter)
        {
            var selectedItems = this.SelectedItems.OfType<DesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double top = selectedItems.OrderBy(p => p.Top + p.ItemHeight).Select(p => p.Top + p.ItemHeight).LastOrDefault();

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.SetOldValue(item.Top, nameof(item.Top), guid.ToString());
                        item.Top = top - item.ItemHeight;
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.Top = item.GetOldValue<double>(nameof(item.Top), guid.ToString());
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.ClearOldValue<double>(nameof(item.Top), guid.ToString());
                    }
                });
            }
        }

        private void ExecuteAlignLeftCommand(object parameter)
        {
            var selectedItems = this.SelectedItems.OfType<DesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double left = selectedItems.OrderBy(p => p.Left).Select(p => p.Left).FirstOrDefault();

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.SetOldValue(item.Left, nameof(item.Left), guid.ToString());
                        item.Left = left;
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.Left = item.GetOldValue<double>(nameof(item.Left), guid.ToString());
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.ClearOldValue<double>(nameof(item.Left), guid.ToString());
                    }
                });
            }
        }

        private void ExecuteAlignHorizontalCentersCommand(object parameter)
        {
            var selectedItems = this.SelectedItems.OfType<DesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double mid = selectedItems.Select(p => p.Left + p.ItemWidth / 2).Average();

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.SetOldValue(item.Left, nameof(item.Left), guid.ToString());
                        item.Left = mid - item.ItemWidth / 2;
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.Left = item.GetOldValue<double>(nameof(item.Left), guid.ToString());
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.ClearOldValue<double>(nameof(item.Left), guid.ToString());
                    }
                });
            }
        }

        private void ExecuteAlignRightCommand(object parameter)
        {
            var selectedItems = this.SelectedItems.OfType<DesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double right = selectedItems.OrderBy(p => p.Left + p.ItemWidth).Select(p => p.Left + p.ItemWidth).LastOrDefault();

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.SetOldValue(item.Left, nameof(item.Left), guid.ToString());
                        item.Left = right - item.ItemWidth;
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.Left = item.GetOldValue<double>(nameof(item.Left), guid.ToString());
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        item.ClearOldValue<double>(nameof(item.Left), guid.ToString());
                    }
                });
            }
        }
        #endregion

        private void ExecuteBringForwardCommand(object parameter)
        {
            List<SelectableDesignerItemViewModelBase> ordered = SelectedItems.OrderByDescending(p => p.ZIndex).ToList();

            List<SelectableDesignerItemViewModelBase> changeditems = new List<SelectableDesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            DoCommandManager.DoNewCommand(this.ToString(),
            () => {
                int count = this.Items.Count;
                for (int i = 0; i < ordered.Count; i++)
                {
                    var item = ordered[i];
                    int currentIndex = item.ZIndex;
                    int newIndex = Math.Min(count - 1 - i, currentIndex + 1);
                    if (currentIndex != newIndex)
                    {
                        item.SetOldValue<int>(item.ZIndex, nameof(item.ZIndex), guid.ToString());
                        item.ZIndex = newIndex;
                        changeditems.Add(item);

                        IEnumerable<SelectableDesignerItemViewModelBase> it = this.Items.Where(p => p.ZIndex == newIndex);

                        foreach (var elm in it)
                        {
                            if (elm != item)
                            {
                                elm.SetOldValue<int>(elm.ZIndex, nameof(elm.ZIndex), guid.ToString());
                                elm.ZIndex = currentIndex;
                                changeditems.Add(elm);
                                break;
                            }
                        }
                    }
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ZIndex = item.GetOldValue<int>(nameof(item.ZIndex), guid.ToString());
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ClearOldValue<double>(nameof(item.ZIndex), guid.ToString());
                }
            });
        }
        private void ExecuteBringToFrontCommand(object parameter)
        {
            List<SelectableDesignerItemViewModelBase> selectionSorted = SelectedItems.OrderByDescending(p => p.ZIndex).ToList();
            List<SelectableDesignerItemViewModelBase> childrenSorted = Items.OrderByDescending(p => p.ZIndex).ToList();

            List<SelectableDesignerItemViewModelBase> changeditems = new List<SelectableDesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            DoCommandManager.DoNewCommand(this.ToString(),
            () => {
                int i = childrenSorted.Count - 1;
                int j = childrenSorted.Count - selectionSorted.Count - 1;

                foreach (SelectableDesignerItemViewModelBase item in childrenSorted)
                {
                    item.SetOldValue<int>(item.ZIndex, nameof(item.ZIndex), guid.ToString());
                    if (selectionSorted.Contains(item))
                    {
                        item.ZIndex = i--;
                    }
                    else
                    {
                        item.ZIndex = j--;
                    }
                    changeditems.Add(item);
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ZIndex = item.GetOldValue<int>(nameof(item.ZIndex), guid.ToString());
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ClearOldValue<double>(nameof(item.ZIndex), guid.ToString());
                }
            });
        }
        private void ExecuteSendBackwardCommand(object parameter)
        {
            List<SelectableDesignerItemViewModelBase> ordered = this.SelectedItems.OrderBy(p => p.ZIndex).ToList();
            int count = this.Items.Count;

            List<SelectableDesignerItemViewModelBase> changeditems = new List<SelectableDesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            DoCommandManager.DoNewCommand(this.ToString(),
            () => {
                for (int i = 0; i < ordered.Count; i++)
                {
                    var item = ordered[i];
                    int currentIndex = item.ZIndex;
                    int newIndex = Math.Max(i, currentIndex - 1);
                    if (currentIndex != newIndex)
                    {
                        item.SetOldValue<int>(item.ZIndex, nameof(item.ZIndex), guid.ToString());
                        item.ZIndex = newIndex;
                        changeditems.Add(item);
                        IEnumerable<SelectableDesignerItemViewModelBase> it = this.Items.Where(p => p.ZIndex == newIndex);

                        foreach (var elm in it)
                        {
                            if (elm != ordered[i])
                            {
                                elm.SetOldValue<int>(elm.ZIndex, nameof(elm.ZIndex), guid.ToString());
                                elm.ZIndex = currentIndex;
                                changeditems.Add(elm);

                                break;
                            }
                        }
                    }
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ZIndex = item.GetOldValue<int>(nameof(item.ZIndex), guid.ToString());
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ClearOldValue<double>(nameof(item.ZIndex), guid.ToString());
                }
            });
        }
        private void ExecuteSendToBackCommand(object parameter)
        {
            List<SelectableDesignerItemViewModelBase> selectionSorted = SelectedItems.OrderByDescending(p => p.ZIndex).ToList();
            List<SelectableDesignerItemViewModelBase> childrenSorted = Items.OrderByDescending(p => p.ZIndex).ToList();

            List<SelectableDesignerItemViewModelBase> changeditems = new List<SelectableDesignerItemViewModelBase>();
            var guid = Guid.NewGuid();

            DoCommandManager.DoNewCommand(this.ToString(),
            () => {
                int i = childrenSorted.Count - 1;
                int j = selectionSorted.Count - 1;

                foreach (SelectableDesignerItemViewModelBase item in childrenSorted)
                {
                    item.SetOldValue<int>(item.ZIndex, nameof(item.ZIndex), guid.ToString());
                    if (selectionSorted.Contains(item))
                    {
                        item.ZIndex = j--;
                    }
                    else
                    {
                        item.ZIndex = i--;
                    }
                    changeditems.Add(item);
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ZIndex = item.GetOldValue<int>(nameof(item.ZIndex), guid.ToString());
                }
            },
            () => {
                foreach (var item in changeditems)
                {
                    item.ClearOldValue<double>(nameof(item.ZIndex), guid.ToString());
                }
            });
        }
        private void ExecuteDistributeHorizontalCommand(object parameter)
        {
            var selectedItems = from item in this.SelectedItems.OfType<DesignerItemViewModelBase>()
                                where item.ParentId == Guid.Empty
                                orderby item.Left
                                select item;
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double left = Double.MaxValue;
                    double right = Double.MinValue;
                    double sumWidth = 0;
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        left = Math.Min(left, item.Left);
                        right = Math.Max(right, item.Left + item.ItemWidth);
                        sumWidth += item.ItemWidth;
                    }

                    double distance = Math.Max(0, (right - left - sumWidth) / (selectedItems.Count() - 1));
                    double offset = selectedItems.First().Left;

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        double delta = offset - item.Left;
                        foreach (DesignerItemViewModelBase di in SelectionService.GetGroupMembers(item))
                        {
                            di.SetOldValue(di.Left, nameof(di.Left), guid.ToString());
                            di.Left += delta;
                        }
                        offset = offset + item.ItemWidth + distance;
                    }

                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        foreach (DesignerItemViewModelBase di in SelectionService.GetGroupMembers(item))
                        {
                            di.Left = di.GetOldValue<double>(nameof(di.Left), guid.ToString());
                        }
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        foreach (DesignerItemViewModelBase di in SelectionService.GetGroupMembers(item))
                        {
                            di.ClearOldValue<double>(nameof(di.Left), guid.ToString());
                        }
                    }
                });
            }
        }

        private void ExecuteDistributeVerticalCommand(object parameter)
        {
            var selectedItems = from item in this.SelectedItems.OfType<DesignerItemViewModelBase>()
                                where item.ParentId == Guid.Empty
                                orderby item.Top
                                select item;
            var guid = Guid.NewGuid();

            if (selectedItems.Count() > 1)
            {
                DoCommandManager.DoNewCommand(this.ToString(),
                () => {
                    double top = Double.MaxValue;
                    double bottom = Double.MinValue;
                    double sumHeight = 0;
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        top = Math.Min(top, item.Top);
                        bottom = Math.Max(bottom, item.Top + item.ItemHeight);
                        sumHeight += item.ItemHeight;
                    }

                    double distance = Math.Max(0, (bottom - top - sumHeight) / (selectedItems.Count() - 1));
                    double offset = selectedItems.First().Top;

                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        double delta = offset - item.Top;
                        foreach (DesignerItemViewModelBase di in SelectionService.GetGroupMembers(item))
                        {
                            di.SetOldValue(di.Top, nameof(di.Top), guid.ToString());
                            di.Top += +delta;
                        }
                        offset = offset + item.ItemHeight + distance;
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        foreach (DesignerItemViewModelBase di in SelectionService.GetGroupMembers(item))
                        {
                            di.Top = di.GetOldValue<double>(nameof(di.Top), guid.ToString());
                        }
                    }
                },
                () => {
                    foreach (DesignerItemViewModelBase item in selectedItems)
                    {
                        foreach (DesignerItemViewModelBase di in SelectionService.GetGroupMembers(item))
                        {
                            di.ClearOldValue<double>(nameof(di.Top), guid.ToString());
                        }
                    }
                });
            }
        }

        private void ExecuteSelectAllCommand(object parameter)
        {
            foreach (var item in Items)
            {
                item.IsSelected = true;
            }
        }

        private void ExecuteCopyCommand(object parameter)
        {
            List<DesignerItemViewModelBase> selectedDesignerItems =
           SelectedItems.OfType<DesignerItemViewModelBase>().ToList();

            List<ConnectorViewModel> selectedConnections =
                SelectedItems.OfType<ConnectorViewModel>().ToList();     

            foreach (ConnectorViewModel connection in Items.OfType<ConnectorViewModel>())
            {
                if (!selectedConnections.Contains(connection))
                {
                    DesignerItemViewModelBase sourceItem = (from item in selectedDesignerItems
                                                            where item.Id == connection.SourceConnectorInfo.DataItem.Id
                                                            select item).FirstOrDefault();

                    DesignerItemViewModelBase sinkItem = (from item in selectedDesignerItems
                                                          where item.Id == ((connection.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem).Id
                                                          select item).FirstOrDefault();

                    if (sourceItem != null &&
                        sinkItem != null &&
                        BelongToSameGroup(sourceItem, sinkItem))
                    {
                        selectedConnections.Add(connection);
                    }
                }
            }

            //连线上的文本
            foreach (var selectedConnection in selectedConnections)
            {
                if (selectedConnection.OutTextItem != null)
                {
                    selectedDesignerItems.Add(selectedConnection.OutTextItem);
                }
            }


            string json = new SerializableObject
            {
                DesignerItems = selectedDesignerItems.Select(p => p.ToSerializabObject()).Where(p => p != null).ToList(),
                Connections = selectedConnections.Select(p => p.ToSerializabObject()).Where(p => p != null).ToList(),
            }.ToJson();

            OffsetX = 10;
            OffsetY = 10;
            Clipboard.Clear();
            Clipboard.SetData(DataFormats.Serializable, json);
        }

        private void ExecutePasteCommand(object parameter)
        {
            if (Clipboard.ContainsData(DataFormats.Serializable))
            {
                String clipboardData = Clipboard.GetData(DataFormats.Serializable) as String;

                if (String.IsNullOrEmpty(clipboardData))
                    return;
                try
                {
                    List<SelectableDesignerItemViewModelBase> items = new List<SelectableDesignerItemViewModelBase>();
                    SerializableObject copyitem = JsonConvert.DeserializeObject<SerializableObject>(clipboardData);


                    Dictionary<Guid, Guid> mappingOldToNewIDs = new Dictionary<Guid, Guid>();

                    foreach (var diagramItemData in copyitem.DesignerItems)
                    {
                        DesignerItemViewModelBase newItem = null;
                
                        Type type = TypeHelper.GetType(diagramItemData.TypeName);

                        DesignerItemViewModelBase itemBase = (DesignerItemViewModelBase)Activator.CreateInstance(type, this, diagramItemData.ObjectJson);
                        Guid newID = Guid.NewGuid();
                        mappingOldToNewIDs.Add(itemBase.Id, newID);
                        itemBase.Id = newID;
                        itemBase.Left += OffsetX;
                        itemBase.Top += OffsetY;

                        newItem = itemBase;

                        if (newItem != null)
                        {
                            items.Add(newItem);
                        }
                    }      

                    DirectAddItemCommand.Execute(items);
                    OffsetX += 10;
                    OffsetY += 10;

                    List<SelectableDesignerItemViewModelBase> connectors = new List<SelectableDesignerItemViewModelBase>();
                    foreach (var connection in copyitem.Connections)
                    {
                        var connectionItem = JsonConvert.DeserializeObject<ConnectionItem>(connection.ObjectJson);
                        Guid newID = Guid.NewGuid();
                        mappingOldToNewIDs.Add(connectionItem.Id, newID);

                        connectionItem.SourceId = mappingOldToNewIDs[connectionItem.SourceId];
                        connectionItem.SinkId = mappingOldToNewIDs[connectionItem.SinkId];

                        connectionItem.SourceType = System.Type.GetType(connectionItem.SourceTypeName);
                        connectionItem.SinkType = System.Type.GetType(connectionItem.SinkTypeName);
                        DesignerItemViewModelBase sourceItem = GetConnectorDataItem(this, connectionItem.SourceId, connectionItem.SourceType);
                        ConnectorOrientation sourceConnectorOrientation = connectionItem.SourceOrientation;
                        FullyCreatedConnectorInfo sourceConnectorInfo = GetFullConnectorInfo(connectionItem.Id, sourceItem, sourceConnectorOrientation, connectionItem.SourceXRatio, connectionItem.SourceYRatio, connectionItem.SourceInnerPoint);

                        DesignerItemViewModelBase sinkItem = GetConnectorDataItem(this, connectionItem.SinkId, connectionItem.SinkType);
                        ConnectorOrientation sinkConnectorOrientation = connectionItem.SinkOrientation;
                        FullyCreatedConnectorInfo sinkConnectorInfo = GetFullConnectorInfo(connectionItem.Id, sinkItem, sinkConnectorOrientation, connectionItem.SinkXRatio, connectionItem.SinkYRatio, connectionItem.SinkInnerPoint);

                        ConnectorViewModel connectionVM = new ConnectorViewModel(this, sourceConnectorInfo, sinkConnectorInfo, connectionItem, connectionItem.VectorLineDrawMode);
                        connectors.Add(connectionVM);

                        DesignerItemViewModelBase textItem = items.OfType<DesignerItemViewModelBase>().FirstOrDefault(x => x.ParentId == connectionItem.Id);
                        if (textItem != null)
                        {
                            connectionVM.OutTextItem = textItem;
                        }
                    }

                    DirectAddItemCommand.Execute(connectors);

                    //修复父级的引用
                    foreach (var item in items)
                    {
                        if (item.ParentId != Guid.Empty)
                        {
                            if (mappingOldToNewIDs.ContainsKey(item.ParentId))
                                item.ParentId = mappingOldToNewIDs[item.ParentId];
                        }
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private DesignerItemViewModelBase GetConnectorDataItem(IDiagramViewModel diagramViewModel, Guid conectorDataItemId, Type connectorDataItemType)
        {
            DesignerItemViewModelBase dataItem = diagramViewModel.Items.OfType<DesignerItemViewModelBase>().Single(x => x.Id == conectorDataItemId);
            return dataItem;
        }

        private FullyCreatedConnectorInfo GetFullConnectorInfo(Guid connectorId, DesignerItemViewModelBase dataItem, ConnectorOrientation connectorOrientation, double xRatio, double yRatio, bool isInnerPoint)
        {
            if (isInnerPoint)
            {
                return dataItem.Connectors.Where(p => p.XRatio == xRatio && p.YRatio == yRatio).FirstOrDefault();
            }
            else
            {
                switch (connectorOrientation)
                {
                    case ConnectorOrientation.Top:
                        return dataItem.TopConnector;
                    case ConnectorOrientation.Left:
                        return dataItem.LeftConnector;
                    case ConnectorOrientation.Right:
                        return dataItem.RightConnector;
                    case ConnectorOrientation.Bottom:
                        return dataItem.BottomConnector;

                    default:
                        throw new InvalidOperationException(
                            string.Format("Found invalid persisted Connector Orientation for Connector Id: {0}", connectorId));
                }
            }
        }

        private bool ItemsToDeleteHasConnector(List<SelectableDesignerItemViewModelBase> itemsToRemove, ConnectorInfoBase connector)
        {
            if (connector is FullyCreatedConnectorInfo fully)
            {
                return itemsToRemove.Contains(fully.DataItem);
            }

            return false;
        }


        private void ExecuteCutCommand(object parameter)
        {
            ExecutePasteCommand(null);
            OffsetX = 0;
            OffsetY = 0;
            ExecuteDeleteCommand(null);
        }

        private void ExecuteDeleteCommand(object parameter)
        {
            List<SelectableDesignerItemViewModelBase> itemsToRemove = SelectedItems.OfType<SelectableDesignerItemViewModelBase>().ToList();
            List<SelectableDesignerItemViewModelBase> connectionsToAlsoRemove = new List<SelectableDesignerItemViewModelBase>();

            foreach (var connector in Items.OfType<ConnectorViewModel>())
            {
                if (ItemsToDeleteHasConnector(itemsToRemove, connector.SourceConnectorInfo))
                {
                    connectionsToAlsoRemove.Add(connector);
                }

                if (ItemsToDeleteHasConnector(itemsToRemove, connector.SinkConnectorInfo))
                {
                    connectionsToAlsoRemove.Add(connector);
                }

            }
            itemsToRemove.AddRange(connectionsToAlsoRemove);

            RemoveItemCommand.Execute(itemsToRemove);
        }

        private void ExecuteLeftMoveCommand(object parameter)
        {
            foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
            {
                item.Left -= 0.5;
            }
        }

        private void ExecuteRightMoveCommand(object parameter)
        {
            foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
            {
                item.Left += 0.5;
            }
        }

        private void ExecuteUpMoveCommand(object parameter)
        {
            foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
            {
                item.Top -= 0.5;
            }
        }

        private void ExecuteDownMoveCommand(object parameter)
        {
            foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
            {
                item.Top += 0.5;
            }
        }

        private void ExecuteCenterMoveCommand(object parameter)
        {
            foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
            {
                item.Left = (PageSize.Width - item.ItemWidth) / 2;
                item.Top = (PageSize.Height - item.ItemHeight) / 2;
            }
        }

        private void ExecuteSameSizeCommand(object parameter)
        {
            if (parameter is DesignerItemViewModelBase designerItem)
            {
                foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
                {
                    item.ItemWidth = designerItem.ItemWidth;
                    item.ItemHeight = designerItem.ItemHeight;
                }
            }
        }

        private void ExecuteSameWidthCommand(object parameter)
        {
            if (parameter is DesignerItemViewModelBase designerItem)
            {
                foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
                {
                    item.ItemWidth = designerItem.ItemWidth;
                }
            }
        }

        private void ExecuteSameHeightCommand(object parameter)
        {
            if (parameter is DesignerItemViewModelBase designerItem)
            {
                foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
                {
                    item.ItemHeight = designerItem.ItemHeight;
                }
            }
        }

        private void ExecuteSameAngleCommand(object parameter)
        {
            if (parameter is DesignerItemViewModelBase designerItem)
            {
                foreach (var item in SelectedItems.OfType<DesignerItemViewModelBase>())
                {
                    item.Angle = designerItem.Angle;
                }
            }
        }

        private void ExecuteGroupCommand(object parameter)
        {
            var items = from item in SelectedItems.OfType<DesignerItemViewModelBase>()
                        where item.ParentId == Guid.Empty
                        select item;

            Rect rect = GetBoundingRectangle(items);

            GroupDesignerItemViewModel groupItem = new GroupDesignerItemViewModel();
            groupItem.IsGroup = true;
            groupItem.ItemWidth = rect.Width;
            groupItem.ItemHeight = rect.Height;
            groupItem.Left = rect.Left;
            groupItem.Top = rect.Top;
            groupItem.ZIndex = Items.Count;

            DirectAddItemCommand.Execute(groupItem);

            foreach (DesignerItemViewModelBase item in items)
                item.ParentId = groupItem.Id;

            ClearSelectedItemsCommand.Execute(null);
            //groupItem.IsSelected = true;
            SelectionService.AddToSelection(groupItem);
        }

        private void ExecuteUngroupCommand(object parameter)
        {
            var groups = (from item in SelectedItems.OfType<DesignerItemViewModelBase>()
                          where item.IsGroup && item.ParentId == Guid.Empty
                          select item).ToArray();

            foreach (DesignerItemViewModelBase groupRoot in groups)
            {
                var children = from child in SelectedItems.OfType<DesignerItemViewModelBase>()
                               where child.ParentId == groupRoot.Id
                               select child;

                foreach (DesignerItemViewModelBase child in children)
                    child.ParentId = Guid.Empty;

                RemoveItemCommand.Execute(groupRoot);
                UpdateZIndex();
            }
        }

        private void ExecuteLockCommand(object parameter)
        {

        }

        private void ExecuteUnlockCommand(object parameter)
        {

        }

        public bool BelongToSameGroup(IGroupable item1, IGroupable item2)
        {
            IGroupable root1 = SelectionService.GetGroupRoot(item1);
            IGroupable root2 = SelectionService.GetGroupRoot(item2);

            return (root1.Id == root2.Id);
        }

        public void UpdateZIndex()
        {
            List<SelectableDesignerItemViewModelBase> ordered = Items.OrderBy(p => p.ZIndex).ToList();


            for (int i = 0; i < ordered.Count; i++)
            {
                ordered[i].ZIndex = i;
            }
        }

        public Rect GetBoundingRectangle(IEnumerable<DesignerItemViewModelBase> items)
        {
            double x1 = Double.MaxValue;
            double y1 = Double.MaxValue;
            double x2 = Double.MinValue;
            double y2 = Double.MinValue;

            foreach (DesignerItemViewModelBase item in items)
            {
                x1 = Math.Min(item.Left, x1);
                y1 = Math.Min(item.Top, y1);

                x2 = Math.Max(item.Left + item.ItemWidth, x2);
                y2 = Math.Max(item.Top + item.ItemHeight, y2);
            }

            return new Rect(new Point(x1, y1), new Point(x2, y2));
        }

        #region 用于wpf大小与物理像素之间转换
        public void SetScreenScale()
        {
            ScreenScale = ScreenHelper.ResetScreenScale();
        }
        #endregion
    }




}
