using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using GMapNET;
using GMapNET.Internals;

namespace System.Windows.Controls
{
   public class GMapItem
   {
      Point position;
      public Point Position
      {
         get
         {
            return position;
         }
         set
         {
            position = value;
         }
      }

      string name;
      public string Name
      {
         get
         {
            return name;
         }
         set
         {
            name = value;
         }
      }

      Brush background;
      public Brush Background
      {
         get
         {
            return background;
         }
         set
         {
            background = value;
         }
      }

      double width;
      public double Width
      {
         get
         {
            return width;
         }
         set
         {
            width = value;
         }
      }

      double height;
      public double Height
      {
         get
         {
            return height;
         }
         set
         {
            height = value;
         }
      }

      public GMapItem()
      {
         Position = new Point(10, 10);
         Width = 50;
         Height = 50;
         Background = Brushes.Red;
      }
   }

   delegate void MethodInvoker();

   public partial class GMap : ItemsControl, IGControl
   {
      readonly Core Core = new Core();
      GMapNET.Rectangle region;
      public Canvas Canvas = new Canvas();
      public int MaxZoom = 19;
      public int MinZoom = 1;

       /// <summary>
       /// The marker that currently has focus (right click etc)
       /// </summary>
      public GMapMarker FocusedMarker
      {
          get { return (GMapMarker)GetValue(FocusedMarkerProperty); }
          set { SetValue(FocusedMarkerProperty, value); }
      }

      // Using a DependencyProperty as the backing store for FocusedMarker.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty FocusedMarkerProperty =
          DependencyProperty.Register("FocusedMarker", typeof(GMapMarker), typeof(GMap), new UIPropertyMetadata(null));



       /// <summary>
       /// The current Marker (when clicked etc)
       /// </summary>
      public GMapMarker CurrentMarker
      {
          get { return (GMapMarker)GetValue(CurrentMarkerProperty); }
          set { SetValue(CurrentMarkerProperty, value); }
      }

      // Using a DependencyProperty as the backing store for CurrentMarker.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty CurrentMarkerProperty =
          DependencyProperty.Register("CurrentMarker", typeof(GMapMarker), typeof(GMap), new UIPropertyMetadata(null, new PropertyChangedCallback(OnCurrentMarkerChanged)));


      private static void OnCurrentMarkerChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
      {
          GMap map = o as GMap;
          if (map == null) return;

          GMapMarker marker = e.NewValue as GMapMarker;
          if (marker == null) return;

          foreach (GMapMarker m in map.MapMarkers)
          {
              m.IsCurrentMarker = false;
          }
          marker.IsCurrentMarker = true;
      }



      public ObservableCollection<GMapOverlay> MapOverlays
      {
          get { return (ObservableCollection<GMapOverlay>)GetValue(MapOverlaysProperty); }
          set { SetValue(MapOverlaysProperty, value); }
      }

      // Using a DependencyProperty as the backing store for MapOverlays.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty MapOverlaysProperty =
          DependencyProperty.Register("MapOverlays", typeof(ObservableCollection<GMapOverlay>), typeof(GMap), new UIPropertyMetadata(new ObservableCollection<GMapOverlay>()));


      public ObservableCollection<GMapMarker> MapMarkers
      {
          get { return (ObservableCollection<GMapMarker>)GetValue(MapMarkersProperty); }
          set { SetValue(MapMarkersProperty, value); }
      }

      // Using a DependencyProperty as the backing store for MapMarkers.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty MapMarkersProperty =
          DependencyProperty.Register("MapMarkers", typeof(ObservableCollection<GMapMarker>), typeof(GMap), new UIPropertyMetadata(new ObservableCollection<GMapMarker>()));


      public readonly ObservableCollectionThreadSafe<GMapItem> Objects = new ObservableCollectionThreadSafe<GMapItem>();
      //BackgroundWorker worker = new BackgroundWorker();

      public GMap()
      {
         MapMarkers.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(MapMarkers_CollectionChanged);
         MapOverlays.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(MapOverlays_CollectionChanged);

         //this.ItemsPanel = new ItemsPanelTemplate(new FrameworkElementFactory(typeof(Canvas)));
         //this.ItemTemplate = new DataTemplate(typeof(GMapItem));
         //FrameworkElementFactory panel = new FrameworkElementFactory(typeof(Canvas));
         //{
         //   // canvas
         //   panel.SetBinding(Canvas.BackgroundProperty, new Binding("Background"));
         //   panel.SetBinding(Canvas.WidthProperty, new Binding("Width"));
         //   panel.SetBinding(Canvas.HeightProperty, new Binding("Height"));

         //   // textBlock
         //   FrameworkElementFactory name = new FrameworkElementFactory(typeof(TextBlock));
         //   name.SetBinding(TextBlock.TextProperty, new Binding("Name"));
         //   panel.AppendChild(name);
         //}
         //this.ItemTemplate.VisualTree = panel;

         ClipToBounds = true;
         SnapsToDevicePixels = true;

         // removes white lines between tiles!...but this changes all elements in the map...need to focus this only on the map surface
         //this.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);

         Purity.Instance.ImageProxy = new WindowsPresentationImageProxy();

         Core.RenderMode = GMapNET.RenderMode.WPF;
         Core.OnNeedInvalidation += new NeedInvalidation(Core_OnNeedInvalidation);
         Core.OnCurrentPositionChanged += new CurrentPositionChanged(Core_OnCurrentPositionChanged);
         Core.OnMapDrag += new MapDrag(UpdateMarkersLocalPositions);
         Core.OnMapZoomChanged += new MapZoomChanged(Core_OnMapZoomChanged);
         SizeChanged += new SizeChangedEventHandler(GMap_SizeChanged);
         Loaded += new RoutedEventHandler(GMap_Loaded);

         //GMapMarkerCircle c = new GMapMarkerCircle(this);
         //GMapMarkerRect c = new GMapMarkerRect(this);
         //GMapMarkerTriangle c = new GMapMarkerTriangle(this);
         //GMapMarkerCross c = new GMapMarkerCross(this);
         ////////c.Label.Content = "Maršrutas: 05\nMašina: 1245\nVairuotojas: Jonas P.\nLaikas: 2009.02.02 15:30:42";
         ////////c.Text = "R1";
         ////////CurrentMarker = c;
         ////////AddMarker(CurrentMarker);

         //worker.DoWork += new DoWorkEventHandler(worker_DoWork);         
      }

   

      void MapMarkers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
      {
          //
          //e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.
          
      }

      void MapOverlays_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
      {
          //throw new NotImplementedException();
      }

      int x = 50;
      int y = 5;
      void worker_DoWork(object sender, DoWorkEventArgs e)
      {
         //for(int i = 0; i < 20; i++)
         //{
         //   GMapItem it = new GMapItem();
         //   it.Name = "Name: worker " + y.ToString();
         //   it.Background = Brushes.Blue;
         //   it.Position = new Point(x+=50, y+=40);
         //   Objects.Add(it);

         //   System.Threading.Thread.Sleep(1000);
         //}
      }

      protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
      {
         FrameworkElement contentitem = element as FrameworkElement;
         contentitem.SetBinding(Canvas.LeftProperty, new Binding("Position.X"));
         contentitem.SetBinding(Canvas.TopProperty, new Binding("Position.Y"));
         base.PrepareContainerForItemOverride(element, item);
      }

       /// <summary>
       /// Zoom level changed
       /// </summary>
      void Core_OnMapZoomChanged()
      {
          SetZoomVisibility();
      }


       /// <summary>
       /// Set markers and overlay visibility on zoom changed
       /// </summary>
      private void SetZoomVisibility()
      {
          int zoom = Core.Zoom;
          foreach (GMapOverlay o in MapOverlays)
          {
              if ((zoom <= o.MaxZoom) && (zoom >= o.MinZoom))
                  o.MarkerElement.Visibility = Visibility.Visible; 
              else
                  o.MarkerElement.Visibility = Visibility.Collapsed; 
          }

          foreach (GMapMarker m in MapMarkers)
          {
              if ((zoom <= m.MaxZoom) && (zoom >= m.MinZoom))
                  m.MarkerElement.Visibility = Visibility.Visible;
              else
                  m.MarkerElement.Visibility = Visibility.Collapsed; 
          }
      }

      /// <summary>
      /// update markers location on map drag
      /// </summary>
      public void UpdateMarkersLocalPositions()
      {
          foreach (GMapMarker m in MapMarkers)
          {
              m.UpdateLocalPosition(this);
          }

          foreach (GMapOverlay o in MapOverlays)
          {
              o.UpdateLocalPosition(this);
          }

      }

      /// <summary>
      /// update current marker
      /// </summary>
      /// <param name="point"></param>
      void Core_OnCurrentPositionChanged(PointLatLng point)
      {
          if (CurrentMarker != null && CurrentMarker.CanDrag)
          {
           //   CurrentMarker.Label.Content = CurrentPosition.ToString();
              CurrentMarker.Position = Core.CurrentPosition;
              CurrentMarker.UpdateLocalPosition(this);
          }
      }

      /// <summary>
      /// inits core system
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      void GMap_Loaded(object sender, RoutedEventArgs e)
      {
         Core.StartSystem();
         if(!this.Items.Contains(Canvas)) this.Items.Add(Canvas);

         //this.ItemsSource = Objects;
         //worker.RunWorkerAsync();
      }

      /// <summary>
      /// on core needs invalidation
      /// </summary>
      void Core_OnNeedInvalidation()
      {
         if(this.Dispatcher.CheckAccess())
         {
            this.InvalidateVisual();
         }
         else
         {
            MethodInvoker m = delegate
            {
               this.InvalidateVisual();
            };
            this.Dispatcher.Invoke(DispatcherPriority.Render, m);
         }
      }

      /// <summary>
      /// on map size changed
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      void GMap_SizeChanged(object sender, SizeChangedEventArgs e)
      {
         Core.sizeOfMapArea.Width = (int) e.NewSize.Width;
         Core.sizeOfMapArea.Height = (int) e.NewSize.Height;
         Core.sizeOfMapArea.Height /= GMaps.Instance.TileSize.Height;
         Core.sizeOfMapArea.Width /= GMaps.Instance.TileSize.Width;
         Core.sizeOfMapArea.Height += 1;
         Core.sizeOfMapArea.Width += 1;

         Core.sizeOfMapArea.Width = Core.sizeOfMapArea.Width/2 + 2;
         Core.sizeOfMapArea.Height = Core.sizeOfMapArea.Height/2 + 2;

         // 50px outside control
         region = new GMapNET.Rectangle(-50, -50, (int) e.NewSize.Width+100, (int) e.NewSize.Height+100);

         Core.OnMapSizeChanged((int) e.NewSize.Width, (int) e.NewSize.Height);

         Core.GoToCurrentPosition();
      }

      /// <summary>
      /// render map in WPF
      /// </summary>
      /// <param name="g"></param>
      void DrawMapWPF(DrawingContext g)
      {
         for(int i = -(Core.sizeOfMapArea.Width + Core.centerTileXYOffset.X); i < (Core.sizeOfMapArea.Width - Core.centerTileXYOffset.X); i++)
         {
            for(int j = -(Core.sizeOfMapArea.Height + Core.centerTileXYOffset.Y); j < (Core.sizeOfMapArea.Height - Core.centerTileXYOffset.Y); j++)
            {
               Core.tilePoint = CurrentPositionGTile;
               Core.tilePoint.X += i;
               Core.tilePoint.Y += j;

               Tile t = Core.Matrix[Core.tilePoint];
               if(t != null) // debug center tile add:  && Core.tilePoint != Core.centerTileXYLocation
               {
                  Core.tileRect.X = Core.tilePoint.X*Core.tileRect.Width;
                  Core.tileRect.Y = Core.tilePoint.Y*Core.tileRect.Height;
                  Core.tileRect.Offset(Core.renderOffset);

                  if(region.IntersectsWith(Core.tileRect))
                  {
                     foreach(WindowsPresentationImage img in t.Overlays)
                     {
                        if(img != null && img.Img != null)
                        {
                           g.DrawImage(img.Img, new Rect(Core.tileRect.X, Core.tileRect.Y, Core.tileRect.Width, Core.tileRect.Height));
                        }
                     }
                  }
               }
            }
         }
      }

      // gets image of the current view
      public ImageSource ToImageSource()
      {
         FrameworkElement obj = this;

         // Save current canvas transform
         Transform transform = obj.LayoutTransform;
         obj.LayoutTransform = null;

         // fix margin offset as well
         Thickness margin = obj.Margin;
         obj.Margin = new Thickness(0, 0,
         margin.Right - margin.Left, margin.Bottom - margin.Top);

         // Get the size of canvas
         Size size = new Size(obj.ActualWidth, obj.ActualHeight);

         // force control to Update
         obj.Measure(size);
         obj.Arrange(new Rect(size));

         RenderTargetBitmap bmp = new RenderTargetBitmap(
         (int) size.Width, (int) size.Height, 96, 96, PixelFormats.Pbgra32);

         bmp.Render(obj);

         // return values as they were before
         obj.LayoutTransform = transform;
         obj.Margin = margin;

         return bmp;
      }

      #region UserControl Events
      protected override void OnRender(DrawingContext drawingContext)
      {
         base.OnRender(drawingContext);

         if(Core.RenderMode == GMapNET.RenderMode.WPF)
         {
            DrawMapWPF(drawingContext);
         }
      }


      protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
      {
      

          if (e.LeftButton == MouseButtonState.Pressed)
          {

              //GMapNET.Point c = Core.FromLatLngToLocal(Core.currentPosition);
              //Point current = new Point( c.X,c.Y);
              //Point target = e.GetPosition(this);
              //List<Point> points = PointsAlongLine( target,current, 10);

             // Core.GoToPositionAnimated(points);

              ///////////////////////////////////////////////////////////
              Point p = e.GetPosition(this);
              Core.mouseDown.X = (int)p.X;
              Core.mouseDown.Y = (int)p.Y;

              SetCurrentPositionOnly(Core.mouseDown.X - Core.renderOffset.X, Core.mouseDown.Y - Core.renderOffset.Y);
              // Center
              Core.GoToCurrentPosition();

                  // ... and zoom
                  //int zm = Zoom + 1;
                  //if (zm <= MaxZoom) Zoom = zm;
                  //InvalidateVisual();
              //////////////////////////////////////////////////////////
          }

         

          base.OnMouseDoubleClick(e);
      }

      private List<Point> PointsAlongLine(Point start, Point end, int spacing)
      {
          Double xDifference = end.X - start.X;
          Double yDifference = end.Y - start.Y;
          Double absoluteXdifference = Math.Abs(start.X - end.X);
          Double absoluteYdifference = Math.Abs(start.Y - end.Y);

          int lineLength = (int)Math.Sqrt((Math.Pow(absoluteXdifference, 2) + Math.Pow(absoluteYdifference, 2))); //pythagoras
          int steps = (int)(lineLength / spacing);
          int xStep = (int)(xDifference / steps);
          int yStep = (int)(yDifference / steps);

          List<Point> result = new List<Point>();
          result.Add(start);
          for (int i = 0; i < steps; i++)
          {
              int x = (int)start.X + (xStep * i);
              int y = (int)start.Y + (yStep * i);
              result.Add( new Point(x, y));
          }
          result.Add(end);

          return result;
      }

      protected override void OnMouseWheel(MouseWheelEventArgs e)
      {
         if(IsMouseDirectlyOver)
         {
            if(e.Delta > 0)
            {
               int zm = Zoom + 1;
               if(zm <= MaxZoom)
               {
                  Zoom = zm;
               }
            }

            if(e.Delta < 0)
            {
               int zm = Zoom - 1;
               if(zm >= MinZoom)
               {
                  Zoom = zm;
               }
            }
         }

         base.OnMouseWheel(e);
      }

      protected override void OnMouseDown(MouseButtonEventArgs e)
      {
         Point p = e.GetPosition(this);
         Core.mouseDown.X = (int) p.X;
         Core.mouseDown.Y = (int) p.Y;

         //if(e.LeftButton == MouseButtonState.Pressed)
         //{
         //   if(CurrentMarkerEnabled && !IsMouseOverMarker)
         //   {
         //      SetCurrentPositionOnly(Core.mouseDown.X - Core.renderOffset.X, Core.mouseDown.Y - Core.renderOffset.Y);

         //      if(Core.MouseVisible)
         //      {
         //         //Cursor = Cursors.None;
         //         Core.MouseVisible = false;
         //      }

         //      Core.BeginDrag(Core.mouseDown);
         //   }
         //}
         //else if(e.RightButton == MouseButtonState.Pressed)
         //{
         //   if(CanDragMap)
         //   {
         //      Cursor = Cursors.SizeAll;
         //      Core.BeginDrag(Core.mouseDown);
         //   }
         //}

         if (e.RightButton == MouseButtonState.Pressed)
         {
             //////if (CurrentMarkerEnabled && !IsMouseOverMarker)
             //////{
             //////    SetCurrentPositionOnly(Core.mouseDown.X - Core.renderOffset.X, Core.mouseDown.Y - Core.renderOffset.Y);

             //////    if (Core.MouseVisible)
             //////    {
             //////        //Cursor = Cursors.None;
             //////        Core.MouseVisible = false;
             //////    }

             //////    Core.BeginDrag(Core.mouseDown);
             //////}
         }
         else if (e.LeftButton == MouseButtonState.Pressed)
         {
             if (CanDragMap)
             {

                 if (CurrentMarkerEnabled && IsMouseOverMarker && CurrentMarker.CanDrag)
                 {
                     if (Core.MouseVisible)
                     {
                         //Cursor = Cursors.None;
                         Core.MouseVisible = false;
                     }
                 }

                 Cursor = Cursors.ScrollAll;
                 Core.BeginDrag(Core.mouseDown);
             }
         }

         InvalidateVisual();

         base.OnMouseDown(e);
      }

      protected override void OnMouseUp(MouseButtonEventArgs e)
      {
         if(Core.IsDragging)
         {
            Core.EndDrag();

            Cursor = Cursors.Arrow;

            if(!Core.MouseVisible)
            {
               Core.MouseVisible = true;
            }
         }

         base.OnMouseUp(e);
      }

      protected override void OnMouseMove(MouseEventArgs e)
      {
         if(Core.IsDragging)
         {
            Point p = e.GetPosition(this);
            Core.mouseCurrent.X = (int) p.X;
            Core.mouseCurrent.Y = (int) p.Y;

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (CurrentMarkerEnabled && IsMouseOverMarker && CurrentMarker.CanDrag)
                {
                    SetCurrentPositionOnly(Core.mouseCurrent.X - Core.renderOffset.X, Core.mouseCurrent.Y - Core.renderOffset.Y);
                    InvalidateVisual();
                }
                else
                {
                    Core.Drag(Core.mouseCurrent);
                }
            }
           
         }

         base.OnMouseMove(e);
      }
      #endregion

      #region IGControl Members

      public void ReloadMap()
      {
         Core.ReloadMap();
      }

      public void GoToCurrentPosition()
      {
         Core.GoToCurrentPosition();
      }

      //public bool ZoomAndCenterMarkers()
      //{
      //   return Core.ZoomAndCenterMarkers();
      //}

      public bool SetCurrentPositionByKeywords(string keys)
      {
         return Core.SetCurrentPositionByKeywords(keys);
      }

      //public void SetCurrentMarkersVisibility(bool visible)
      //{
      //   Core.SetCurrentMarkersVisibility(visible);
      //}

      //public void SetCurrentMarkersTooltipMode(MarkerTooltipMode mode)
      //{
      //   Core.SetCurrentMarkersTooltipMode(mode);
      //}

      public PointLatLng FromLocalToLatLng(int x, int y)
      {
         return Core.FromLocalToLatLng(x, y);
      }

      /// <summary>
      /// gets position by geocoding
      /// </summary>
      /// <param name="keys"></param>
      /// <returns></returns>
      public PointLatLng GetLatLngFromGeocoder(string keys)
      {
          return Core.GetLatLngFromGeocoder(keys);
      }

      public GMapNET.Point FromLatLngToLocal(PointLatLng point)
      {
         return Core.FromLatLngToLocal(point);
      }

      //public void AddRoute(Route item)
      //{
      //   Core.AddRoute(item);
      //}

      //public void RemoveRoute(Route item)
      //{
      //   Core.RemoveRoute(item);
      //}

      //public void ClearAllRoutes()
      //{
      //   Core.ClearAllRoutes();
      //}

      public int GetMapOverlayIndex(MapObject item)
      {
          GMapOverlay overlay = item as GMapOverlay;
          if (overlay == null) return -1;
          if (!MapOverlays.Contains(overlay)) return -1;
          return MapOverlays.IndexOf(overlay);
      }

      public void AddOverlay(MapObject item)
      {
          AddOverlay("Default", item);
      }

      public void AddOverlay(String group, MapObject item)
      {
          GMapOverlay overlay = item as GMapOverlay;
          if (overlay == null) return;
          overlay.Group = group;
          MapOverlays.Add(overlay);

          foreach (KeyValuePair<UIElement, Point> el in overlay.Objects)
          {
              Canvas.Children.Add(el.Key);
          }

          if ((Core.Zoom <= overlay.MaxZoom) && (Core.Zoom >= overlay.MinZoom))
              overlay.MarkerElement.Visibility = Visibility.Visible;
          else
              overlay.MarkerElement.Visibility = Visibility.Collapsed;

          overlay.ZIndex = -1000 + MapOverlays.Count-1;
          overlay.UpdateLocalPosition(this);
      }

      public void RemoveOverlay(MapObject item)
      {
          GMapOverlay overlay = item as GMapOverlay;
          if (overlay == null) return;
          if (MapOverlays.Contains(overlay))
          {
              MapOverlays.Remove(overlay);
              foreach (KeyValuePair<UIElement, Point> el in overlay.Objects)
              {
                  Canvas.Children.Remove(el.Key);
              }
          }
      }

      public void ClearAllOverlays()
      {
          foreach (MapObject m in MapOverlays)
          {
              foreach (KeyValuePair<UIElement, Point> el in ((GMapOverlay)m).Objects)
              {
                  Canvas.Children.Remove(el.Key);
              }
          }
          MapOverlays.Clear();
      }


      public int GetMapMarkerIndex(MapObject item)
      {
          GMapMarker marker = item as GMapMarker;
          if (marker == null) return -1;
          if (!MapMarkers.Contains(marker)) return -1;
          return MapMarkers.IndexOf(marker);
      }

      public void AddMarker(MapObject item)
      {
          AddMarker("Default", item);
      }

      public void AddMarker(String group, MapObject item)
      {
          GMapMarker marker = item as GMapMarker;
          if (marker == null) return;
          marker.Group = group;
          MapMarkers.Add(marker);

          foreach (KeyValuePair<UIElement, Point> el in marker.Objects)
         {
            Canvas.Children.Add(el.Key);
         }

          if ((Core.Zoom <= marker.MaxZoom) && (Core.Zoom >= marker.MinZoom))
              marker.MarkerElement.Visibility = Visibility.Visible;
          else
              marker.MarkerElement.Visibility = Visibility.Collapsed;


          marker.ZIndex = MapMarkers.Count - 1;
          marker.UpdateLocalPosition(this);
      }

      public void RemoveMarker(MapObject item)
      {
          GMapMarker marker = item as GMapMarker;
          if (marker == null) return;
          if (MapMarkers.Contains(marker))
          {
              MapMarkers.Remove(marker);
              foreach (KeyValuePair<UIElement, Point> el in marker.Objects)
              {
                  Canvas.Children.Remove(el.Key);
              }
          }
      }

      public void ClearAllMarkers()
      {
          foreach (MapObject m in MapMarkers)
          {
              foreach (KeyValuePair<UIElement, Point> el in ((GMapMarker)m).Objects)
              {
                  Canvas.Children.Remove(el.Key);
              }
          }
         MapMarkers.Clear();
      }

      public void SetCurrentPositionOnly(int x, int y)
      {
         Core.SetCurrentPositionOnly(x, y);
      }

      public void SetCurrentPositionOnly(PointLatLng point)
      {
         Core.SetCurrentPositionOnly(point);
      }

      public bool ShowExportDialog()
      {
         Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
         {
            dlg.CheckPathExists = true;
            dlg.CheckFileExists = false;
            dlg.AddExtension = true;
            dlg.DefaultExt = "gmdb";
            dlg.ValidateNames = true;
            dlg.Title = "GMap.NET: Export map to db, if file exsist only new data will be added";
            dlg.FileName = "DataExp";
            dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            dlg.Filter = "GMap.NET DB files (*.gmdb)|*.gmdb";
            dlg.FilterIndex = 1;
            dlg.RestoreDirectory = true;

            if(dlg.ShowDialog() == true)
            {
               bool ok = GMaps.Instance.ExportToGMDB(dlg.FileName);
               if(ok)
               {
                  MessageBox.Show("Complete!", "GMap.NET", MessageBoxButton.OK, MessageBoxImage.Information);
               }
               else
               {
                  MessageBox.Show("  Failed!", "GMap.NET", MessageBoxButton.OK, MessageBoxImage.Warning);
               }

               return ok;
            }
         }
         return false;
      }

      public bool ShowImportDialog()
      {
         Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
         {
            dlg.CheckPathExists = true;
            dlg.CheckFileExists = false;
            dlg.AddExtension = true;
            dlg.DefaultExt = "gmdb";
            dlg.ValidateNames = true;
            dlg.Title = "GMap.NET: Import to db, only new data will be added";
            dlg.FileName = "DataImport";
            dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            dlg.Filter = "GMap.NET DB files (*.gmdb)|*.gmdb";
            dlg.FilterIndex = 1;
            dlg.RestoreDirectory = true;

            if(dlg.ShowDialog() == true)
            {
               Cursor = Cursors.Wait;

               bool ok = GMaps.Instance.ImportFromGMDB(dlg.FileName);
               if(ok)
               {
                  MessageBox.Show("Complete!", "GMap.NET", MessageBoxButton.OK, MessageBoxImage.Information);
                  ReloadMap();
               }
               else
               {
                  MessageBox.Show("  Failed!", "GMap.NET", MessageBoxButton.OK, MessageBoxImage.Warning);
               }

               Cursor = Cursors.Arrow;

               return ok;
            }
         }
         return false;
      }



      [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
      public int Zoom
      {
         get
         {
            return Core.Zoom;
         }
         set
         {
            Core.Zoom = value;
         }
      }

      public PointLatLng CurrentPosition
      {
         get
         {
            return Core.CurrentPosition;
         }
         set
         {
            Core.CurrentPosition = value;
         }
      }

      public GMapNET.Point CurrentPositionGPixel
      {
         get
         {
            return Core.CurrentPositionGPixel;
         }
      }

      public GMapNET.Point CurrentPositionGTile
      {
         get
         {
            return Core.CurrentPositionGTile;
         }
      }

      [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
      public string CacheLocation
      {
         get
         {
            return Cache.Instance.CacheLocation;
         }
         set
         {
            Cache.Instance.CacheLocation = value;
         }
      }

      public long TotalTiles
      {
         get
         {
            return Core.TotalTiles;
         }
      }

      public bool IsDragging
      {
         get
         {
            return Core.IsDragging;
         }
      }

      public bool IsMouseOverMarker
      {
         get
         {
            return Core.IsMouseOverMarker;
         }

           set
         {
            Core.IsMouseOverMarker = value;
         }
      }

      public bool CurrentMarkerEnabled
      {
         get
         {
            return Core.CurrentMarkerEnabled;
         }
         set
         {
             Core.CurrentMarkerEnabled = value;
             if (CurrentMarker != null)
             {
                 CurrentMarker.MarkerElement.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
             }
         }
      }

      public RectLatLng CurrentViewArea
      {
         get
         {
            return Core.CurrentViewArea;
         }
      }

      public MapType MapType
      {
         get
         {
            return Core.MapType;
         }
         set
         {
            Core.MapType = value;
         }
      }

      public bool RoutesEnabled
      {
         get
         {
            return Core.RoutesEnabled;
         }
         set
         {
            Core.RoutesEnabled = value;
         }
      }

      public bool MarkersEnabled
      {
         get
         {
            return Core.MarkersEnabled;
         }
         set
         {
            Core.MarkersEnabled = value;
         }
      }

      public bool CanDragMap
      {
         get
         {
            return Core.CanDragMap;
         }
         set
         {
            Core.CanDragMap = value;
         }
      }

      //public CurrentMarkerType CurrentMarkerStyle
      //{
      //   get
      //   {
      //      return Core.CurrentMarkerStyle;
      //   }
      //   set
      //   {
      //      Core.CurrentMarkerStyle = value;
      //   }
      //}

      public GMapNET.RenderMode RenderMode
      {
         get
         {
            return GMapNET.RenderMode.WPF;
         }
      }

      #endregion

      #region IGControl event Members

      public event CurrentPositionChanged OnCurrentPositionChanged
      {
         add
         {
            Core.OnCurrentPositionChanged += value;
         }
         remove
         {
            Core.OnCurrentPositionChanged -= value;
         }
      }

      public event TileLoadComplete OnTileLoadComplete
      {
         add
         {
            Core.OnTileLoadComplete += value;
         }
         remove
         {
            Core.OnTileLoadComplete -= value;
         }
      }

      public event TileLoadStart OnTileLoadStart
      {
         add
         {
            Core.OnTileLoadStart += value;
         }
         remove
         {
            Core.OnTileLoadStart -= value;
         }
      }

      //public event MarkerClick OnMarkerClick
      //{
      //   add
      //   {
      //      Core.OnMarkerClick += value;
      //   }
      //   remove
      //   {
      //      Core.OnMarkerClick -= value;
      //   }
      //}

      //public event MarkerEnter OnMarkerEnter
      //{
      //   add
      //   {
      //      Core.OnMarkerEnter += value;
      //   }
      //   remove
      //   {
      //      Core.OnMarkerEnter -= value;
      //   }
      //}

      //public event MarkerLeave OnMarkerLeave
      //{
      //   add
      //   {
      //      Core.OnMarkerLeave += value;
      //   }
      //   remove
      //   {
      //      Core.OnMarkerLeave -= value;
      //   }
      //}

      public event MapDrag OnMapDrag
      {
         add
         {
            Core.OnMapDrag += value;
         }
         remove
         {
            Core.OnMapDrag -= value;
         }
      }

      public event MapZoomChanged OnMapZoomChanged
      {
         add
         {
            Core.OnMapZoomChanged += value;
         }
         remove
         {
            Core.OnMapZoomChanged -= value;
         }
      }

      #endregion
   }

   #region purity control
   internal class WindowsPresentationImage : PureImage
   {
      public ImageSource Img;

      public override IntPtr GetHbitmap()
      {
         return IntPtr.Zero;
      }

      public override object Clone()
      {
         if(Img != null)
         {
            WindowsPresentationImage ret = new WindowsPresentationImage();
            ret.Img = Img.CloneCurrentValue();
            return ret;
         }
         return null;
      }

      public override void Dispose()
      {
         if(Img != null)
         {
            Img = null;
         }
      }
   }

   internal class WindowsPresentationImageProxy : PureImageProxy
   {
      public override PureImage FromStream(Stream stream)
      {
         WindowsPresentationImage ret = null;
         if(stream != null)
         {
            {
               // try png decoder
               try
               {
                  PngBitmapDecoder bitmapDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                  ImageSource m = bitmapDecoder.Frames[0];

                  if(m != null)
                  {
                     ret = new WindowsPresentationImage();
                     ret.Img = m;
                  }
               }
               catch
               {
                  ret = null;
               }

               // try jpeg decoder
               if(ret == null)
               {
                  try
                  {
                     stream.Seek(0, SeekOrigin.Begin);

                     JpegBitmapDecoder bitmapDecoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                     ImageSource m = bitmapDecoder.Frames[0];

                     if(m != null)
                     {
                        ret = new WindowsPresentationImage();
                        ret.Img = m;
                     }
                  }
                  catch
                  {
                     ret = null;
                  }
               }
            }
         }
         return ret;
      }

      public override bool Save(Stream stream, PureImage image)
      {
         WindowsPresentationImage ret = (WindowsPresentationImage) image;
         if(ret.Img != null)
         {
            try
            {
               PngBitmapEncoder e = new PngBitmapEncoder();
               e.Frames.Add(BitmapFrame.Create(ret.Img as BitmapSource));
               e.Save(stream);
            }
            catch
            {
               return false;
            }
         }
         else
         {
            return false;
         }

         return true;
      }
   }
   #endregion
}
