using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace OPlanner {

	public class ODrawingObjectVisual : OObjectVisual {

		List<LinePointAdorner> adorners;
		bool isPointDragging;
		bool hasPointMoved;
		Point dragPointStart;

		public ODrawingObjectVisual() {
			adorners = new List<LinePointAdorner>();
			isPointDragging = false;
			hasPointMoved = false;
		}

		public override void UpdateObject(UpdateType updateType) {
			base.UpdateObject(updateType);
			if (updateType == UpdateType.Drawing || updateType == UpdateType.FreehandDrawing) {
				ODrawingObject drawingObject = (ODrawingObject)ObjectData.GetObject(typeof(ODrawingObject));
				if (drawingObject == null) {
					return;
				}
				Path path = NormalVisual as Path;
				if (path != null) {
					path.Data = drawingObject.Path.GetGeometry();
				} else {
				HatchedArea hatchedArea = NormalVisual as HatchedArea;
					if (hatchedArea != null) {
						hatchedArea.Data = drawingObject.Path.GetGeometry();
					}
				}
				Path outline = OutlineVisual as Path;
				if (outline != null) {
					outline.Data = drawingObject.Path.GetGeometry();
				}
			}
		}

		protected override void DrawObject() {
			base.DrawObject();
			DrawAdorners();
		}

		void ClearAdorners() {
			AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
			if (adornerLayer == null) {
				return;
			}
			for (int i = 0; i < adorners.Count; i++) {
				adornerLayer.Remove(adorners[i]);
			}
			adorners.Clear();
		}

		void DrawAdorners() {
			ClearAdorners();
			if (info.CourseViewType == CourseViewType.Preview || IsSelected == false) {
				return;
			}
			ODrawingObject drawingObject = (ODrawingObject)ObjectData.GetObject(typeof(ODrawingObject));
			if (drawingObject == null) {
				return;
			}
			AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
			if (adornerLayer == null) {
				return;
			}
			if (drawingObject.Path.Segments.Count == 0) {
				return;
			}
			LinePointAdorner adorner;
			adorner = new LinePointAdorner(this);
			adorner.SegmentNum = 0;
			adorner.CustomPath = drawingObject.Path;
			adorner.PointType = PointType.Start;
			adornerLayer.Add(adorner);
			adorner.MouseLeftButtonDown += new MouseButtonEventHandler(AdornerMouseDown);
			adorner.MouseLeftButtonUp += new MouseButtonEventHandler(AdornerMouseUp);
			adorner.MouseMove += new MouseEventHandler(AdornerMouseMove);
			adorner.LostMouseCapture += new MouseEventHandler(AdornerLostMouseCapture);
			adorners.Add(adorner);
			for (int i = 0; i < drawingObject.Path.Segments.Count; i++) {
				if (drawingObject.Path.Segments[i] is CustomQuadraticBezierSegment) {
					CustomQuadraticBezierSegment quadratic = (CustomQuadraticBezierSegment)drawingObject.Path.Segments[i];
					adorner = new LinePointAdorner(this);
					adorner.SegmentNum = i;
					adorner.CustomPath = drawingObject.Path;
					adorner.PointType = PointType.Control;
					adorner.MouseLeftButtonDown += new MouseButtonEventHandler(AdornerMouseDown);
					adorner.MouseLeftButtonUp += new MouseButtonEventHandler(AdornerMouseUp);
					adorner.MouseMove += new MouseEventHandler(AdornerMouseMove);
					adorner.LostMouseCapture += new MouseEventHandler(AdornerLostMouseCapture);
					adornerLayer.Add(adorner);
					adorners.Add(adorner);
				}
				adorner = new LinePointAdorner(this);
				adorner.SegmentNum = i;
				adorner.CustomPath = drawingObject.Path;
				adorner.PointType = PointType.End;
				adorner.MouseLeftButtonDown += new MouseButtonEventHandler(AdornerMouseDown);
				adorner.MouseLeftButtonUp += new MouseButtonEventHandler(AdornerMouseUp);
				adorner.MouseMove += new MouseEventHandler(AdornerMouseMove);
				adorner.LostMouseCapture += new MouseEventHandler(AdornerLostMouseCapture);
				adornerLayer.Add(adorner);
				adorners.Add(adorner);
			}
		}

		public override void CleanUp() {
			base.CleanUp();
			ClearAdorners();
		}

		public override void Select() {
			base.Select();
			DrawAdorners();
		}

		public override void Deselect() {
			base.Deselect();
			ClearAdorners();
		}

		private void AdornerLostMouseCapture(object sender, MouseEventArgs e) {
			ODrawingObject obj = (ODrawingObject)ObjectData.GetObject(typeof(ODrawingObject));
			if (obj == null) {
				return;
			}
			LinePointAdorner adorner = (LinePointAdorner)sender;
			if (isPointDragging) {
				if (hasPointMoved) {
					Point mousePos = Mouse.GetPosition(parentCanvas);
					if (mousePos.X < 0) {
						mousePos.X = 0;
					}
					if (mousePos.Y < 0) {
						mousePos.Y = 0;
					}
					if (mousePos.X > parentCanvas.CanvasWidth) {
						mousePos.X = parentCanvas.CanvasWidth;
					}
					if (mousePos.Y > parentCanvas.CanvasHeight) {
						mousePos.Y = parentCanvas.CanvasHeight;
					}
					Point centre = GetCentre();
					if (adorner.SegmentNum == 0 && adorner.PointType == PointType.Start) {
						obj.Path.Offset(new Vector(-(mousePos.X - dragPointStart.X - centre.X), -(mousePos.Y - dragPointStart.Y - centre.Y)));
						obj.X += mousePos.X - dragPointStart.X - centre.X;
						obj.Y += mousePos.Y - dragPointStart.Y - centre.Y;
						adorner.SetPoint(new Point(0, 0));
						UpdateAdorners();
					} else {
						adorner.SetPoint(new Point(mousePos.X - dragPointStart.X - centre.X, mousePos.Y - dragPointStart.Y - centre.Y));
						adorner.InvalidateArrange();
					}
					info.InvokeDataChanged(parentCanvas, "Move Point");
					info.InvokeObjectsUpdated(parentCanvas, UpdateType.Drawing, new List<OBaseObject>(new OBaseObject[] { ObjectData }), false);
					info.InvokeObjectsUpdated(parentCanvas, UpdateType.Move, new List<OBaseObject>(new OBaseObject[] { ObjectData }), true);
					if (SelectAdorner != null) {
						SelectAdorner.InvalidateArrange();
					}
					hasPointMoved = false;
				}
				isPointDragging = false;
			}
		}

		private void AdornerMouseDown(object sender, MouseButtonEventArgs e) {
			LinePointAdorner adorner = (LinePointAdorner)sender;
			if (info.SelectedTool == ToolType.RemovePoint) {
				ODrawingObject obj = (ODrawingObject)ObjectData.GetObject(typeof(ODrawingObject));
				if (obj == null) {
					return;
				}
				bool result = obj.Path.RemovePoint(adorner.Segment, adorner.PointType, new Action(beforeRemovePoint));
				if (result == true) {
					if (adorner.SegmentNum == 0 && adorner.PointType == PointType.Start) {
						obj.X += obj.Path.Start.X;
						obj.Y += obj.Path.Start.Y;
						obj.Path.Offset(new Vector(-obj.Path.Start.X, -obj.Path.Start.Y));
					}
					info.InvokeObjectsUpdated(parentCanvas, UpdateType.Drawing, new List<OBaseObject>(new OBaseObject[] { ObjectData }), false);
					info.InvokeObjectsUpdated(parentCanvas, UpdateType.Move, new List<OBaseObject>(new OBaseObject[] { ObjectData }), true);
					info.InvokeDataChanged(parentCanvas, "Remove Point");
					DrawAdorners();
				}
				return;
			}
			Point mousePos = Mouse.GetPosition(parentCanvas);
			Point point = adorner.Centre;
			Point centre = GetCentre();
			point.X += centre.X;
			point.Y += centre.Y;
			dragPointStart = new Point(mousePos.X - point.X, mousePos.Y - point.Y);
			isPointDragging = true;
			Mouse.Capture(adorner);
			e.Handled = true;
		}

		private void AdornerMouseMove(object sender, MouseEventArgs e) {
			ODrawingObject obj = (ODrawingObject)ObjectData.GetObject(typeof(ODrawingObject));
			if (obj == null) {
				return;
			}
			LinePointAdorner adorner = (LinePointAdorner)sender;
			if (isPointDragging) {
				Point mousePos = Mouse.GetPosition(parentCanvas);
				if (mousePos.X < 0) {
					mousePos.X = 0;
				}
				if (mousePos.Y < 0) {
					mousePos.Y = 0;
				}
				if (mousePos.X > parentCanvas.CanvasWidth) {
					mousePos.X = parentCanvas.CanvasWidth;
				}
				if (mousePos.Y > parentCanvas.CanvasHeight) {
					mousePos.Y = parentCanvas.CanvasHeight;
				}
				Point point = adorner.Centre;
				Point centre = GetCentre();
				point.X += centre.X;
				point.Y += centre.Y;
				double x = point.X;
				double y = point.Y;
				if (!hasPointMoved) {
					if (mousePos.X - x != dragPointStart.X || mousePos.Y - y != dragPointStart.Y) {
						info.InvokeDataChanging(parentCanvas, "Move Point");
						hasPointMoved = true;
					}
				}
				if (hasPointMoved) {
					if (adorner.SegmentNum == 0 && adorner.PointType == PointType.Start) {
						obj.Path.Offset(new Vector(-(mousePos.X - dragPointStart.X - centre.X), -(mousePos.Y - dragPointStart.Y - centre.Y)));
						obj.X += mousePos.X - dragPointStart.X - centre.X;
						obj.Y += mousePos.Y - dragPointStart.Y - centre.Y;
						adorner.SetPoint(new Point(0, 0));
					} else {
						adorner.SetPoint(new Point(mousePos.X - dragPointStart.X - centre.X, mousePos.Y - dragPointStart.Y - centre.Y));
						adorner.InvalidateArrange();
					}
					info.InvokeObjectsUpdated(parentCanvas, UpdateType.Drawing, new List<OBaseObject>(new OBaseObject[] { ObjectData }), false);
					info.InvokeObjectsUpdated(parentCanvas, UpdateType.Move, new List<OBaseObject>(new OBaseObject[] { ObjectData }), false);
					if (SelectAdorner != null) {
						SelectAdorner.InvalidateArrange();
					}
				}
			}
		}

		private void AdornerMouseUp(object sender, MouseButtonEventArgs e) {
			if (isPointDragging) {
				Mouse.Capture(null);
				return;
			}
		}

		void UpdateAdorners() {
			for (int i = 0; i < adorners.Count; i++) {
				adorners[i].InvalidateArrange();
			}
		}

		void beforeRemovePoint() {
			info.InvokeDataChanging(parentCanvas, "Remove Point");
		}

		void beforeInsertPoint() {
			info.InvokeDataChanging(parentCanvas, "Add Point");
		}

		protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
			ODrawingObject obj = (ODrawingObject)ObjectData.GetObject(typeof(ODrawingObject));
			if (obj != null) {
				Point mousePos = Mouse.GetPosition(parentCanvas);
				Point centre = GetCentre();
				Point point = new Point(mousePos.X - centre.X, mousePos.Y - centre.Y);
				if (info.SelectedTool == ToolType.CreateNormalPoint) {
					bool result = obj.Path.InsertPoint(point, PointType.Start, info.Data.LineWidth * 5, new Action(beforeInsertPoint));
					if (result == true) {
						info.InvokeObjectsUpdated(parentCanvas, UpdateType.Drawing, new List<OBaseObject>(new OBaseObject[] { ObjectData }), false);
						info.InvokeObjectsUpdated(parentCanvas, UpdateType.Move, new List<OBaseObject>(new OBaseObject[] { ObjectData }), true);
						info.InvokeDataChanged(parentCanvas, "Add Point");
						DrawAdorners();
						e.Handled = true;
					}
				} else if (info.SelectedTool == ToolType.CreateQuadraticBezierPoint) {
					bool result = obj.Path.InsertPoint(point, PointType.Control, info.Data.LineWidth * 5, new Action(beforeInsertPoint));
					if (result == true) {
						info.InvokeObjectsUpdated(parentCanvas, UpdateType.Drawing, new List<OBaseObject>(new OBaseObject[] { ObjectData }), false);
						info.InvokeObjectsUpdated(parentCanvas, UpdateType.Move, new List<OBaseObject>(new OBaseObject[] { ObjectData }), true);
						info.InvokeDataChanged(parentCanvas, "Add Point");
						DrawAdorners();
						e.Handled = true;
					}
				}
			}
			base.OnMouseLeftButtonDown(e);
		}

	}

}
