using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Media;

namespace OcadMapLibrary {

	public class Ocad8FileReader : OcadBaseFileReader {

		TFileHeader header;
		TSymHeader symHeader;
		List<TBaseSym> symbols;
		List<TElement> objects;
		TStp setup;
		bool substractStrokeWidth;

		public Ocad8FileReader() {
			substractStrokeWidth = true;//true in Ocad 8, false in Ocad 9
			UnloadMap();
		}

		public override void UnloadMap() {
			header = null;
			symHeader = null;
			symbols = null;
			objects = null;
			IsLoaded = false;
			setup = null;
		}

		public override bool LoadMap(Stream stream) {
			UnloadMap();
			bool result;
			header = new TFileHeader();
			result = header.Load(stream);
			if (!result) {
				UnloadMap();
				return false;
			}
			symHeader = new TSymHeader();
			result = symHeader.Load(stream);
			if (!result) {
				UnloadMap();
				return false;
			}
			symbols = ReadSymbols(stream);
			objects = ReadObjects(stream);
			setup = ReadSetup(stream);
			IsLoaded = true;
			return true;
		}

		public override MapInfo GetMapInfo() {
			if (!IsLoaded) {
				return null;
			}
			MapInfo mapInfo = new MapInfo();
			mapInfo.OcadMainVersion = header.Version;
			mapInfo.OcadSubVersion = header.Subversion;
			mapInfo.Scale = setup.MapScale;
			return mapInfo;
		}

		private Color GetColor(TCmyk cmyk) {
			Color color;
			byte r, g, b;
			double R, G, B;
			double C, M, Y, K;
			C = (double)cmyk.cyan;
			M = (double)cmyk.magenta;
			Y = (double)cmyk.yellow;
			K = (double)cmyk.black;
			C = C / 200.0;
			M = M / 200.0;
			Y = Y / 200.0;
			K = K / 200.0;
			R = C * (1.0 - K) + K;
			G = M * (1.0 - K) + K;
			B = Y * (1.0 - K) + K;
			R = (1.0 - R) * 255.0;
			G = (1.0 - G) * 255.0;
			B = (1.0 - B) * 255.0;
			r = (byte)Math.Round(R);
			g = (byte)Math.Round(G);
			b = (byte)Math.Round(B);
			color = Color.FromRgb(r, g, b);
			return color;
		}

		public override List<MapColor> GetColors() {
			if (!IsLoaded) {
				return null;
			}
			List<MapColor> colors = new List<MapColor>();
			for (int i = 0; i < symHeader.nColors; i++) {
				MapColor color = new MapColor();
				color.ID = symHeader.aColorInfo[i].ColorNum;
				color.Color = GetColor(symHeader.aColorInfo[i].Color);
				colors.Insert(0, color);
			}
			return colors;
		}

		public override List<MapSymbol> GetSymbols() {
			if (!IsLoaded) {
				return null;
			}
			List<MapSymbol> mapSymbols = new List<MapSymbol>();
			MapSymbol mapSymbol;
			for (int i = 0; i < symbols.Count; i++) {
				if (symbols[i] == null) {
					continue;
				}
				mapSymbol = CreateSymbol(symbols[i]);
				if (mapSymbol != null) {
					mapSymbols.Add(mapSymbol);
				}
			}
			return mapSymbols;
		}

		public override List<MapObject> GetObjects() {
			if (!IsLoaded) {
				return null;
			}
			List<MapObject> mapObjects = new List<MapObject>();
			for (int i = 0; i < objects.Count; i++) {
				MapObject mapObject = CreateObject(objects[i]);
				if (mapObject != null) {
					mapObjects.Add(mapObject);
				}
			}
			return mapObjects;
		}

		private List<TBaseSym> ReadSymbols(Stream stream) {
			List<TBaseSym> symbols = new List<TBaseSym>();
			long pos = stream.Seek((long)header.FirstSymBlk, SeekOrigin.Begin);
			while (pos > 0) {
				TSymbolBlock symBlock = new TSymbolBlock();
				symBlock.Load(stream);
				for (int i = 0; i < symBlock.FilePos.Length; i++) {
					if (symBlock.FilePos[i] == 0) {
						continue;
					}
					stream.Seek((long)symBlock.FilePos[i], SeekOrigin.Begin);
					TBaseSym symbol = TBaseSym.Create(stream);
					if (symbol == null) {
						continue;
					}
					symbols.Add(symbol);
				}
				pos = stream.Seek((long)symBlock.NextBlock, SeekOrigin.Begin);
			}
			return symbols;
		}

		private List<TElement> ReadObjects(Stream stream) {
			List<TElement> objects = new List<TElement>();
			long pos = stream.Seek((long)header.FirstIdxBlk, SeekOrigin.Begin);
			while (pos > 0) {
				TIndexBlock indexBlock = new TIndexBlock();
				indexBlock.Load(stream);
				for (int i = 0; i < indexBlock.IndexArr.Length; i++) {
					if (indexBlock.IndexArr[i].Len == 0 || indexBlock.IndexArr[i].Pos == 0 || indexBlock.IndexArr[i].Sym == 0) {
						continue;
					}
					stream.Seek((long)indexBlock.IndexArr[i].Pos, SeekOrigin.Begin);
					TElement element = new TElement();
					element.Load(stream);
					objects.Add(element);
				}
				pos = stream.Seek((long)indexBlock.NextBlock, SeekOrigin.Begin);
			}
			return objects;
		}

		private TStp ReadSetup(Stream stream) {
			TStp tempSetup = new TStp();
			long pos = stream.Seek((long)header.SetupPos, SeekOrigin.Begin);
			tempSetup.Load(stream);
			return tempSetup;
		}

		MapSymbol CreateSymbol(TBaseSym symbol) {
			MapSymbol mapSymbol = null;
			if (symbol == null) {
				return mapSymbol;
			}
			if (symbol is TPointSym) {
				mapSymbol = CreatePointSymbol(((TPointSym)symbol).Elts, substractStrokeWidth);
			} else if (symbol is TLineSym) {
				mapSymbol = CreateLineSymbol((TLineSym)symbol);
			} else if (symbol is TAreaSym) {
				mapSymbol = CreateAreaSymbol((TAreaSym)symbol);
			} else if (symbol is TRectSym) {
				mapSymbol = CreateRectangleSymbol((TRectSym)symbol);
			} else if (symbol is TTextSym) {
				mapSymbol = CreateTextSymbol((TTextSym)symbol);
			} else if (symbol is TLTextSym) {
				mapSymbol = CreateLineTextSymbol((TLTextSym)symbol);
			}
			if (mapSymbol != null) {
				if (symbol.Status == 2) {
					mapSymbol.IsHidden = true;
				}
				mapSymbol.ID = symbol.Sym;
				mapSymbol.Name = symbol.Description;
			}
			return mapSymbol;
		}

		LineSymbol CreateLineSymbol(TLineSym symbol) {
			LineSymbol mapSymbol = new LineSymbol();
			MapStroke mapStroke;
			mapStroke = new MapStroke();
			mapStroke.Thickness = GetLength(symbol.LineWidth);
			mapStroke.Color = new MapColorRef(symbol.LineColor);
			mapStroke.LineJoinStyle = FlagsToLineJoin(symbol.LineEnds);
			mapSymbol.MainStroke = mapStroke;
			DashInfo dashInfo = new DashInfo();
			dashInfo.DashMainLength = GetLength(symbol.MainLength);
			dashInfo.DashEndGap = GetLength(symbol.EndGap);
			dashInfo.DashEndLength = GetLength(symbol.EndLength);
			dashInfo.DashMainGap = GetLength(symbol.MainGap);
			dashInfo.DashSecondaryGap = GetLength(symbol.SecGap);
			dashInfo.MinNumGaps = symbol.MinSym + 1;
			mapSymbol.MainDashInfo = dashInfo;
			if (symbol.MainSymbol.Length > 0) {
				mapSymbol.MainSymbol = CreatePointSymbol(symbol.MainSymbol, substractStrokeWidth);
				mapSymbol.NumMainSymbols = symbol.nPrimSym;
				mapSymbol.MainSymbolSeparation = GetLength(symbol.PrimSymDist);
			}
			if (symbol.CornerSymbol.Length > 0) {
				mapSymbol.CornerSymbol = CreatePointSymbol(symbol.CornerSymbol, substractStrokeWidth);
			}
			if (symbol.StartSymbol.Length > 0) {
				mapSymbol.StartSymbol = CreatePointSymbol(symbol.StartSymbol, substractStrokeWidth);
			}
			if (symbol.EndSymbol.Length > 0) {
				mapSymbol.EndSymbol = CreatePointSymbol(symbol.EndSymbol, substractStrokeWidth);
			}
			if (symbol.DblMode != 0) {
				mapSymbol.DoubleMiddleWidth = GetLength(symbol.DblWidth);
				mapSymbol.DoubleDashGap = GetLength(symbol.DblGap);
				mapSymbol.DoubleDashLength = GetLength(symbol.DblLength);
				if (symbol.DblMode == 2 || symbol.DblMode == 3) {
					mapSymbol.DoubleIsDashedLeft = true;
				}
				if (symbol.DblMode == 3) {
					mapSymbol.DoubleIsDashedRight = true;
				}
				if (symbol.DblFlags != 0) {
					mapSymbol.DoubleFillColor = new MapColorRef(symbol.DblFillColor);
				}
				if (symbol.DblLeftWidth > 0) {
					mapStroke = new MapStroke();
					mapStroke.Thickness = GetLength(symbol.DblLeftWidth);
					mapStroke.Color = new MapColorRef(symbol.DblLeftColor);
					mapSymbol.DoubleLeftStroke = mapStroke;
				}
				if (symbol.DblRightWidth > 0) {
					mapStroke = new MapStroke();
					mapStroke.Thickness = GetLength(symbol.DblRightWidth);
					mapStroke.Color = new MapColorRef(symbol.DblRightColor);
					mapSymbol.DoubleRightStroke = mapStroke;
				}
			}
			if ((symbol.LineEnds & 2) == 2) {
				mapSymbol.HasPointedEnds = true;
				mapSymbol.PointedStartDistance = GetLength(symbol.DistFromStart);
				mapSymbol.PointedEndDistance = GetLength(symbol.DistToEnd);
			}
			return mapSymbol;
		}

		AreaSymbol CreateAreaSymbol(TAreaSym symbol) {
			AreaSymbol mapSymbol = new AreaSymbol();
			if (symbol.FillOn != 0) {
				mapSymbol.FillColor = new MapColorRef(symbol.FillColor);
			}
			if (symbol.HatchMode == 1 || symbol.HatchMode == 2) {
				MapHatchInfo hatching = new MapHatchInfo();
				hatching.StrokeThickness = GetLength(symbol.HatchLineWidth);
				hatching.HatchSpacing = GetLength(symbol.HatchDist + symbol.HatchLineWidth);//Ocad 8
				hatching.Color = new MapColorRef(symbol.HatchColor);
				hatching.HatchAngle = -0.1 * (symbol.HatchAngle1 - 90 * 10);
				mapSymbol.Hatching1 = hatching;
			}
			if (symbol.HatchMode == 2) {
				MapHatchInfo hatching = new MapHatchInfo();
				hatching.StrokeThickness = GetLength(symbol.HatchLineWidth);
				hatching.HatchSpacing = GetLength(symbol.HatchDist + symbol.HatchLineWidth);//Ocad 8
				hatching.Color = new MapColorRef(symbol.HatchColor);
				hatching.HatchAngle = -0.1 * (symbol.HatchAngle2 - 90 * 10);
				mapSymbol.Hatching2 = hatching;
			}
			if ((symbol.StructMode == 1 || symbol.StructMode == 2) && symbol.Elts.Length > 0) {
				MapTileInfo tileInfo = new MapTileInfo();
				tileInfo.TileSymbol = CreatePointSymbol(symbol.Elts, substractStrokeWidth);
				tileInfo.Rotation = -0.1 * symbol.StructAngle;
				tileInfo.TileWidth = GetLength(symbol.StructWidth);
				tileInfo.TileHeight = GetLength(symbol.StructHeight);
				if (symbol.StructMode == 2) {
					tileInfo.TileMode = MapTileMode.Honeycomb;
				} else {
					tileInfo.TileMode = MapTileMode.Normal;
				}
				mapSymbol.TileInfo = tileInfo;
			}
			return mapSymbol;
		}

		RectangleSymbol CreateRectangleSymbol(TRectSym symbol) {
			RectangleSymbol mapSymbol = new RectangleSymbol();
			mapSymbol.StrokeThickness = GetLength(symbol.LineWidth);
			mapSymbol.StrokeColor = new MapColorRef(symbol.LineColor);
			mapSymbol.CornerRadiusX = GetLength(symbol.Radius);
			mapSymbol.CornerRadiusY = GetLength(symbol.Radius);
			return mapSymbol;
		}

		TextSymbol CreateTextSymbol(TTextSym symbol) {
			TextSymbol mapSymbol = new TextSymbol();
			mapSymbol.TextColor = new MapColorRef(symbol.FontColor);
			mapSymbol.FontFamily = new FontFamily(symbol.FontName);
			mapSymbol.Style = FontStyles.Normal;
			mapSymbol.Weight = FontWeights.Normal;
			mapSymbol.Alignment = TextAlignment.Left;
			if (symbol.Alignment == 1) {
				mapSymbol.Alignment = TextAlignment.Center;
			} else if (symbol.Alignment == 2) {
				mapSymbol.Alignment = TextAlignment.Right;
			}
			if (symbol.Weight == 700) {
				mapSymbol.Weight = FontWeights.Bold;
			}
			if (symbol.Italic == true) {
				mapSymbol.Style = FontStyles.Italic;
			}
			double fontSize = ((double)symbol.FontSize) / 10.0 * 96.0 / 72.0;
			mapSymbol.FontSize = fontSize;
			mapSymbol.LineHeight = symbol.LineSpace * fontSize / 100.0;
			if (symbol.Tabs.Length > 0) {
				List<double> columnWidths = new List<double>();
				double currentDistance = 0;
				for (int i = 0; i < symbol.Tabs.Length; i++) {
					double currentWidth = GetLength(symbol.Tabs[i]) - currentDistance;
					columnWidths.Add(currentWidth);
					currentDistance += currentWidth;
				}
				mapSymbol.ColumnWidths = columnWidths;
			}
			if (symbol.LBOn != 0) {
				mapSymbol.LineBelowColor = new MapColorRef(symbol.LBColor);
				mapSymbol.LineBelowWidth = GetLength(symbol.LBWidth);
				mapSymbol.LineBelowOffset = GetLength(symbol.LBDist);
			}
			mapSymbol.ParagraphSpace = GetLength(symbol.ParaSpace);
			return mapSymbol;
		}

		LineTextSymbol CreateLineTextSymbol(TLTextSym symbol) {
			LineTextSymbol mapSymbol = new LineTextSymbol();
			mapSymbol.TextColor = new MapColorRef(symbol.FontColor);
			mapSymbol.FontFamily = new FontFamily(symbol.FontName);
			mapSymbol.Style = FontStyles.Normal;
			mapSymbol.Weight = FontWeights.Normal;
			mapSymbol.Alignment = TextAlignment.Left;
			if (symbol.Alignment == 1) {
				mapSymbol.Alignment = TextAlignment.Center;
			} else if (symbol.Alignment == 2) {
				mapSymbol.Alignment = TextAlignment.Right;
			} else if (symbol.Alignment == 3) {
				mapSymbol.Alignment = TextAlignment.Justify;
			}
			if (symbol.Weight == 700) {
				mapSymbol.Weight = FontWeights.Bold;
			}
			if (symbol.Italic == true) {
				mapSymbol.Style = FontStyles.Italic;
			}
			double fontSize = ((double)symbol.FontSize) / 10.0 * 96.0 / 72.0;
			mapSymbol.FontSize = fontSize;
			mapSymbol.CharacterSpace = symbol.CharSpace;
			return mapSymbol;
		}

		private TBaseSym GetSymbolByNum(int symbolNum) {
			for (int i = 0; i < symbols.Count; i++) {
				if (symbols[i].Sym == symbolNum) {
					return symbols[i];
				}
			}
			return null;
		}

		private MapObject CreateObject(TElement element) {
			if (element.Poly.Length <= 0) {
				return null;
			}
			TBaseSym symbol = GetSymbolByNum(element.Sym);
			if (symbol == null) {
				return null;
			}
			MapSymbolRef mapSymbol = new MapSymbolRef();
			mapSymbol.MapSymbolID = element.Sym;
			MapObject mapObject = null;
			if (symbol is TPointSym) {
				PointObject pointObject = new PointObject();
				pointObject.Position = ConvertToPoint(element.Poly[0]);
				pointObject.Rotation = -((double)element.Ang) * 0.1;
				mapObject = pointObject;
			} else if (symbol is TLineSym) {
				LineObject lineObject = new LineObject();
				lineObject.Data = ConvertToMapPathFigure(element.Poly);
				mapObject = lineObject;
			} else if (symbol is TAreaSym) {
				AreaObject areaObject = new AreaObject();
				areaObject.Data = ConvertToMapAreaGeometry(element.Poly);
				areaObject.FillRotation = -0.1 * element.Ang;
				mapObject = areaObject;
			} else if (symbol is TRectSym) {
				RectangleObject rectangleObject = new RectangleObject();
				Rect rect = ConvertToRect(element.Poly);
				if (rect.IsEmpty) {
					return null;
				}
				rectangleObject.Data = rect;
				mapObject = rectangleObject;
			} else if (symbol is TTextSym) {
				TextObject textObject = new TextObject();
				if (element.Poly.Length == 4) {
					Rect rect = ConvertToRect(element.Poly);
					if (rect.IsEmpty) {
						return null;
					}
					textObject.LayoutRect = rect;
				} else {
					textObject.LayoutRect = new Rect(ConvertToPoint(element.Poly[0]), new Size(0, 0));
				}
				textObject.Text = element.ExtraString;
				textObject.Rotation = -((double)element.Ang) * 0.1;
				mapObject = textObject;
			} else if (symbol is TLTextSym) {
				LineTextObject lineTextObject = new LineTextObject();
				lineTextObject.Text = element.ExtraString;
				lineTextObject.Data = ConvertToMapPathFigure(element.Poly);
				mapObject = lineTextObject;
			}
			if (mapObject != null) {
				mapObject.TargetSymbol = mapSymbol;
			}
			return mapObject;
		}


		//Ocad 8 classes

		private class TFileHeader {

			public Int16 OCADMark;
			public Int16 SectionMark;
			public Int16 Version;
			public Int16 Subversion;
			public Int32 FirstSymBlk;
			public Int32 FirstIdxBlk;
			public Int32 SetupPos;
			public Int32 SetupSize;
			public Int32 InfoPos;
			public Int32 InfoSize;
			public Int32 FirstStIndexBlk;
			public Int32 Reserved2;
			public Int32 Reserved3;
			public Int32 Reserved4;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				OCADMark = br.ReadInt16();
				if (OCADMark != 0x0cad) {
					return false;
				}
				SectionMark = br.ReadInt16();
				Version = br.ReadInt16();
				if (Version < 6 || Version > 8) {
					return false;
				}
				Subversion = br.ReadInt16();
				FirstSymBlk = br.ReadInt32();
				FirstIdxBlk = br.ReadInt32();
				SetupPos = br.ReadInt32();
				SetupSize = br.ReadInt32();
				InfoPos = br.ReadInt32();
				InfoSize = br.ReadInt32();
				FirstStIndexBlk = br.ReadInt32();
				Reserved2 = br.ReadInt32();
				Reserved3 = br.ReadInt32();
				Reserved4 = br.ReadInt32();
				return true;
			}

		}

		protected class TSymHeader {

			public Int16 nColors;
			public Int16 nColorSep;
			public Int16 CyanFreq;
			public Int16 CyanAng;
			public Int16 MagentaFreq;
			public Int16 MagentaAng;
			public Int16 YellowFreq;
			public Int16 YellowAng;
			public Int16 BlackFreq;
			public Int16 BlackAng;
			public Int16 Res1;
			public Int16 Res2;
			public TColorInfo[] aColorInfo = new TColorInfo[256];
			public TColorSep[] aColorSep = new TColorSep[32];

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				nColors = br.ReadInt16();
				nColorSep = br.ReadInt16();
				CyanFreq = br.ReadInt16();
				CyanAng = br.ReadInt16();
				MagentaFreq = br.ReadInt16();
				MagentaAng = br.ReadInt16();
				YellowFreq = br.ReadInt16();
				YellowAng = br.ReadInt16();
				BlackFreq = br.ReadInt16();
				BlackAng = br.ReadInt16();
				Res1 = br.ReadInt16();
				Res2 = br.ReadInt16();
				for (int i = 0; i < aColorInfo.Length; i++) {
					TColorInfo item = new TColorInfo();
					item.Load(stream);
					aColorInfo[i] = item;
				}
				for (int i = 0; i < aColorSep.Length; i++) {
					TColorSep item = new TColorSep();
					item.Load(stream);
					aColorSep[i] = item;
				}
				return true;
			}

		}

		protected class TColorInfo {

			public Int16 ColorNum;
			public Int16 Reserved;
			public TCmyk Color;
			public string ColorName;
			public byte[] SepPercentage = new byte[32];

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				ColorNum = br.ReadInt16();
				Reserved = br.ReadInt16();
				TCmyk color = new TCmyk();
				color.Load(stream);
				Color = color;
				byte[] chars = br.ReadBytes(32);
				string s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				ColorName = s;
				SepPercentage = br.ReadBytes(32);
				return true;
			}

		}

		protected class TColorSep {

			public string SepName;
			public TCmyk Color;
			public Int16 RasterFreq;
			public Int16 RasterAngle;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				byte[] chars = br.ReadBytes(16);
				string s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				SepName = s;
				TCmyk color = new TCmyk();
				color.Load(stream);
				Color = color;
				RasterFreq = br.ReadInt16();
				RasterAngle = br.ReadInt16();
				return true;
			}

		}

		protected class TCmyk {

			public byte cyan;
			public byte magenta;
			public byte yellow;
			public byte black;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				cyan = br.ReadByte();
				magenta = br.ReadByte();
				yellow = br.ReadByte();
				black = br.ReadByte();
				return true;
			}

		}

		private abstract class TBaseSym {

			public static TBaseSym Create(Stream stream) {
				TBaseSym symbol;
				long pos = stream.Position;
				stream.Seek(sizeof(Int16) * 2, SeekOrigin.Current);
				BinaryReader br = new BinaryReader(stream);
				Int16 otp = br.ReadInt16();
				byte type = br.ReadByte();
				stream.Seek(pos, SeekOrigin.Begin);
				switch (otp) {
					case 1:
						symbol = new TPointSym();
						break;
					case 2:
						if (type == 0) {
							symbol = new TLineSym();
						} else if (type == 1) {
							symbol = new TLTextSym();
						} else {
							return null;
						}
						break;
					case 3:
						symbol = new TAreaSym();
						break;
					case 4:
						symbol = new TTextSym();
						break;
					case 5:
						symbol = new TRectSym();
						break;
					default:
						return null;
				}
				symbol.Load(stream);
				return symbol;
			}

			public Int16 Size;
			public Int16 Sym;
			public Int16 Otp;
			public byte SymTp;
			public byte Flags;
			public Int16 Extent;
			public bool Selected;
			public byte Status;
			public Int16 Res2;
			public Int16 Res3;
			public Int32 FilePos;
			public byte[] Cols = new byte[32];
			public string Description;
			public byte[] IconBits = new byte[264];

			public virtual bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				Size = br.ReadInt16();
				Sym = br.ReadInt16();
				Otp = br.ReadInt16();
				SymTp = br.ReadByte();
				Flags = br.ReadByte();
				Extent = br.ReadInt16();
				Selected = br.ReadBoolean();
				Status = br.ReadByte();
				Res2 = br.ReadInt16();
				Res3 = br.ReadInt16();
				FilePos = br.ReadInt32();
				Cols = br.ReadBytes(32);
				byte[] chars = br.ReadBytes(32);
				string s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				Description = s;
				IconBits = br.ReadBytes(264);
				return true;
			}

		}

		private class TPointSym : TBaseSym {

			public Int16 DataSize;
			public Int16 Reserved;

			public override bool Load(Stream stream) {
				base.Load(stream);
				BinaryReader br = new BinaryReader(stream);
				DataSize = br.ReadInt16();
				Reserved = br.ReadInt16();
				int i = 0;
				List<TSymElt> elts = new List<TSymElt>();
				while (i < DataSize) {
					TSymElt elt = new TSymElt();
					elt.Load(stream);
					elts.Add(elt);
					i += elt.stnPoly + 2; //2 = size of TSymElt Header
				}
				Elts = elts.ToArray();
				return true;
			}

			//Custom Members

			public TSymElt[] Elts;

		}

		private class TLineSym : TBaseSym {

			public Int16 LineColor;
			public Int16 LineWidth;
			public UInt16 LineEnds;
			public Int16 DistFromStart;
			public Int16 DistToEnd;
			public Int16 MainLength;
			public Int16 EndLength;
			public Int16 MainGap;
			public Int16 SecGap;
			public Int16 EndGap;
			public Int16 MinSym;
			public Int16 nPrimSym;
			public Int16 PrimSymDist;
			public UInt16 DblMode;
			public UInt16 DblFlags;
			public Int16 DblFillColor;
			public Int16 DblLeftColor;
			public Int16 DblRightColor;
			public Int16 DblWidth;
			public Int16 DblLeftWidth;
			public Int16 DblRightWidth;
			public Int16 DblLength;
			public Int16 DblGap;
			public Int16[] DblRes = new Int16[3];
			public UInt16 DecMode;
			public Int16 DecLast;
			public Int16 DecRes;
			public Int16 FrColor;
			public Int16 FrWidth;
			public Int16 FrStyle;
			public Int16 PrimDSize;
			public Int16 SecDSize;
			public Int16 CornerDSize;
			public Int16 StartDSize;
			public Int16 EndDSize;
			public Int16 Reserved;

			public override bool Load(Stream stream) {
				base.Load(stream);
				BinaryReader br = new BinaryReader(stream);
				LineColor = br.ReadInt16();
				LineWidth = br.ReadInt16();
				LineEnds = br.ReadUInt16();
				DistFromStart = br.ReadInt16();
				DistToEnd = br.ReadInt16();
				MainLength = br.ReadInt16();
				EndLength = br.ReadInt16();
				MainGap = br.ReadInt16();
				SecGap = br.ReadInt16();
				EndGap = br.ReadInt16();
				MinSym = br.ReadInt16();
				nPrimSym = br.ReadInt16();
				PrimSymDist = br.ReadInt16();
				DblMode = br.ReadUInt16();
				DblFlags = br.ReadUInt16();
				DblFillColor = br.ReadInt16();
				DblLeftColor = br.ReadInt16();
				DblRightColor = br.ReadInt16();
				DblWidth = br.ReadInt16();
				DblLeftWidth = br.ReadInt16();
				DblRightWidth = br.ReadInt16();
				DblLength = br.ReadInt16();
				DblGap = br.ReadInt16();
				for (int j = 0; j < DblRes.Length; j++) {
					DblRes[j] = br.ReadInt16();
				}
				DecMode = br.ReadUInt16();
				DecLast = br.ReadInt16();
				DecRes = br.ReadInt16();
				FrColor = br.ReadInt16();
				FrWidth = br.ReadInt16();
				FrStyle = br.ReadInt16();
				PrimDSize = br.ReadInt16();
				SecDSize = br.ReadInt16();
				CornerDSize = br.ReadInt16();
				StartDSize = br.ReadInt16();
				EndDSize = br.ReadInt16();
				Reserved = br.ReadInt16();
				int i;
				List<TSymElt> elts;
				elts = new List<TSymElt>();
				i = 0;
				while (i < PrimDSize) {
					TSymElt elt = new TSymElt();
					elt.Load(stream);
					elts.Add(elt);
					i += elt.stnPoly + 2; //2 = size of TSymElt Header
				}
				MainSymbol = elts.ToArray();
				elts = new List<TSymElt>();
				i = 0;
				while (i < SecDSize) {
					TSymElt elt = new TSymElt();
					elt.Load(stream);
					elts.Add(elt);
					i += elt.stnPoly + 2; //2 = size of TSymElt Header
				}
				SecondarySymbol = elts.ToArray();
				elts = new List<TSymElt>();
				i = 0;
				while (i < CornerDSize) {
					TSymElt elt = new TSymElt();
					elt.Load(stream);
					elts.Add(elt);
					i += elt.stnPoly + 2; //2 = size of TSymElt Header
				}
				CornerSymbol = elts.ToArray();
				elts = new List<TSymElt>();
				i = 0;
				while (i < StartDSize) {
					TSymElt elt = new TSymElt();
					elt.Load(stream);
					elts.Add(elt);
					i += elt.stnPoly + 2; //2 = size of TSymElt Header
				}
				StartSymbol = elts.ToArray();
				elts = new List<TSymElt>();
				i = 0;
				while (i < EndDSize) {
					TSymElt elt = new TSymElt();
					elt.Load(stream);
					elts.Add(elt);
					i += elt.stnPoly + 2; //2 = size of TSymElt Header
				}
				EndSymbol = elts.ToArray();
				return true;
			}

			//Custom Members

			public TSymElt[] MainSymbol;
			public TSymElt[] SecondarySymbol;
			public TSymElt[] CornerSymbol;
			public TSymElt[] StartSymbol;
			public TSymElt[] EndSymbol;

		}

		private class TAreaSym : TBaseSym {

			public UInt16 AreaFlags;
			public Int16 FillOn; //wordbool
			public Int16 FillColor;
			public Int16 HatchMode;
			public Int16 HatchColor;
			public Int16 HatchLineWidth;
			public Int16 HatchDist;
			public Int16 HatchAngle1;
			public Int16 HatchAngle2;
			public Int16 HatchRes;
			public Int16 StructMode;
			public Int16 StructWidth;
			public Int16 StructHeight;
			public Int16 StructAngle;
			public Int16 StructRes;
			public Int16 DataSize;

			public override bool Load(Stream stream) {
				base.Load(stream);
				BinaryReader br = new BinaryReader(stream);
				AreaFlags = br.ReadUInt16();
				FillOn = br.ReadInt16();
				FillColor = br.ReadInt16();
				HatchMode = br.ReadInt16();
				HatchColor = br.ReadInt16();
				HatchLineWidth = br.ReadInt16();
				HatchDist = br.ReadInt16();
				HatchAngle1 = br.ReadInt16();
				HatchAngle2 = br.ReadInt16();
				HatchRes = br.ReadInt16();
				StructMode = br.ReadInt16();
				StructWidth = br.ReadInt16();
				StructHeight = br.ReadInt16();
				StructAngle = br.ReadInt16();
				StructRes = br.ReadInt16();
				DataSize = br.ReadInt16();
				int i = 0;
				List<TSymElt> elts = new List<TSymElt>();
				while (i < DataSize) {
					TSymElt elt = new TSymElt();
					elt.Load(stream);
					elts.Add(elt);
					i += elt.stnPoly + 2; //2 = size of TSymElt Header
				}
				Elts = elts.ToArray();
				return true;
			}

			//Custom Members

			public TSymElt[] Elts;

		}

		private class TTextSym : TBaseSym {

			public string FontName;
			public Int16 FontColor;
			public Int16 FontSize;
			public Int16 Weight;
			public bool Italic;
			public byte CharSet;
			public Int16 CharSpace;
			public Int16 WordSpace;
			public Int16 Alignment;
			public Int16 LineSpace;
			public Int16 ParaSpace;
			public Int16 IndentFirst;
			public Int16 IndentOther;
			public Int16 nTabs;
			public Int32[] Tabs;
			public Int16 LBOn; //wordbool
			public Int16 LBColor;
			public Int16 LBWidth;
			public Int16 LBDist;
			public Int16 Res4;
			public Int16 FrMode;
			public string FrName;
			public Int16 FrColor;
			public Int16 FrSize;
			public Int16 FrWeight;
			public UInt16 FrItalic;
			public Int16 FrOfX;
			public Int16 FrOfY;

			public override bool Load(Stream stream) {
				base.Load(stream);
				BinaryReader br = new BinaryReader(stream);
				string s;
				byte[] chars;
				chars = br.ReadBytes(32);
				s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				FontName = s;
				FontColor = br.ReadInt16();
				FontSize = br.ReadInt16();
				Weight = br.ReadInt16();
				Italic = br.ReadBoolean();
				CharSet = br.ReadByte();
				CharSpace = br.ReadInt16();
				WordSpace = br.ReadInt16();
				Alignment = br.ReadInt16();
				LineSpace = br.ReadInt16();
				ParaSpace = br.ReadInt16();
				IndentFirst = br.ReadInt16();
				IndentOther = br.ReadInt16();
				nTabs = br.ReadInt16();
				Tabs = new Int32[nTabs];
				int i;
				for (i = 0; i < Tabs.Length; i++) {
					Tabs[i] = br.ReadInt32();
				}
				while (i < 32) {
					br.ReadInt32();
					i++;
				}
				LBOn = br.ReadInt16();
				LBColor = br.ReadInt16();
				LBWidth = br.ReadInt16();
				LBDist = br.ReadInt16();
				Res4 = br.ReadInt16();
				FrMode = br.ReadInt16();
				chars = br.ReadBytes(32);
				s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				FrName = s;
				FrColor = br.ReadInt16();
				FrSize = br.ReadInt16();
				FrWeight = br.ReadInt16();
				FrItalic = br.ReadUInt16();
				FrOfX = br.ReadInt16();
				FrOfY = br.ReadInt16();
				return true;
			}

		}

		private class TRectSym : TBaseSym {

			public Int16 LineColor;
			public Int16 LineWidth;
			public Int16 Radius;
			public UInt16 GridFlags;
			public Int16 CellWidth;
			public Int16 CellHeight;
			public Int16 ResGridLineColor;
			public Int16 ResGridLineWidth;
			public Int16 UnnumCells;
			public string UnnumText;
			public Int16 GridRes2;
			public string ResFontName;
			public Int16 ResFontColor;
			public Int16 ResFontSize;
			public Int16 ResWeight;
			public Int16 ResItalic; //wordbool
			public Int16 ResOfsX;
			public Int16 ResOfsY;

			public override bool Load(Stream stream) {
				base.Load(stream);
				BinaryReader br = new BinaryReader(stream);
				string s;
				byte[] chars;
				LineColor = br.ReadInt16();
				LineWidth = br.ReadInt16();
				Radius = br.ReadInt16();
				GridFlags = br.ReadUInt16();
				CellWidth = br.ReadInt16();
				CellHeight = br.ReadInt16();
				ResGridLineColor = br.ReadInt16();
				ResGridLineWidth = br.ReadInt16();
				UnnumCells = br.ReadInt16();
				chars = br.ReadBytes(4);
				s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				UnnumText = s;
				GridRes2 = br.ReadInt16();
				chars = br.ReadBytes(32);
				s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				ResFontName = s;
				ResFontColor = br.ReadInt16();
				ResFontSize = br.ReadInt16();
				ResWeight = br.ReadInt16();
				ResItalic = br.ReadInt16();
				ResOfsX = br.ReadInt16();
				ResOfsY = br.ReadInt16();
				return true;
			}

		}

		private class TLTextSym : TBaseSym {

			public string FontName;
			public Int16 FontColor;
			public Int16 FontSize;
			public Int16 Weight;
			public bool Italic;
			public byte CharSet;
			public Int16 CharSpace;
			public Int16 WordSpace;
			public Int16 Alignment;
			public Int16 FrMode;
			public string FrName;
			public Int16 FrColor;
			public Int16 FrSize;
			public Int16 FrWeight;
			public Int16 FrItalic;//wordbool;
			public Int16 FrOfX;
			public Int16 FrOfY;

			public override bool Load(Stream stream) {
				base.Load(stream);
				BinaryReader br = new BinaryReader(stream);
				string s;
				byte[] chars;
				chars = br.ReadBytes(32);
				s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				FontName = s;
				FontColor = br.ReadInt16();
				FontSize = br.ReadInt16();
				Weight = br.ReadInt16();
				Italic = br.ReadBoolean();
				CharSet = br.ReadByte();
				CharSpace = br.ReadInt16();
				WordSpace = br.ReadInt16();
				Alignment = br.ReadInt16();
				FrMode = br.ReadInt16();
				chars = br.ReadBytes(32);
				s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				FrName = s;
				FrColor = br.ReadInt16();
				FrSize = br.ReadInt16();
				FrWeight = br.ReadInt16();
				FrItalic = br.ReadInt16();
				FrOfX = br.ReadInt16();
				FrOfY = br.ReadInt16();
				return true;
			}

		}

		private class TIndexBlock {

			public Int32 NextBlock;
			public TIndex[] IndexArr = new TIndex[256];

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				NextBlock = br.ReadInt32();
				for (int i = 0; i < IndexArr.Length; i++) {
					TIndex index = new TIndex();
					index.Load(stream);
					IndexArr[i] = index;
				}
				return true;
			}

		}

		private class TIndex {

			public TCord LowerLeft;
			public TCord UpperRight;
			public Int32 Pos;
			public UInt16 Len; //word
			public Int16 Sym;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				TCord cord;
				cord = new TCord();
				cord.Load(stream);
				LowerLeft = cord;
				cord = new TCord();
				cord.Load(stream);
				UpperRight = cord;
				Pos = br.ReadInt32();
				Len = br.ReadUInt16();
				Sym = br.ReadInt16();
				return true;
			}

		}

		private class TElement {

			public Int16 Sym;
			public byte Otp;
			public byte Unicode;
			public Int16 nItem;
			public Int16 nText;
			public Int16 Ang;
			public Int16 Res1;
			public Int32 ResHeight;
			public string ResId;
			public TCord[] Poly;
			//Semi-Custom
			public string ExtraString;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
				Sym = br.ReadInt16();
				Otp = br.ReadByte();
				Unicode = br.ReadByte();
				nItem = br.ReadInt16();
				nText = br.ReadInt16();
				Ang = br.ReadInt16();
				Res1 = br.ReadInt16();
				ResHeight = br.ReadInt32();
				byte[] chars = br.ReadBytes(16);
				string s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				ResId = s;
				Poly = new TCord[nItem];
				for (int i = 0; i < nItem; i++) {
					TCord cord;
					cord = new TCord();
					cord.Load(stream);
					Poly[i] = cord;
				}
				if (nText > 0) {
					if (Unicode == 1) {
						char[] text = br.ReadChars(sizeof(Int32) * nText);//TCordSize*nText/2
						int pos = text.Length;
						for (int i = 0; i < text.Length; i++) {
							if (text[i] == '\0') {
								pos = i;
								break;
							}
						}
						ExtraString = new string(text, 0, pos);
					} else {
						byte[] text = br.ReadBytes(sizeof(Int32) * 2 * nText);//TCordSize*nText
						int pos = text.Length;
						for (int i = 0; i < text.Length; i++) {
							if (text[i] == '\0') {
								pos = i;
								break;
							}
						}
						Encoding encoding = Encoding.GetEncoding(1252);//Western European Codepage
						ExtraString = encoding.GetString(text, 0, pos);
					}
				}
				return true;
			}

		}

		private class TStp {

			public TCord Offset;
			public double rGridDist;
			public Int16 WorkMode;
			public Int16 LineMode;
			public Int16 EditMode;
			public Int16 ActSym;
			public double MapScale;
			public double RealWorldOfsX;
			public double RealWorldOfsY;
			public double RealWorldAngle;
			public double RealWorldGrid;
			public double GpsAngle;
			public TGpsAdjPoint[] aGpsAdjust = new TGpsAdjPoint[12];
			public Int32 nGpsAdjust;
			public double DraftScaleX;
			public double DraftScaleY;
			public TCord TempOffset;
			public string TemplateFileName;
			public Int16 TemplateEnabled;
			public Int16 TempResol;
			public double rTempAng;
			public TCord Reserved1;
			public double Reserved2;
			public TCord PrLowerLeft;
			public TCord PrUpperRight;
			public Int16 PrGrid;
			public Int16 PrGridColor;
			public Int16 PrOverlapX;
			public Int16 PrOverlapY;
			public double PrintScale;
			public Int16 PrIntensity;
			public Int16 PrLineWidth;
			public Int16 PrReserved;
			public Int16 PrStdFonts;
			public Int16 PrReserved2;
			public Int16 PrReserved3;
			public TCord PartialLowerLeft;
			public TCord PartialUpperRight;
			public double Zoom;
			public TZoomRec[] ZoomHist = new TZoomRec[9];
			public Int32 nZoomHist;
			public Int16 RealWorldCord;
			public string FileName;
			public Int16 HatchAreas;
			public Int16 DimTemp;
			public Int16 HideTemp;
			public Int16 TempMode;
			public Int16 TempColor;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
				string s;
				byte[] chars;
				try {
					Offset = new TCord();
					Offset.Load(stream);
					rGridDist = br.ReadDouble();
					WorkMode = br.ReadInt16();
					LineMode = br.ReadInt16();
					EditMode = br.ReadInt16();
					ActSym = br.ReadInt16();
					MapScale = br.ReadDouble();
				} catch (Exception) {
					return true;
				}
				try {
					RealWorldOfsX = br.ReadDouble();
					RealWorldOfsY = br.ReadDouble();
					RealWorldAngle = br.ReadDouble();
					RealWorldGrid = br.ReadDouble();
					GpsAngle = br.ReadDouble();
					for (int i = 0; i < 12; i++) {
						aGpsAdjust[i] = new TGpsAdjPoint();
						aGpsAdjust[i].Load(stream);
					}
					nGpsAdjust = br.ReadInt32();
					DraftScaleX = br.ReadDouble();
					DraftScaleY = br.ReadDouble();
					TempOffset = new TCord();
					TempOffset.Load(stream);
					chars = br.ReadBytes(256);
					s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
					TemplateFileName = s;
					TemplateEnabled = br.ReadInt16();
					TempResol = br.ReadInt16();
					rTempAng = br.ReadDouble();
					Reserved1 = new TCord();
					Reserved1.Load(stream);
					Reserved2 = br.ReadDouble();
					PrLowerLeft = new TCord();
					PrLowerLeft.Load(stream);
					PrUpperRight = new TCord();
					PrUpperRight.Load(stream);
					PrGrid = br.ReadInt16();
					PrGridColor = br.ReadInt16();
					PrOverlapX = br.ReadInt16();
					PrOverlapY = br.ReadInt16();
					PrintScale = br.ReadDouble();
					PrIntensity = br.ReadInt16();
					PrLineWidth = br.ReadInt16();
					PrReserved = br.ReadInt16();
					PrStdFonts = br.ReadInt16();
					PrReserved2 = br.ReadInt16();
					PrReserved3 = br.ReadInt16();
					PartialLowerLeft = new TCord();
					PartialLowerLeft.Load(stream);
					PartialUpperRight = new TCord();
					PartialUpperRight.Load(stream);
					Zoom = br.ReadDouble();
					for (int i = 0; i < 9; i++) {
						ZoomHist[i] = new TZoomRec();
						ZoomHist[i].Load(stream);
					}
					nZoomHist = br.ReadInt32();
					RealWorldCord = br.ReadInt16();
					chars = br.ReadBytes(256);
					s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
					FileName = s;
					HatchAreas = br.ReadInt16();
					DimTemp = br.ReadInt16();
					HideTemp = br.ReadInt16();
					TempMode = br.ReadInt16();
					TempColor = br.ReadInt16();
				} catch (Exception) {
					return true;
				}
				return true;
			}

		}

		private class TGpsAdjPoint {

			public TCord lpMap;
			public double Lat;
			public double Long;
			public string Name;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
				string s;
				byte[] chars;
				lpMap = new TCord();
				lpMap.Load(stream);
				Lat = br.ReadDouble();
				Long = br.ReadDouble();
				chars = br.ReadBytes(16);
				//s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				int pos = -1;
				for (int i = 0; i < chars.Length; i++) {
					if (chars[i] == 0) {
						pos = i;
						break;
					}
				}
				if (pos < 0) {
					pos = chars.Length;
				}
				if (pos > 0) {
					s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 0, pos);
				} else {
					s = "";
				}
				Name = s;
				return true;
			}

		}

		private class TZoomRec {

			public double Zoom;
			public TCord Offset;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
				Zoom = br.ReadDouble();
				Offset = new TCord();
				Offset.Load(stream);
				return true;
			}

		}

	}

}
