using BrightSharp.Extensions;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media.Animation;

namespace BrightSharp.Diagrams
{

    public partial class ZoomControl : ItemsControl
    {
        public ZoomControl()
        {
            Loaded += ZoomControl_Loaded;

        }

        private void ZoomControl_Loaded(object sender, RoutedEventArgs e)
        {
            SelectionBehavior.Attach(this);
        }
        
        public ContentControl SelectedControl
        {
            get { return (ContentControl)GetValue(SelectedControlProperty); }
            set { SetValue(SelectedControlProperty, value); }
        }

        public static readonly DependencyProperty SelectedControlProperty =
            DependencyProperty.Register("SelectedControl", typeof(ContentControl), typeof(ZoomControl), new PropertyMetadata(null));

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

            if (e.MiddleButton == MouseButtonState.Pressed)
            {
                _panPoint = e.GetPosition(this);
            }
        }

        private Point? _panPoint;

        #region Props

        public double TranslateX
        {
            get { return (double)GetValue(TranslateXProperty); }
            set { SetValue(TranslateXProperty, value); }
        }

        public static readonly DependencyProperty TranslateXProperty =
            DependencyProperty.Register("TranslateX", typeof(double), typeof(ZoomControl), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsArrange, TranslateChangedHandler));



        public double TranslateY
        {
            get { return (double)GetValue(TranslateYProperty); }
            set { SetValue(TranslateYProperty, value); }
        }

        public static readonly DependencyProperty TranslateYProperty =
            DependencyProperty.Register("TranslateY", typeof(double), typeof(ZoomControl), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsArrange, TranslateChangedHandler));



        public double GridSize
        {
            get { return (double)GetValue(GridSizeProperty); }
            set { SetValue(GridSizeProperty, value); }
        }

        public static readonly DependencyProperty GridSizeProperty =
            DependencyProperty.Register("GridSize", typeof(double), typeof(ZoomControl), new PropertyMetadata(0.0));

        public double RenderZoom
        {
            get { return (double)GetValue(RenderZoomProperty); }
            set { SetValue(RenderZoomProperty, value); }
        }

        public static readonly DependencyProperty RenderZoomProperty =
            DependencyProperty.Register("RenderZoom", typeof(double), typeof(ZoomControl), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsArrange, ZoomChangedHandler));

        private static void ZoomChangedHandler(DependencyObject d, DependencyPropertyChangedEventArgs baseValue)
        {
            ZoomControl zc = (ZoomControl)d;
        }
        private static void TranslateChangedHandler(DependencyObject d, DependencyPropertyChangedEventArgs baseValue)
        {
            ZoomControl zc = (ZoomControl)d;
        }

        #endregion

        private static readonly RoutedEvent ZoomChangedEvent = EventManager.RegisterRoutedEvent("ZoomChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ZoomControl));

        public event RoutedEventHandler ZoomChanged
        {
            add { AddHandler(ZoomChangedEvent, value); }
            remove { RemoveHandler(ZoomChangedEvent, value); }
        }

        private void RaiseZoomChangedEvent()
        {
            var newEventArgs = new RoutedEventArgs(ZoomChangedEvent);
            RaiseEvent(newEventArgs);
        }

        static ZoomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomControl), new FrameworkPropertyMetadata(typeof(ZoomControl)));
        }

        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            if (CheckMouseOverControlWheelHandle(_panPoint.HasValue))
                return;

            var point = e.GetPosition(this);


            var oldValue = RenderZoom;
            const double zoomPower = 1.25;
            var newValue = RenderZoom * (e.Delta > 0 ? zoomPower : 1 / zoomPower);
            if (UseAnimation)
            {
                DoubleAnimation anim = new DoubleAnimation(newValue, TimeSpan.FromSeconds(.3));
                anim.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
                BeginAnimation(ZoomControl.RenderZoomProperty, anim);
            }
            else 
            {
                DoubleAnimation anim = new DoubleAnimation(newValue, TimeSpan.FromSeconds(0));
                anim.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
                BeginAnimation(ZoomControl.RenderZoomProperty, anim);
            }

            RaiseZoomChangedEvent();

            var translateXTo = CoercePanTranslate(TranslateX, point.X, oldValue, newValue);
            var translateYTo = CoercePanTranslate(TranslateY, point.Y, oldValue, newValue);
            if (UseAnimation)
            {
                DoubleAnimation anim = new DoubleAnimation(translateXTo, TimeSpan.FromSeconds(.3));
                anim.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
                BeginAnimation(ZoomControl.TranslateXProperty, anim);

                anim = new DoubleAnimation(translateYTo, TimeSpan.FromSeconds(.3));
                anim.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
                BeginAnimation(ZoomControl.TranslateYProperty, anim);
            }
            else
            {
                DoubleAnimation anim = new DoubleAnimation(translateXTo, TimeSpan.FromSeconds(0));
                anim.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
                BeginAnimation(ZoomControl.TranslateXProperty, anim);

                anim = new DoubleAnimation(translateYTo, TimeSpan.FromSeconds(0));
                anim.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
                BeginAnimation(ZoomControl.TranslateYProperty, anim);
            }

            e.Handled = true;

            base.OnPreviewMouseWheel(e);
        }
        public bool UseAnimation { get; set; }
        private static bool CheckMouseOverControlWheelHandle(bool checkFlowDoc = false)
        {
            if (Keyboard.IsKeyDown(Key.LeftCtrl)) return false;
            var element = Mouse.DirectlyOver as DependencyObject;
            if (element is ScrollViewer) return true;

            var scrollViewer = element.FindAncestor<ScrollViewer>();
            if (scrollViewer != null)
            {
                return scrollViewer.ComputedVerticalScrollBarVisibility == Visibility.Visible
                        || scrollViewer.ComputedHorizontalScrollBarVisibility == Visibility.Visible;
            }
            if (checkFlowDoc)
            {
                var flowDocument = element.FindAncestor<FlowDocument>();
                if (flowDocument != null)
                {
                    return true;
                }
            }

            return false;
        }

        private static double CoercePanTranslate(double oldTranslate, double mousePos, double oldZoom, double newZoom)
        {
            return Math.Round(oldTranslate + (oldTranslate - mousePos) * (newZoom - oldZoom) / oldZoom);
        }

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

            if (e.MiddleButton == MouseButtonState.Released)
            {
                _panPoint = null;
                ReleaseMouseCapture();
            }
        }
        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            base.OnPreviewMouseMove(e);
            if (e.MiddleButton == MouseButtonState.Pressed && _panPoint.HasValue)
            {
                //PAN MODE
                var vector = e.GetPosition(this) - _panPoint.Value;
                _panPoint = _panPoint + vector;
                CaptureMouse();
                TranslateX += vector.X;
                TranslateY += vector.Y;
                BeginAnimation(TranslateXProperty, null);
                BeginAnimation(TranslateYProperty, null);
            }
        }

    }
}
