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;
using System.Windows.Controls.Primitives;

namespace OPlanner {

    public class OObjectVisual : UserControl {

		private OBaseObject objectData;
		public OBaseObject ObjectData {
			get {
				return objectData;
			}
		}
		public bool IsSelected;
		private SelectObjectAdorner adorner;
		protected SelectObjectAdorner SelectAdorner {
			get {
				return adorner;
			}
		}
		protected OCanvas parentCanvas;
		private Line associatedLine;
		public event SenderEventHandler CustomDoubleClick;
		private bool hasClickedOnce;
		private int firstClickTime;
		private int doubleClickInterval = 500;
		protected OCurrentInfo info;
		private ContentControl mainVisual;
		protected ContentControl MainVisual {
			get {
				return mainVisual;
			}
		}
		FrameworkElement outlineVisual;
		FrameworkElement normalVisual;
		protected FrameworkElement OutlineVisual {
			get {
				return outlineVisual;
			}
		}
		protected FrameworkElement NormalVisual {
			get {
				return normalVisual;
			}
		}
		string brushColor;

		public void SetInfo(OCurrentInfo newInfo) {
			info = newInfo;
		}

		public void SetParentCanvas(OCanvas newCanvas) {
			parentCanvas = newCanvas;
		}

		public void LoadData(OBaseObject data) {
			objectData = data;
		}

		public OObjectVisual() {
			IsSelected = false;
			hasClickedOnce = false;
			firstClickTime = 0;
			MouseLeftButtonUp += new MouseButtonEventHandler(MouseLeftButtonUpHandler);
			mainVisual = new ContentControl();
			Content = mainVisual;
			brushColor = "";
		}

		void MouseLeftButtonUpHandler(object sender, MouseButtonEventArgs e) {
			if (hasClickedOnce) {
				int timeNow = Environment.TickCount;
				if (timeNow - firstClickTime <= doubleClickInterval) {
					OnCustomDoubleClick();
					hasClickedOnce = false;
					firstClickTime = 0;
				} else {
					hasClickedOnce = true;
					firstClickTime = Environment.TickCount;
				}
			} else {
				hasClickedOnce = true;
				firstClickTime = Environment.TickCount;
			}
		}

		public virtual void Select() {
			IsSelected = true;
			AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
			if (adornerLayer == null) {
				return;
			}
			adorner = new SelectObjectAdorner(this);
			adornerLayer.Add(adorner);
		}

		public virtual void Deselect() {
			IsSelected = false;
			AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
			if (adornerLayer != null && adorner != null) {
				adornerLayer.Remove(adorner);
			}
		}

		public Point GetCentre() {
			IMapObject obj = (IMapObject)objectData.GetObject(typeof(IMapObject));
			if (obj == null) {
				return new Point(double.NaN, double.NaN);
			}
			Point centre = obj.GetCentre();
			centre.X += parentCanvas.CanvasWidth / 2;
			centre.Y += parentCanvas.CanvasHeight / 2;
			return centre;
		}

		protected virtual void SetVisibility(Visibility visibility) {
			MainVisual.Visibility = visibility;
		}

		private void DrawLine() {
			IConnectableObject obj = (IConnectableObject)ObjectData.GetObject(typeof(IConnectableObject));
			if (obj == null) {
				return;
			}
			Course course = ObjectData.GetCourse();
			if (course == null) {
				return;
			}
			IAutoJoin joinObj = (IAutoJoin)ObjectData.GetObject(typeof(IAutoJoin));
			if (joinObj != null) {
				if (joinObj.AutoJoin == false) {
					if (associatedLine != null) {
						parentCanvas.Children.Remove(associatedLine);
						brushColor = "";
						associatedLine = null;
					}
					return;
				}
			}
			OBaseObject tempObject = course.GetNextConnectableObject(ObjectData);
			if (tempObject == null) {
				return;
			}
			IConnectableObject nextObject = (IConnectableObject)tempObject.GetObject(typeof(IConnectableObject));
			if (nextObject == null) {
				return;
			}
			if (associatedLine == null) {
				associatedLine = new Line();
				Canvas.SetZIndex(associatedLine, 0);
				parentCanvas.OObjectContainer.Children.Add(associatedLine);
			}
			Point? start = obj.GetLineStart(nextObject.GetLineEndTarget());
			Point? end = nextObject.GetLineEnd(obj.GetLineStartTarget());
			if (start == null || end == null) {
				parentCanvas.Children.Remove(associatedLine);
				brushColor = "";
				associatedLine = null;
				return;
			}
			if (obj.ContainsPoint(end) || nextObject.ContainsPoint(start)) {
				parentCanvas.Children.Remove(associatedLine);
				brushColor = "";
				associatedLine = null;
				return;
			}
			associatedLine.X1 = start.Value.X + parentCanvas.CanvasWidth / 2;
			associatedLine.Y1 = start.Value.Y + parentCanvas.CanvasHeight / 2;
			associatedLine.X2 = end.Value.X + parentCanvas.CanvasWidth / 2;
			associatedLine.Y2 = end.Value.Y + parentCanvas.CanvasHeight / 2;
			if (info.CourseViewType == CourseViewType.Outline) {
				associatedLine.StrokeThickness = 1;
				associatedLine.Stroke = Brushes.Black;
				brushColor = "";
				if (tempObject.GetObject(typeof(OFinish)) != null && ObjectData.GetObject(typeof(OControl)) != null && course.TapesToFinish == true) {
					associatedLine.StrokeDashArray = new DoubleCollection(new double[] { 8.0, 2.0 });
				} else {
					associatedLine.StrokeDashArray = null;
				}
			} else {
				associatedLine.StrokeThickness = info.Data.LineWidth;
				if (brushColor != info.Data.ControlColour) {
					associatedLine.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(info.Data.ControlColour));
					brushColor = info.Data.ControlColour;
				}
				if (tempObject.GetObject(typeof(OFinish)) != null && ObjectData.GetObject(typeof(OControl)) != null && course.TapesToFinish == true) {
					associatedLine.StrokeDashArray = new DoubleCollection(new double[] { 8.0 / info.Data.LineWidth, 2.0 / info.Data.LineWidth });
				} else {
					associatedLine.StrokeDashArray = null;
				}
			}
		}

		public virtual void CleanUp() {
			if (associatedLine != null) {
				parentCanvas.Children.Remove(associatedLine);
				brushColor = "";
				associatedLine = null;
			}
		}

		protected virtual void OnCustomDoubleClick() {
			if (CustomDoubleClick != null) {
				CustomDoubleClick.Invoke(this);
			}
		}

		public virtual void UpdateObject(UpdateType updateType) {
			if (updateType == UpdateType.Redraw) {
				DrawObject();
				DrawLine();
				PositionObject();
				RotateObject();
			}
			if (updateType == UpdateType.Move) {
				RotateObject();
				PositionObject();
				DrawLine();
			}
			if (updateType == UpdateType.Rotate) {
				RotateObject();
				if (adorner != null) {
					adorner.InvalidateArrange();
				}
			}
			OConnectableObjectRef objRef = (OConnectableObjectRef)ObjectData.GetObject(typeof(OConnectableObjectRef));
			if (objRef == null) {
				return;
			}
			Course course = objRef.GetCourse();
			if (course != null) {
				if (course.GetFirstMatch(objRef.TargetObject) != objRef) {
					SetVisibility(Visibility.Hidden);
				} else {
					SetVisibility(Visibility.Visible);
				}
			}
		}

		protected virtual void PositionObject() {
			IMapObject obj = (IMapObject)objectData.GetObject(typeof(IMapObject));
			if (obj == null) {
				return;
			}
			Canvas.SetLeft(this, obj.X + parentCanvas.CanvasWidth / 2);
			Canvas.SetTop(this, obj.Y + parentCanvas.CanvasHeight / 2);
		}

		protected double GetRotation() {
			IRotatableObject obj = (IRotatableObject)objectData.GetObject(typeof(IRotatableObject));
			if (obj != null) {
				return obj.Rotation;
			}
			IAutoRotate autoObj = (IAutoRotate)objectData.GetObject(typeof(IAutoRotate));
			if (autoObj != null) {
				Course course = objectData.GetCourse();
				if (course != null) {
					OBaseObject tempObject = course.GetNextConnectableObject(objectData);
					if (tempObject != null) {
						IConnectableObject nextObject = (IConnectableObject)tempObject.GetObject(typeof(IConnectableObject));
						if (nextObject != null) {
							Point nextCentre = nextObject.GetCentre();
							return autoObj.GetRotation(nextCentre);
						}
					}
				}
			}
			return 0;
		}

		protected virtual void RotateObject() {
			double rotation = GetRotation();
			if (rotation == 0) {
				return;
			}
			MainVisual.RenderTransform = new RotateTransform(rotation, 0, 0);
		}

		protected virtual void DrawObject() {
			IGetVisual obj = (IGetVisual)objectData.GetObject(typeof(IGetVisual));
			if (obj == null) {
				return;
			}
			outlineVisual = null;
			normalVisual = null;
			Grid container = new Grid();
			FrameworkElement visual = obj.GetVisual(info.CourseViewType);
			if (visual != null) {
				normalVisual = visual;
				container.Children.Add(visual);
			}
			if (info.CourseViewType == CourseViewType.Outline && ObjectData.GetCourse() != null) {
				FrameworkElement outline = obj.GetOutlineVisual();
				if (outline != null) {
					outlineVisual = outline;
					container.Children.Add(outline);
				}
			}
			IOpacityObject opacityObj = (IOpacityObject)objectData.GetObject(typeof(IOpacityObject));
			if (opacityObj != null) {
				if (opacityObj.Opacity != 100 || MainVisual.Opacity != 1) {
					MainVisual.Opacity = opacityObj.Opacity / 100;
				}
			}
			MainVisual.Content = container;
		}

		public virtual Rect GetVisualBounds() {
			IGetVisual obj = (IGetVisual)objectData.GetObject(typeof(IGetVisual));
			if (obj == null) {
				return new Rect(RenderSize);;
			}
			double rotation = GetRotation();
			if (rotation == 0) {
				return obj.GetVisualBounds(null);
			}
			return obj.GetVisualBounds(new RotateTransform(rotation, 0, 0));
		}

    }

}