using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Windows;
using System.Collections;
using System.Windows.Media;
using System.Printing;
using System.Windows.Markup;
using System.Text.RegularExpressions;
using System.Windows.Shapes;
using System.Windows.Controls;
using OcadMapLibrary;

namespace OPlanner {

	public class OMapInfo {

		private double defaultScale;
		public double DefaultScale {
			get {
				return defaultScale;
			}
			set {
				defaultScale = value;
			}
		}
		public string DefaultScaleString {
			get {
				if (defaultScale == 0) {
					return "";
				}
				return "1:" + defaultScale.ToString();
			}
		}
		private int ocadMainVersion;
		public int OcadMainVersion {
			get {
				return ocadMainVersion;
			}
			set {
				ocadMainVersion = value;
			}
		}
		private int ocadSubVersion;
		public int OcadSubVersion {
			get {
				return ocadSubVersion;
			}
			set {
				ocadSubVersion = value;
			}
		}
		public string OcadVersionString {
			get {
				if (OcadMainVersion == 0) {
					return "";
				}
				return OcadMainVersion.ToString()+ "." + OcadSubVersion.ToString();
			}
		}
		private MapFileType mapType;
		public MapFileType MapType {
			get {
				return mapType;
			}
			set {
				mapType = value;
			}
		}

		public void Load(OMapInfo data) {
			OcadMainVersion = data.OcadMainVersion;
			OcadSubVersion = data.OcadSubVersion;
			DefaultScale = data.DefaultScale;
			MapType = data.MapType;
		}

		public void Clear() {
			DefaultScale = 0;
			OcadMainVersion = 0;
			OcadSubVersion = 0;
			MapType = MapFileType.None;
		}

	}

	public interface IRepresentable {

		object GetRepresentation();

	}

	public interface IOData {

		string XmlTag {
			get;
		}
		XmlElement GetXmlElement(XmlDocument doc);
		bool Load(XmlNode node);
		OData GetObject(Type type);

	}

	public abstract class OData : IOData {

		public abstract string XmlTag {
			get;
		}

		public virtual XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = doc.CreateElement(XmlTag);
			return node;
		}

		public virtual OData GetObject(Type type) {
			if (type.IsAssignableFrom(GetType())) {
				return this;
			}
			return null;
		}

		public abstract bool Load(XmlNode node);

	}

	public interface IBaseObject : IOData, IRepresentable {
		OCodeContainer Parent {
			get;
		}
		bool MatchesObject(OBaseObject obj);
		string ObjectCodePrefix {
			get;
		}
		string ObjectCode {
			get;
			set;
		}
		void PopulateClone(OBaseObject clone);
		OBaseObject Clone(OCodeContainer newParent);
		Course GetCourse();
		OPlannerData GetData();
	}

	public abstract class OBaseObject : OData, IBaseObject {

		public OBaseObject(OCodeContainer newParent) {
			parent = newParent;
		}

		private OCodeContainer parent;
		public OCodeContainer Parent {
			get {
				return parent;
			}
		}

		public virtual bool MatchesObject(OBaseObject obj) {
			if (obj == this) {
				return true;
			}
			return false;
		}

		public abstract string ObjectCodePrefix {
			get;
		}
		private string objectCode;
		public string ObjectCode {
			get {
				return objectCode;
			}
			set {
				objectCode = value;
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			node.SetAttribute("ObjectCode", ObjectCode);
			return node;
		}

		public override bool Load(XmlNode node) {
			XmlAttribute attribute;
			attribute = node.Attributes["ObjectCode"];
			if (attribute == null) {
				return false;
			}
			if (attribute.Value == "") {
				return false;
			}
			if (parent.GetObjectByCode(attribute.Value) != null) {
				return false;
			}
			ObjectCode = attribute.Value;
			return true;
		}

		public virtual void PopulateClone(OBaseObject clone) {
			clone.ObjectCode = ObjectCode;
		}

		public abstract OBaseObject Clone(OCodeContainer newParent);

		public abstract object GetRepresentation();

		public Course GetCourse() {
			return (Course)Parent.GetObject(typeof(Course));
		}

		public OPlannerData GetData() {
			return (OPlannerData)Parent.GetObject(typeof(OPlannerData));
		}

	}

	public abstract class OCodeContainer : OData {

		private List<KeyValuePair<Type,int>> minCodes;
		public abstract List<OBaseObject> CodeObjects {
			get;
		}

		public OCodeContainer() {
			minCodes = new List<KeyValuePair<Type, int>>();
			SetMinCodeNum(100, typeof(OControl));
		}

		public void SetMinCodeNum(int minCode, Type type) {
			for (int i = 0; i < minCodes.Count; i++) {
				if (minCodes[i].Key == type) {
					minCodes[i] = new KeyValuePair<Type, int>(minCodes[i].Key, minCode);
					return;
				}
			}
			minCodes.Add(new KeyValuePair<Type,int>(type,minCode));
		}

		public int GetMinCodeNum(Type type) {
			for (int i = 0; i < minCodes.Count; i++) {
				if (minCodes[i].Key == type) {
					return minCodes[i].Value;
				}
			}
			return 0;
		}

		public int GetNextCodeNum(Type type) {
			return GetNextCodeNum(GetMinCodeNum(type), type);
		}

		public static int GetCodeNum(string code) {
			Regex re = new Regex("0*[1-9][0-9]*");
			Match m = re.Match(code);
			if (m.Success == false) {
				return -1;
			}
			int val = Convert.ToInt32(m.Value);
			return val;
		}

		public int GetMinExistingCode(Type type) {
			bool started = false;
			int min = -1;
			foreach (OBaseObject obj in CodeObjects) {
				if (obj.GetType() == type) {
					int num = GetCodeNum(obj.ObjectCode);
					if (num < 0) {
						continue;
					}
					if (started == false) {
						min = num;
						started = true;
					} else {
						if (num < min) {
							min = num;
						}
					}
				}
			}
			return min;
		}

		public int GetNextCodeNum(int minCode, Type type) {
			int min = GetMinExistingCode(type);
			if (min < 0) {
				min = minCode;
			}
			bool done = false;
			while (!done) {
				done = true;
				foreach (OBaseObject obj in CodeObjects) {
					if (obj.GetType() == type) {
						if (obj.ObjectCode == obj.ObjectCodePrefix + Convert.ToString(min)) {
							min++;
							done = false;
							break;
						}
					}
				}
			}
			return min;
		}

		public virtual void PopulateClone(OCodeContainer clone) {
			for (int i = 0; i < minCodes.Count; i++) {
				clone.minCodes.Add(new KeyValuePair<Type, int>(minCodes[i].Key, minCodes[i].Value));
			}
		}

		public OBaseObject GetObjectByCode(string code) {
			foreach (OBaseObject obj in CodeObjects) {
				if (obj.ObjectCode == code) {
					return obj;
				}
			}
			return null;
		}

	}

	public enum AutoManual {
		Auto, Manual
	}

	public class OPlannerData : OCodeContainer {

		public List<OBaseObject> OObjects;
		public override List<OBaseObject> CodeObjects {
			get { return OObjects; }
		}
		public List<Course> Courses;
		public string MapFileAbsolute;
		public string MapFileRelative {
			get {
				return OPlannerHelper.GetRelativePath(BasePath, MapFileAbsolute);
			}
		}
		public string BasePath;
		public List<OCourseControl> AllControls;
		public AutoManual CanvasSizeMode;
		public Size ActualCanvasSize;
		public Size CanvasSize;
		private double numberOffsetDistance;
		public double NumberOffsetDistance {
			get {
				return numberOffsetDistance;
			}
			set { 
				numberOffsetDistance = value;
			}
		}
		private double numberSize;
		public double NumberSize {
			get {
				return numberSize;
			}
			set { 
				numberSize = value;
			}
		}
		private double controlSize;
		public double ControlSize {
			get {
					return controlSize;
			}
			set {
				controlSize = value;
			}
		}
		private double descriptionSize;
		public double DescriptionSize {
			get {
				return descriptionSize;
			}
			set {
				descriptionSize = value;
			}
		}
		private double lineWidth;
		public double LineWidth {
			get {
				return lineWidth;
			}
			set {
				lineWidth = value;
			}
		}
		public string ControlColour;
		public double MapScale;
		public double ScaleMapBy;
		public double CoursesPrintXOffset;
		public double CoursesPrintYOffset;
		public PageMediaSizeName? CoursesPageMediaSize;
		public PageOrientation? CoursesPageOrientation;
		public PageMediaSizeName? DescriptionsPageMediaSize;
		public PageOrientation? DescriptionsPageOrientation;
		private OMapInfo mapInfo;
		public OMapInfo MapInfo{
			get {
				return mapInfo;
			}
		}

		public OPlannerData() {
			Courses = new List<Course>();
			OObjects = new List<OBaseObject>();
			AllControls = new List<OCourseControl>();
			Clear();
		}

		public override string XmlTag {
			get {
				return "OPlannerData";
			}
		}

		public override void PopulateClone(OCodeContainer clone) {
			base.PopulateClone(clone);
			OPlannerData obj = (OPlannerData)clone;
			obj.MapFileAbsolute = MapFileAbsolute;
			obj.CanvasSize = new Size(CanvasSize.Width, CanvasSize.Height);
			obj.ActualCanvasSize = ActualCanvasSize;
			obj.CanvasSizeMode = CanvasSizeMode;
			obj.ControlColour = ControlColour;
			obj.controlSize = controlSize;
			obj.LineWidth = LineWidth;
			obj.MapScale = MapScale;
			obj.ScaleMapBy = ScaleMapBy;
			obj.numberOffsetDistance = numberOffsetDistance;
			obj.numberSize = numberSize;
			obj.DescriptionSize = DescriptionSize;
			obj.CoursesPrintXOffset = CoursesPrintXOffset;
			obj.CoursesPrintYOffset = CoursesPrintYOffset;
			obj.CoursesPageMediaSize = CoursesPageMediaSize;
			obj.CoursesPageOrientation = CoursesPageOrientation;
			obj.DescriptionsPageMediaSize = DescriptionsPageMediaSize;
			obj.DescriptionsPageOrientation = DescriptionsPageOrientation;
			obj.BasePath = BasePath;
			obj.MapInfo.Load(MapInfo);
			for (int i = 0; i < OObjects.Count; i++) {
				obj.OObjects.Add(OObjects[i].Clone(obj));
			}
			for (int i = 0; i < AllControls.Count; i++) {
				obj.AllControls.Add((OCourseControl)AllControls[i].Clone(obj));
			}
			for (int i = 0; i < Courses.Count; i++) {
				obj.Courses.Add(Courses[i].Clone(obj));
			}
		}

		public OPlannerData Clone() {
			OPlannerData clone = new OPlannerData();
			PopulateClone(clone);
			return clone;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (MapFileAbsolute != "" && MapFileAbsolute != null) {
				XmlElement map = doc.CreateElement("Map");
				map.SetAttribute("FileName", MapFileRelative);
				node.AppendChild(map);
			}
			XmlElement settings = doc.CreateElement("Settings");
			if (CanvasSizeMode == AutoManual.Manual) {
				settings.SetAttribute("CanvasSizeMode", "Manual");
			}
			if (CanvasSize.Width != OPlannerDefaults.DefaultCanvasSize.Width || CanvasSizeMode == AutoManual.Manual) {
				settings.SetAttribute("CanvasWidth", Convert.ToString(CanvasSize.Width));
			}
			if (CanvasSize.Height != OPlannerDefaults.DefaultCanvasSize.Height || CanvasSizeMode == AutoManual.Manual) {
				settings.SetAttribute("CanvasHeight", Convert.ToString(CanvasSize.Height));
			}
			if (MapScale != OPlannerDefaults.MapScale) {
				settings.SetAttribute("MapScale", Convert.ToString(MapScale));
			}
			if (ScaleMapBy != 100) {
				settings.SetAttribute("ScaleMapBy", Convert.ToString(ScaleMapBy));
			}
			if (NumberOffsetDistance != OPlannerDefaults.NumberOffsetDistance) {
				settings.SetAttribute("NumberOffsetDistance", Convert.ToString(NumberOffsetDistance));
			}
			if (NumberSize != OPlannerDefaults.NumberSize) {
				settings.SetAttribute("NumberSize", Convert.ToString(NumberSize));
			}
			if (ControlSize != OPlannerDefaults.ControlSize) {
				settings.SetAttribute("ControlSize", Convert.ToString(ControlSize));
			}
			if (ControlColour != OPlannerDefaults.ControlColour) {
				settings.SetAttribute("ControlColour", Convert.ToString(ControlColour));
			}
			if (DescriptionSize != OPlannerDefaults.DescriptionSize) {
				settings.SetAttribute("DescriptionSize", Convert.ToString(DescriptionSize));
			}
			if (LineWidth != OPlannerDefaults.LineWidth) {
				settings.SetAttribute("LineWidth", Convert.ToString(LineWidth));
			}
			if (CoursesPrintXOffset != 0) {
				settings.SetAttribute("CoursesPrintXOffset", Convert.ToString(CoursesPrintXOffset));
			}
			if (CoursesPrintYOffset != 0) {
				settings.SetAttribute("CoursesPrintYOffset", Convert.ToString(CoursesPrintYOffset));
			}
			if (CoursesPageMediaSize != null) {
				settings.SetAttribute("CoursesPageMediaSize", CoursesPageMediaSize.ToString());
			}
			if (CoursesPageOrientation != null) {
				settings.SetAttribute("CoursesPageOrientation", CoursesPageOrientation.ToString());
			}
			if (DescriptionsPageMediaSize != null) {
				settings.SetAttribute("DescriptionsPageMediaSize", DescriptionsPageMediaSize.ToString());
			}
			if (DescriptionsPageOrientation != null) {
				settings.SetAttribute("DescriptionsPageOrientation", DescriptionsPageOrientation.ToString());
			}
			if (settings.Attributes.Count > 0) {
				node.AppendChild(settings);
			}
			XmlElement objects = doc.CreateElement("OObjects");
			XmlElement obj;
			for (int i = 0; i < OObjects.Count; i++) {
				obj = OObjects[i].GetXmlElement(doc);
				objects.AppendChild(obj);
			}
			node.AppendChild(objects);
			objects = doc.CreateElement("AllControls");
			for (int i = 0; i < AllControls.Count; i++) {
				obj = AllControls[i].GetXmlElement(doc);
				objects.AppendChild(obj);
			}
			node.AppendChild(objects);
			objects = doc.CreateElement("Courses");
			for (int i = 0; i < Courses.Count; i++) {
				obj = Courses[i].GetXmlElement(doc);
				objects.AppendChild(obj);
			}
			node.AppendChild(objects);
			return node;
		}

		public override bool Load(XmlNode node) {
			XmlAttribute attribute;
			XmlNode map = node.SelectSingleNode("Map");
			if (map != null) {
				attribute = map.Attributes["FileName"];
				if (attribute != null) {
					if (attribute.Value != "") {
						MapFileAbsolute = OPlannerHelper.GetAbsolutePath(BasePath, attribute.Value);
					}
				}
			}
			XmlNode settings = node.SelectSingleNode("Settings");
			if (settings != null) {
				attribute = settings.Attributes["CanvasSizeMode"];
				if (attribute != null) {
					if (attribute.Value == "Manual") {
						CanvasSizeMode = AutoManual.Manual;
					} else {
						CanvasSizeMode = AutoManual.Auto;
					}
				}
				attribute = settings.Attributes["CanvasWidth"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						CanvasSize.Width = num;
					}
				}
				attribute = settings.Attributes["CanvasHeight"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						CanvasSize.Height = num;
					}
				}
				attribute = settings.Attributes["MapScale"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						MapScale = num;
					}
				}
				attribute = settings.Attributes["ScaleMapBy"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						ScaleMapBy = num;
						if (ScaleMapBy <= 0) {
							ScaleMapBy = 100;
						}
					}
				}
				attribute = settings.Attributes["NumberOffsetDistance"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						NumberOffsetDistance = num;
					}
				}
				attribute = settings.Attributes["NumberSize"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						NumberSize = num;
					}
				}
				attribute = settings.Attributes["ControlSize"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						ControlSize = num;
					}
				}
				attribute = settings.Attributes["ControlColour"];
				if (attribute != null) {
					try {
						ColorConverter.ConvertFromString(attribute.Value);
						ControlColour = attribute.Value;
					} catch (Exception) {
						ControlColour = OPlannerDefaults.ControlColour;
					}
				}
				attribute = settings.Attributes["DescriptionSize"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						DescriptionSize = num;
					}
				}
				attribute = settings.Attributes["LineWidth"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						LineWidth = num;
					}
				}
				attribute = settings.Attributes["CoursesPrintXOffset"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						CoursesPrintXOffset = num;
					}
				}
				attribute = settings.Attributes["CoursesPrintYOffset"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						CoursesPrintYOffset = num;
					}
				}
				attribute = settings.Attributes["CoursesPageMediaSize"];
				if (attribute != null) {
					if (Enum.IsDefined(typeof(PageMediaSizeName), attribute.Value)) {
						CoursesPageMediaSize = (PageMediaSizeName)Enum.Parse(typeof(PageMediaSizeName), attribute.Value);
					}
				}
				attribute = settings.Attributes["CoursesPageOrientation"];
				if (attribute != null) {
					if (Enum.IsDefined(typeof(PageOrientation), attribute.Value)) {
						CoursesPageOrientation = (PageOrientation)Enum.Parse(typeof(PageOrientation), attribute.Value);
					}
				}
				attribute = settings.Attributes["DescriptionsPageMediaSize"];
				if (attribute != null) {
					if (Enum.IsDefined(typeof(PageMediaSizeName), attribute.Value)) {
						DescriptionsPageMediaSize = (PageMediaSizeName)Enum.Parse(typeof(PageMediaSizeName), attribute.Value);
					}
				}
				attribute = settings.Attributes["DescriptionsPageOrientation"];
				if (attribute != null) {
					if (Enum.IsDefined(typeof(PageOrientation), attribute.Value)) {
						DescriptionsPageOrientation = (PageOrientation)Enum.Parse(typeof(PageOrientation), attribute.Value);
					}
				}
			}
			XmlNodeList nodes = node.SelectNodes("OObjects/*");
			for (int i = 0; i < nodes.Count; i++) {
				OBaseObject data = null;
				if (nodes[i].Name == "OControl") {
					data = new OControl(this);
				} else if (nodes[i].Name == "OStart") {
					data = new OStart(this);
				} else if (nodes[i].Name == "OFinish") {
					data = new OFinish(this);
				} else if (nodes[i].Name == "OCrossingPoint") {
					data = new OCrossingPoint(this);
				} else if (nodes[i].Name == "OTransparentArea") {
					data = new OTransparentArea(this);
				} else if (nodes[i].Name == "OCourseDescriptions") {
					data = new OCourseDescriptions(this);
				} else if (nodes[i].Name == "OTapedRoute") {
					data = new OTapedRoute(this);
				} else if (nodes[i].Name == "OText") {
					data = new OText(this);
				} else if (nodes[i].Name == "ODrinksPoint") {
					data = new ODrinksPoint(this);
				} else if (nodes[i].Name == "OOOBCross") {
					data = new OOOBCross(this);
				} else if (nodes[i].Name == "OOOBArea") {
					data = new OOOBArea(this);
				} else if (nodes[i].Name == "OAreaBlock") {
					data = new OAreaBlock(this);
				} else if (nodes[i].Name == "OFirstAidPoint") {
					data = new OFirstAidPoint(this);
				} else if (nodes[i].Name == "OUncrossableBoundary") {
					data = new OUncrossableBoundary(this);
				}
				if (data != null) {
					if (data.Load(nodes[i])) {
						OObjects.Add(data);
					}
				}
			}
			nodes = node.SelectNodes("AllControls/OCourseControl");
			for (int i = 0; i < nodes.Count; i++) {
				OCourseControl data = new OCourseControl(this);
				if (data.Load(nodes[i])) {
					AllControls.Add(data);
				}
			}
			for (int i = 0; i < OObjects.Count; i++) {
				if (OObjects[i] is OControl) {
					if (!AllControlsContains((OControl)OObjects[i])) {
						OCourseControl control = new OCourseControl(this);
						control.TargetObject = OObjects[i];
						control.ObjectCode = control.ObjectCodePrefix + Convert.ToString(GetNextAllControlNum());
						AllControls.Add(control);
					}
				}
			}
			nodes = node.SelectNodes("Courses/Course");
			for (int i = 0; i < nodes.Count; i++) {
				XmlNode courseNode = nodes[i];
				Course course = new Course(this);
				if (course.Load(nodes[i])) {
					Courses.Add(course);
				}
			}
			return true;
		}

		public bool AllControlsContains(OBaseObject obj) {
			for (int i = 0; i < AllControls.Count; i++) {
				if (AllControls[i].MatchesObject(obj)) {
					return true;
				}
			}
			return false;
		}

		private int GetNextAllControlNum() {
			int min = 0;
			bool done = false;
			while (!done) {
				done = true;
				foreach (OBaseObject obj in AllControls) {
					if (obj.ObjectCode == obj.ObjectCodePrefix + Convert.ToString(min)) {
						min++;
						done = false;
						break;
					}
				}
			}
			return min;
		}

		public void TotallyRenumberControls(int minNum) {
			int num = minNum;
			foreach (OBaseObject obj in OObjects) {
				OControl control = obj as OControl;
				if (control != null) {
					control.ObjectCode = Convert.ToString(num);
					num++;
				}
			}
		}

		public void ShiftControlCodes(int minNum) {
			int shift;
			int minCurrent;
			minCurrent = GetMinExistingCode(typeof(OControl));
			shift = minCurrent - minNum;
			foreach (OBaseObject obj in OObjects) {
				OControl control = obj as OControl;
				if (control != null) {
					int code;
					if (int.TryParse(control.ObjectCode, out code)) {
						control.ObjectCode = Convert.ToString(code-shift);
					}
				}
			}
		}

		public void AddObject(OBaseObject oObject) {
			OObjects.Add(oObject);
			if (oObject is OControl) {
				OCourseControl control = new OCourseControl(this);
				control.TargetObject = oObject;
				control.ObjectCode = control.ObjectCodePrefix + Convert.ToString(GetNextAllControlNum());
				AllControls.Add(control);
			}
		}

		public void RemoveObject(OBaseObject oObject) {
			for (int i = 0; i < OObjects.Count; i++) {
				if(OObjects[i].MatchesObject(oObject)){
					OObjects.RemoveAt(i);
					i--;
				}
			}
			for (int i = 0; i < AllControls.Count; i++) {
				if (AllControls[i].MatchesObject(oObject)) {
					AllControls.RemoveAt(i);
					i--;
				}
			}
			for (int i = 0; i < Courses.Count; i++) {
				Courses[i].RemoveObject(oObject);
			}
		}

		public void RemoveObjects(List<OBaseObject> objects) {
			for (int i = 0; i < objects.Count; i++) {
				RemoveObject(objects[i]);
			}
		}

		public void RemoveObjectFully(OBaseObject oObject) {
			IObjectRef objRef = (IObjectRef)oObject.GetObject(typeof(IObjectRef));
			if (objRef != null) {
				RemoveObject(objRef.TargetObject);
			} else {
				RemoveObject(oObject);
			}
		}

		public void RemoveObjectsFully(List<OBaseObject> objects) {
			for (int i = 0; i < objects.Count; i++) {
				RemoveObjectFully(objects[i]);
			}
		}

		public void WriteToFile(string fileName) {
			BasePath = fileName;
			XmlDocument doc = new XmlDocument();
			XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "utf-8", null);
			doc.AppendChild(xmlDeclaration);
			doc.AppendChild(GetXmlElement(doc));
			doc.Save(fileName);
		}

		private void Clear() {
			AllControls.Clear();
			Courses.Clear();
			OObjects.Clear();
			MapFileAbsolute = "";
			BasePath = "";
			CanvasSizeMode = AutoManual.Auto;
			CanvasSize = OPlannerDefaults.DefaultCanvasSize;
			ActualCanvasSize = CanvasSize;
			MapScale = OPlannerDefaults.MapScale;
			ScaleMapBy = 100;
			ControlSize = OPlannerDefaults.ControlSize;
			NumberSize = OPlannerDefaults.NumberSize;
			ControlColour = OPlannerDefaults.ControlColour;
			NumberOffsetDistance = OPlannerDefaults.NumberOffsetDistance;
			DescriptionSize = OPlannerDefaults.DescriptionSize;
			LineWidth = OPlannerDefaults.LineWidth;
			CoursesPrintXOffset = 0;
			CoursesPrintYOffset = 0;
			CoursesPageMediaSize = null;
			CoursesPageOrientation = null;
			DescriptionsPageMediaSize = null;
			DescriptionsPageOrientation = null;
			mapInfo = new OMapInfo();
		}

		public void LoadFromFile(string fileName) {
			Clear();
			BasePath = fileName;
			XmlDocument doc = new XmlDocument();
			try {
				doc.Load(fileName);
			} catch (Exception) {
				return;
			}
			XmlNode node = doc.SelectSingleNode("OPlannerData");
			if (node != null) {
				Load(node);
			}
		}

		public Course GetCourseByName(string name) {
			for (int i = 0; i < Courses.Count; i++) {
				if (Courses[i].Name == name) {
					return Courses[i];
				}
			}
			return null;
		}

		public bool CourseExists(string name) {
			if (name.ToLower() == "all controls") {
				return true;
			}
			for (int i = 0; i < Courses.Count; i++) {
				if (Courses[i].Name.ToLower() == name.ToLower()) {
					return true;
				}
			}
			return false;
		}

		public List<OBaseObject> GetUnusedControls() {
			List<OBaseObject> controls = new List<OBaseObject>();
			for (int i = 0; i < OObjects.Count; i++) {
				if (!(OObjects[i] is OControl)) {
					continue;
				}
				bool used = false;
				for (int j = 0; j < Courses.Count; j++) {
					if (Courses[j].Contains(OObjects[i])) {
						used = true;
						break;
					}
				}
				if (!used) {
					controls.Add(OObjects[i]);
				}
			}
			return controls;
		}

	}

	public class Course : OCodeContainer {

		public string Name;
		public double Climb;
		public double ExtraLength;
		public bool TapesToFinish;
		public List<OBaseObject> OObjects;
		public override List<OBaseObject> CodeObjects {
			get { return OObjects; }
		}
		public List<OBaseObject> OrderedObjects;
		public List<OBaseObject> FixedObjects;
		public OPlannerData Parent;

		public Course(OPlannerData newParent) {
			Name = "";
			Climb = 0;
			ExtraLength = 0;
			TapesToFinish = false;
			OObjects = new List<OBaseObject>();
			OrderedObjects = new List<OBaseObject>();
			FixedObjects = new List<OBaseObject>();
			Parent = newParent;
		}

		public OObjectRef GetObjectRef(OBaseObject target) {
			OBaseObject newTarget = target;
			if (target is OObjectRef) {
				newTarget = ((OObjectRef)target).TargetObject;
			}
			OObjectRef newObj;
			if (newTarget is OControl) {
				newObj = new OCourseControl(this);
			} else if (newTarget is IConnectableObject) {
				newObj = new OConnectableObjectRef(this);
			} else {
				newObj = new OObjectRef(this);
			}
			newObj.ObjectCode = newObj.ObjectCodePrefix + Convert.ToString(GetNextCodeNum(newObj.GetType()));
			newObj.TargetObject = newTarget;
			return newObj;
		}

		public void AddObject(OBaseObject obj) {
			ICourseObject courseObj = (ICourseObject)obj.GetObject(typeof(ICourseObject));
			if (courseObj != null) {
				if (courseObj.DefaultObjectType == CourseObjectType.Ordered) {
					AddOrderedObject(obj);
				} else {
					AddFixedObject(obj);
				}
			} else {
				AddFixedObject(obj);
			}
		}

		public OObjectRef AddObjectRef(OBaseObject target) {
			OObjectRef newObj = GetObjectRef(target);
			ICourseObject courseObj = (ICourseObject)target.GetObject(typeof(ICourseObject));
			if (courseObj != null) {
				if (courseObj.DefaultObjectType == CourseObjectType.Ordered) {
					AddOrderedObject(newObj);
				} else {
					AddFixedObject(newObj);
				}
			} else {
				AddFixedObject(newObj);
			}
			return newObj;
		}

		public void AddFixedObject(OBaseObject obj) {
			OObjects.Add(obj);
			FixedObjects.Add(obj);
		}

		public void AddOrderedObject(OBaseObject obj) {
			InsertOrderedObject(OrderedObjects.Count, obj);
		}

		public void InsertOrderedObject(int pos, OBaseObject obj) {
			if (pos < 0 || pos > OrderedObjects.Count) {
				return;
			}
			OObjects.Add(obj);
			OrderedObjects.Insert(pos, obj);
		}

		public void MoveOrderedObject(int oldPos, int newPos){
			OBaseObject temp = OrderedObjects[oldPos];
			OrderedObjects.RemoveAt(oldPos);
			OrderedObjects.Insert(newPos, temp);
		}

		public void RemoveObject(OBaseObject obj) {
			for (int i = 0; i < OObjects.Count; i++) {
				if (OObjects[i].MatchesObject(obj)) {
					OrderedObjects.Remove(OObjects[i]);
					FixedObjects.Remove(OObjects[i]);
					OObjects.RemoveAt(i);
					i--;
				}
			}
		}

		public override string XmlTag {
			get {
				return "Course";
			}
		}

		public override void PopulateClone(OCodeContainer clone) {
			base.PopulateClone(clone);
			Course obj = (Course)clone;
			obj.Name = Name;
			obj.Climb = Climb;
			obj.ExtraLength = ExtraLength;
			obj.TapesToFinish = TapesToFinish;
			for (int i = 0; i < FixedObjects.Count; i++) {
				OBaseObject newObj = FixedObjects[i].Clone(obj);
				obj.FixedObjects.Add(newObj);
				obj.OObjects.Add(newObj);
			}
			for (int i = 0; i < OrderedObjects.Count; i++) {
				OBaseObject newObj = OrderedObjects[i].Clone(obj);
				obj.OrderedObjects.Add(newObj);
				obj.OObjects.Add(newObj);
			}
		}

		public Course Clone(OPlannerData newParent) {
			Course clone = new Course(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			XmlElement subNode;
			XmlElement obj;
			subNode = doc.CreateElement("OrderedObjects");
			for (int i = 0; i < OrderedObjects.Count; i++) {
				obj = OrderedObjects[i].GetXmlElement(doc);
				subNode.AppendChild(obj);
			}
			if (subNode.ChildNodes.Count > 0) {
				node.AppendChild(subNode);
			}
			subNode = doc.CreateElement("FixedObjects");
			for (int i = 0; i < FixedObjects.Count; i++) {
				obj = FixedObjects[i].GetXmlElement(doc);
				subNode.AppendChild(obj);
			}
			if (subNode.ChildNodes.Count > 0) {
				node.AppendChild(subNode);
			}
			node.SetAttribute("Name", Name);
			if (Climb != 0) {
				node.SetAttribute("Climb", Convert.ToString(Climb));
			}
			if (ExtraLength != 0) {
				node.SetAttribute("ExtraLength", Convert.ToString(ExtraLength));
			}
			if (TapesToFinish == true) {
				node.SetAttribute("TapesToFinish", Convert.ToString(TapesToFinish));
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			XmlAttribute attribute = node.Attributes["Name"];
			if (attribute == null) {
				return false;
			}
			Name = node.Attributes["Name"].Value;
			if (Name == "") {
				return false;
			}
			attribute = node.Attributes["Climb"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					Climb = num;
				}
			}
			attribute = node.Attributes["ExtraLength"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					ExtraLength = num;
				}
			}
			attribute = node.Attributes["TapesToFinish"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					TapesToFinish = val;
				}
			}
			XmlNode subNode;
			subNode = node.SelectSingleNode("OrderedObjects");
			LoadObjects(subNode, OrderedObjects);
			subNode = node.SelectSingleNode("FixedObjects");
			LoadObjects(subNode, FixedObjects);
			return true;
		}

		private void LoadObjects(XmlNode node, List<OBaseObject> targetList) {
			if (node == null) {
				return;
			}
			XmlNodeList nodes = node.ChildNodes;
			for (int i = 0; i < nodes.Count; i++) {
				OBaseObject obj = null;
				if (nodes[i].Name == "OCourseControl") {
					obj = new OCourseControl(this);
				} else if (nodes[i].Name == "OTextDescription") {
					obj = new OTextDescription(this);
				} else if (nodes[i].Name == "OObjectRef") {
					obj = new OObjectRef(this);
				} else if (nodes[i].Name == "OConnectableObjectRef") {
					obj = new OConnectableObjectRef(this);
				} else if (nodes[i].Name == "OLine") {
					obj = new OLine(this);
				} else if (nodes[i].Name == "OTapedRoute") {
					obj = new OTapedRoute(this);
				} else if (nodes[i].Name == "OText") {
					obj = new OText(this);
				} else if (nodes[i].Name == "OInvisiblePoint") {
					obj = new OInvisiblePoint(this);
				}
				if (obj != null) {
					if (obj.Load(nodes[i])) {
						targetList.Add(obj);
						OObjects.Add(obj);
					}
				}
			}
		}

		public bool Contains(OBaseObject obj){
			for (int i = 0; i < OObjects.Count; i++) {
				if (OObjects[i].MatchesObject(obj)) {
					return true;
				}
			}
			return false;
		}

		public override OData GetObject(Type type) {
			OData obj = base.GetObject(type);
			if (obj == null) {
				return Parent.GetObject(type);
			} else {
				return obj;
			}
		}

		public int GetControlNum(OCourseControl control) {
			int num = 0;
			for (int i = 0; i < OrderedObjects.Count; i++) {
				if (OrderedObjects[i] is OCourseControl) {
					num++;
				}
				if (OrderedObjects[i] == control) {
					return num;
				}
			}
			return 0;
		}

		public string GetNumberText(OCourseControl control) {
			List<OCourseControl> controls = new List<OCourseControl>();
			for (int i = 0; i < OrderedObjects.Count; i++) {
				OCourseControl temp = (OCourseControl)OrderedObjects[i].GetObject(typeof(OCourseControl));
				if (temp == null) {
					continue;
				}
				if (temp.MatchesObject(control.TargetObject)) {
					controls.Add(temp);
				}
			}
			if (controls.Count == 0) {
				return "";
			}
			if (controls[0] != control) {
				return "";
			}
			string text = "";
			for (int i = 0; i < controls.Count; i++) {
				int num = GetControlNum(controls[i]);
				if (num == 0) {
					return "";
				}
				if (text != "") {
					text += ",";
				}
				text += num.ToString();
			}
			return text;
		}

		public OBaseObject GetNextConnectableObject(OBaseObject obj) {
			if (obj.GetObject(typeof(IConnectableObject)) == null) {
				return null;
			}
			for (int i = 0; i < OrderedObjects.Count; i++) {
				if (OrderedObjects[i] == obj) {
					for (int j = i+1; j < OrderedObjects.Count; j++) {
						if (OrderedObjects[j].GetObject(typeof(IConnectableObject)) != null) {
							return OrderedObjects[j];
						}
					}
					return null;
				}
			}
			return null;
		}

		public OBaseObject GetPreviousConnectableObject(OBaseObject obj) {
			if (obj.GetObject(typeof(IConnectableObject)) == null) {
				return null;
			}
			OBaseObject previousObject = null;
			for (int i = 0; i < OrderedObjects.Count; i++) {
				if (OrderedObjects[i] == obj) {
					return previousObject;
				}
				if (OrderedObjects[i].GetObject(typeof(IConnectableObject)) != null) {
					previousObject = OrderedObjects[i];
				}
			}
			return null;
		}

		public OBaseObject GetFirstMatch(OBaseObject obj) {
			for (int i = 0; i < OrderedObjects.Count; i++) {
				if (OrderedObjects[i].MatchesObject(obj)) {
					return OrderedObjects[i];
				}
			}
			for (int i = 0; i < FixedObjects.Count; i++) {
				if (FixedObjects[i].MatchesObject(obj)) {
					return FixedObjects[i];
				}
			}
			return null;
		}

		public List<OBaseObject> GetMatches(OBaseObject obj) {
			List<OBaseObject> list = new List<OBaseObject>();
			for (int i = 0; i < OrderedObjects.Count; i++) {
				if (OrderedObjects[i].MatchesObject(obj)) {
					list.Add(OrderedObjects[i]);
				}
			}
			for (int i = 0; i < FixedObjects.Count; i++) {
				if (FixedObjects[i].MatchesObject(obj)) {
					list.Add(FixedObjects[i]);
				}
			}
			return list;
		}

		public double GetLength() {
			return Math.Round(GetLength(0, OrderedObjects.Count - 1)/1000.0, 2);
		}

		private double GetLength(int start, int finish) {
			if (finish > OrderedObjects.Count - 1) {
				finish = OrderedObjects.Count - 1;
			}
			if (start < 0) {
				start = 0;
			}
			double length = 0;
			IConnectableObject previousObject = null;
			IConnectableObject currentObject = null;
			OLinearDrawingObject previousDrawingObject = null;
			OLinearDrawingObject currentDrawingObject = null;
			for (int i = start; i <= finish; i++) {
				currentObject = (IConnectableObject)OrderedObjects[i].GetObject(typeof(IConnectableObject));
				if (currentObject == null) {
					continue;
				}
				currentDrawingObject = (OLinearDrawingObject)currentObject.GetObject(typeof(OLinearDrawingObject));
				if (currentDrawingObject != null) {
					length += currentDrawingObject.GetLength();
				}
				if (previousObject != null) {
					previousDrawingObject = (OLinearDrawingObject)previousObject.GetObject(typeof(OLinearDrawingObject));
					Point previousCentre;
					if (previousDrawingObject == null) {
						previousCentre = previousObject.GetCentre();
					} else {
						previousCentre = previousDrawingObject.GetLineStartTarget().Value;
					}
					Point currentCentre;
					if (currentDrawingObject == null) {
						currentCentre = currentObject.GetCentre();
					} else {
						currentCentre = currentDrawingObject.GetLineEndTarget().Value;
					}
					Vector vector = Point.Subtract(currentCentre, previousCentre);
					length += vector.Length;
				}
				previousObject = currentObject;
			}
			return length / 96 * 2.54 / 100.0 * Parent.MapScale + ExtraLength;
		}

		public double GetLegLength(int legNum) {
			int finishNum = -1;
			int startNum = -1;
			int num = 0;
			int numControls = GetNumControls();
			if (legNum < 1 || legNum > numControls) {
				return 0;
			}
			for (int i = 0; i < OrderedObjects.Count; i++) {
				if (OrderedObjects[i] is OCourseControl || OrderedObjects[i].GetObject(typeof(OStart)) != null || OrderedObjects[i].GetObject(typeof(OFinish)) != null) {
					if (num == legNum-1) {
						startNum = num;
					}
					if (num == legNum) {
						finishNum = num;
						break;
					}
					num++;
				}
			}
			if (startNum < 0 || finishNum < 0) {
				return 0;
			}
			return GetLength(startNum, finishNum);
			
		}

		public int GetNumControls() {
			int num = 0;
			for (int i = 0; i < OrderedObjects.Count; i++) {
				if (OrderedObjects[i] is OCourseControl) {
					num++;
				}
			}
			return num;
		}

		public double GetDistanceToFinish() {
			double distance = 0;
			OFinish finish = null;
			int i;
			int finishNum;
			int startNum;
			for (i = OrderedObjects.Count - 1; i > 0; i--) {
				finish = (OFinish)OrderedObjects[i].GetObject(typeof(OFinish));
				if (finish != null) {
					break;
				}
			}
			if (finish == null) {
				return distance;
			}
			finishNum = i;
			OCourseControl previousControl = null;
			for (i = i-1; i >= 0; i--) {
				previousControl = (OCourseControl)OrderedObjects[i].GetObject(typeof(OCourseControl));
				if (previousControl != null) {
					break;
				}
			}
			if (previousControl == null) {
				return distance;
			}
			startNum = i;
			distance = Math.Round(GetLength(startNum, finishNum));
			return distance;
		}

		public OStart GetStart() {
			OStart start = null;
			for (int i = 0; i < OrderedObjects.Count; i++) {
				start = (OStart)OrderedObjects[i].GetObject(typeof(OStart));
				if (start != null) {
					return start;
				}
			}
			return null;
		}

		public OFinish GetFinish() {
			OFinish finish = null;
			for (int i = OrderedObjects.Count - 1; i > 0; i--) {
				finish = (OFinish)OrderedObjects[i].GetObject(typeof(OFinish));
				if (finish != null) {
					return finish;
				}
			}
			return null;
		}

	}

	public interface ICodeObject : IBaseObject {
	}
	public interface ITextDescription : IBaseObject {
		string TextDescription {
			get;
			set;
		}
	}
	public interface IText : IBaseObject, IMapObject {
		string Text {
			get;
			set;
		}
	}
	public interface ISymbolDescription : IBaseObject {
		void SetDescription(int column, string code);
		string GetDescription(int column);
		Size GetFeatureSize();
		void SetFeatureSize(double x, double y);
	}
	public interface ISymbolTextDescription : ITextDescription, ISymbolDescription {
		bool AutoGenerateText {
			get;
			set;
		}
	}
	public interface IMapObject : IBaseObject {
		double X {
			get;
			set;
		}
		double Y {
			get;
			set;
		}
		Point GetCentre();
		int Depth {
			get;
		}
		bool IsCentred {
			get;
		}
	}
	public interface IOpacityObject {
		double Opacity {
			get;
			set;
		}
	}
	public interface IAutoRotate : IMapObject {
		double GetRotation(Point? nextPoint);
	}
	public interface IRotatableObject : IMapObject {
		double Rotation {
			get;
			set;
		}
	}
	public interface IGlobalObject : IBaseObject {
		bool DisplayGlobally {
			get;
			set;
		}
	}
	public interface IConnectableObject : IMapObject {
		Point? GetLineStart(Nullable<Point> nextPoint);
		Point? GetLineEnd(Nullable<Point> previousPoint);
		Point? GetLineStartTarget();
		Point? GetLineEndTarget();
		bool ContainsPoint(Point? point);
	}
	public interface IObjectRef : IBaseObject {
		OBaseObject TargetObject {
			get;
			set;
		}
	}
	public interface IAutoJoin : IBaseObject {
		bool AutoJoin {
			get;
			set;
		}
	}
	public interface IGetVisual {
		FrameworkElement GetVisual(CourseViewType viewType);
		FrameworkElement GetOutlineVisual();
		Rect GetVisualBounds(Transform transform);
	}
	public interface IOneSizeable : IMapObject {
		double Size {
			get;
			set;
		}
	}
	public interface ICourseObject {
		bool IsFixedObject {
			get;
		}
		bool IsOrderedObject {
			get;
		}
		CourseObjectType DefaultObjectType {
			get;
		}
	}
	public interface IAllControls {
	}
	public enum CourseObjectType {
		None, Ordered, Fixed
	}

	public class OTextDescription : OBaseObject, ICodeObject, ITextDescription, ICourseObject {

		public OTextDescription(OCodeContainer newParent)
			: base(newParent) {
		}

		private string textDescription;
		public string TextDescription {
			get { return textDescription; }
			set { textDescription = value; }
		}

		public override string ObjectCodePrefix {
			get {
				return "TD";
			}
		}

		public override string XmlTag {
			get {
				return "OTextDescription";
			}
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OTextDescription obj = (OTextDescription)clone;
			obj.TextDescription = TextDescription;
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OTextDescription clone = new OTextDescription(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override object GetRepresentation() {
			string str = "Text Description";
			if (TextDescription != "" && TextDescription != null) {
				str += ": " + TextDescription;
			}
			if (str.Length > 30) {
				str = str.Substring(0, 27) + "...";
			}
			return str;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (TextDescription != null && TextDescription != "") {
				node.SetAttribute("TextDescription", TextDescription);
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["TextDescription"];
			if (attribute != null) {
				TextDescription = attribute.Value;
			}
			return true;
		}

		public virtual bool IsFixedObject {
			get { return false; }
		}

		public virtual bool IsOrderedObject {
			get { return true; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

	}

	public class OSymbolTextDescription : OTextDescription, ISymbolTextDescription {

		public OSymbolTextDescription(OCodeContainer newParent)
			: base(newParent) {
			descriptionSymbolCodes = new string[8];
			featureSize = new Size(0, 0);
		}

		private Size featureSize;

		public Size GetFeatureSize() {
			return featureSize;
		}

		public void SetFeatureSize(double x, double y) {
			featureSize.Width = x;
			featureSize.Height = y;
			descriptionSymbolCodes[5] = null;
		}

		public override string ObjectCodePrefix {
			get {
				return "D";
			}
		}
		private bool autoGenerateText;
		public bool AutoGenerateText {
			get { return autoGenerateText; }
			set { autoGenerateText = value; }
		}
		private string[] descriptionSymbolCodes;

		public void SetDescription(int column, string code) {
			if (column < 3 || column > 8) {
				return;
			}
			descriptionSymbolCodes[column - 1] = code;
			if (column == 6) {
				featureSize.Width = 0;
				featureSize.Height = 0;
			}
		}

		public string GetDescription(int column) {
			if (column < 3 || column > 8) {
				return null;
			}
			return descriptionSymbolCodes[column - 1];
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			XmlElement featureSizeNode = doc.CreateElement("FeatureSize");
			if (featureSize.Width != 0) {
				featureSizeNode.SetAttribute("X", featureSize.Width.ToString());
			}
			if (featureSize.Height != 0) {
				featureSizeNode.SetAttribute("Y", featureSize.Height.ToString());
			}
			if (featureSizeNode.Attributes.Count > 0) {
				node.AppendChild(featureSizeNode);
			}
			XmlElement descriptions = doc.CreateElement("DescriptionSymbols");
			XmlElement symbol;
			for (int i = 0; i < descriptionSymbolCodes.Length; i++) {
				if (descriptionSymbolCodes[i] == null || descriptionSymbolCodes[i] == "") {
					continue;
				}
				symbol = doc.CreateElement("DescriptionSymbol");
				symbol.SetAttribute("SymbolCode", descriptionSymbolCodes[i]);
				symbol.SetAttribute("Column", Convert.ToString(i + 1));
				descriptions.AppendChild(symbol);
			}
			if (descriptions.ChildNodes.Count > 0) {
				node.AppendChild(descriptions);
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			XmlNodeList nodes = node.SelectNodes("DescriptionSymbols/DescriptionSymbol");
			for (int i = 0; i < nodes.Count; i++) {
				attribute = nodes[i].Attributes["Column"];
				if (attribute == null) {
					continue;
				}
				int col;
				if (!int.TryParse(attribute.Value, out col)) {
					continue;
				}
				if (col < 3 || col > 8) {
					continue;
				}
				attribute = nodes[i].Attributes["SymbolCode"];
				if (attribute == null) {
					continue;
				}
				string code = attribute.Value;
				descriptionSymbolCodes[col - 1] = code;
			}
			XmlNode featureSizeNode = node.SelectSingleNode("FeatureSize");
			if (featureSizeNode != null) {
				attribute = featureSizeNode.Attributes["X"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						featureSize.Width = num;
					}
				}
				attribute = featureSizeNode.Attributes["Y"];
				if (attribute != null) {
					double num;
					if (double.TryParse(attribute.Value, out num)) {
						featureSize.Height = num;
					}
				}
			}
			return true;
		}

		public override string XmlTag {
			get {
				return "OSymbolTextDescription";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OSymbolTextDescription clone = new OSymbolTextDescription(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OSymbolTextDescription obj = (OSymbolTextDescription)clone;
			descriptionSymbolCodes.CopyTo(obj.descriptionSymbolCodes, 0);
			obj.TextDescription = TextDescription;
			obj.featureSize.Width = featureSize.Width;
			obj.featureSize.Height = featureSize.Height;
		}

	}

	public abstract class OMapDescriptionObject : OSymbolTextDescription, IMapObject {

		public OMapDescriptionObject(OCodeContainer newParent)
			: base(newParent) {
			depth = 0;
			x = 0;
			y = 0;
		}

		protected int depth;
		public int Depth {
			get { return depth; }
		}
		private double x;
		private double y;
		public double X {
			get { return x; }
			set { x = value; }
		}
		public double Y {
			get { return y; }
			set { y = value; }
		}
		public Point GetCentre() {
			return new Point(X, Y);
		}
		public virtual bool IsCentred {
			get {
				return true;
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			node.SetAttribute("X", Convert.ToString(X));
			node.SetAttribute("Y", Convert.ToString(Y));
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["X"];
			if (attribute != null) {
				double newX;
				if (double.TryParse(attribute.Value, out newX)) {
					X = newX;
				}
			}
			attribute = node.Attributes["Y"];
			if (attribute != null) {
				double newY;
				if (double.TryParse(attribute.Value, out newY)) {
					Y = newY;
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OMapDescriptionObject obj = (OMapDescriptionObject)clone;
			obj.X = X;
			obj.Y = Y;
		}

		public override object GetRepresentation() {
			return ObjectCode;
		}

	}

	public abstract class OOpacityMapDescriptionObject : OMapDescriptionObject, IOpacityObject {

		public OOpacityMapDescriptionObject(OCodeContainer newParent)
			: base(newParent) {
			opacity = DefaultOpacity;
		}

		public virtual double DefaultOpacity {
			get {
				return 100;
			}
		}
		double opacity;
		public double Opacity {
			get { return opacity; }
			set {
				opacity = value;
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (opacity != DefaultOpacity) {
				node.SetAttribute("Opacity", Convert.ToString(Opacity));
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["Opacity"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					if (num >= 0 && num <= 100) {
						Opacity = num;
					}
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OOpacityMapDescriptionObject obj = (OOpacityMapDescriptionObject)clone;
			obj.Opacity = Opacity;
		}

	}

	public abstract class OMapObject : OBaseObject, IMapObject, ICodeObject {

		public OMapObject(OCodeContainer newParent)
			: base(newParent) {
			depth = 0;
			x = 0;
			y = 0;
		}

		protected int depth;
		public int Depth {
			get { return depth; }
		}
		private double x;
		private double y;
		public virtual double X {
			get { return x; }
			set { x = value; }
		}
		public virtual double Y {
			get { return y; }
			set { y = value; }
		}
		public virtual Point GetCentre() {
			return new Point(X, Y);
		}
		public virtual bool IsCentred {
			get {
				return true;
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			node.SetAttribute("X", Convert.ToString(X));
			node.SetAttribute("Y", Convert.ToString(Y));
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["X"];
			if (attribute != null) {
				double newX;
				if (double.TryParse(attribute.Value, out newX)) {
					X = newX;
				}
			}
			attribute = node.Attributes["Y"];
			if (attribute != null) {
				double newY;
				if (double.TryParse(attribute.Value, out newY)) {
					Y = newY;
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OMapObject obj = (OMapObject)clone;
			obj.X = X;
			obj.Y = Y;
		}

		public override object GetRepresentation() {
			return ObjectCode;
		}

	}

	public abstract class OOpacityMapObject : OMapObject, IOpacityObject {

		public OOpacityMapObject(OCodeContainer newParent)
			: base(newParent) {
			opacity = DefaultOpacity;
		}

		public virtual double DefaultOpacity {
			get {
				return 100;
			}
		}
		double opacity;
		public double Opacity {
			get { return opacity; }
			set {
				opacity = value;
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (opacity != DefaultOpacity) {
				node.SetAttribute("Opacity", Convert.ToString(Opacity));
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["Opacity"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					if (num >= 0 && num <= 100) {
						Opacity = num;
					}
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OOpacityMapObject obj = (OOpacityMapObject)clone;
			obj.Opacity = Opacity;
		}

	}

	public abstract class ORotatableObject : OOpacityMapObject, IRotatableObject {

		public ORotatableObject(OCodeContainer newParent)
			: base(newParent) {
			rotation = 0;
		}

		private double rotation;
		public double Rotation {
			get { return rotation; }
			set { rotation = value; }
		}

		public double GetRotation() {
			return rotation;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (rotation != 0) {
				node.SetAttribute("Rotation", Convert.ToString(rotation));
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["Rotation"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					rotation = num;
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			ORotatableObject obj = (ORotatableObject)clone;
			obj.Rotation = Rotation;
		}

	}

	public class OControl : OOpacityMapDescriptionObject, IConnectableObject, IGetVisual, IAllControls, ICourseObject {

		public OControl(OCodeContainer newParent)
			: base(newParent) {
		}

		public override string ObjectCodePrefix {
			get {
				return "";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			return node;
		}

		public override string XmlTag {
			get {
				return "OControl";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OControl clone = new OControl(newParent);
			PopulateClone(clone);
			return clone;
		}

		public Point? GetLineStart(Nullable<Point> nextPoint) {
			Point point = GetCentre();
			double angle;
			if (nextPoint == null) {
				angle = 0;
			} else {
				angle = Math.Atan2(nextPoint.Value.Y - point.Y, nextPoint.Value.X - point.X);
			}
			double distance = GetData().ControlSize / 2;
			point.X += distance * Math.Cos(angle);
			point.Y += distance * Math.Sin(angle);
			return point;
		}

		public bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			Vector vector = Point.Subtract(point.Value, GetCentre());
			if (vector.Length <= GetData().ControlSize / 2) {
				return true;
			}
			return false;
		}

		public Point? GetLineEnd(Nullable<Point> previousPoint) {
			return GetLineStart(previousPoint);
		}

		public Point? GetLineStartTarget() {
			return GetCentre();
		}

		public Point? GetLineEndTarget() {
			return GetCentre();
		}

		public override bool IsFixedObject {
			get { return false; }
		}

		public override bool IsOrderedObject {
			get { return true; }
		}

		public override CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

		public FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			path.Fill = Brushes.Transparent;
			return path;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			double scale = GetData().ControlSize / 100;
			Geometry geometry = new EllipseGeometry(new Point(0, 0), 50, 50);
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			return geometry;
		}

	}

	public class OFinish : OOpacityMapObject, IConnectableObject, IGetVisual, IGlobalObject, IAllControls, ICourseObject {

		public OFinish(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = false;
		}

		public override string ObjectCodePrefix {
			get {
				return "F";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override string XmlTag {
			get {
				return "OFinish";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OFinish clone = new OFinish(newParent);
			PopulateClone(clone);
			return clone;
		}

		public Point? GetLineStart(Nullable<Point> nextPoint) {
			Point point = GetCentre();
			double angle;
			if (nextPoint == null) {
				angle = 0;
			} else {
				angle = Math.Atan2(nextPoint.Value.Y - point.Y, nextPoint.Value.X - point.X);
			}
			double distance = (GetData().ControlSize * 6 / 5) / 2;
			point.X += distance * Math.Cos(angle);
			point.Y += distance * Math.Sin(angle);
			return point;
		}

		public bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			Vector vector = Point.Subtract(point.Value, GetCentre());
			if (vector.Length <= (GetData().ControlSize * 6 / 5) / 2) {
				return true;
			}
			return false;
		}

		public Point? GetLineEnd(Nullable<Point> previousPoint) {
			return GetLineStart(previousPoint);
		}

		public Point? GetLineStartTarget() {
			return GetCentre();
		}

		public Point? GetLineEndTarget() {
			return GetCentre();
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OFinish obj = (OFinish)clone;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return true; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

		public FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			Canvas canvas = new Canvas();
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			path.Fill = Brushes.Transparent;
			canvas.Children.Add(path);
			double scale = data.ControlSize / 100;
			Geometry geometry = new EllipseGeometry(new Point(0, 0), 60, 60);
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			path = new Path();
			path.Data = geometry;
			path.Fill = Brushes.Transparent;
			canvas.Children.Add(path);
			return canvas;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			double scale = GetData().ControlSize / 100;
			GeometryGroup geometry = new GeometryGroup();
			EllipseGeometry ellipse;
			ellipse = new EllipseGeometry(new Point(0, 0), 60, 60);
			geometry.Children.Add(ellipse);
			ellipse = new EllipseGeometry(new Point(0, 0), 40, 40);
			geometry.Children.Add(ellipse);
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			return geometry;
		}

	}

	public class OStart : OOpacityMapDescriptionObject, IConnectableObject, IGetVisual, IGlobalObject, IAllControls, ICourseObject, IAutoRotate {

		public OStart(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = false;
		}

		public override string ObjectCodePrefix {
			get {
				return "S";
			}
		}

		public override string XmlTag {
			get {
				return "OStart";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OStart clone = new OStart(newParent);
			PopulateClone(clone);
			return clone;
		}

		public Point? GetLineStart(Nullable<Point> nextPoint) {
			Point point = GetCentre();
			double angle;
			if (nextPoint == null) {
				angle = 0;
			} else {
				angle = Math.Atan2(nextPoint.Value.Y - point.Y, nextPoint.Value.X - point.X);
			}
			double distance = GetData().ControlSize * 1.15 / 2;
			point.X += distance * Math.Cos(angle);
			point.Y += distance * Math.Sin(angle);
			return point;
		}

		public bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			Vector vector = Point.Subtract(point.Value, GetCentre());
			if (vector.Length <= GetData().ControlSize * 1.15 / 2) {
				return true;
			}
			return false;
		}

		public Point? GetLineEnd(Nullable<Point> previousPoint) {
			return GetLineStart(previousPoint);
		}

		public Point? GetLineStartTarget() {
			return GetCentre();
		}

		public Point? GetLineEndTarget() {
			return GetCentre();
		}

		public double GetRotation(Nullable<Point> nextPoint) {
			Point point = GetCentre();
			double angle;
			if (nextPoint == null) {
				angle = 0;
			} else {
				angle = -Math.PI/2 -  Math.Atan2(nextPoint.Value.Y - point.Y, nextPoint.Value.X - point.X);
			}
			return -angle * 180 / Math.PI;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OStart obj = (OStart)clone;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public override bool IsFixedObject {
			get { return true; }
		}

		public override bool IsOrderedObject {
			get { return true; }
		}

		public override CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

		public FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			path.Fill = Brushes.Transparent;
			return path;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			double scale = GetData().ControlSize * 1.15 / 100;
			Geometry geometry = Geometry.Parse("M 0,-57.74 L 50,28.87 L -50,28.87 L 0,-57.74").Clone();
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			return geometry;
		}

	}

	public class OCrossingPoint : ORotatableObject, IConnectableObject, IGetVisual, IGlobalObject, ICourseObject, ITextDescription {

		public OCrossingPoint(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = true;
		}

		public override string ObjectCodePrefix {
			get {
				return "CP";
			}
		}

		public override string XmlTag {
			get {
				return "OCrossingPoint";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OCrossingPoint clone = new OCrossingPoint(newParent);
			PopulateClone(clone);
			return clone;
		}

		public Point? GetLineStart(Nullable<Point> nextPoint) {
			Point point = GetCentre();
			double angle;
			if (nextPoint == null) {
				angle = 0;
			} else {
				angle = Math.Atan2(nextPoint.Value.Y - point.Y, nextPoint.Value.X - point.X);
			}
			double distance = GetData().ControlSize / 2;
			point.X += distance * Math.Cos(angle);
			point.Y += distance * Math.Sin(angle);
			return point;
		}

		public bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			Vector vector = Point.Subtract(point.Value, GetCentre());
			if (vector.Length <= GetData().ControlSize / 2) {
				return true;
			}
			return false;
		}

		public Point? GetLineEnd(Nullable<Point> previousPoint) {
			return GetLineStart(previousPoint);
		}

		public Point? GetLineStartTarget() {
			return GetCentre();
		}

		public Point? GetLineEndTarget() {
			return GetCentre();
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			if (TextDescription != null && TextDescription != "") {
				node.SetAttribute("TextDescription", TextDescription);
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OCrossingPoint obj = (OCrossingPoint)clone;
			obj.DisplayGlobally = DisplayGlobally;
			obj.TextDescription = TextDescription;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			attribute = node.Attributes["TextDescription"];
			if (attribute != null) {
				TextDescription = attribute.Value;
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return true; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

		private string textDescription;
		public string TextDescription {
			get { return textDescription; }
			set { textDescription = value; }
		}

		public FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Canvas canvas = new Canvas();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			canvas.Children.Add(path);
			double scale = GetData().ControlSize / 100;
			Geometry geometry = Geometry.Parse("M -20,-35 Q 0,0 -20,35 L 20,35 Q 0,0 20,-35 L -20,-35").Clone();
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			path = new Path();
			path.Data = geometry;
			path.Fill = Brushes.Transparent;
			canvas.Children.Add(path);
			return canvas;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			double scale = GetData().ControlSize / 100;
			Geometry geometry = Geometry.Parse("M -20,-35 Q 0,0 -20,35 M 20,-35 Q 0,0 20,35").Clone();
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			return geometry;
		}

	}

	public class ODrinksPoint : OOpacityMapObject, IGetVisual, IGlobalObject, ICourseObject {

		public ODrinksPoint(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = true;
		}

		public override string ObjectCodePrefix {
			get {
				return "DP";
			}
		}

		public override string XmlTag {
			get {
				return "ODrinksPoint";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			ODrinksPoint clone = new ODrinksPoint(newParent);
			PopulateClone(clone);
			return clone;
		}

		public bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			Vector vector = Point.Subtract(point.Value, GetCentre());
			if (vector.Length <= GetData().ControlSize / 2) {
				return true;
			}
			return false;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			ODrinksPoint obj = (ODrinksPoint)clone;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return false; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

		public FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Canvas container = new Canvas();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			container.Children.Add(path);
			path = new Path();
			double scale = GetData().ControlSize / 100;
			Geometry geometry = Geometry.Parse("M -30,-30 A 60,60 0 0 1 30,-30 L 15,30 A 30,30 0 0 0 -15,30 L -30,-30").Clone();
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			path.Data = geometry;
			path.Fill = Brushes.Transparent;
			container.Children.Add(path);
			return container;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			double scale = GetData().ControlSize / 100;
			Geometry geometry = Geometry.Parse("M -30,-30 A 60,60 0 0 0 30,-30 M -30,-30 A 60,60 0 0 1 30,-30 M -30,-30 L -15,30  M 30,-30 L 15,30 M-15,30 A 30,30 0 0 0 15,30").Clone();
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			return geometry;
		}

	}

	public class OFirstAidPoint : OOpacityMapObject, IGetVisual, IGlobalObject, ICourseObject {

		public OFirstAidPoint(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = true;
		}

		public override string ObjectCodePrefix {
			get {
				return "FA";
			}
		}

		public override string XmlTag {
			get {
				return "OFirstAidPoint";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OFirstAidPoint clone = new OFirstAidPoint(newParent);
			PopulateClone(clone);
			return clone;
		}

		public bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			Vector vector = Point.Subtract(point.Value, GetCentre());
			if (vector.Length <= GetData().ControlSize / 2) {
				return true;
			}
			return false;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OFirstAidPoint obj = (OFirstAidPoint)clone;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return false; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

		public FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = GetGeometry();
			path.Fill = mainBrush;
			return path;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			double scale = GetData().ControlSize / 100;
			Geometry geometry = Geometry.Combine(Geometry.Parse("M -30,-7 L 30,-7 L 30,7 L -30,7 L -30,-7").Clone(), Geometry.Parse("M -7,-30 L -7,30 L 7,30 L 7,-30 L -7,-30").Clone(), GeometryCombineMode.Union, null);
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			return geometry;
		}

	}

	public class OOOBCross : OOpacityMapObject, IGetVisual, IGlobalObject, ICourseObject {

		public OOOBCross(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = true;
		}

		public override string ObjectCodePrefix {
			get {
				return "OOBC";
			}
		}

		public override string XmlTag {
			get {
				return "OOOBCross";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OOOBCross clone = new OOOBCross(newParent);
			PopulateClone(clone);
			return clone;
		}

		public bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			Vector vector = Point.Subtract(point.Value, GetCentre());
			if (vector.Length <= GetData().ControlSize / 2) {
				return true;
			}
			return false;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OOOBCross obj = (OOOBCross)clone;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return false; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

		public FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Canvas canvas = new Canvas();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			canvas.Children.Add(path);
			double scale = GetData().ControlSize / 100;
			Geometry geometry = Geometry.Parse("M -30,-30 L -30,30 L 30,30 L 30,-30 L -30,-30").Clone();
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			path = new Path();
			path.Data = geometry;
			path.Fill = Brushes.Transparent;
			canvas.Children.Add(path);
			return canvas;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			double scale = GetData().ControlSize / 100;
			Geometry geometry = Geometry.Parse("M -30,-30 L 30,30  M 30,-30 L -30,30").Clone();
			geometry.Transform = new ScaleTransform(scale, scale, 0, 0);
			return geometry;
		}

	}

	public class OTransparentArea : OMapObject, IOneSizeable, IGlobalObject, ICourseObject {

		public OTransparentArea(OCodeContainer newParent)
			: base(newParent) {
			size = OPlannerDefaults.TransparentAreaSize;
			displayGlobally = true;
			depth = 500;
		}

		private double size;
		public double Size {
			get { return size; }
			set { size = value; }
		}

		public override string ObjectCodePrefix {
			get {
				return "TA";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (size != OPlannerDefaults.TransparentAreaSize) {
				node.SetAttribute("Size", size.ToString());
			}
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override string XmlTag {
			get {
				return "OTransparentArea";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OTransparentArea clone = new OTransparentArea(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["Size"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					size = num;
				}
			}
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OTransparentArea obj = (OTransparentArea)clone;
			obj.Size = Size;
			obj.DisplayGlobally = DisplayGlobally;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return false; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

	}

	public class OCourseDescriptions : OOpacityMapObject, IGlobalObject, ICourseObject {

		public OCourseDescriptions(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = true;
			depth = 1000;
			isText = false;
			maxRows = 0;
			title = null;
		}

		public override string ObjectCodePrefix {
			get {
				return "CD";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			if (isText) {
				node.SetAttribute("IsText", IsText.ToString());
			}
			if (Title != null && title != "") {
				node.SetAttribute("Title", Title);
			}
			if (MaxRows>0) {
				node.SetAttribute("MaxRows", MaxRows.ToString());
			}
			return node;
		}

		public override string XmlTag {
			get {
				return "OCourseDescriptions";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OCourseDescriptions clone = new OCourseDescriptions(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			attribute = node.Attributes["IsText"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					isText = val;
				}
			}
			attribute = node.Attributes["MaxRows"];
			if (attribute != null) {
				int val;
				if (int.TryParse(attribute.Value, out val)) {
					maxRows = val;
				}
			}
			attribute = node.Attributes["Title"];
			if (attribute != null) {
				title = attribute.Value;
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OCourseDescriptions obj = (OCourseDescriptions)clone;
			obj.DisplayGlobally = DisplayGlobally;
			obj.IsText = IsText;
			obj.MaxRows = MaxRows;
			obj.Title = Title;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		private bool isText;
		public bool IsText {
			get { return isText; }
			set { isText = value; }
		}

		private string title;
		public string Title {
			get { return title; }
			set { title = value; }
		}

		private int maxRows;
		public int MaxRows {
			get { return maxRows; }
			set { maxRows = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return false; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

		public override bool IsCentred {
			get {
				return false;
			}
		}

	}

	public abstract class ODrawingObject : OOpacityMapObject, IMapObject, IGetVisual, ICourseObject {

		private CustomPath path;
		public DrawingMode DrawingMode;

		public ODrawingObject(OCodeContainer newParent)
			: base(newParent) {
			path = new CustomPath();
			DrawingMode = DrawingMode.Freehand;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			PathGeometry geometry = path.GetGeometry();
			if (geometry != null) {
				node.SetAttribute("Geometry", geometry.ToString());
			}
			if (DrawingMode != DrawingMode.Freehand) {
				node.SetAttribute("DrawingMode", DrawingMode.ToString());
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["Geometry"];
			if (attribute != null) {
				GeometryConverter converter = new GeometryConverter();
				Geometry temp = null;
				try {
					temp = Geometry.Parse(attribute.Value);
				} catch (Exception) {
					temp = null;
				}
				if (temp != null) {
					path.Load(PathGeometry.CreateFromGeometry(temp));
				}
			}
			attribute = node.Attributes["DrawingMode"];
			if (attribute != null) {
				if(Enum.IsDefined(typeof(DrawingMode),attribute.Value)){
					DrawingMode = (DrawingMode)Enum.Parse(typeof(DrawingMode), attribute.Value);
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			ODrawingObject obj = (ODrawingObject)clone;
			PathGeometry geometry = path.GetGeometry();
			if (geometry != null) {
				obj.Path.Load(geometry.Clone());
			}
			obj.DrawingMode = DrawingMode;
		}

		public CustomPath Path {
			get {
				return path;
			}
		}

		public void SetStartPoint(Point point) {
			X = point.X;
			Y = point.Y;
		}

		public abstract bool ContainsPoint(Point? point);
		
		public double GetLength() {
			return path.Length;
		}

		public abstract bool IsFixedObject {
			get;
		}

		public abstract bool IsOrderedObject {
			get;
		}

		public abstract CourseObjectType DefaultObjectType {
			get;
		}

		public abstract bool HasStroke {
			get;
		}

		public abstract bool HasFill {
			get;
		}

		public abstract FrameworkElement GetVisual(CourseViewType viewType);

		public virtual FrameworkElement GetOutlineVisual() {
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = path.GetGeometry();
			if (geometry == null) {
				return new Rect(0, 0, 0, 0);
			}
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		public void AddLineSegment(Point point) {
			Path.AddLineSegment(new Point(point.X-X,point.Y-Y));
		}

		public void AddQuadraticBezierSegment(Point control, Point end) {
			Path.AddQuadraticBezierSegment(new Point(control.X - X, control.Y - Y), new Point(end.X - X, end.Y - Y));
		}

	}

	public abstract class OLinearDrawingObject : ODrawingObject, IConnectableObject, IAutoJoin {

		public OLinearDrawingObject(OCodeContainer newParent)
			: base(newParent) {
			autoJoin = true;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (autoJoin == false) {
				node.SetAttribute("AutoJoin", Convert.ToString(autoJoin));
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["AutoJoin"];
			if (attribute != null) {
				bool temp;
				if (bool.TryParse(attribute.Value, out temp)) {
					autoJoin = temp;
				}
			}
			return true;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OLinearDrawingObject obj = (OLinearDrawingObject)clone;
			obj.AutoJoin = AutoJoin;
		}

		public Point? GetLineStart(Nullable<Point> nextPoint) {
			return GetLineStartTarget();
		}

		public Point? GetLineEnd(Nullable<Point> previousPoint) {
			return GetLineEndTarget();
		}

		public Point? GetLineStartTarget() {
			Point point = Path.End;
			point.X += X;
			point.Y += Y;
			return point;
		}

		public Point? GetLineEndTarget() {
			Point point = Path.Start;
			point.X += X;
			point.Y += Y;
			return point;
		}

		public override bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			return false;
		}

		private bool autoJoin;
		public bool AutoJoin {
			get { return autoJoin; }
			set { autoJoin = value; }
		}

		public override bool IsFixedObject {
			get { return true; }
		}

		public override bool IsOrderedObject {
			get { return true; }
		}

		public override CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

		public override bool HasStroke {
			get {
				return true;
			}
		}

		public override bool HasFill {
			get {
				return false;
			}
		}

		public override FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			return path;
		}

	}

	public class OLine : OLinearDrawingObject {

		public OLine(OCodeContainer newParent) : base(newParent) {
		}

		public override string ObjectCodePrefix {
			get {
				return "L";
			}
		}

		public override string XmlTag {
			get {
				return "OLine";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OLine clone = new OLine(newParent);
			PopulateClone(clone);
			return clone;
		}

	}

	//Not Stored
	public class ODistanceMeasure : OLinearDrawingObject {

		public ODistanceMeasure(OCodeContainer newParent)
			: base(newParent) {
		}

		public override string ObjectCodePrefix {
			get {
				return "DM";
			}
		}

		public override string XmlTag {
			get {
				return "ODistanceMeasure";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			ODistanceMeasure clone = new ODistanceMeasure(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = Brushes.Red;
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			return path;
		}

	}
	//

	public class OUncrossableBoundary : OLinearDrawingObject, IGlobalObject {

		public OUncrossableBoundary(OCodeContainer newParent)
			: base(newParent) {
				displayGlobally = true;
		}

		public override double DefaultOpacity {
			get {
				return 100;//
			}
		}

		public override string ObjectCodePrefix {
			get {
				return "UB";
			}
		}

		public override string XmlTag {
			get {
				return "OUncrossableBoundary";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OUncrossableBoundary obj = (OUncrossableBoundary)clone;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OUncrossableBoundary clone = new OUncrossableBoundary(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth * 2;
			return path;
		}

	}

	public abstract class OAreaDrawingObject : ODrawingObject {

		public OAreaDrawingObject(OCodeContainer newParent)
			: base(newParent) {
			Path.IsClosed = true;
		}

		public override bool HasStroke {
			get {
				return false;
			}
		}

		public override bool HasFill {
			get {
				return true;
			}
		}

		public override FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Fill = mainBrush;
			return path;
		}

	}

	public class OOOBArea : OAreaDrawingObject, IGlobalObject {

		public OOOBArea(OCodeContainer newParent)
			: base(newParent) {
				displayGlobally = true;
		}

		public override string ObjectCodePrefix {
			get {
				return "OOBA";
			}
		}

		public override string XmlTag {
			get {
				return "OOOBArea";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OOOBArea obj = (OOOBArea)clone;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OOOBArea clone = new OOOBArea(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			return Path.GetGeometry().FillContains(point.Value);
		}

		public override bool IsFixedObject {
			get { return true; }
		}

		public override bool IsOrderedObject {
			get { return false; }
		}

		public override CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

		public override FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			HatchedArea hatchedArea = new HatchedArea();
			double spacing = data.LineWidth * 2;
			double lineThickness = data.LineWidth;
			Color hatchColor = (Color)ColorConverter.ConvertFromString(data.ControlColour);
			hatchedArea.HatchColor = hatchColor;
			hatchedArea.HatchLineSpacing = spacing;
			hatchedArea.HatchLineWidth = lineThickness;
			hatchedArea.Data = Path.GetGeometry();
			return hatchedArea;
		}

	}

	public class OAreaBlock : OAreaDrawingObject, IGlobalObject {

		public OAreaBlock(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = true;
			FillColor = "#FFFFFF";
		}

		public override string ObjectCodePrefix {
			get {
				return "AB";
			}
		}

		public override string XmlTag {
			get {
				return "OAreaBlock";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			if (!string.IsNullOrWhiteSpace(FillColor)) {
				node.SetAttribute("FillColor", FillColor);
			}
			return node;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OAreaBlock obj = (OAreaBlock)clone;
			obj.DisplayGlobally = DisplayGlobally;
			obj.FillColor = FillColor;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			attribute = node.Attributes["FillColor"];
			if (attribute != null) {
				fillColor = attribute.Value;
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		private string fillColor;
		public string FillColor {
			get { return fillColor; }
			set { fillColor = value; }
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OAreaBlock clone = new OAreaBlock(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override bool ContainsPoint(Point? point) {
			if (point == null) {
				return false;
			}
			return Path.GetGeometry().FillContains(point.Value);
		}

		public override bool IsFixedObject {
			get { return true; }
		}

		public override bool IsOrderedObject {
			get { return false; }
		}

		public override CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

		public override FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Color color;
			try {
				color = (Color)ColorConverter.ConvertFromString(FillColor);
			} catch (Exception) {
				color = Colors.Transparent;
			}
			Brush mainBrush = new SolidColorBrush(color);
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Fill = mainBrush;
			return path;
		}

	}

	public class OTapedRoute : OLinearDrawingObject, ITextDescription, IGlobalObject {

		public OTapedRoute(OCodeContainer newParent)
			: base(newParent) {
				displayGlobally = false;
		}

		public override string ObjectCodePrefix {
			get {
				return "T";
			}
		}

		public override string XmlTag {
			get {
				return "OTapedRoute";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OTapedRoute clone = new OTapedRoute(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

		private string textDescription;
		public string TextDescription {
			get { return textDescription; }
			set { textDescription = value; }
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OTapedRoute obj = (OTapedRoute)clone;
			obj.TextDescription = TextDescription;
			obj.DisplayGlobally = DisplayGlobally;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (TextDescription != null && TextDescription != "") {
				node.SetAttribute("TextDescription", TextDescription);
			}
			if (displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["TextDescription"];
			if (attribute != null) {
				TextDescription = attribute.Value;
			}
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			return true;
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public override FrameworkElement GetOutlineVisual() {
			OPlannerData data = GetData();
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = 1;
			path.StrokeDashArray = new DoubleCollection(new double[] { 8.0, 2.0 });
			return path;
		}

		public override FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			Path path = new Path();
			path.Data = Path.GetGeometry();
			path.Stroke = mainBrush;
			path.StrokeThickness = data.LineWidth;
			path.StrokeDashArray = new DoubleCollection(new double[] { 8.0 / data.LineWidth, 2.0 / data.LineWidth });
			return path;
		}

	}

	public class OText : OOpacityMapObject, ICodeObject, IText, ICourseObject, IGlobalObject, IOneSizeable, IGetVisual {

		public OText(OCodeContainer newParent)
			: base(newParent) {
			displayGlobally = false;
			size = OPlannerDefaults.OTextSize;
		}

		private string text;
		public string Text {
			get { return text; }
			set { text = value; }
		}

		public override string ObjectCodePrefix {
			get {
				return "TXT";
			}
		}

		public override string XmlTag {
			get {
				return "OText";
			}
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OText obj = (OText)clone;
			obj.Text = Text;
			obj.DisplayGlobally = DisplayGlobally;
			obj.Size = Size;
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OText clone = new OText(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (Text != null && Text != "") {
				node.SetAttribute("Text", Text);
			}
			if (displayGlobally) {
				node.SetAttribute("DisplayGlobally", DisplayGlobally.ToString());
			}
			if (size != OPlannerDefaults.OTextSize) {
				node.SetAttribute("Size", size.ToString());
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["Text"];
			if (attribute != null) {
				Text = attribute.Value;
			}
			attribute = node.Attributes["DisplayGlobally"];
			if (attribute != null) {
				bool val;
				if (bool.TryParse(attribute.Value, out val)) {
					displayGlobally = val;
				}
			}
			attribute = node.Attributes["Size"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					size = num;
				}
			}
			return true;
		}

		private double size;
		public double Size {
			get { return size; }
			set { size = value; }
		}

		public virtual bool IsFixedObject {
			get { return true; }
		}

		public virtual bool IsOrderedObject {
			get { return false; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Fixed; }
		}

		private bool displayGlobally;
		public bool DisplayGlobally {
			get { return displayGlobally; }
			set { displayGlobally = value; }
		}

		public FrameworkElement GetOutlineVisual() {
			OPlannerData data = GetData();
			Brush mainBrush = Brushes.Black;
			TextBlock textBlock = new TextBlock();
			textBlock.Text = Text;
			textBlock.Foreground = mainBrush;
			textBlock.FontSize = Size;
			textBlock.FontWeight = FontWeights.Light;
			return textBlock;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(data.ControlColour));
			TextBlock textBlock = new TextBlock();
			textBlock.Text = Text;
			textBlock.Foreground = mainBrush;
			textBlock.FontSize = Size;
			return textBlock;
		}

		public Rect GetVisualBounds(Transform transform) {
			TextBlock textBlock = new TextBlock();
			textBlock.Text = Text;
			textBlock.FontSize = Size;
			textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
			Size textSize = textBlock.DesiredSize;
			textSize.Width += Size/2;
			int pos = 0;
			int num = 1;
			while ((pos = Text.IndexOf("\n", pos)) >= 0) {
				pos++;
				num++;
			}
			textSize.Height = Size * num * 1.2;
			return new Rect(textSize);
		}

		public override bool IsCentred {
			get {
				return false;
			}
		}

	}

	public class OInvisiblePoint : OMapObject, IConnectableObject, ICourseObject, IAutoJoin, IGetVisual {

		public OInvisiblePoint(OCodeContainer newParent)
			: base(newParent) {
			autoJoin = true;
		}

		public override string ObjectCodePrefix {
			get {
				return "P";
			}
		}

		public override string XmlTag {
			get {
				return "OInvisiblePoint";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OInvisiblePoint clone = new OInvisiblePoint(newParent);
			PopulateClone(clone);
			return clone;
		}

		public virtual bool IsFixedObject {
			get { return false; }
		}

		public virtual bool IsOrderedObject {
			get { return true; }
		}

		public virtual CourseObjectType DefaultObjectType {
			get { return CourseObjectType.Ordered; }
		}

		public Point? GetLineStart(Nullable<Point> nextPoint) {
			return GetCentre();
		}

		public Point? GetLineEnd(Nullable<Point> previousPoint) {
			return GetCentre();
		}

		public Point? GetLineStartTarget() {
			return GetCentre();
		}

		public Point? GetLineEndTarget() {
			return GetCentre();
		}

		public bool ContainsPoint(Point? point) {
			return false;
		}

		private bool autoJoin;
		public bool AutoJoin {
			get { return autoJoin; }
			set { autoJoin = value; }
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OInvisiblePoint obj = (OInvisiblePoint)clone;
			obj.AutoJoin = AutoJoin;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (autoJoin == false) {
				node.SetAttribute("AutoJoin", Convert.ToString(autoJoin));
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["AutoJoin"];
			if (attribute != null) {
				bool temp;
				if (bool.TryParse(attribute.Value, out temp)) {
					autoJoin = temp;
				}
			}
			return true;
		}

		public FrameworkElement GetOutlineVisual() {
			return null;
		}

		public FrameworkElement GetVisual(CourseViewType viewType) {
			OPlannerData data = GetData();
			Brush mainBrush = Brushes.Black;
			Path path = new Path();
			path.Data = GetGeometry();
			if (viewType == CourseViewType.Outline) {
				path.Stroke = mainBrush;
				path.StrokeThickness = data.LineWidth;
			}
			path.Fill = Brushes.Transparent;
			return path;
		}

		public Rect GetVisualBounds(Transform transform) {
			Pen pen = new Pen(Brushes.Black, GetData().LineWidth);
			Geometry geometry = GetGeometry();
			if (transform == null) {
				return geometry.GetRenderBounds(pen);
			}
			geometry = geometry.Clone();
			TransformGroup group = new TransformGroup();
			if (geometry.Transform != null) {
				group.Children.Add(geometry.Transform.Clone());
			}
			group.Children.Add(transform);
			geometry.Transform = group;
			return geometry.GetRenderBounds(pen);
		}

		private Geometry GetGeometry() {
			Geometry geometry = new EllipseGeometry(new Point(0, 0), 3, 3);
			return geometry;
		}

	}

	public class OObjectRef : OBaseObject, IObjectRef {

		public OObjectRef(OCodeContainer newParent)
			: base(newParent) {
		}

		private OBaseObject targetObject;
		public OBaseObject TargetObject {
			get { return targetObject; }
			set { targetObject = value; }
		}

		public override object GetRepresentation() {
			if (TargetObject == null) {
				return null;
			}
			return TargetObject.GetRepresentation();
		}

		public override string ObjectCodePrefix {
			get {
				return "OR";
			}
		}

		public override OData GetObject(Type type) {
			OData obj = base.GetObject(type);
			if (obj != null) {
				return obj;
			}
			if (TargetObject == null) {
				return null;
			}
			return TargetObject.GetObject(type);
		}

		public override bool MatchesObject(OBaseObject obj) {
			if (base.MatchesObject(obj)) {
				return true;
			}
			if (TargetObject == null) {
				return false;
			}
			return TargetObject.MatchesObject(obj);
		}

		public override string XmlTag {
			get {
				return "OObjectRef";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			node.SetAttribute("TargetObject", TargetObject.ObjectCode);
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["TargetObject"];
			if (attribute == null) {
				return false;
			}
			OPlannerData data = (OPlannerData)Parent.GetObject(typeof(OPlannerData));
			if (data == null) {
				TargetObject = null;
			} else {
				TargetObject = data.GetObjectByCode(attribute.Value);
			}
			if (TargetObject == null) {
				return false;
			}
			return true;
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OObjectRef clone = new OObjectRef(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OObjectRef obj = (OObjectRef)clone;
			OPlannerData data = (OPlannerData)clone.Parent.GetObject(typeof(OPlannerData));
			if (data == null) {
				obj.TargetObject = null;
			} else {
				obj.TargetObject = data.GetObjectByCode(TargetObject.ObjectCode);
			}
		}

	}

	public class OConnectableObjectRef : OObjectRef, IAutoJoin {

		public OConnectableObjectRef(OCodeContainer newParent)
			: base(newParent) {
			autoJoin = true;
		}

		public override string ObjectCodePrefix {
			get {
				return "COR";
			}
		}

		private bool autoJoin;
		public bool AutoJoin {
			get { return autoJoin; }
			set { autoJoin = value; }
		}

		public override string XmlTag {
			get {
				return "OConnectableObjectRef";
			}
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OConnectableObjectRef clone = new OConnectableObjectRef(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OConnectableObjectRef obj = (OConnectableObjectRef)clone;
			obj.AutoJoin = AutoJoin;
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (autoJoin == false) {
				node.SetAttribute("AutoJoin", Convert.ToString(autoJoin));
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["AutoJoin"];
			if (attribute != null) {
				bool temp;
				if (bool.TryParse(attribute.Value, out temp)) {
					autoJoin = temp;
				}
			}
			return true;
		}

	}

	public enum AutoManualNone {
		Auto, Manual, None
	}

	public class OCourseControl : OConnectableObjectRef {

		public OCourseControl(OCodeContainer newParent)
			: base(newParent) {
			NumberXOffset = double.NaN;
			NumberYOffset = double.NaN;
			NumberType = AutoManualNone.Auto;
			NumberOffsetType = AutoManual.Auto;
			NumberText = "";
		}

		public override string ObjectCodePrefix {
			get {
				return "CC";
			}
		}

		public override string XmlTag {
			get {
				return "OCourseControl";
			}
		}

		public override XmlElement GetXmlElement(XmlDocument doc) {
			XmlElement node = base.GetXmlElement(doc);
			if (!double.IsNaN(NumberXOffset)) {
				node.SetAttribute("NumberXOffset", Convert.ToString(NumberXOffset));
			}
			if (!double.IsNaN(NumberYOffset)) {
				node.SetAttribute("NumberYOffset", Convert.ToString(NumberYOffset));
			}
			if (NumberType != AutoManualNone.Auto) {
				node.SetAttribute("NumberType", NumberType.ToString());
			}
			if (NumberOffsetType != AutoManual.Auto) {
				node.SetAttribute("NumberOffsetType", NumberOffsetType.ToString());
			}
			if (NumberText != "" && NumberText != null && NumberType == AutoManualNone.Manual) {
				node.SetAttribute("NumberText", NumberText);
			}
			return node;
		}

		public override bool Load(XmlNode node) {
			if (!base.Load(node)) {
				return false;
			}
			XmlAttribute attribute;
			attribute = node.Attributes["NumberXOffset"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					NumberXOffset = num;
				}
			}
			attribute = node.Attributes["NumberYOffset"];
			if (attribute != null) {
				double num;
				if (double.TryParse(attribute.Value, out num)) {
					NumberYOffset = num;
				}
			}
			attribute = node.Attributes["NumberType"];
			if (attribute != null) {
				if (Enum.IsDefined(typeof(AutoManualNone), attribute.Value)) {
					NumberType = (AutoManualNone)Enum.Parse(typeof(AutoManualNone), attribute.Value);
				}
			}
			attribute = node.Attributes["NumberOffsetType"];
			if (attribute != null) {
				if (Enum.IsDefined(typeof(AutoManual), attribute.Value)) {
					NumberOffsetType = (AutoManual)Enum.Parse(typeof(AutoManual), attribute.Value);
				}
			}
			attribute = node.Attributes["NumberText"];
			if (attribute != null) {
				NumberText = attribute.Value;
			}
			return true;
		}

		public override OBaseObject Clone(OCodeContainer newParent) {
			OCourseControl clone = new OCourseControl(newParent);
			PopulateClone(clone);
			return clone;
		}

		public override void PopulateClone(OBaseObject clone) {
			base.PopulateClone(clone);
			OCourseControl obj = (OCourseControl)clone;
			obj.NumberXOffset = NumberXOffset;
			obj.NumberYOffset = NumberYOffset;
			obj.NumberType = NumberType;
			obj.NumberText = NumberText;
			obj.NumberOffsetType = NumberOffsetType;
		}

		public double NumberXOffset;
		public double NumberYOffset;
		private AutoManualNone numberType;
		public AutoManualNone NumberType {
			get {
				return numberType;
			}
			set {
				numberType = value;
				if (value != AutoManualNone.Manual) {
					NumberText = "";
				}
			}
		}
		private string numberText;
		public string NumberText {
			get {
				if (numberText == null) {
					return "";
				}
				return numberText;
			}
			set {
				numberText = value;
			}
		}
		public AutoManual NumberOffsetType;

		public string GetNumberText() {
			if (NumberType == AutoManualNone.None) {
				return "";
			}
			if (NumberType == AutoManualNone.Manual) {
				return NumberText;
			}
			Course course = (Course)Parent.GetObject(typeof(Course));
			if (course == null) {
				if (TargetObject != null) {
					return TargetObject.ObjectCode;
				} else {
					return "";
				}
			}
			return course.GetNumberText(this);
		}

		public Point GetNumberOffset() {
			if (NumberOffsetType == AutoManual.Manual) {
				return new Point(NumberXOffset, NumberYOffset);
			}
			OControl target = TargetObject as OControl;
			if (target == null) {
				return new Point(0,0);
			}
			Point centre = target.GetCentre();
			double distance = GetData().ControlSize / 2 + GetData().NumberOffsetDistance;
			double nextAngle = double.NaN;
			double previousAngle = double.NaN;
			Course course = GetCourse();
			if (course != null) {
				OBaseObject nextObject = course.GetNextConnectableObject(this);
				if (nextObject != null) {
					IConnectableObject obj = (IConnectableObject)nextObject.GetObject(typeof(IConnectableObject));
					if (obj != null) {
						Point nextPoint = obj.GetCentre();
						nextAngle = Math.Atan2(nextPoint.Y - centre.Y, nextPoint.X - centre.X);
					}
				}
				OBaseObject previousObject = course.GetPreviousConnectableObject(this);
				if (previousObject != null) {
					IConnectableObject obj = (IConnectableObject)previousObject.GetObject(typeof(IConnectableObject));
					if (obj != null) {
						Point previousPoint = obj.GetCentre();
						previousAngle = Math.Atan2(previousPoint.Y - centre.Y, previousPoint.X - centre.X);
					}
				}
			}
			if (double.IsNaN(nextAngle)) {
				if (!double.IsNaN(previousAngle)) {
					nextAngle = previousAngle;
				} else {
					nextAngle = Math.PI * 3 / 4;
				}
			}
			if (double.IsNaN(previousAngle)) {
				if (!double.IsNaN(nextAngle)) {
					previousAngle = nextAngle;
				} else {
					previousAngle = Math.PI * 3 / 4;
				}
			}
			double sign = 1;
			if (Math.Abs(nextAngle - previousAngle) < Math.PI) {
				sign = -1;
			}
			double angle = (nextAngle + previousAngle) / 2;
			Point point = new Point();
			point.X = Math.Cos(angle) * distance * sign;
			point.Y = Math.Sin(angle) * distance * sign;
			return point;
		}

		public override object GetRepresentation() {
			Course course = GetCourse();
			if (course != null && TargetObject != null) {
				string str = course.GetControlNum(this).ToString();
				if (str != "") {
					str += ". ";
				}
				str += TargetObject.ObjectCode;
				return str;
			}
			return base.GetRepresentation();
		}

	}

}
