using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Xml;
using System.Linq;
using System.Windows.Shapes;
using System.Windows.Resources;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using AIStudio.Wpf.DiagramDesigner.Models;
using AIStudio.Wpf.DiagramDesigner.ViewModels;
using AIStudio.Wpf.DiagramDesigner.ViewModels.BaseViewModel;
using System.Diagnostics;

namespace AIStudio.Wpf.DiagramDesigner
{
    public class DesignerCanvas : Canvas
    {
        #region 属性
        private IDiagramViewModel _viewModel
        {
            get
            {
                return DataContext as IDiagramViewModel;
            }
        }
        private IDiagramServiceProvider _service
        {
            get
            {
                return DiagramServicesProvider.Instance.Provider;
            }
        }
        private ConnectionViewModel partialConnection;

        private Point? rubberbandSelectionStartPoint = null;

        private Connector _sourceConnector;
        public Connector SourceConnector
        {
            get
            {
                return _sourceConnector;
            }
            set
            {
                if (_sourceConnector != value)
                {
                    _sourceConnector = value;
                    if (_sourceConnector != null)
                    {
                        ConnectorInfoBase sourceDataItem = _sourceConnector.Info;

                        //Rect rectangleBounds = _sourceConnector.TransformToVisual(this).TransformBounds(new Rect(_sourceConnector.RenderSize));
                        //Point point = new Point(rectangleBounds.Left + (rectangleBounds.Width / 2),
                        //                        rectangleBounds.Bottom + (rectangleBounds.Height / 2));

                        Point point = sourceDataItem.MiddlePosition;

                        partialConnection = new ConnectionViewModel(_viewModel, sourceDataItem, new PartCreatedConnectorInfo(point.X, point.Y), DrawMode, RouterMode);

                        _viewModel.Add(partialConnection);
                        partialConnection.ZIndex = -1;
                    }
                }
            }
        }

        private Connector _sinkConnector;
        public Connector SinkConnector
        {
            get
            {
                return _sinkConnector;
            }
            set
            {
                if (_sinkConnector != null)
                {
                    _sinkConnector.Info.DisableAttachTo = false;
                }
                if (_sinkConnector != value)
                {
                    _sinkConnector = value;
                }
            }
        }

        private DrawMode DrawMode
        {
            get
            {
                if (_viewModel.DrawModeViewModel != null)
                {
                    return _viewModel.DrawModeViewModel.LineDrawMode;
                }
                else
                {
                    return _service.DrawModeViewModel.LineDrawMode;
                }
            }
        }
        private RouterMode RouterMode
        {
            get
            {
                if (_viewModel.DrawModeViewModel != null)
                {
                    return _viewModel.DrawModeViewModel.LineRouterMode;
                }
                else
                {
                    return _service.DrawModeViewModel.LineRouterMode;
                }
            }
        }

        #region GridCellSize

        public static readonly DependencyProperty GridCellSizeProperty =
            DependencyProperty.Register(nameof(GridCellSize),
                                       typeof(Size),
                                       typeof(DesignerCanvas),
                                       new FrameworkPropertyMetadata(new Size(50, 50), FrameworkPropertyMetadataOptions.AffectsRender));

        public Size GridCellSize
        {
            get
            {
                return (Size)GetValue(GridCellSizeProperty);
            }
            set
            {
                SetValue(GridCellSizeProperty, value);
            }
        }

        #endregion

        #region ShowGrid

        public static readonly DependencyProperty ShowGridProperty =
            DependencyProperty.Register(nameof(ShowGrid),
                                       typeof(bool),
                                       typeof(DesignerCanvas),
                                       new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));

        public bool ShowGrid
        {
            get
            {
                return (bool)GetValue(ShowGridProperty);
            }
            set
            {
                SetValue(ShowGridProperty, value);
            }
        }

        #endregion

        #region GridColor

        public static readonly DependencyProperty GridColorProperty =
            DependencyProperty.Register(nameof(GridColor),
                                       typeof(Color),
                                       typeof(DesignerCanvas),
                                       new FrameworkPropertyMetadata(Colors.LightGray, FrameworkPropertyMetadataOptions.AffectsRender));

        public Color GridColor
        {
            get
            {
                return (Color)GetValue(GridColorProperty);
            }
            set
            {
                SetValue(GridColorProperty, value);
            }
        }

        #endregion

        #region GridMarginSize 单位mm

        public static readonly DependencyProperty GridMarginSizeProperty =
            DependencyProperty.Register(nameof(GridMarginSize),
                                       typeof(Size),
                                       typeof(DesignerCanvas),
                                       new FrameworkPropertyMetadata(new Size(28, 28), FrameworkPropertyMetadataOptions.AffectsRender));

        public Size GridMarginSize
        {
            get
            {
                return (Size)GetValue(GridMarginSizeProperty);
            }
            set
            {
                SetValue(GridMarginSizeProperty, value);
            }
        }

        #endregion

        #endregion

        #region 初始化
        public DesignerCanvas()
        {
            this.Focusable = true;
            Mediator.Instance.Register(this);

            _service.PropertyChanged += _service_PropertyChanged;
            this.Loaded += DesignerCanvas_Loaded;
            this.IsVisibleChanged += DesignerCanvas_IsVisibleChanged;
        }

        private void DesignerCanvas_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (IsVisible)
            {
                this.Focus();
            }
        }

        private void DesignerCanvas_Loaded(object sender, RoutedEventArgs e)
        {
            this.Focus();
        }

        protected override void OnRender(DrawingContext dc)
        {
            var rect = new Rect(0, 0, RenderSize.Width, RenderSize.Height);
            dc.DrawRectangle(Background, null, rect);
            if (ShowGrid && GridCellSize.Width > 0 && GridCellSize.Height > 0)
                DrawGrid(dc, rect);
        }

        protected virtual void DrawGrid(DrawingContext dc, Rect rect)
        {
            //using .5 forces wpf to draw a single pixel line
            for (var i = GridMarginSize.Height + 0.5; i < rect.Height - GridMarginSize.Height; i += GridCellSize.Height)
                dc.DrawLine(new Pen(new SolidColorBrush(GridColor), 1), new Point(GridMarginSize.Width, i), new Point(rect.Width - GridMarginSize.Width, i));
            dc.DrawLine(new Pen(new SolidColorBrush(GridColor), 1), new Point(GridMarginSize.Width, rect.Height - GridMarginSize.Height), new Point(rect.Width - GridMarginSize.Width, rect.Height - GridMarginSize.Height));

            for (var i = GridMarginSize.Width + 0.5; i < rect.Width - GridMarginSize.Width; i += GridCellSize.Width)
                dc.DrawLine(new Pen(new SolidColorBrush(GridColor), 1), new Point(i, GridMarginSize.Height), new Point(i, rect.Height - GridMarginSize.Height));
            dc.DrawLine(new Pen(new SolidColorBrush(GridColor), 1), new Point(rect.Width - GridMarginSize.Width, GridMarginSize.Height), new Point(rect.Width - GridMarginSize.Width, rect.Height - GridMarginSize.Height));
        }
        #endregion

        #region Format/Move
        private void _service_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (sender is IDrawModeViewModel)
            {
                if (e.PropertyName == nameof(CursorMode))
                {
                    if (_service.DrawModeViewModel.CursorMode == CursorMode.Format)
                    {
                        EnterFormat();
                    }
                    else if (_service.DrawModeViewModel.CursorMode == CursorMode.Move)
                    {
                        EnterMove();
                    }
                }
            }
        }

        private void EnterFormat()
        {
            StreamResourceInfo sri = Application.GetResourceStream(new Uri("pack://application:,,,/AIStudio.Wpf.DiagramDesigner;component/Images/FormatPainter.cur", UriKind.RelativeOrAbsolute));
            this.Cursor = new Cursor(sri.Stream);

            foreach (SelectableDesignerItemViewModelBase item in _viewModel.Items)
            {
                item.IsHitTestVisible = false;
            }
        }

        private void EnterMove()
        {
            this.Cursor = Cursors.SizeAll;
            foreach (SelectableDesignerItemViewModelBase item in _viewModel.Items)
            {
                item.IsHitTestVisible = false;
            }
        }

        private void ExitCursor()
        {
            this.Cursor = Cursors.Arrow;
            foreach (SelectableDesignerItemViewModelBase item in _viewModel.Items)
            {
                item.IsHitTestVisible = true;
            }
            _service.DrawModeViewModel.CursorMode = CursorMode.Normal;
        }

        private void Format(SelectableDesignerItemViewModelBase source, SelectableDesignerItemViewModelBase target)
        {
            CopyHelper.CopyPropertyValue(source.ColorViewModel, target.ColorViewModel);
            CopyHelper.CopyPropertyValue(source.FontViewModel, target.FontViewModel);
            CopyHelper.CopyPropertyValue(source.ShapeViewModel, target.ShapeViewModel);
            CopyHelper.CopyPropertyValue(source.AnimationViewModel, target.AnimationViewModel);
        }
        #endregion

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            if (_viewModel.IsReadOnly) return;

            if (_service.DrawModeViewModel.CursorMode == CursorMode.Format)
            {
                var element = (e.OriginalSource as FrameworkElement);
                if (element.DataContext is SelectableDesignerItemViewModelBase target)
                {
                    Format(_viewModel.SelectedItems.FirstOrDefault(), target);
                    return;
                }

                ExitCursor();
            }
            else if (_service.DrawModeViewModel.CursorMode == CursorMode.Move)
            {
                ExitCursor();
                return;
            }

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                //if we are source of event, we are rubberband selecting
                if (e.Source == this)
                {
                    // in case that this click is the start for a 
                    // drag operation we cache the start point
                    Point currentPoint = e.GetPosition(this);
                    rubberbandSelectionStartPoint = currentPoint;

                    if (!(Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
                    {
                        _viewModel.ClearSelectedItems();

                    }

                    if (_service.DrawModeViewModel.LineDrawModeSelected)//画线模式,可以不命中实体
                    {
                        if (SourceConnector == null)
                        {
                            //新建一个Part连接点
                            SourceConnector = new Connector() { Content = new PartCreatedConnectorInfo(currentPoint.X, currentPoint.Y), Tag = "虚拟的连接点" };
                        }
                    }
                    else if (_service.DrawModeViewModel.DrawingDrawModeSelected)
                    {
                        // create rubberband adorner
                        AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                        if (adornerLayer != null)
                        {
                            DrawingRubberbandAdorner adorner = new DrawingRubberbandAdorner(this, currentPoint);
                            if (adorner != null)
                            {
                                adornerLayer.Add(adorner);
                            }
                        }
                    }

                    e.Handled = true;
                }
            }

        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            var focusedElement = Keyboard.FocusedElement;
            Debug.WriteLine("focusedElement：" + focusedElement?.ToString());

            base.OnMouseMove(e);

            if (_viewModel.IsReadOnly) return;

            Point currentPoint = e.GetPosition(this);
            _viewModel.CurrentPoint = new Point(ScreenHelper.WidthToMm(currentPoint.X), ScreenHelper.WidthToMm(currentPoint.Y));
            var point = CursorPointManager.GetCursorPosition();
            _viewModel.CurrentColor = ColorPickerManager.GetColor(point.X, point.Y);

            //移动
            if (_service.DrawModeViewModel.CursorMode == CursorMode.Move)
            {
                _viewModel.SelectedItems.OfType<DesignerItemViewModelBase>().ToList().ForEach(p => {
                    p.Left = currentPoint.X;
                    p.Top = currentPoint.Y;
                });
                return;
            }

            if (SourceConnector != null)
            {
                if (e.LeftButton == MouseButtonState.Pressed)
                {
                    partialConnection.SinkConnectorInfo = new PartCreatedConnectorInfo(currentPoint.X, currentPoint.Y);

                    SinkConnector = HitTesting(currentPoint);
                    if (SinkConnector?.Info?.CanAttachTo(SourceConnector?.Info) == false)
                    {
                        SinkConnector.Info.DisableAttachTo = true;
                    }

                    if (_viewModel.DiagramOption.SnappingOption.EnableSnapping)
                    {
                        var nearPort = _viewModel.FindNearPortToAttachTo(partialConnection);
                        if (nearPort != null)
                        {
                            partialConnection.SinkConnectorInfo = nearPort;
                        }
                    }
                }
            }
            else
            {
                // if mouse button is not pressed we have no drag operation, ...
                if (e.LeftButton != MouseButtonState.Pressed)
                    rubberbandSelectionStartPoint = null;

                // ... but if mouse button is pressed and start
                // point value is set we do have one
                if (this.rubberbandSelectionStartPoint.HasValue)
                {
                    // create rubberband adorner
                    AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                    if (adornerLayer != null)
                    {
                        RubberbandAdorner adorner = new RubberbandAdorner(this, rubberbandSelectionStartPoint);
                        if (adorner != null)
                        {
                            adornerLayer.Add(adorner);
                        }
                    }
                }
            }

            e.Handled = true;
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);

            if (_viewModel.IsReadOnly) return;

            Mediator.Instance.NotifyColleagues<bool>("DoneDrawingMessage", true);

            if (SourceConnector != null)
            {
                ConnectorInfoBase sourceDataItem = SourceConnector.Info;
                if (SinkConnector != null && SinkConnector.Info?.DisableAttachTo == false)
                {
                    ConnectorInfoBase sinkDataItem = SinkConnector.Info;

                    _viewModel.Remove(partialConnection);
                    _viewModel.AddItemCommand.Execute(new ConnectionViewModel(_viewModel, sourceDataItem, sinkDataItem, DrawMode, RouterMode));
                }
                else if (partialConnection.IsFullConnection)//自动连接模式
                {
                    _viewModel.ClearNearPort();
                }
                else if (_service.DrawModeViewModel.LineDrawModeSelected)
                {
                    Point currentPoint = e.GetPosition(this);
                    ConnectorInfoBase sinkDataItem = new PartCreatedConnectorInfo(currentPoint.X, currentPoint.Y);

                    _viewModel.Remove(partialConnection);
                    _viewModel.AddItemCommand.Execute(new ConnectionViewModel(_viewModel, sourceDataItem, sinkDataItem, DrawMode, RouterMode));
                }
                else
                {
                    //Need to remove last item as we did not finish drawing the path
                    _viewModel.Remove(partialConnection);
                }
            }

            SourceConnector = null;
            SinkConnector = null;
            partialConnection = null;

            _service.DrawModeViewModel.ResetDrawMode();
        }

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);

            if (_viewModel.IsReadOnly) return;

            e.Handled = _viewModel.ExecuteShortcut(e);
        }

        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            base.OnPreviewMouseWheel(e);

            if (Keyboard.IsKeyDown(Key.LeftCtrl) == false
               && Keyboard.IsKeyDown(Key.RightCtrl) == false)
            {
                return;
            }

            var newZoomValue = _viewModel.ZoomValue + (e.Delta > 0 ? 0.1 : -0.1);

            _viewModel.ZoomValue = Math.Max(Math.Min(newZoomValue, _viewModel.MaximumZoomValue), _viewModel.MinimumZoomValue);

            e.Handled = true;
        }

        protected override Size MeasureOverride(Size constraint)
        {
            Size size = new Size();
            foreach (UIElement element in this.InternalChildren)
            {
                double left = Canvas.GetLeft(element);
                double top = Canvas.GetTop(element);
                left = double.IsNaN(left) ? 0 : left;
                top = double.IsNaN(top) ? 0 : top;

                //measure desired size for each child
                element.Measure(constraint);

                Size desiredSize = element.DesiredSize;
                if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
                {
                    size.Width = Math.Max(size.Width, left + desiredSize.Width);
                    size.Height = Math.Max(size.Height, top + desiredSize.Height);
                }
            }
            // add margin 
            size.Width += 10;
            size.Height += 10;

            return size;
        }

        private Connector HitTesting(Point hitPoint)
        {
            DependencyObject hitObject = this.InputHitTest(hitPoint) as DependencyObject;
            while (hitObject != null &&
                    hitObject.GetType() != typeof(DesignerCanvas))
            {
                if (hitObject is Connector connector)
                {
                    return connector;
                }
                hitObject = VisualTreeHelper.GetParent(hitObject);
            }

            return null;
        }

        protected override void OnDrop(DragEventArgs e)
        {
            base.OnDrop(e);

            if (_viewModel.IsReadOnly) return;

            DragObject dragObject = e.Data.GetData(typeof(DragObject)) as DragObject;
            if (dragObject != null)
            {
                _viewModel.ClearSelectedItems();
                Point position = e.GetPosition(this);
                if (dragObject.DesignerItem is SerializableObject serializableObject)
                {
                    var designerItems = serializableObject.ToObject();
                    var minleft = designerItems.OfType<DesignerItemViewModelBase>().Min(p => p.Left);
                    var mintop = designerItems.OfType<DesignerItemViewModelBase>().Min(p => p.Top);
                    var maxright = designerItems.OfType<DesignerItemViewModelBase>().Max(p => p.Left + p.ItemWidth);
                    var maxbottom = designerItems.OfType<DesignerItemViewModelBase>().Max(p => p.Top + p.ItemHeight);
                    var itemswidth = maxright - minleft;
                    var itemsheight = maxbottom - mintop;

                    foreach (var item in designerItems.OfType<DesignerItemViewModelBase>())
                    {
                        item.Left += position.X - itemswidth / 2;
                        item.Top += position.Y - itemsheight / 2;
                    }
                    _viewModel.AddItemCommand.Execute(designerItems);
                }
                else
                {
                    DesignerItemViewModelBase itemBase = null;
                    if (dragObject.DesignerItem is DesignerItemBase)
                    {
                        itemBase = Activator.CreateInstance(dragObject.ContentType, _viewModel, dragObject.DesignerItem) as DesignerItemViewModelBase;
                    }
                    else
                    {
                        itemBase = Activator.CreateInstance(dragObject.ContentType) as DesignerItemViewModelBase;
                        itemBase.Icon = dragObject.Icon;
                        itemBase.ColorViewModel = CopyHelper.Mapper(dragObject.ColorViewModel);
                        if (dragObject.DesiredSize != null)
                        {
                            itemBase.ItemWidth = dragObject.DesiredSize.Value.Width;
                            itemBase.ItemHeight = dragObject.DesiredSize.Value.Height;
                        }
                    }
                    itemBase.Left = Math.Max(0, position.X - itemBase.ItemWidth / 2);
                    itemBase.Top = Math.Max(0, position.Y - itemBase.ItemHeight / 2);
                    _viewModel.AddItemCommand.Execute(itemBase);
                }
            }
            var dragFile = e.Data.GetData(DataFormats.FileDrop);
            if (dragFile != null && dragFile is string[] files)
            {
                foreach (var file in files)
                {
                    _viewModel.ClearSelectedItems();
                    Point position = e.GetPosition(this);
                    ImageItemViewModel itemBase = new ImageItemViewModel();
                    itemBase.Icon = file;
                    itemBase.Suffix = System.IO.Path.GetExtension(itemBase.Icon).ToLower();
                    itemBase.InitWidthAndHeight();
                    itemBase.AutoSize();

                    itemBase.Left = Math.Max(0, position.X - itemBase.ItemWidth / 2);
                    itemBase.Top = Math.Max(0, position.Y - itemBase.ItemHeight / 2);
                    _viewModel.AddItemCommand.Execute(itemBase);
                }
            }
            e.Handled = true;

            this.Focus();
        }
    }
}