using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Linq;
using System.Reactive.Linq;
using AIStudio.Wpf.DiagramDesigner.Models;
using AIStudio.Wpf.DiagramDesigner.Geometrys;

namespace AIStudio.Wpf.DiagramDesigner
{
    public abstract class DesignerItemViewModelBase : SelectableDesignerItemViewModelBase
    {
        public DesignerItemViewModelBase() : this(null)
        {

        }

        public DesignerItemViewModelBase(IDiagramViewModel root) : base(root)
        {
            ShapeDefiner = Shapes.Rectangle;
        }

        public DesignerItemViewModelBase(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer)
        {
            ShapeDefiner = Shapes.Rectangle;
        }

        public DesignerItemViewModelBase(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType)
        {
            ShapeDefiner = Shapes.Rectangle;
        }

        public override SelectableItemBase GetSerializableObject()
        {
            return new DesignerItemBase(this);
        }

        protected override void InitNew()
        {
            AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Top));
            AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Bottom));
            AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Left));
            AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Right));
        }

        protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase)
        {
            base.LoadDesignerItemViewModel(designerbase);

            if (designerbase is DesignerItemBase designer)
            {
                this.PhysicalLeft = designer.PhysicalLeft;
                this.PhysicalTop = designer.PhysicalTop;
                this.Angle = designer.Angle;
                this.ScaleX = designer.ScaleX;
                this.ScaleY = designer.ScaleY;
                this.PhysicalItemWidth = designer.PhysicalItemWidth;
                this.PhysicalItemHeight = designer.PhysicalItemHeight;
                this.Icon = designer.Icon;
                this.CornerRadius = designer.CornerRadius;
                this.BorderThickness = designer.BorderThickness;
                if (designer.Connectors != null)
                {
                    foreach (var connector in designer.Connectors)
                    {
                        FullyCreatedConnectorInfo fullyCreatedConnectorInfo = new FullyCreatedConnectorInfo(this.Root, this, connector);
                        AddConnector(fullyCreatedConnectorInfo);
                    }
                }
            }
        }
        #region 属性

        public FullyCreatedConnectorInfo FirstConnector
        {
            get
            {
                return connectors?.FirstOrDefault();
            }
        }

        public FullyCreatedConnectorInfo TopConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Top);
            }
        }

        public FullyCreatedConnectorInfo BottomConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Bottom);
            }
        }

        public FullyCreatedConnectorInfo LeftConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Left);
            }
        }

        public FullyCreatedConnectorInfo RightConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Right);
            }
        }

        public FullyCreatedConnectorInfo TopLeftConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.TopLeft);
            }
        }

        public FullyCreatedConnectorInfo TopRightConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.TopRight);
            }
        }

        public FullyCreatedConnectorInfo BottomLeftConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.BottomLeft);
            }
        }

        public FullyCreatedConnectorInfo BottomRightConnector
        {
            get
            {
                return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.BottomRight);
            }
        }

        private FullyCreatedConnectorInfo _portlessConnector;
        public FullyCreatedConnectorInfo PortlessConnector
        {
            get
            {
                if (_portlessConnector == null)
                    _portlessConnector = new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.None) { IsPortless = true };

                return _portlessConnector;
            }
        }

        public Style ConnectorStyle
        {
            get; set;
        }

        public ShapeDefiner ShapeDefiner
        {
            get;
        }

        public virtual PointBase MiddlePosition
        {
            get
            {
                return GetBounds().Center;
            }
        }

        private string _icon;
        [CanDo]
        public string Icon
        {
            get
            {
                return _icon;
            }
            set
            {
                SetProperty(ref _icon, value);
            }
        }

        private double _itemWidth = 65;
        [CanDo]
        [Browsable(true)]
        public double ItemWidth
        {
            get
            {
                return _itemWidth;
            }
            set
            {
                if (value <= 0) return;
                if (SetProperty(ref _itemWidth, value))
                {
                    RaisePropertyChanged(nameof(PhysicalItemWidth));
                }
            }
        }

        private double _itemHeight = 65;       
        [CanDo]
        [Browsable(true)]
        public double ItemHeight
        {
            get
            {
                return _itemHeight;
            }
            set
            {
                if (value <= 0) return;
                if (SetProperty(ref _itemHeight, value))
                {
                    RaisePropertyChanged(nameof(PhysicalItemHeight));
                }
            }
        }       

        [DisplayName("ItemWidth(mm)")]
        [Browsable(true)]
        public double PhysicalItemWidth
        {
            get
            {
                return ScreenHelper.WidthToMm(ItemWidth);
            }
            set
            {
                ItemWidth = ScreenHelper.MmToWidth(value);
            }
        }

        [DisplayName("ItemHeight(mm)")]
        [Browsable(true)]
        public double PhysicalItemHeight
        {
            get
            {
                return ScreenHelper.WidthToMm(ItemHeight);
            }
            set
            {
                ItemHeight = ScreenHelper.MmToWidth(value);
            }
        }

        [CanDo]
        public SizeBase Size
        {
            get
            {
                return new SizeBase(ItemWidth, ItemHeight);
            }
            set
            {
                ItemWidth = value.Width;
                ItemHeight = value.Height;
            }
        }

        private double _connectorMargin = -4;
        public double ConnectorMargin
        {
            get
            {
                return _connectorMargin;
            }
            set
            {
                SetProperty(ref _connectorMargin, value);
            }
        }

        private bool _showConnectors = false;
        public bool ShowConnectors
        {
            get
            {
                return _showConnectors;
            }
            set
            {
                if (SetProperty(ref _showConnectors, value))
                {
                    foreach (var connector in connectors)
                    {
                        connector.ShowConnectors = value;
                    }
                }
            }
        }

        private bool _showResize = true;
        public bool ShowResize
        {
            get
            {
                return _showResize;
            }
            set
            {
                SetProperty(ref _showResize, value);
            }
        }

        private bool _showRotate = false;
        public bool ShowRotate
        {
            get
            {
                return _showRotate;
            }
            set
            {
                SetProperty(ref _showRotate, value);
            }
        }

        public bool ShowArrow { get; set; } = true;

        public bool ShowResized
        {
            get; set;
        }

        private bool alwayForResized;
        public bool AlwayForResized
        {
            get
            {
                return alwayForResized;
            }
            set
            {
                SetProperty(ref alwayForResized, value);
            }
        }

        private bool enabledForConnection = true;
        public bool EnabledForConnection
        {
            get
            {
                return enabledForConnection;
            }
            set
            {
                SetProperty(ref enabledForConnection, value);
            }
        }

        private double _left;
        [CanDo]
        [Browsable(true)]
        public double Left
        {
            get
            {
                return _left;
            }
            set
            {
                SetProperty(ref _left, value);
            }
        }

        private double _top;
        [CanDo]
        [Browsable(true)]
        public double Top
        {
            get
            {
                return _top;
            }
            set
            {
                SetProperty(ref _top, value);
            }
        }

        [DisplayName("Left(mm)")]
        [Browsable(true)]
        public double PhysicalLeft
        {
            get
            {
                return ScreenHelper.WidthToMm(Left);
            }
            set
            {
                Left = ScreenHelper.MmToWidth(value);
            }
        }

        [DisplayName("Top(mm)")]
        [Browsable(true)]
        public double PhysicalTop
        {
            get
            {
                return ScreenHelper.WidthToMm(Top);
            }
            set
            {
                Top = ScreenHelper.MmToWidth(value);
            }
        }

        public PointBase Position
        {
            get
            {
                return new PointBase(Left, Top);
            }
        }

        [CanDo]
        public PointBase TopLeft
        {
            get
            {
                return new PointBase(Left, Top);
            }
            set
            {
                Left = value.X;
                Top = value.Y;
            }
        }

        private double _angle;
        [CanDo]
        public double Angle
        {
            get
            {
                return _angle;
            }
            set
            {
                SetProperty(ref _angle, value);
            }
        }

        private double _scaleX = 1;
        [CanDo]
        public double ScaleX
        {
            get
            {
                return _scaleX;
            }
            set
            {
                SetProperty(ref _scaleX, value);
            }
        }

        private double _scaleY = 1;
        [CanDo]
        public double ScaleY
        {
            get
            {
                return _scaleY;
            }
            set
            {
                SetProperty(ref _scaleY, value);
            }
        }

        private double _margin;

        public double Margin
        {
            get
            {
                return _margin;
            }
            set
            {
                SetProperty(ref _margin, value);
            }
        }

        private CornerRadius _cornerRadius = new CornerRadius(3);
        public CornerRadius CornerRadius
        {
            get
            {
                return _cornerRadius;
            }
            set
            {
                SetProperty(ref _cornerRadius, value);
            }
        }

        private Thickness _borderThickness = new Thickness(1);
        public Thickness BorderThickness
        {
            get
            {
                return _borderThickness;
            }
            set
            {
                SetProperty(ref _borderThickness, value);
            }
        }

        /// <summary>
        /// 连接点是否可以按偏移自定义
        /// </summary>
        public bool IsInnerConnector
        {
            get; set;
        }

        protected ObservableCollection<FullyCreatedConnectorInfo> connectors = new ObservableCollection<FullyCreatedConnectorInfo>();
        public IEnumerable<FullyCreatedConnectorInfo> Connectors
        {
            get
            {
                return connectors;
            }
        }

        protected ObservableCollection<CinchMenuItem> menuOptions;
        public IEnumerable<CinchMenuItem> MenuOptions
        {
            get
            {
                return menuOptions;
            }
        }

        public bool ShowMenuOptions
        {
            get
            {
                if (MenuOptions == null || MenuOptions.Count() == 0)
                    return false;
                else
                    return true;
            }
        }

        public IObservable<NotifyCollectionChangedEventArgs> WhenConnectorsChanged
        {
            get
            {
                return Observable
                    .FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
                        h => this.connectors.CollectionChanged += h,
                        h => this.connectors.CollectionChanged -= h)
                    .Select(x => x.EventArgs);
            }
        }

        #endregion

        #region 方法
        public void AddConnector(FullyCreatedConnectorInfo connector)
        {
            if (!connectors.Contains(connector))
            {
                connectors.Add(connector);
                ConnectorMargin = 0 - connector.ConnectorWidth / 2;
            }
        }

        public void RemoveConnector(FullyCreatedConnectorInfo connector)
        {
            if (connectors.Contains(connector))
            {
                connectors.Remove(connector);
            }
        }

        public virtual void ClearConnectors()
        {
            connectors.Clear();
        }

        public void SetCellAlignment()
        {
            if (!(this is TextDesignerItemViewModel))
            {
                if (Root.CellHorizontalAlignment == CellHorizontalAlignment.Center)
                {
                    if (Root.GridCellSize.Width > this.ItemWidth)
                    {
                        this.Left = (int)(this.Left / Root.GridCellSize.Width) * Root.GridCellSize.Width + Root.GridMarginSize.Width + (Root.GridCellSize.Width - this.ItemWidth) / 2;
                    }
                }
                else if (Root.CellHorizontalAlignment == CellHorizontalAlignment.Left)
                {
                    this.Left = (int)(this.Left / Root.GridCellSize.Width) * Root.GridCellSize.Width + Root.GridMarginSize.Width;
                }
                else if (Root.CellHorizontalAlignment == CellHorizontalAlignment.Right)
                {
                    if (Root.GridCellSize.Width > this.ItemWidth)
                    {
                        this.Left = (int)(this.Left / Root.GridCellSize.Width) * Root.GridCellSize.Width + Root.GridMarginSize.Width + (Root.GridCellSize.Width - this.ItemWidth);
                    }
                }

                if (Root.CellVerticalAlignment == CellVerticalAlignment.Center)
                {
                    if (Root.GridCellSize.Height > this.ItemHeight)
                    {
                        this.Top = (int)(this.Top / Root.GridCellSize.Height) * Root.GridCellSize.Height + Root.GridMarginSize.Height + (Root.GridCellSize.Height - this.ItemHeight) / 2;
                    }
                }
                else if (Root.CellVerticalAlignment == CellVerticalAlignment.Top)
                {
                    this.Top = (int)(this.Top / Root.GridCellSize.Height) * Root.GridCellSize.Height + Root.GridMarginSize.Height;
                }
                else if (Root.CellVerticalAlignment == CellVerticalAlignment.Bottom)
                {
                    if (Root.GridCellSize.Height > this.ItemHeight)
                    {
                        this.Top = (int)(this.Top / Root.GridCellSize.Height) * Root.GridCellSize.Height + Root.GridMarginSize.Height + (Root.GridCellSize.Height - this.ItemHeight);
                    }
                }
            }
        }

        public void RaiseTopLeft()
        {
            this.RaisePropertyChanged(nameof(TopLeft), new PointBase(GetOldValue<double>(nameof(Left)), GetOldValue<double>(nameof(Top))), TopLeft);
        }

        public void RaiseItemWidthHeight()
        {
            this.RaisePropertyChanged(nameof(Size), new SizeBase(GetOldValue<double>(nameof(ItemWidth)), GetOldValue<double>(nameof(ItemHeight))), Size);
        }

        public void RaiseAngle()
        {
            this.RaisePropertyChanged(nameof(Angle), GetOldValue<double>(nameof(Angle)), Angle);
        }

        public FullyCreatedConnectorInfo GetFullConnectorInfo(Guid connectorId, ConnectorOrientation connectorOrientation, double xRatio, double yRatio, bool isInnerPoint, bool isPortless)
        {
            if (isInnerPoint)
            {
                return this.Connectors.Where(p => p.XRatio == xRatio && p.YRatio == yRatio).FirstOrDefault();
            }
            else if (isPortless)
            {
                return this.PortlessConnector;
            }
            else
            {
                switch (connectorOrientation)
                {
                    case ConnectorOrientation.Left:
                        return this.LeftConnector;
                    case ConnectorOrientation.TopLeft:
                        return this.TopLeftConnector;
                    case ConnectorOrientation.Top:
                        return this.TopConnector;
                    case ConnectorOrientation.TopRight:
                        return this.TopRightConnector;
                    case ConnectorOrientation.Right:
                        return this.RightConnector;
                    case ConnectorOrientation.BottomRight:
                        return this.BottomRightConnector;
                    case ConnectorOrientation.Bottom:
                        return this.BottomConnector;
                    case ConnectorOrientation.BottomLeft:
                        return this.BottomLeftConnector;

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

        public RectangleBase GetBounds(bool includePorts = false)
        {
            if (!includePorts)
                return new RectangleBase(Position, Size);

            var leftPort = LeftConnector;
            var topPort = TopConnector;
            var rightPort = RightConnector;
            var bottomPort = BottomConnector;

            var left = leftPort == null ? Position.X : Math.Min(Position.X, leftPort.Position.X);
            var top = topPort == null ? Position.Y : Math.Min(Position.Y, topPort.Position.Y);
            var right = rightPort == null ? Position.X + ItemWidth :
                Math.Max(rightPort.Position.X + rightPort.ConnectorWidth, Position.X + ItemWidth);
            var bottom = bottomPort == null ? Position.Y + ItemHeight :
                Math.Max(bottomPort.Position.Y + bottomPort.ConnectorHeight, Position.Y + ItemHeight);

            return new RectangleBase(left, top, right, bottom, true);
        }

        public IShape GetShape() => ShapeDefiner(this);

        public override string ToString()
        {
            return $"{Id}-{Name}-{Text}-({Left},{Top},{ItemWidth},{ItemHeight})";
        }
        #endregion
    }
}
