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 Ocad9FileReader : OcadBaseFileReader {

		TFileHeader header;
		List<TBaseSym> symbols;
		List<TElement> objects;
		List<CustomParameterString> strings;
		bool substractStrokeWidth;

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

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

		public override bool LoadMap(Stream stream) {
			UnloadMap();
			bool result;
			header = new TFileHeader();
			result = header.Load(stream);
			if (!result) {
				UnloadMap();
				return false;
			}
			symbols = ReadSymbols(stream);
			objects = ReadObjects(stream);
			strings = ReadStrings(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 = GetScale();
			return mapInfo;
		}

		public override List<MapColor> GetColors() {
			if (!IsLoaded) {
				return null;
			}
			List<MapColor> colors = new List<MapColor>();
			for (int i = 0; i < strings.Count; i++) {
				if (strings[i].Type == 9) {
					CustomColor color = new CustomColor();
					color.Load(strings[i]);
					MapColor mapColor = new MapColor();
					mapColor.Color = color.Color;
					mapColor.ID = color.Num;
					colors.Insert(0, mapColor);
				}
			}
			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;
		}

		double GetScale() {
			if (!IsLoaded) {
				return 0;
			}
			CustomParameterString scaleString = GetString(1039);
			if (scaleString == null) {
				return 0;
			}
			string scale = scaleString["m"];
			if (scale == null || scale == "") {
				return 0;
			}
			double d = 0;
			if (!double.TryParse(scale, out d)) {
				return 0;
			}
			return d;
		}

		CustomParameterString GetString(int code) {
			for (int i = 0; i < strings.Count; i++) {
				if (strings[i].Type == code) {
					return strings[i];
				}
			}
			return null;
		}

		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 || indexBlock.IndexArr[i].Status != 1) {
						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 List<CustomParameterString> ReadStrings(Stream stream) {
			List<CustomParameterString> strings = new List<CustomParameterString>();
			long pos = stream.Seek((long)header.FirstStIndexBlk, SeekOrigin.Begin);
			while (pos > 0) {
				TStringIndexBlock indexBlock = new TStringIndexBlock();
				indexBlock.Load(stream);
				for (int i = 0; i < indexBlock.Table.Length; i++) {
					if (indexBlock.Table[i].Pos == 0 || indexBlock.Table[i].Len == 0) {
						continue;
					}
					CustomParameterString param = new CustomParameterString();
					param.Load(stream, indexBlock.Table[i]);
					strings.Add(param);
				}
				pos = stream.Seek((long)indexBlock.FilePos, SeekOrigin.Begin);
			}
			return strings;
		}

		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 == true) {
				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);//Ocad 9
				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);//Ocad 9
				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 9 Classes

		private class TFileHeader {

			public Int16 OCADMark;
			public byte FileType;
			public byte FileStatus;
			public Int16 Version;
			public Int16 Subversion;
			public Int32 FirstSymBlk;
			public Int32 FirstIdxBlk;
			public Int32 Res0;
			public Int32 Res1;
			public Int32 Res2;
			public Int32 InfoSize;
			public Int32 FirstStIndexBlk;
			public Int32 FileNamePos;
			public Int32 FileNameSize;
			public Int32 Res4;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				OCADMark = br.ReadInt16();
				if (OCADMark != 0x0cad) {
					return false;
				}
				FileType = br.ReadByte();
				FileStatus = br.ReadByte();
				Version = br.ReadInt16();
				if (Version != 9 && Version != 10) {
					return false;
				}
				Subversion = br.ReadInt16();
				FirstSymBlk = br.ReadInt32();
				FirstIdxBlk = br.ReadInt32();
				Res0 = br.ReadInt32();
				Res1 = br.ReadInt32();
				Res2 = br.ReadInt32();
				InfoSize = br.ReadInt32();
				FirstStIndexBlk = br.ReadInt32();
				FileNamePos = br.ReadInt32();
				FileNameSize = br.ReadInt32();
				Res4 = br.ReadInt32();
				return true;
			}

		}

		private abstract class TBaseSym {

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

			public Int32 Size;
			public Int32 Sym;
			public byte Otp;
			public byte Flags;
			public bool Selected;
			public byte Status;
			public byte DrawingTool;
			public byte CsMode;
			public byte CsObjType;
			public byte CsCdFlags;
			public Int32 Extent;
			public Int32 FilePos;
			public Int16 Group;
			public Int16 nColors;
			public Int16[] Colors = new Int16[14];//?
			public string Description;
			public byte[] IconBits = new byte[484];

			public virtual bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				Size = br.ReadInt32();
				Sym = br.ReadInt32();
				Otp = br.ReadByte();
				Flags = br.ReadByte();
				Selected = br.ReadBoolean();
				Status = br.ReadByte();
				DrawingTool = br.ReadByte();
				CsMode = br.ReadByte();
				CsObjType = br.ReadByte();
				CsCdFlags = br.ReadByte();
				Extent = br.ReadInt32();
				FilePos = br.ReadInt32();
				Group = br.ReadInt16();
				nColors = br.ReadInt16();
				for (int i = 0; i < Colors.Length; i++) {
					Colors[i] = br.ReadInt16();
				}
				byte[] chars = br.ReadBytes(32);
				string s = System.Text.ASCIIEncoding.ASCII.GetString(chars, 1, (int)chars[0]);
				Description = s;
				IconBits = br.ReadBytes(484);
				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 Int32 BorderSym;
			public Int16 FillColor;
			public Int16 HatchMode;
			public Int16 HatchColor;
			public Int16 HatchLineWidth;
			public Int16 HatchDist;
			public Int16 HatchAngle1;
			public Int16 HatchAngle2;
			public bool FillOn;
			public bool BorderOn;
			public Int16 StructMode;
			public Int16 StructWidth;
			public Int16 StructHeight;
			public Int16 StructAngle;
			public Int16 Res;
			public Int16 DataSize;

			public override bool Load(Stream stream) {
				base.Load(stream);
				BinaryReader br = new BinaryReader(stream);
				BorderSym = br.ReadInt32();
				FillColor = br.ReadInt16();
				HatchMode = br.ReadInt16();
				HatchColor = br.ReadInt16();
				HatchLineWidth = br.ReadInt16();
				HatchDist = br.ReadInt16();
				HatchAngle1 = br.ReadInt16();
				HatchAngle2 = br.ReadInt16();
				FillOn = br.ReadBoolean();
				BorderOn = br.ReadBoolean();
				StructMode = br.ReadInt16();
				StructWidth = br.ReadInt16();
				StructHeight = br.ReadInt16();
				StructAngle = br.ReadInt16();
				Res = 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 byte FrMode;
			public byte FrLineStyle;
			public string Res2;
			public Int16 FrLeft;
			public Int16 FrBottom;
			public Int16 FrRight;
			public Int16 FrTop;
			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.ReadByte();
				FrLineStyle = br.ReadByte();
				chars = br.ReadBytes(24);
				Res2 = "";
				FrLeft = br.ReadInt16();
				FrBottom = br.ReadInt16();
				FrRight = br.ReadInt16();
				FrTop = br.ReadInt16();
				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 Int32 Len;
			public Int32 Sym;
			public byte ObjType;
			public byte Res1;
			public byte Status;
			public byte ViewType;
			public Int16 Color;
			public Int16 Res2;
			public Int16 ImpLayer;
			public Int16 Res3;

			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.ReadInt32();
				Sym = br.ReadInt32();
				ObjType = br.ReadByte();
				Res1 = br.ReadByte();
				Status = br.ReadByte();
				ViewType = br.ReadByte();
				Color = br.ReadInt16();
				Res2 = br.ReadInt16();
				ImpLayer = br.ReadInt16();
				Res3 = br.ReadInt16();
				return true;
			}

		}

		private class TElement {

			public Int32 Sym;
			public byte Otp;
			public byte Res0;
			public Int16 Ang;
			public Int32 nItem;
			public Int16 nText;
			public Int16 Res1;
			public Int32 Col;
			public Int16 LineWidth;
			public Int16 DiamFlags;
			public double Res2;
			public double Res3;
			public TCord[] Poly;
			//Semi-Custom
			public string ExtraString;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
				Sym = br.ReadInt32();
				Otp = br.ReadByte();
				Res0 = br.ReadByte();
				Ang = br.ReadInt16();
				nItem = br.ReadInt32();
				nText = br.ReadInt16();
				Res1 = br.ReadInt16();
				Col = br.ReadInt32();
				LineWidth = br.ReadInt16();
				DiamFlags = br.ReadInt16();
				Res2 = br.ReadDouble();
				Res3 = br.ReadDouble();
				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) {
					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);
				}
				return true;
			}

		}

		private class TStringIndexBlock {

			public Int32 FilePos;
			public TStringIndex[] Table = new TStringIndex[256];

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

		}

		private class TStringIndex {

			public Int32 Pos;
			public Int32 Len;
			public Int32 RecType;
			public Int32 ObjIndex;

			public bool Load(Stream stream) {
				BinaryReader br = new BinaryReader(stream);
				Pos = br.ReadInt32();
				Len = br.ReadInt32();
				RecType = br.ReadInt32();
				ObjIndex = br.ReadInt32();
				return true;
			}
		
		}

		private class CustomParameterString {

			string rawString;

			Int32 type;
			public Int32 Type {
				get {
					return type;
				}
			}

			string firstField;
			public string FirstField {
				get {
					return firstField;
				}
			}

			private List<KeyValuePair<string,string>> parameters;

			public bool Load(Stream stream, TStringIndex tStringIndex) {
				if (tStringIndex.Len == 0 || tStringIndex.Pos == 0) {
					rawString = "";
					return true;
				}
				stream.Seek((long)tStringIndex.Pos, SeekOrigin.Begin);
				string str = "";
				StreamReader reader = new StreamReader(stream);
				char[] chars = new char[tStringIndex.Len];
				reader.Read(chars, 0, tStringIndex.Len);
				int i;
				for (i = 0; i < chars.Length; i++) {
					if (chars[i] == '\0') {
						break;
					}
				}
				str = new string(chars, 0, i);
				rawString = str;
				type = tStringIndex.RecType;
				parameters = new List<KeyValuePair<string, string>>();
				string[] strs = rawString.Split('\t');
				if (strs.Length == 0) {
					return true;
				}
				firstField = strs[0];
				for (int j = 1; j < strs.Length; j ++) {
					if (strs[j].Length < 2) {
						continue;
					}
					KeyValuePair<string, string> pair = new KeyValuePair<string, string>(strs[j].Substring(0,1), strs[j].Substring(1));
					parameters.Add(pair);
				}
				return true;
			}

			public string this[string key] {
				get {
					for (int i = 0; i < parameters.Count; i++) {
						if (parameters[i].Key == key) {
							return parameters[i].Value;
						}
					}
					return null;
				}
			}

		}

		private class CustomColor {

			public CustomColor() {
				isTransparent = false;
			}

			Int16 num;
			public Int16 Num {
				get {
					return num;
				}
			}

			Color color;
			public Color Color {
				get {
					return color;
				}
			}

			double c;
			double m;
			double y;
			double k;
			double t;
			bool isTransparent;

			public bool Load(CustomParameterString str) {
				if (str.Type != 9) {
					return false;
				}
				string temp;
				temp = str["n"];
				double val;
				if (temp != null) {
					num = Convert.ToInt16(temp);
				}
				temp = str["c"];
				if (temp != null) {
					val = Convert.ToDouble(temp);
					c = val;
				}
				temp = str["m"];
				if (temp != null) {
					val = Convert.ToDouble(temp);
					m = val;
				}
				temp = str["y"];
				if (temp != null) {
					val = Convert.ToDouble(temp);
					y = val;
				}
				temp = str["k"];
				if (temp != null) {
					val = Convert.ToDouble(temp);
					k = val;
				}
				temp = str["t"];
				if (temp != null) {
					val = Convert.ToDouble(temp);
					t = val;
					isTransparent = true;
				}
				color = GetColor();
				return true;
			}

			private Color GetColor() {
				Color color;
				byte a, r, g, b;
				double A, R, G, B;
				double C, M, Y, K;
				C = c / 100.0;
				M = m / 100.0;
				Y = y / 100.0;
				K = k / 100.0;
				A = t / 100.0;
				R = C * (1.0 - K) + K;
				G = M * (1.0 - K) + K;
				B = Y * (1.0 - K) + K;
				A = A * 255.0;
				R = (1.0 - R) * 255.0;
				G = (1.0 - G) * 255.0;
				B = (1.0 - B) * 255.0;
				a = (byte)Math.Round(A);
				r = (byte)Math.Round(R);
				g = (byte)Math.Round(G);
				b = (byte)Math.Round(B);
				if (isTransparent && t < 100) {
					color = Color.FromArgb(a, r, g, b);
				} else {
					color = Color.FromRgb(r, g, b);
				}
				return color;
			}

		}

	}

}
