using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Documents;
using System.IO;
using System.Xml;
using System.Windows.Markup;
using System.Windows.Xps;
using System.Windows.Xps.Packaging;
using System.IO.Packaging;
using OcadMapLibrary;

namespace OPlanner {

	public enum MapFileType {
		None, Xaml, Xps, Image, Ocad
	}

	public delegate void ObjectsChangedEventHandler(List<OBaseObject> objects);

	public class OCanvas : Canvas {

		private List<OObjectVisual> oObjects;
		private List<OObjectVisual> selectedObjects;
		private bool isDragging;
		private bool hasMoved;
		private Point dragStart;
		private bool isSelecting;
		private bool isCreating;
		private bool isPanning;
		private SelectionRectAdorner selectingRect;
		private Point selectStart;
		private OCurrentInfo info;
		public OCurrentInfo Info {
			get {
				return info;
			}
		}
		private OBaseObject creatingObject;
		private UserControl mapVisual;
		public UserControl MapVisual {
			get {
				return mapVisual;
			}
		}
		public double CanvasWidth {
			get {
				return canvasSize.Width;
			}
		}
		public double CanvasHeight {
			get {
				return canvasSize.Height;
			}
		}
		private MapFileType mapType;
		public MapFileType MapType {
			get {
				return mapType;
			}
		}
		private string currentMapFile;
		private Size mapSize;
		public Size GetMapSize() {
			if (info.Data.ScaleMapBy != 100) {
				ScaleTransform tranform = new ScaleTransform(info.Data.ScaleMapBy / 100, info.Data.ScaleMapBy / 100);
				Rect rect = new Rect(new Point(0, 0), mapSize);
				rect.Transform(tranform.Value);
				return new Size(rect.Width, rect.Height);
			}
			return new Size(mapSize.Width, mapSize.Height);
		}
		private Size canvasSize;
		private bool isRotating;
		private bool hasRotated;
		private double rotateStart;
		private bool isDrawing;
		private Point drawingStart;
		ODrawingObject drawingObject;
		private CursorManager cursorManager;
		private bool hasClickedOnce;
		private int firstClickTime;
		private int doubleClickInterval = 500;
		private FrameworkElement drawingPreview;
		private Point lastDrawingPoint;
		private Point lastFreehandPoint;
		private bool drawingHasContent;
		private PathSegment drawingPreviewSegment;
		private PathSegmentCollection drawingSegments;
		private OBaseObject lastSelectedObject;
		private OObjectVisual distanceObject;
		private Canvas oObjectContainer;
		private ScrollViewer scrollContainer;
		public ScrollViewer ScrollContainer {
			get {
				return scrollContainer;
			}
			set {
				scrollContainer = value;
			}
		}
		private Window parentWindow;
		public Window ParentWindow {
			get {
				return parentWindow;
			}
			set {
				parentWindow = value;
				CreateContextMenu();
			}
		}

		public bool IsDrawing {
			get {
				return isDrawing;
			}
		}

		public OCanvas() {
			oObjects = new List<OObjectVisual>();
			selectedObjects = new List<OObjectVisual>();
			isDragging = false;
			isSelecting = false;
			hasMoved = false;
			isCreating = false;
			ClipToBounds = true;
			isRotating = false;
			hasRotated = false;
			isDrawing = false;
			isPanning = false;
			CreateMap();
			RemoveMap();
			CreateContainer();
			cursorManager = new CursorManager();
			cursorManager.LoadCursors();
			hasClickedOnce = false;
			firstClickTime = 0;
			drawingHasContent = false;
		}

		public void SetInfo(OCurrentInfo newInfo) {
			info = newInfo;
			info.DataLoaded += new BasicEventHandler(DataLoadedHandler);
			info.ObjectsSelected += new SenderEventHandler(ObjectsSelectedHandler);
			info.ObjectsDeselected += new SenderEventHandler(ObjectsSelectedHandler);
			info.ObjectsCreated += new SenderEventHandler(RedrawHandler);
			info.ObjectsDeleted += new SenderEventHandler(RedrawHandler);
			info.CourseSelected += new SenderEventHandler(RedrawHandler);
			info.CourseViewModeChanged += new SenderEventHandler(RedrawHandler);
			info.ObjectsUpdated += new UpdateEventHandler(UpdateObjectsHandler);
			info.MapUpdated += new SenderEventHandler(MapUpdatedHandler);
			info.ToolSelected += new SenderEventHandler(ToolSelectedHandler);
		}

		void ToolSelectedHandler(object sender) {
			UpdateCursor();
			if (drawingObject == null) {
				if (distanceObject != null) {
					info.SelectedObjects.Clear();
					info.InvokeObjectsDeselected(this);
				}
			}
			EndDrawing();
		}

		void MapUpdatedHandler(object sender) {
			LoadMapFile(info.Data.MapFileAbsolute);
			UpdateObjects(UpdateType.Move);
		}

		private void CreateContainer() {
			if (oObjectContainer != null) {
				Children.Remove(oObjectContainer);
			}
			oObjectContainer = new Canvas();
			Children.Add(oObjectContainer);
		}

		public Canvas OObjectContainer {
			get {
				return oObjectContainer;
			}
		}

		private void CreateMap() {
			if (mapVisual != null) {
				Children.Remove(mapVisual);
			}
			mapVisual = new UserControl();
			mapVisual.HorizontalAlignment = HorizontalAlignment.Center;
			mapVisual.VerticalAlignment = VerticalAlignment.Center;
			mapVisual.Background = Brushes.White;
			Children.Add(mapVisual);
		}

		public CustomResult LoadMapFile(string fileName) {
			FrameworkElement mapBaseVisual = null;
			OMapInfo mapInfo = new OMapInfo();
			if (currentMapFile == fileName) {
				SetCanvasSize();
				return new CustomResult(true);
			}
			if (fileName == null || fileName == "") {
				RemoveMap();
				SetCanvasSize();
				return new CustomResult(true);
			}
			if (!File.Exists(fileName)) {
				return new CustomResult(false, "Cannot find map");
			}
			string ext = System.IO.Path.GetExtension(fileName);
			if (ext.ToLower() == ".xaml") {
				FrameworkElement element;
				try {
					XmlReader reader = XmlReader.Create(fileName);
					element = (FrameworkElement)XamlReader.Load(reader);
					reader.Close();
				} catch (Exception) {
					return new CustomResult(false, "Error reading file");
				}
				mapBaseVisual = element;
				mapType = MapFileType.Xaml;
			} else if (ext.ToLower() == ".xps") {
				XpsDocument doc;
				try {
					doc = new XpsDocument(fileName, FileAccess.Read, CompressionOption.SuperFast);
				} catch (Exception) {
					return new CustomResult(false, "Error reading file");
				}
				DocumentPageView view = new DocumentPageView();
				FixedDocumentSequence fds = doc.GetFixedDocumentSequence();
				if (fds == null) {
					return new CustomResult(false, "Error reading file");
				}
				if (fds.DocumentPaginator == null) {
					return new CustomResult(false, "Error reading file");
				}
				view.DocumentPaginator = fds.DocumentPaginator;
				if (view.DocumentPaginator.PageCount < 1) {
					return new CustomResult(false, "Error reading file");
				}
				DocumentPage page = view.DocumentPaginator.GetPage(0);
				view.Width = page.Size.Width;
				view.Height = page.Size.Height;
				mapBaseVisual = view;
				mapType = MapFileType.Xps;
			} else if (ext.ToLower() == ".ocd") {
				OcadMap tempMap = new OcadMap();
				try {
					if (!tempMap.LoadMap(fileName)) {
						return new CustomResult(false, "Error reading file");
					}
					OcadMapVisual ocadMapVisual = new OcadMapVisual();
					ocadMapVisual.Map = tempMap;
					mapBaseVisual = new Canvas();
					((Canvas)mapBaseVisual).Children.Add(ocadMapVisual);
					Rect rect = tempMap.GetBoundingRect();
					mapBaseVisual.Width = rect.Width;
					mapBaseVisual.Height = rect.Height;
					Canvas.SetLeft(ocadMapVisual, -rect.Left);
					Canvas.SetTop(ocadMapVisual, -rect.Top);
					MapInfo tempInfo = tempMap.MapInfo;
					mapInfo.OcadMainVersion = tempInfo.OcadMainVersion;
					mapInfo.OcadSubVersion = tempInfo.OcadSubVersion;
					mapInfo.DefaultScale = tempInfo.Scale;
				} catch (Exception) {
					mapBaseVisual = null;
					return new CustomResult(false, "Unknown error reading file");
				}
				mapType = MapFileType.Ocad;
			} else {
				BitmapImage bi;
				try {
					bi = new BitmapImage(new Uri(fileName));
				} catch (Exception) {
					return new CustomResult(false, "Error reading file");
				}
				Image image = new Image();
				image.Source = bi;
				mapBaseVisual = image;
				mapType = MapFileType.Image;
			}
			mapInfo.MapType = mapType;
			RemoveMap();
			currentMapFile = fileName;
			mapBaseVisual.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
			mapSize = new Size(mapBaseVisual.DesiredSize.Width, mapBaseVisual.DesiredSize.Height);
			mapBaseVisual.Width =  mapSize.Width;
			mapBaseVisual.Height =  mapSize.Height;
			mapBaseVisual.HorizontalAlignment = HorizontalAlignment.Center;
			mapBaseVisual.VerticalAlignment = VerticalAlignment.Center;
			mapVisual.Content = mapBaseVisual;
			SetCanvasSize();
			info.Data.MapInfo.Load(mapInfo);
			mapType = mapInfo.MapType;
			if (mapInfo.DefaultScale > 0) {
				info.Data.MapScale = mapInfo.DefaultScale;
			}
			return new CustomResult(true);
		}

		private void SetCanvasSize() {
			if (info.Data.CanvasSizeMode == AutoManual.Auto && info.Data.MapFileAbsolute != null && info.Data.MapFileAbsolute != "") {
				mapVisual.Height = double.NaN;
				mapVisual.Width = double.NaN;
				canvasSize = new Size(mapSize.Width, mapSize.Height);
			} else {
				mapVisual.Width = info.Data.CanvasSize.Width;
				mapVisual.Height = info.Data.CanvasSize.Height;
				canvasSize = new Size(info.Data.CanvasSize.Width, info.Data.CanvasSize.Height);
			}
			if (info.Data.ScaleMapBy != 100) {
				ScaleTransform tranform = new ScaleTransform(info.Data.ScaleMapBy / 100, info.Data.ScaleMapBy / 100);
				mapVisual.LayoutTransform = tranform;
				Rect rect = new Rect(new Point(0, 0), canvasSize);
				rect.Transform(tranform.Value);
				canvasSize.Width = rect.Width;
				canvasSize.Height = rect.Height;
			} else {
				mapVisual.LayoutTransform = null;
			}
			info.Data.ActualCanvasSize = canvasSize;
			Width = canvasSize.Width;
			Height = canvasSize.Height;
		}

		private void RemoveMap() {
			currentMapFile = null;
			mapType = MapFileType.None;
			mapVisual.Content = null;
			mapVisual.Width = double.NaN;
			mapVisual.Height = double.NaN;
			mapSize = new Size(0, 0);
			canvasSize = new Size(0, 0);
			Width = 0;
			Height = 0;
			if (info != null) {
				if (info.Data != null) {
					info.Data.MapInfo.Clear();
				}
			}
		}

		private OBaseObject FindPreviousObject(OBaseObject obj) {
			OBaseObject prev = null;
			Course course = obj.GetCourse();
			if (course != null) {
				prev = course.GetPreviousConnectableObject(obj);
			}
			return prev;
		}

		private OObjectVisual FindPreviousVisual(OObjectVisual obj) {
			OBaseObject prev = FindPreviousObject(obj.ObjectData);
			if (prev == null) {
				return null;
			}
			return FindObject(prev);
		}

		private void UpdateObjectsHandler(object sender, UpdateType updateType, List<OBaseObject> objects, bool updatePropertiesPanel) {
			if (updateType == UpdateType.Display) {
				DrawObjects();
			} else {
				if (objects == null || updateType == UpdateType.Description) {
					UpdateObjects(updateType);
				} else {
					for (int i = 0; i < objects.Count; i++) {
						OObjectVisual visual = FindObject(objects[i]);
						if (visual != null) {
							visual.UpdateObject(updateType);
							OObjectVisual prev = FindPreviousVisual(visual);
							if (prev != null) {
								prev.UpdateObject(updateType);
							}
						}
					}
				}
			}
		}
		private void RedrawHandler(object sender) {
			if (distanceObject != null) {
				info.SelectedObjects.Clear();
				info.InvokeObjectsDeselected(this);
			}
			DrawObjects();
		}
		private void ObjectsSelectedHandler(object sender) {
			UpdateSelectedObjects();
		}
		private void DataLoadedHandler() {
			LoadData();
		}

		private void UpdateObjects(UpdateType updateType) {
			for (int i = 0; i < oObjects.Count; i++) {
				oObjects[i].UpdateObject(updateType);
			}
			if (distanceObject != null) {
				distanceObject.UpdateObject(updateType);
			}
		}

		private OObjectVisual CreateObject(OBaseObject obj) {
			OObjectVisual oObject;
			if (obj.GetObject(typeof(OControl)) != null) {
				oObject = new OControlVisual();
			} else if (obj.GetObject(typeof(OTransparentArea)) != null) {
				oObject = new OTransparentAreaVisual();
			} else if (obj.GetObject(typeof(OCourseDescriptions)) != null) {
				oObject = new OCourseDescriptionsVisual();
			} else if (obj.GetObject(typeof(ODrawingObject)) != null) {
				oObject = new ODrawingObjectVisual();
			} else if (obj.GetObject(typeof(IMapObject)) != null) {
				oObject = new OObjectVisual();
			} else {
				return null;
			}
			oObject.SetParentCanvas(this);
			IMapObject mapObj = (IMapObject)obj.GetObject(typeof(IMapObject));
			if (mapObj != null) {
				Canvas.SetZIndex(oObject, mapObj.Depth);
			}
			oObject.SetInfo(info);
			oObject.LostMouseCapture += new MouseEventHandler(ObjectLostMouseCapture);
			oObject.MouseLeftButtonDown += new MouseButtonEventHandler(ObjectMouseDown);
			oObject.MouseMove += new MouseEventHandler(ObjectMouseMove);
			oObject.MouseLeftButtonUp += new MouseButtonEventHandler(ObjectMouseUp);
			oObject.CustomDoubleClick += new SenderEventHandler(ObjectCustomDoubleClick);
			oObject.MouseRightButtonDown += new MouseButtonEventHandler(ObjectMouseRightButtonDown);
			oObject.ContextMenu = ContextMenu;
			oObjects.Add(oObject);
			oObjectContainer.Children.Add(oObject);
			oObject.LoadData(obj);
			return oObject;
		}

		private OObjectVisual CreateDistanceObject(OBaseObject obj) {
			OObjectVisual oObject = new ODrawingObjectVisual();
			oObject.SetParentCanvas(this);
			IMapObject mapObj = (IMapObject)obj.GetObject(typeof(IMapObject));
			if (mapObj != null) {
				Canvas.SetZIndex(oObject, mapObj.Depth);
			}
			oObject.SetInfo(info);
			oObjects.Add(oObject);
			oObjectContainer.Children.Add(oObject);
			oObject.LoadData(obj);
			oObject.IsHitTestVisible = false;
			oObject.UpdateObject(UpdateType.Redraw);
			return oObject;
		}

		private void DeleteObject(OObjectVisual oObject) {
			if (oObject == null) {
				return;
			}
			info.SelectedObjects.Clear();
			info.InvokeObjectsDeselected(this);
			info.InvokeDataChanging(this, "Delete Object");
			if (info.CourseViewType == CourseViewType.Outline) {
				info.Data.RemoveObjectFully(oObject.ObjectData);
			} else {
				info.Data.RemoveObject(oObject.ObjectData);
			}
			info.InvokeDataChanged(this, "Delete Object");
			info.InvokeObjectsDeleted(this);
		}

		private void DeleteObjects(List<OObjectVisual> objects) {
			if (objects.Count == 0) {
				return;
			}
			info.SelectedObjects.Clear();
			info.InvokeObjectsDeselected(this);
			info.InvokeDataChanging(this, "Delete Objects");
			for (int i = 0; i < objects.Count; i++) {
				if (info.CourseViewType == CourseViewType.Outline) {
					info.Data.RemoveObjectFully(objects[i].ObjectData);
				} else {
					info.Data.RemoveObject(objects[i].ObjectData);
				}
			}
			info.InvokeDataChanged(this, "Delete Objects");
			info.InvokeObjectsDeleted(this);
		}

		private double GetAngle(Point objPos) {
			double angle = 0;
			Point mousePos = Mouse.GetPosition(this);
			mousePos.X -= CanvasWidth / 2;
			mousePos.Y -= CanvasHeight / 2;
			Point centre = objPos;
			angle = Math.Atan2(mousePos.Y - centre.Y, mousePos.X - centre.X) * 180 / Math.PI;
			return angle;
		}

		private void EndDrawing() {
			if (drawingObject != null) {
				if (drawingHasContent) {
					info.SelectedObjects.Clear();
					info.SelectedObjects.Add(drawingObject);
					info.InvokeObjectsSelected(this);
				}
				if(!(drawingObject is ODistanceMeasure)){
					info.InvokeDataChanged(this, "Create Object");
				}
				drawingObject = null;
				isDrawing = false;
				hasMoved = false;
				drawingHasContent = false;
				if (drawingPreview != null) {
					oObjectContainer.Children.Remove(drawingPreview);
					drawingPreview = null;
					drawingPreviewSegment = null;
				}
				drawingSegments = null;
			}
		}

		private void CreateContextMenu() {
			ContextMenu menu = new ContextMenu();
			MenuItem item = new MenuItem();
			item.Header = "Copy";
			item.Name = "copyItem";
			item.Command = ApplicationCommands.Copy;
			item.CommandTarget = ParentWindow;
			menu.Items.Add(item);
			item = new MenuItem();
			item.Header = "Paste";
			item.Name = "pasteItem";
			item.Command = ApplicationCommands.Paste;
			item.CommandTarget = ParentWindow;
			menu.Items.Add(item);
			item = new MenuItem();
			item.Header = "Delete";
			item.Name = "deleteItem";
			item.Click += new RoutedEventHandler(DeleteItemClick);
			menu.Items.Add(item);
			item = new MenuItem();
			item.Header = "Deselect";
			item.Name = "deselectItem";
			item.Click += new RoutedEventHandler(DeselectItemClick);
			menu.Items.Add(item);
			menu.Opened += new RoutedEventHandler(ContextMenuOpened);
			ContextMenu = menu;
		}

		void DeleteItemClick(object sender, RoutedEventArgs e) {
			DeleteSelectedObjects();
		}

		void DeselectItemClick(object sender, RoutedEventArgs e) {
			if (info.SelectedObjects.Count == 0) {
				return;
			}
			info.SelectedObjects.Clear();
			info.InvokeObjectsDeselected(this);
		}

		void ContextMenuOpened(object sender, RoutedEventArgs e) {
			UpdateContextMenu();
		}

		private void UpdateContextMenu() {
			if (ContextMenu == null) {
				return;
			}
			for (int i = 0; i < ContextMenu.Items.Count; i++) {
				MenuItem item = ContextMenu.Items[i] as MenuItem;
				if (item == null) {
					continue;
				}
				if (item.Name == "deleteItem") {
					if (info.SelectedObjects.Count > 0) {
						item.IsEnabled = true;
					} else {
						item.IsEnabled = false;
					}
				}
				if (item.Name == "deselectItem") {
					if (info.SelectedObjects.Count > 0) {
						item.IsEnabled = true;
					} else {
						item.IsEnabled = false;
					}
				}
			}
		}

		public void Scroll(double x, double y) {
			if (ScrollContainer == null) {
				return;
			}
			ScrollContainer.ScrollToHorizontalOffset(ScrollContainer.HorizontalOffset + x);
			ScrollContainer.ScrollToVerticalOffset(ScrollContainer.VerticalOffset + y);
		}

		protected override void OnMouseRightButtonDown(MouseButtonEventArgs e) {
			base.OnMouseRightButtonDown(e);
			if (e.LeftButton == MouseButtonState.Pressed) {
				return;
			}
			if (ScrollContainer == null) {
				return;
			}
			if (e.Source is OObjectVisual) {
				return;
			}
			dragStart = e.GetPosition(ScrollContainer);
			isPanning = true;
			hasMoved = false;
			Mouse.Capture(this);
		}

		protected override void OnMouseRightButtonUp(MouseButtonEventArgs e) {
			base.OnMouseRightButtonUp(e);
			if (isPanning) {
				if (hasMoved) {
					e.Handled = true;
				}
				Mouse.Capture(null);
			}
		}

		void ObjectMouseRightButtonDown(object sender, MouseButtonEventArgs e) {
			OObjectVisual oObject = (OObjectVisual)sender;
			if (!oObject.IsSelected) {
				info.SelectedObjects.Clear();
				info.SelectedObjects.Add(oObject.ObjectData);
				info.InvokeObjectsSelected(this);
			}
		}

		protected override void OnLostMouseCapture(MouseEventArgs e) {
			base.OnLostMouseCapture(e);
			if (isPanning) {
				if (hasMoved) {
					hasMoved = false;
				} else {
					if (e.Source is OObjectVisual) {
						if (((OObjectVisual)e.Source).IsSelected) {
							return;
						}
					}
					if (info.SelectedObjects.Count > 0) {
						info.SelectedObjects.Clear();
						info.InvokeObjectsDeselected(this);
					}
					lastSelectedObject = null;
				}
				isPanning = false;
			}
			if (isSelecting) {
				isSelecting = false;
				for (int i = 0; i < oObjects.Count; i++) {
					if (selectingRect.GetRect().Contains(oObjects[i].GetCentre())) {
						info.SelectedObjects.Add(oObjects[i].ObjectData);
					}
				}
				AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
				adornerLayer.Remove(selectingRect);
				selectingRect = null;
				if (info.SelectedObjects.Count > 0) {
					info.InvokeObjectsSelected(this);
				}
			}
			if (isCreating) {
				info.SelectedObjects.Clear();
				info.SelectedObjects.Add(creatingObject);
				info.InvokeObjectsSelected(this);
				creatingObject = null;
				isCreating = false;
			}
			if (isRotating) {
				if (hasRotated) {
					if (info.SelectedObjects.Count > 0) {
						IRotatableObject oObjectData = (IRotatableObject)info.SelectedObjects[0].GetObject(typeof(IRotatableObject));
						if (oObjectData != null) {
							double angle = GetAngle(oObjectData.GetCentre()) - rotateStart;
							oObjectData.Rotation = angle;
							info.InvokeDataChanged(this, "Rotate Object");
							info.InvokeObjectsUpdated(this, UpdateType.Rotate, new List<OBaseObject>(new OBaseObject[] { info.SelectedObjects[0] }), true);
						}
					}
					hasRotated = false;
				}
				isRotating = false;
			}
			if (isDrawing && info.DrawingMode == DrawingMode.Freehand) {
				if (hasMoved) {
					info.SelectedObjects.Clear();
					info.SelectedObjects.Add(drawingObject);
					if (!(drawingObject is ODistanceMeasure)) {
						info.InvokeDataChanged(this, "Create Object");
					}
					info.InvokeObjectsSelected(this);
					info.InvokeObjectsUpdated(this, UpdateType.Drawing, new List<OBaseObject>(new OBaseObject[] { drawingObject }), true);
					drawingObject = null;
					hasMoved = false;
				}
				isDrawing = false;
			}
			if (isDrawing && info.DrawingMode == DrawingMode.Line) {
				bool doubleClick = false;
				if (hasClickedOnce) {
					int timeNow = Environment.TickCount;
					if (timeNow - firstClickTime <= doubleClickInterval) {
						doubleClick = true;
					}
				}
				hasClickedOnce = true;
				firstClickTime = Environment.TickCount;
				if (drawingObject == null) {
					ODrawingObject objectData = null;
					Type objectType = null;
					OCodeContainer parent = null;
					if (info.CreateMode == CreateMode.Normal) {
						parent = info.Data;
					} else {
						parent = info.SelectedCourse;
					}
					switch (info.SelectedTool) {
						case ToolType.CreateLine:
							objectData = new OLine(parent);
							objectType = typeof(OLine);
							break;
						case ToolType.CreateTapedRoute:
							objectData = new OTapedRoute(parent);
							objectType = typeof(OTapedRoute);
							break;
						case ToolType.CreateOOBArea:
							objectData = new OOOBArea(parent);
							objectType = typeof(OOOBArea);
							break;
						case ToolType.CreateAreaBlock:
							objectData = new OAreaBlock(parent);
							objectType = typeof(OAreaBlock);
							break;
						case ToolType.CreateUncrossableBoundary:
							objectData = new OUncrossableBoundary(parent);
							objectType = typeof(OUncrossableBoundary);
							break;
						case ToolType.MeasureDistance:
							objectData = new ODistanceMeasure(parent);
							objectType = typeof(ODistanceMeasure);
							break;
					}
					if (objectData != null && parent != null) {
						objectData.DrawingMode = DrawingMode.Line;
						objectData.ObjectCode = objectData.ObjectCodePrefix + Convert.ToString(parent.GetNextCodeNum(objectType));
						lastDrawingPoint = new Point(drawingStart.X - canvasSize.Width / 2, drawingStart.Y - canvasSize.Height / 2);
						objectData.SetStartPoint(lastDrawingPoint);
						drawingObject = objectData;
					}
				} else {
					if (doubleClick && drawingStart.X - canvasSize.Width / 2 == lastDrawingPoint.X && drawingStart.Y - canvasSize.Height / 2 == lastDrawingPoint.Y) {
						EndDrawing();
						return;
					}
					if (!drawingHasContent) {
						if (!(drawingObject is ODistanceMeasure)) {
							info.InvokeDataChanging(this, "Create Object");
							if (info.CreateMode == CreateMode.Course && info.SelectedCourse != null) {
								info.SelectedCourse.AddObject(drawingObject);
							} else {
								info.Data.AddObject(drawingObject);
							}
							info.InvokeObjectsCreated(this);
						} else {
							distanceObject = CreateDistanceObject(drawingObject);
						}
						drawingHasContent = true;
					}
					Point mousePos = Mouse.GetPosition(this);
					if (mousePos.X != drawingStart.X || mousePos.Y != drawingStart.Y) {
						drawingObject.AddQuadraticBezierSegment(new Point(mousePos.X - canvasSize.Width / 2, mousePos.Y - canvasSize.Height / 2), new Point(drawingStart.X - canvasSize.Width / 2, drawingStart.Y - canvasSize.Height / 2));
					} else {
						drawingObject.AddLineSegment(new Point(drawingStart.X - canvasSize.Width / 2, drawingStart.Y - canvasSize.Height / 2));
					}
					drawingPreviewSegment = null;
					if (drawingPreview != null) {
						oObjectContainer.Children.Remove(drawingPreview);
						drawingPreview = null;
					}
					drawingSegments = null;
					lastDrawingPoint = new Point(drawingStart.X - canvasSize.Width / 2, drawingStart.Y - canvasSize.Height / 2);
					info.InvokeObjectsUpdated(this, UpdateType.Drawing, new List<OBaseObject>(new OBaseObject[] { drawingObject }), true);
				}
				isDrawing = false;
				hasMoved = false;
			}
		}

		protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
			base.OnMouseLeftButtonDown(e);
			if (e.RightButton == MouseButtonState.Pressed) {
				return;
			}
			if (e.Source is OObjectVisual) {
				if (((OObjectVisual)e.Source).IsSelected) {
					return;
				}
			}
			lastSelectedObject = null;
			if (info.SelectedTool == ToolType.Rotate) {
				if (info.SelectedObjects.Count == 1) {
					IRotatableObject obj = (IRotatableObject)info.SelectedObjects[0].GetObject(typeof(IRotatableObject));
					if (obj != null) {
						isRotating = true;
						rotateStart = GetAngle(obj.GetCentre()) - obj.Rotation;
						Mouse.Capture(this);
						return;
					}
				}
			}
			List<OBaseObject> prevSelectedObjects = new List<OBaseObject>(info.SelectedObjects);
			if (info.SelectedObjects.Count > 0) {
				info.SelectedObjects.Clear();
				info.InvokeObjectsDeselected(this);
			}
			if (e.ButtonState != MouseButtonState.Pressed) {
				return;
			}
			if (info.SelectedTool == ToolType.Select) {
				isSelecting = true;
				selectingRect = new SelectionRectAdorner(this);
				Point mousePos = Mouse.GetPosition(this);
				selectingRect.SetRect(new Rect(mousePos, Size.Empty));
				AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
				adornerLayer.Add(selectingRect);
				selectStart = mousePos;
				Mouse.Capture(this);
			} else if (info.CreateMode != CreateMode.None) {
				if (info.DrawingMode == DrawingMode.None) {
					IMapObject objectData = null;
					Type objectType = null;
					OCodeContainer parent = null;
					if (info.CreateMode == CreateMode.Normal) {
						parent = info.Data;
					} else {
						parent = info.SelectedCourse;
					}
					if (parent == null) {
						return;
					}
					switch (info.SelectedTool) {
						case ToolType.CreateStart:
							objectData = new OStart(parent);
							objectType = typeof(OStart);
							break;
						case ToolType.CreateFinish:
							objectData = new OFinish(parent);
							objectType = typeof(OFinish);
							break;
						case ToolType.CreateCrossingPoint:
							objectData = new OCrossingPoint(parent);
							objectType = typeof(OCrossingPoint);
							break;
						case ToolType.CreateTransparentArea:
							objectData = new OTransparentArea(parent);
							objectType = typeof(OTransparentArea);
							break;
						case ToolType.CreateCourseDescriptions:
							objectData = new OCourseDescriptions(parent);
							objectType = typeof(OCourseDescriptions);
							break;
						case ToolType.CreateControl:
							objectData = new OControl(parent);
							objectType = typeof(OControl);
							break;
						case ToolType.CreateText:
							objectData = new OText(parent);
							objectType = typeof(OText);
							((OText)objectData).Text = "Text";
							break;
						case ToolType.CreateInvisiblePoint:
							objectData = new OInvisiblePoint(parent);
							objectType = typeof(OInvisiblePoint);
							break;
						case ToolType.CreateDrinksPoint:
							objectData = new ODrinksPoint(parent);
							objectType = typeof(ODrinksPoint);
							break;
						case ToolType.CreateOOBCross:
							objectData = new OOOBCross(parent);
							objectType = typeof(OOOBCross);
							break;
						case ToolType.CreateFirstAidPoint:
							objectData = new OFirstAidPoint(parent);
							objectType = typeof(OFirstAidPoint);
							break;
						default:
							break;
					}
					if (objectData != null) {
						Point mousePos = Mouse.GetPosition(this);
						objectData.X = mousePos.X - canvasSize.Width / 2;
						objectData.Y = mousePos.Y - canvasSize.Height / 2;
						objectData.ObjectCode = objectData.ObjectCodePrefix + Convert.ToString(info.Data.GetNextCodeNum(objectType));
						info.InvokeDataChanging(this, "Create Object");
						if (info.CreateMode == CreateMode.Course && info.SelectedCourse != null) {
							info.SelectedCourse.AddObject((OBaseObject)objectData);
						} else {
							info.Data.AddObject((OBaseObject)objectData);
						}
						OBaseObject tempObject;
						if (info.SelectedCourse != null && info.CourseViewType == CourseViewType.Preview) {
							int pos = info.SelectedCourse.OrderedObjects.Count;
							if (prevSelectedObjects.Count > 0) {
								for (int i = 0; i < info.SelectedCourse.OrderedObjects.Count; i++) {
									if (info.SelectedCourse.OrderedObjects[i].MatchesObject(prevSelectedObjects[prevSelectedObjects.Count - 1])) {
										pos = i + 1;
									}
								}
							}
							tempObject = info.SelectedCourse.GetObjectRef((OBaseObject)objectData);
							info.SelectedCourse.InsertOrderedObject(pos, tempObject);
						} else {
							tempObject = (OBaseObject)objectData;
						}
						info.InvokeDataChanged(this, "Create Object");
						info.InvokeObjectsCreated(this);
						creatingObject = tempObject;
						isCreating = true;
						Mouse.Capture(this);
					}
				} else {
					if ((info.CreateMode == CreateMode.Course && info.SelectedCourse != null) || info.CreateMode == CreateMode.Normal) {
						if (info.CourseViewType != CourseViewType.Preview || info.SelectedTool == ToolType.MeasureDistance || info.SelectedCourse == null) {
							if (info.DrawingMode == DrawingMode.Freehand || info.DrawingMode == DrawingMode.Line) {
								isDrawing = true;
								hasMoved = false;
								Point mousePos = Mouse.GetPosition(this);
								drawingStart = mousePos;
								lastFreehandPoint = drawingStart;
								Mouse.Capture(this);
							}
						}
					}
				}
			}
		}

		protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) {
			base.OnMouseLeftButtonUp(e);
			if (isSelecting || isCreating || isRotating || isDrawing) {
				Mouse.Capture(null);
				return;
			}
		}

		protected OObjectVisual FindObject(OBaseObject objectData) {
			for (int i = 0; i < oObjects.Count; i++) {
				if (oObjects[i].ObjectData.MatchesObject(objectData)) {
					return oObjects[i];
				}
			}
			return null;
		}

		protected OObjectVisual FindSelectedObject(OBaseObject objectData) {
			for (int i = 0; i < selectedObjects.Count; i++) {
				if (selectedObjects[i].ObjectData.MatchesObject(objectData)) {
					return selectedObjects[i];
				}
			}
			return null;
		}

		protected override void OnMouseMove(MouseEventArgs e) {
			base.OnMouseMove(e);
			if (isPanning) {
				Point mousePos = e.GetPosition(ScrollContainer);
				if (!hasMoved) {
					Vector moved = Point.Subtract(mousePos, dragStart);
					if (moved.Length > 1) {//
						hasMoved = true;
					}
				}
				if (hasMoved) {
					Vector moved = Point.Subtract(mousePos, dragStart);
					Scroll(-moved.X, -moved.Y);
					dragStart = mousePos;
				}
			}
			if (isSelecting) {
				double x, y, w, h;
				Point mousePos = Mouse.GetPosition(this);
				x = selectStart.X;
				y = selectStart.Y;
				w = mousePos.X - x;
				h = mousePos.Y - y;
				if (w < 0) {
					x += w;
					w = (-w);
				}
				if (h < 0) {
					y += h;
					h = (-h);
				}
				selectingRect.SetRect(new Rect(x, y, w, h));
			}
			if (isRotating) {
				if (info.SelectedObjects.Count > 0) {
					IRotatableObject oObjectData = (IRotatableObject)info.SelectedObjects[0].GetObject(typeof(IRotatableObject));
					if (oObjectData != null) {
						double angle = GetAngle(oObjectData.GetCentre()) - rotateStart;
						if (!hasRotated) {
							if (angle != oObjectData.Rotation) {
								hasRotated = true;
								info.InvokeDataChanging(this, "Rotate Object");
							}
						}
						if (hasRotated) {
							oObjectData.Rotation = angle;
							OObjectVisual objectVisual = FindSelectedObject(info.SelectedObjects[0]);
							if (objectVisual != null) {
								objectVisual.UpdateObject(UpdateType.Rotate);
							}
						}
					} else {
						isRotating = false;
						hasRotated = false;
					}
				} else {
					isRotating = false;
					hasRotated = false;
				}
			}
			if (isDrawing && info.DrawingMode == DrawingMode.Freehand) {
				Point mousePos = Mouse.GetPosition(this);
				if (!hasMoved) {
					if (mousePos.X != drawingStart.X || mousePos.Y != drawingStart.Y) {
						ODrawingObject objectData = null;
						Type objectType = null;
						OCodeContainer parent = null;
						if (info.CreateMode == CreateMode.Normal) {
							parent = info.Data;
						} else {
							parent = info.SelectedCourse;
						}
						switch (info.SelectedTool) {
							case ToolType.CreateLine:
								objectData = new OLine(parent);
								objectType = typeof(OLine);
								break;
							case ToolType.CreateTapedRoute:
								objectData = new OTapedRoute(parent);
								objectType = typeof(OTapedRoute);
								break;
							case ToolType.CreateOOBArea:
								objectData = new OOOBArea(parent);
								objectType = typeof(OOOBArea);
								break;
							case ToolType.CreateAreaBlock:
								objectData = new OAreaBlock(parent);
								objectType = typeof(OAreaBlock);
								break;
							case ToolType.CreateUncrossableBoundary:
								objectData = new OUncrossableBoundary(parent);
								objectType = typeof(OUncrossableBoundary);
								break;
							case ToolType.MeasureDistance:
								objectData = new ODistanceMeasure(parent);
								objectType = typeof(ODistanceMeasure);
								break;
						}
						if (objectData != null && parent != null) {
							if (info.SelectedTool != ToolType.MeasureDistance) {
								info.InvokeDataChanging(this, "Create Object");
							}
							hasMoved = true;
							objectData.DrawingMode = DrawingMode.Freehand;
							objectData.ObjectCode = objectData.ObjectCodePrefix + Convert.ToString(parent.GetNextCodeNum(objectType));
							objectData.SetStartPoint(new Point(drawingStart.X - canvasSize.Width / 2, drawingStart.Y - canvasSize.Height / 2));
							objectData.AddLineSegment(new Point(mousePos.X - canvasSize.Width / 2, mousePos.Y - canvasSize.Height / 2));
							lastFreehandPoint = new Point(mousePos.X - canvasSize.Width / 2, mousePos.Y - canvasSize.Height / 2);
							drawingObject = objectData;
							if (info.SelectedTool != ToolType.MeasureDistance) {
								if (info.CreateMode == CreateMode.Course && info.SelectedCourse != null) {
									info.SelectedCourse.AddObject(objectData);
								} else {
									info.Data.AddObject(objectData);
								}
								info.InvokeObjectsCreated(this);
							} else {
								distanceObject = CreateDistanceObject(drawingObject);
							}
						} else {
							isDrawing = false;
							hasMoved = false;
							Mouse.Capture(null);
						}
					}
				} else {
					Point newPoint = new Point(mousePos.X - canvasSize.Width / 2, mousePos.Y - canvasSize.Height / 2);
					Vector vector = Point.Subtract(newPoint, lastFreehandPoint);
					if (vector.Length > 5/info.ZoomLevel) {//
						lastFreehandPoint = newPoint;
						drawingObject.AddLineSegment(lastFreehandPoint);
						OObjectVisual objectVisual = FindObject(drawingObject);
						if (objectVisual != null) {
							objectVisual.UpdateObject(UpdateType.FreehandDrawing);
						}
					}
				}
			}
			if (drawingObject != null && info.DrawingMode == DrawingMode.Line) {
				Point mousePos = Mouse.GetPosition(this);
				PathSegmentCollection segments = drawingSegments;
				if (drawingPreview == null || segments == null) {
					System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
					path.Stroke = Brushes.Black;
					path.StrokeThickness = 1;
					PathGeometry geometry = new PathGeometry();
					PathFigure figure = new PathFigure();
					segments = new PathSegmentCollection();
					figure.StartPoint = new Point(0, 0);
					figure.Segments = segments;
					geometry.Figures.Add(figure);
					path.Data = geometry;
					Canvas.SetLeft(path, lastDrawingPoint.X + CanvasWidth / 2);
					Canvas.SetTop(path, lastDrawingPoint.Y + CanvasHeight / 2);
					drawingPreview = path;
					path.IsHitTestVisible = false;
					oObjectContainer.Children.Add(path);
					drawingSegments = segments;
				}
				QuadraticBezierSegment bezierSegment = drawingPreviewSegment as QuadraticBezierSegment;
				LineSegment lineSegment = drawingPreviewSegment as LineSegment;
				if (Mouse.LeftButton == MouseButtonState.Released) {
					if (drawingPreviewSegment == null || lineSegment == null) {
						lineSegment = new LineSegment(new Point(mousePos.X - canvasSize.Width / 2 - lastDrawingPoint.X, mousePos.Y - canvasSize.Height / 2 - lastDrawingPoint.Y), true);
						segments.Clear();
						segments.Add(lineSegment);
						drawingPreviewSegment = lineSegment;
					} else {
						lineSegment.Point = new Point(mousePos.X - canvasSize.Width / 2 - lastDrawingPoint.X, mousePos.Y - canvasSize.Height / 2 - lastDrawingPoint.Y);
					}
				} else {
					if (drawingPreviewSegment == null || bezierSegment == null) {
						bezierSegment = new QuadraticBezierSegment(new Point(mousePos.X - canvasSize.Width / 2 - lastDrawingPoint.X, mousePos.Y - canvasSize.Height / 2 - lastDrawingPoint.Y), new Point(drawingStart.X - canvasSize.Width / 2 - lastDrawingPoint.X, drawingStart.Y - canvasSize.Height / 2 - lastDrawingPoint.Y), true);
						segments.Clear();
						segments.Add(bezierSegment);
						drawingPreviewSegment = bezierSegment;
					} else {
						bezierSegment.Point1 = new Point(mousePos.X - canvasSize.Width / 2 - lastDrawingPoint.X, mousePos.Y - canvasSize.Height / 2 - lastDrawingPoint.Y);
						bezierSegment.Point2 = new Point(drawingStart.X - canvasSize.Width / 2 - lastDrawingPoint.X, drawingStart.Y - canvasSize.Height / 2 - lastDrawingPoint.Y);
					}
				}
			}
		}

		private void ObjectLostMouseCapture(object sender, MouseEventArgs e) {
			OObjectVisual oObject = (OObjectVisual)sender;
			if (isDragging) {
				if (hasMoved) {
					IMapObject oObjectData = (IMapObject)oObject.ObjectData.GetObject(typeof(IMapObject));
					if (oObjectData == null) {
						isDragging = false;
						hasMoved = false;
						return;
					}
					Point dragObjectCentre = oObjectData.GetCentre();
					Point mousePos = Mouse.GetPosition(this);
					if (mousePos.X < 0) {
						mousePos.X = 0;
					}
					if (mousePos.Y < 0) {
						mousePos.Y = 0;
					}
					if (mousePos.X > canvasSize.Width) {
						mousePos.X = canvasSize.Width;
					}
					if (mousePos.Y > canvasSize.Height) {
						mousePos.Y = canvasSize.Height;
					}
					bool updateClip = false;
					for (int i = 0; i < selectedObjects.Count; i++) {
						IMapObject obj = (IMapObject)selectedObjects[i].ObjectData.GetObject(typeof(IMapObject));
						if (obj == null) {
							continue;
						}
						obj.X = mousePos.X - dragStart.X + obj.X - dragObjectCentre.X;
						obj.Y = mousePos.Y - dragStart.Y + obj.Y - dragObjectCentre.Y;
						if (selectedObjects[i].ObjectData.GetObject(typeof(OTransparentArea)) != null) {
							updateClip = true;
						}
					}
					info.InvokeDataChanged(this, "Move Object");
					info.InvokeObjectsUpdated(this, UpdateType.Move, new List<OBaseObject>(info.SelectedObjects), true);
					if (updateClip) {
						UpdateClip();
					}
					hasMoved = false;
				}
				isDragging = false;
				UpdateCursor();
			}
		}

		private void ObjectMouseDown(object sender, MouseButtonEventArgs e) {
			OObjectVisual oObject = (OObjectVisual)sender;
			if (info.SelectedTool != ToolType.Select && oObject.IsSelected != true) {
				return;
			}
			IMapObject oObjectData = (IMapObject)oObject.ObjectData.GetObject(typeof(IMapObject));
			if (oObjectData == null) {
				return;
			}
			if (!oObject.IsSelected) {
				lastSelectedObject = null;
				if (info.SelectedCourse != null) {
					for (int i = 0; i < info.SelectedCourse.OrderedObjects.Count; i++) {
						if (info.SelectedObjects.Contains(info.SelectedCourse.OrderedObjects[i])) {
							lastSelectedObject = info.SelectedCourse.OrderedObjects[i];
						}
					}
				}
				info.SelectedObjects.Clear();
				info.SelectedObjects.Add(oObject.ObjectData);
				info.InvokeObjectsSelected(this);
			}
			if (e.ButtonState != MouseButtonState.Pressed) {
				return;
			}
			Point mousePos = Mouse.GetPosition(this);
			dragStart = new Point(mousePos.X - oObjectData.X, mousePos.Y - oObjectData.Y);
			isDragging = true;
			Mouse.Capture(oObject);
			SetArrowCursor();
		}

		private void ObjectMouseMove(object sender, MouseEventArgs e) {
			OObjectVisual oObject = (OObjectVisual)sender;
			if (isDragging) {
				IMapObject oObjectData = (IMapObject)oObject.ObjectData.GetObject(typeof(IMapObject));
				if (oObjectData == null) {
					isDragging = false;
					hasMoved = false;
					Mouse.Capture(null);
					return;
				}
				Point dragObjectCentre = oObjectData.GetCentre();
				Point mousePos = Mouse.GetPosition(this);
				if (mousePos.X < 0) {
					mousePos.X = 0;
				}
				if (mousePos.Y < 0) {
					mousePos.Y = 0;
				}
				if (mousePos.X > canvasSize.Width) {
					mousePos.X = canvasSize.Width;
				}
				if (mousePos.Y > canvasSize.Height) {
					mousePos.Y = canvasSize.Height;
				}
				if (!hasMoved) {
					Vector moved = Point.Subtract(mousePos, new Point(dragObjectCentre.X+dragStart.X,dragObjectCentre.Y+dragStart.Y));
					if (moved.Length > 4.0 / info.ZoomLevel) {//
						info.InvokeDataChanging(this, "Move Object");
						hasMoved = true;
					}
				}
				if (hasMoved) {
					List<IMapObject> movedObjects = new List<IMapObject>();
					for (int i = 0; i < selectedObjects.Count; i++) {
						IMapObject obj = (IMapObject)selectedObjects[i].ObjectData.GetObject(typeof(IMapObject));
						if (obj == null) {
							continue;
						}
						if (movedObjects.Contains(obj)) {
							continue;
						}
						obj.X = mousePos.X - dragStart.X + obj.X - dragObjectCentre.X;
						obj.Y = mousePos.Y - dragStart.Y + obj.Y - dragObjectCentre.Y;
						movedObjects.Add(obj);
					}
					UpdateObjects(UpdateType.Move);
				}
			}
		}

		private void ObjectMouseUp(object sender, MouseButtonEventArgs e) {
			if (isDragging) {
				Mouse.Capture(null);
			}
		}

		void ObjectCustomDoubleClick(object sender) {
			if (info.SelectedTool == ToolType.Select) {
				OObjectVisual oObject = (OObjectVisual)sender;
				OBaseObject obj = oObject.ObjectData;
				if (info.SelectedCourse == null) {
					return;
				}
				ICourseObject courseObj = (ICourseObject)obj.GetObject(typeof(ICourseObject));
				if (courseObj != null) {
					info.InvokeDataChanging(this, "Add Course Object");
					OObjectRef objRef = info.SelectedCourse.GetObjectRef(obj);
					int pos = info.SelectedCourse.OrderedObjects.Count;
					if (lastSelectedObject != null) {
						for (int i = 0; i < info.SelectedCourse.OrderedObjects.Count; i++) {
							if (info.SelectedCourse.OrderedObjects[i].MatchesObject(lastSelectedObject)) {
								pos = i + 1;
							}
						}
					}
					if (courseObj.DefaultObjectType == CourseObjectType.Ordered) {
						info.SelectedCourse.InsertOrderedObject(pos, objRef);
					} else {
						info.SelectedCourse.AddFixedObject(objRef);
					}
					info.SelectedObjects.Clear();
					info.SelectedObjects.Add(objRef);
					info.InvokeDataChanged(this, "Add Course Object");
					info.InvokeObjectsCreated(this);
				}
			}
		}

		public void DeleteSelectedObjects() {
			List<OObjectVisual> temp = new List<OObjectVisual>(selectedObjects);
			DeleteObjects(temp);
		}

		public void HandleKeyDownEvent(object sender, KeyEventArgs e) {
			if (e.Key == Key.Delete) {
				DeleteSelectedObjects();
			}
		}

		public void HandlePreviewKeyDownEvent(object sender, KeyEventArgs e) {
			if (e.Key == Key.Right) {
				List<OBaseObject> tempList = new List<OBaseObject>();
				for (int i = 0; i < info.SelectedObjects.Count; i++) {
					IMapObject mapObj = (IMapObject)info.SelectedObjects[i].GetObject(typeof(IMapObject));
					if (mapObj != null) {
						if (tempList.Count == 0 && e.IsRepeat == false) {
							info.InvokeDataChanging(this, "Move Object");
						}
						mapObj.X += 1;
						tempList.Add(info.SelectedObjects[i]);
					}
				}
				if (tempList.Count > 0) {
					info.InvokeObjectsUpdated(this, UpdateType.Move, tempList, false);
					e.Handled = true;
				}
			}
			if (e.Key == Key.Left) {
				List<OBaseObject> tempList = new List<OBaseObject>();
				for (int i = 0; i < info.SelectedObjects.Count; i++) {
					IMapObject mapObj = (IMapObject)info.SelectedObjects[i].GetObject(typeof(IMapObject));
					if (mapObj != null) {
						if (tempList.Count == 0 && e.IsRepeat == false) {
							info.InvokeDataChanging(this, "Move Object");
						}
						mapObj.X -= 1;
						tempList.Add(info.SelectedObjects[i]);
					}
				}
				if (tempList.Count > 0) {
					info.InvokeObjectsUpdated(this, UpdateType.Move, tempList, false);
					e.Handled = true;
				}
			}
			if (e.Key == Key.Up) {
				List<OBaseObject> tempList = new List<OBaseObject>();
				for (int i = 0; i < info.SelectedObjects.Count; i++) {
					IMapObject mapObj = (IMapObject)info.SelectedObjects[i].GetObject(typeof(IMapObject));
					if (mapObj != null) {
						if (tempList.Count == 0 && e.IsRepeat == false) {
							info.InvokeDataChanging(this, "Move Object");
						}
						mapObj.Y -= 1;
						tempList.Add(info.SelectedObjects[i]);
					}
				}
				if (tempList.Count > 0) {
					info.InvokeObjectsUpdated(this, UpdateType.Move, tempList, false);
					e.Handled = true;
				}
			}
			if (e.Key == Key.Down) {
				List<OBaseObject> tempList = new List<OBaseObject>();
				for (int i = 0; i < info.SelectedObjects.Count; i++) {
					IMapObject mapObj = (IMapObject)info.SelectedObjects[i].GetObject(typeof(IMapObject));
					if (mapObj != null) {
						if (tempList.Count == 0 && e.IsRepeat == false) {
							info.InvokeDataChanging(this, "Move Object");
						}
						mapObj.Y += 1;
						tempList.Add(info.SelectedObjects[i]);
					}
				}
				if (tempList.Count > 0) {
					info.InvokeObjectsUpdated(this, UpdateType.Move, tempList, false);
					e.Handled = true;
				}
			}
		}

		public void HandlePreviewKeyUpEvent(object sender, KeyEventArgs e) {
			if ((e.Key == Key.Down || e.Key == Key.Up || e.Key == Key.Left || e.Key == Key.Right) && info.SelectedObjects.Count>0) {
				info.InvokeDataChanged(this, "Move Object");
				info.InvokeObjectsUpdated(this, UpdateType.Move, new List<OBaseObject>(info.SelectedObjects), true);
			}
		}

		private void LoadData() {
			if (info.Data == null) {
				Clear(true);
				return;
			}
			if (currentMapFile != null) {
				if (info.Data.MapFileAbsolute == currentMapFile) {
					Clear(false);
				} else {
					Clear(true);
				}
			} else {
				Clear(true);
			}
			if (info.Data == null) {
				return;
			}
			CustomResult customResult = LoadMapFile(info.Data.MapFileAbsolute);
			DrawObjects();
			UpdateCursor();
			if (customResult.Success == false) {
				ErrorManager errorManager = new ErrorManager();
				errorManager.AddError(customResult.Message);
				errorManager.ShowErrors(OPlannerHelper.MainWindow);
			}
		}

		private void DrawObjects() {
			ClearObjects();
			if (info.Data == null) {
				return;
			}
			if (info.CourseViewType == CourseViewType.HideAll) {
				return;
			}
			List<OBaseObject> tempList = new List<OBaseObject>();
			if (info.SelectedCourse == null) {
				for (int i = 0; i < info.Data.AllControls.Count; i++) {
					tempList.Add(info.Data.AllControls[i]);
				}
				for (int i = 0; i < info.Data.OObjects.Count; i++) {
					if (info.Data.OObjects[i].GetObject(typeof(IMapObject)) == null) {
						continue;
					}
					if (info.Data.AllControlsContains(info.Data.OObjects[i])) {
						continue;
					}
					if (info.CourseViewType == CourseViewType.Preview) {
						if (info.Data.OObjects[i].GetObject(typeof(OCourseDescriptions)) != null) {
							continue;
						}
						IGlobalObject globalObj = (IGlobalObject)info.Data.OObjects[i].GetObject(typeof(IGlobalObject));
						IAllControls allObj = (IAllControls)info.Data.OObjects[i].GetObject(typeof(IAllControls));
						if (allObj == null) {
							if (globalObj == null) {
								continue;
							}
							if (globalObj.DisplayGlobally == false) {
								continue;
							}
						}
					}
					tempList.Add(info.Data.OObjects[i]);
				}
			} else {
				Course course = info.SelectedCourse;
				for (int i = 0; i < info.Data.OObjects.Count; i++) {
					if (info.Data.OObjects[i].GetObject(typeof(IMapObject)) == null) {
						continue;
					}
					if (info.CourseViewType == CourseViewType.Preview) {
						IGlobalObject globalObj = (IGlobalObject)info.Data.OObjects[i].GetObject(typeof(IGlobalObject));
						if (globalObj == null) {
							continue;
						}
						if (globalObj.DisplayGlobally == false) {
							continue;
						}
					}
					if (course.Contains(info.Data.OObjects[i])) {
						continue;
					}
					tempList.Add(info.Data.OObjects[i]);
				}
				for (int i = 0; i < course.OObjects.Count; i++) {
					if (course.OObjects[i].GetObject(typeof(IMapObject)) == null) {
						continue;
					}
					tempList.Add(course.OObjects[i]);
				}
			}
			for (int i = 0; i < tempList.Count; i++) {
				OObjectVisual obj = CreateObject(tempList[i]);
			}
			UpdateObjects(UpdateType.Redraw);
			UpdateSelectedObjects();
			UpdateClip();
		}

		private void Clear(bool removeMap) {
			if (removeMap) {
				RemoveMap();
			}
			ClearObjects();
			SetArrowCursor();
		}

		private void ClearObjects() {
			ClearSelected();
			for (int i = 0; i < oObjects.Count; i++) {
				oObjects[i].CleanUp();
			}
			oObjectContainer.Children.Clear();
			oObjects.Clear();
			isDragging = false;
			isSelecting = false;
			isCreating = false;
			creatingObject = null;
			distanceObject = null;
			ClearClip();
		}

		private void ClearSelected() {
			for (int i = 0; i < selectedObjects.Count; i++) {
				selectedObjects[i].Deselect();
			}
			selectedObjects.Clear();
			if (distanceObject != null) {
				oObjects.Remove(distanceObject);
				oObjectContainer.Children.Remove(distanceObject);
				distanceObject = null;
			}
		}

		private void UpdateSelectedObjects() {
			ClearSelected();
			for (int i = 0; i < info.SelectedObjects.Count; i++) {
				for (int j = 0; j < oObjects.Count; j++) {
					if (oObjects[j].ObjectData.MatchesObject(info.SelectedObjects[i])) {
						oObjects[j].Select();
						selectedObjects.Add(oObjects[j]);
					}
				}
				if (info.SelectedObjects[i] is ODistanceMeasure && distanceObject == null) {
					distanceObject = CreateDistanceObject(info.SelectedObjects[i]);
				}
			}
		}

		public void UpdateCursor() {
			if (info.CreateMode != CreateMode.None) {
				Cursor = Cursors.Cross;
			} else if (info.SelectedTool == ToolType.Rotate) {
				Cursor = cursorManager.RotateCursor;
			} else {
				Cursor = Cursors.Arrow;
			}
		}

		public void SetArrowCursor() {
			Cursor = Cursors.Arrow;
		}

		public void AddOObjectVisual(UIElement visual) {
			oObjectContainer.Children.Add(visual);
		}

		public void RemoveOObjectVisual(UIElement visual) {
			oObjectContainer.Children.Remove(visual);
		}

		public void ClearClip() {
			oObjectContainer.Clip = null;
		}

		public void UpdateClip() {
			if (info.CourseViewType == CourseViewType.Outline) {
				oObjectContainer.Clip = null;
				return;
			}
			Geometry clipGeometry = new RectangleGeometry(new Rect(0, 0, CanvasWidth, CanvasHeight));
			bool needsClip = false;
			for (int i = 0; i < oObjects.Count; i++) {
				OTransparentArea obj = (OTransparentArea)oObjects[i].ObjectData.GetObject(typeof(OTransparentArea));
				if (obj == null) {
					continue;
				}
				Point centre = obj.GetCentre();
				centre.Offset(CanvasWidth / 2, CanvasHeight / 2);
				EllipseGeometry ellipse = new EllipseGeometry(centre, obj.Size, obj.Size);
				clipGeometry = Geometry.Combine(clipGeometry, ellipse, GeometryCombineMode.Exclude, null);
				needsClip = true;
			}
			if (needsClip == false) {
				oObjectContainer.Clip = null;
			} else {
				oObjectContainer.Clip = clipGeometry;
			}
		}

	}

}
