﻿using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Effects;
using Demo.WindowsPresentation.Controls;
using GMap.NET.WindowsPresentation;

namespace Demo.WindowsPresentation.CustomMarkers
{
   public class CircleVisual : FrameworkElement
   {
      public readonly Popup Popup = new Popup();
      public readonly TrolleyTooltip Tooltip = new TrolleyTooltip();
      public readonly GMapMarker Marker;

      public CircleVisual(GMapMarker m, Brush background)
      {
         Marker = m;
         Marker.ZIndex = 100;

         Popup.AllowsTransparency = true;
         Popup.PlacementTarget = this;
         Popup.Placement = PlacementMode.Mouse;
         Popup.Child = Tooltip;
         Popup.Child.Opacity = 0.777;

         SizeChanged += new SizeChangedEventHandler(CircleVisual_SizeChanged);
         MouseEnter += new System.Windows.Input.MouseEventHandler(CircleVisual_MouseEnter);
         MouseLeave += new System.Windows.Input.MouseEventHandler(CircleVisual_MouseLeave);
         Loaded += new RoutedEventHandler(OnLoaded);

         Text = "?";

         StrokeArrow.EndLineCap = PenLineCap.Triangle;
         StrokeArrow.LineJoin = PenLineJoin.Round;

         RenderTransform = scale;

         Width = Height = 22;
         FontSize = (Width/1.55);

         Background = background;
         Angle = null;
      }

      void CircleVisual_SizeChanged(object sender, SizeChangedEventArgs e)
      {
         Marker.Offset = new System.Windows.Point(-e.NewSize.Width/2, -e.NewSize.Height/2);
         scale.CenterX = -Marker.Offset.X;
         scale.CenterY = -Marker.Offset.Y;
      }

      void OnLoaded(object sender, RoutedEventArgs e)
      {
         UpdateVisual(true);
      }

      readonly ScaleTransform scale = new ScaleTransform(1, 1);

      void CircleVisual_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
      {
         if(Popup.IsOpen)
         {
            Popup.IsOpen = false;
         }

         Marker.ZIndex -= 10000;
         Cursor = Cursors.Arrow;

         this.Effect = null;

         scale.ScaleY = 1;
         scale.ScaleX = 1;
      }

      void CircleVisual_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
      {
         if(!Popup.IsOpen)
         {
            Popup.IsOpen = true;
         }

         Marker.ZIndex += 10000;
         Cursor = Cursors.Hand;

         this.Effect = ShadowEffect;

         scale.ScaleY = 1.5;
         scale.ScaleX = 1.5;
      }

      public DropShadowEffect ShadowEffect;

      static readonly Typeface Font = new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal);
      FormattedText FText;

      private Brush background = Brushes.Blue;
      public Brush Background
      {
         get
         {
            return background;
         }
         set
         {
            if(background != value)
            {
               background = value;
               IsChanged = true;
            }
         }
      }

      private Brush foreground = Brushes.White;
      public Brush Foreground
      {
         get
         {
            return foreground;
         }
         set
         {
            if(foreground != value)
            {
               foreground = value;
               IsChanged = true;

               ForceUpdateText();
            }
         }
      }

      private Pen stroke = new Pen(Brushes.Blue, 2.0);
      public Pen Stroke
      {
         get
         {
            return stroke;
         }
         set
         {
            if(stroke != value)
            {
               stroke = value;
               IsChanged = true;
            }
         }
      }

      private Pen strokeArrow = new Pen(Brushes.Blue, 2.0);
      public Pen StrokeArrow
      {
         get
         {
            return strokeArrow;
         }
         set
         {
            if(strokeArrow != value)
            {
               strokeArrow = value;
               IsChanged = true;
            }
         }
      }

      public double FontSize = 16;

      private double? angle = 0;
      public double? Angle
      {
         get
         {
            return angle;
         }
         set
         {
            if(!Angle.HasValue || !value.HasValue || (Angle.HasValue && value.HasValue && Math.Abs(angle.Value - value.Value) > 11))
            {
               angle = value;
               IsChanged = true;
            }
         }
      }
      public bool IsChanged = true;

      void ForceUpdateText()
      {
         FText = new FormattedText(text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, Font, FontSize, Foreground);
         IsChanged = true;
      }

      string text;
      public string Text
      {
         get
         {
            return text;
         }
         set
         {
            if(text != value)
            {
               text = value;
               ForceUpdateText();
            }
         }
      }

      Visual _child;
      public virtual Visual Child
      {
         get
         {
            return _child;
         }
         set
         {
            if(_child != value)
            {
               if(_child != null)
               {
                  RemoveLogicalChild(_child);
                  RemoveVisualChild(_child);
               }

               if(value != null)
               {
                  AddVisualChild(value);
                  AddLogicalChild(value);
               }

               // cache the new child
               _child = value;

               InvalidateVisual();
            }
         }
      }

      public bool UpdateVisual(bool forceUpdate)
      {
         if(forceUpdate || IsChanged)
         {
            Child = Create();
            IsChanged = false;
            return true;
         }

         return false;
      }

      int countCreate = 0;

      private DrawingVisual Create()
      {
         countCreate++;

         var square = new DrawingVisualFx();

         using(DrawingContext dc = square.RenderOpen())
         {
            dc.DrawEllipse(null, Stroke, new Point(Width/2, Height/2), Width/2 + Stroke.Thickness/2, Height/2 + Stroke.Thickness/2);

            if(Angle.HasValue)
            {
               dc.PushTransform(new RotateTransform(Angle.Value, Width/2, Height/2));
               {
                  PolyLineSegment polySeg = new PolyLineSegment(new Point[] { new Point(Width*0.2, Height*0.3), new Point(Width*0.8, Height*0.3) }, true);
                  PathFigure pathFig = new PathFigure(new Point(Width*0.5, -Height*0.22), new PathSegment[] { polySeg }, true);
                  PathGeometry pathGeo = new PathGeometry(new PathFigure[] { pathFig });
                  dc.DrawGeometry(Brushes.AliceBlue, StrokeArrow, pathGeo);
               }
               dc.Pop();
            }

            dc.DrawEllipse(Background, null, new Point(Width/2, Height/2), Width/2, Height/2);
            dc.DrawText(FText, new Point(Width/2 - FText.Width/2, Height/2 - FText.Height/2));
         }

         return square;
      }

      #region Necessary Overrides -- Needed by WPF to maintain bookkeeping of our hosted visuals
      protected override int VisualChildrenCount
      {
         get
         {
            return (Child == null ? 0 : 1);
         }
      }

      protected override Visual GetVisualChild(int index)
      {
         return Child;
      }
      #endregion
   }

   public class DrawingVisualFx : DrawingVisual
   {
      public static readonly DependencyProperty EffectProperty = DependencyProperty.Register("Effect", typeof(Effect), typeof(DrawingVisualFx),
                                      new FrameworkPropertyMetadata(null, (FrameworkPropertyMetadataOptions.AffectsRender), new PropertyChangedCallback(OnEffectChanged)));
      public Effect Effect
      {
         get
         {
            return (Effect) GetValue(EffectProperty);
         }
         set
         {
            SetValue(EffectProperty, value);
         }
      }

      private static void OnEffectChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
      {
         DrawingVisualFx drawingVisualFx = o as DrawingVisualFx;
         if(drawingVisualFx != null)
         {
            drawingVisualFx.setMyProtectedVisualEffect((Effect) e.NewValue);
         }
      }

      private void setMyProtectedVisualEffect(Effect effect)
      {
         VisualEffect = effect;
      }
   }
}
