/*
	Copyright (c) 2015 Ki

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

using System;
using System.Diagnostics;
using System.IO;

namespace ILSpy.BamlDecompiler.Baml {
	internal enum BamlRecordType : byte {
		ClrEvent = 0x13,
		Comment = 0x17,
		AssemblyInfo = 0x1c,
		AttributeInfo = 0x1f,
		ConstructorParametersStart = 0x2a,
		ConstructorParametersEnd = 0x2b,
		ConstructorParameterType = 0x2c,
		ConnectionId = 0x2d,
		ContentProperty = 0x2e,
		DefAttribute = 0x19,
		DefAttributeKeyString = 0x26,
		DefAttributeKeyType = 0x27,
		DeferableContentStart = 0x25,
		DefTag = 0x18,
		DocumentEnd = 0x2,
		DocumentStart = 0x1,
		ElementEnd = 0x4,
		ElementStart = 0x3,
		EndAttributes = 0x1a,
		KeyElementEnd = 0x29,
		KeyElementStart = 0x28,
		LastRecordType = 0x39,
		LineNumberAndPosition = 0x35,
		LinePosition = 0x36,
		LiteralContent = 0xf,
		NamedElementStart = 0x2f,
		OptimizedStaticResource = 0x37,
		PIMapping = 0x1b,
		PresentationOptionsAttribute = 0x34,
		ProcessingInstruction = 0x16,
		Property = 0x5,
		PropertyArrayEnd = 0xa,
		PropertyArrayStart = 0x9,
		PropertyComplexEnd = 0x8,
		PropertyComplexStart = 0x7,
		PropertyCustom = 0x6,
		PropertyDictionaryEnd = 0xe,
		PropertyDictionaryStart = 0xd,
		PropertyListEnd = 0xc,
		PropertyListStart = 0xb,
		PropertyStringReference = 0x21,
		PropertyTypeReference = 0x22,
		PropertyWithConverter = 0x24,
		PropertyWithExtension = 0x23,
		PropertyWithStaticResourceId = 0x38,
		RoutedEvent = 0x12,
		StaticResourceEnd = 0x31,
		StaticResourceId = 0x32,
		StaticResourceStart = 0x30,
		StringInfo = 0x20,
		Text = 0x10,
		TextWithConverter = 0x11,
		TextWithId = 0x33,
		TypeInfo = 0x1d,
		TypeSerializerInfo = 0x1e,
		XmlAttribute = 0x15,
		XmlnsProperty = 0x14
	}

	internal abstract class BamlRecord {
		public abstract BamlRecordType Type { get; }
		public long Position { get; internal set; }
		public abstract void Read(BamlBinaryReader reader);
		public abstract void Write(BamlBinaryWriter writer);
	}

	internal abstract class SizedBamlRecord : BamlRecord {
		public override void Read(BamlBinaryReader reader) {
			long pos = reader.BaseStream.Position;
			int size = reader.ReadEncodedInt();

			ReadData(reader, size - (int)(reader.BaseStream.Position - pos));
		}

		int SizeofEncodedInt(int val) {
			if ((val & ~0x7F) == 0) {
				return 1;
			}
			if ((val & ~0x3FFF) == 0) {
				return 2;
			}
			if ((val & ~0x1FFFFF) == 0) {
				return 3;
			}
			if ((val & ~0xFFFFFFF) == 0) {
				return 4;
			}
			return 5;
		}

		public override void Write(BamlBinaryWriter writer) {
			long pos = writer.BaseStream.Position;
			WriteData(writer);
			var size = (int)(writer.BaseStream.Position - pos);
			size = SizeofEncodedInt(SizeofEncodedInt(size) + size) + size;
			writer.BaseStream.Position = pos;
			writer.WriteEncodedInt(size);
			WriteData(writer);
		}

		protected abstract void ReadData(BamlBinaryReader reader, int size);
		protected abstract void WriteData(BamlBinaryWriter writer);
	}

	internal interface IBamlDeferRecord {
		long Position { get; }
		BamlRecord Record { get; set; }
		void ReadDefer(BamlDocument doc, int index, Func<long, BamlRecord> resolve);
		void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr);
	}

	internal class XmlnsPropertyRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.XmlnsProperty;

		public string Prefix { get; set; }
		public string XmlNamespace { get; set; }
		public ushort[] AssemblyIds { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			Prefix = reader.ReadString();
			XmlNamespace = reader.ReadString();
			AssemblyIds = new ushort[reader.ReadUInt16()];
			for (int i = 0; i < AssemblyIds.Length; i++)
				AssemblyIds[i] = reader.ReadUInt16();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(Prefix);
			writer.Write(XmlNamespace);
			writer.Write((ushort)AssemblyIds.Length);
			foreach (ushort i in AssemblyIds)
				writer.Write(i);
		}
	}

	internal class PresentationOptionsAttributeRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.PresentationOptionsAttribute;

		public string Value { get; set; }
		public ushort NameId { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			Value = reader.ReadString();
			NameId = reader.ReadUInt16();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(Value);
			writer.Write(NameId);
		}
	}

	internal class PIMappingRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.PIMapping;

		public string XmlNamespace { get; set; }
		public string ClrNamespace { get; set; }
		public ushort AssemblyId { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			XmlNamespace = reader.ReadString();
			ClrNamespace = reader.ReadString();
			AssemblyId = reader.ReadUInt16();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(XmlNamespace);
			writer.Write(ClrNamespace);
			writer.Write(AssemblyId);
		}
	}

	internal class AssemblyInfoRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.AssemblyInfo;

		public ushort AssemblyId { get; set; }
		public string AssemblyFullName { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			AssemblyId = reader.ReadUInt16();
			AssemblyFullName = reader.ReadString();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(AssemblyId);
			writer.Write(AssemblyFullName);
		}
	}

	internal class PropertyRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.Property;

		public ushort AttributeId { get; set; }
		public string Value { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			AttributeId = reader.ReadUInt16();
			Value = reader.ReadString();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(AttributeId);
			writer.Write(Value);
		}
	}

	internal class PropertyWithConverterRecord : PropertyRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyWithConverter;

		public ushort ConverterTypeId { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			base.ReadData(reader, size);
			ConverterTypeId = reader.ReadUInt16();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			base.WriteData(writer);
			writer.Write(ConverterTypeId);
		}
	}

	internal class PropertyCustomRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyCustom;

		public ushort AttributeId { get; set; }
		public ushort SerializerTypeId { get; set; }
		public byte[] Data { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			long pos = reader.BaseStream.Position;
			AttributeId = reader.ReadUInt16();
			SerializerTypeId = reader.ReadUInt16();
			Data = reader.ReadBytes(size - (int)(reader.BaseStream.Position - pos));
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(AttributeId);
			writer.Write(SerializerTypeId);
			writer.Write(Data);
		}
	}

	internal class DefAttributeRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.DefAttribute;

		public string Value { get; set; }
		public ushort NameId { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			Value = reader.ReadString();
			NameId = reader.ReadUInt16();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(Value);
			writer.Write(NameId);
		}
	}

	internal class DefAttributeKeyStringRecord : SizedBamlRecord, IBamlDeferRecord {
		internal uint pos = 0xffffffff;

		public override BamlRecordType Type => BamlRecordType.DefAttributeKeyString;

		public ushort ValueId { get; set; }
		public bool Shared { get; set; }
		public bool SharedSet { get; set; }

		public BamlRecord Record { get; set; }

		public void ReadDefer(BamlDocument doc, int index, Func<long, BamlRecord> resolve) {
			bool keys = true;
			do {
				switch (doc[index].Type) {
					case BamlRecordType.DefAttributeKeyString:
					case BamlRecordType.DefAttributeKeyType:
					case BamlRecordType.OptimizedStaticResource:
						keys = true;
						break;
					case BamlRecordType.StaticResourceStart:
						NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index);
						keys = true;
						break;
					case BamlRecordType.KeyElementStart:
						NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index);
						keys = true;
						break;
					default:
						keys = false;
						index--;
						break;
				}
				index++;
			} while (keys);
			Record = resolve(doc[index].Position + pos);
		}

		public void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr) {
			bool keys = true;
			do {
				switch (doc[index].Type) {
					case BamlRecordType.DefAttributeKeyString:
					case BamlRecordType.DefAttributeKeyType:
					case BamlRecordType.OptimizedStaticResource:
						keys = true;
						break;
					case BamlRecordType.StaticResourceStart:
						NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index);
						keys = true;
						break;
					case BamlRecordType.KeyElementStart:
						NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index);
						keys = true;
						break;
					default:
						keys = false;
						index--;
						break;
				}
				index++;
			} while (keys);
			wtr.BaseStream.Seek(pos, SeekOrigin.Begin);
			wtr.Write((uint)(Record.Position - doc[index].Position));
		}

		protected override void ReadData(BamlBinaryReader reader, int size) {
			ValueId = reader.ReadUInt16();
			pos = reader.ReadUInt32();
			Shared = reader.ReadBoolean();
			SharedSet = reader.ReadBoolean();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(ValueId);
			pos = (uint)writer.BaseStream.Position;
			writer.Write((uint)0);
			writer.Write(Shared);
			writer.Write(SharedSet);
		}

		static void NavigateTree(BamlDocument doc, BamlRecordType start, BamlRecordType end, ref int index) {
			index++;
			while (true) //Assume there alway is a end
			{
				if (doc[index].Type == start)
					NavigateTree(doc, start, end, ref index);
				else if (doc[index].Type == end)
					return;
				index++;
			}
		}
	}

	internal class TypeInfoRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.TypeInfo;

		public ushort TypeId { get; set; }
		public ushort AssemblyId { get; set; }
		public string TypeFullName { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			TypeId = reader.ReadUInt16();
			AssemblyId = reader.ReadUInt16();
			TypeFullName = reader.ReadString();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(TypeId);
			writer.Write(AssemblyId);
			writer.Write(TypeFullName);
		}
	}

	internal class TypeSerializerInfoRecord : TypeInfoRecord {
		public override BamlRecordType Type => BamlRecordType.TypeSerializerInfo;

		public ushort SerializerTypeId { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			base.ReadData(reader, size);
			SerializerTypeId = reader.ReadUInt16();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			base.WriteData(writer);
			writer.Write(SerializerTypeId);
		}
	}

	internal class AttributeInfoRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.AttributeInfo;

		public ushort AttributeId { get; set; }
		public ushort OwnerTypeId { get; set; }
		public byte AttributeUsage { get; set; }
		public string Name { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			AttributeId = reader.ReadUInt16();
			OwnerTypeId = reader.ReadUInt16();
			AttributeUsage = reader.ReadByte();
			Name = reader.ReadString();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(AttributeId);
			writer.Write(OwnerTypeId);
			writer.Write(AttributeUsage);
			writer.Write(Name);
		}
	}

	internal class StringInfoRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.StringInfo;

		public ushort StringId { get; set; }
		public string Value { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			StringId = reader.ReadUInt16();
			Value = reader.ReadString();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(StringId);
			writer.Write(Value);
		}
	}

	internal class TextRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.Text;

		public string Value { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) => Value = reader.ReadString();

		protected override void WriteData(BamlBinaryWriter writer) => writer.Write(Value);
	}

	internal class TextWithConverterRecord : TextRecord {
		public override BamlRecordType Type => BamlRecordType.TextWithConverter;

		public ushort ConverterTypeId { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			base.ReadData(reader, size);
			ConverterTypeId = reader.ReadUInt16();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			base.WriteData(writer);
			writer.Write(ConverterTypeId);
		}
	}

	internal class TextWithIdRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.TextWithId;

		public ushort ValueId { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) => ValueId = reader.ReadUInt16();

		protected override void WriteData(BamlBinaryWriter writer) => writer.Write(ValueId);
	}

	internal class LiteralContentRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.LiteralContent;

		public string Value { get; set; }
		public uint Reserved0 { get; set; }
		public uint Reserved1 { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			Value = reader.ReadString();
			Reserved0 = reader.ReadUInt32();
			Reserved1 = reader.ReadUInt32();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(Value);
			writer.Write(Reserved0);
			writer.Write(Reserved1);
		}
	}

	internal class RoutedEventRecord : SizedBamlRecord {
		public override BamlRecordType Type => BamlRecordType.RoutedEvent;

		public string Value { get; set; }
		public ushort AttributeId { get; set; }
		public uint Reserved1 { get; set; }

		protected override void ReadData(BamlBinaryReader reader, int size) {
			AttributeId = reader.ReadUInt16();
			Value = reader.ReadString();
		}

		protected override void WriteData(BamlBinaryWriter writer) {
			writer.Write(Value);
			writer.Write(AttributeId);
		}
	}

	internal class DocumentStartRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.DocumentStart;

		public bool LoadAsync { get; set; }
		public uint MaxAsyncRecords { get; set; }
		public bool DebugBaml { get; set; }

		public override void Read(BamlBinaryReader reader) {
			LoadAsync = reader.ReadBoolean();
			MaxAsyncRecords = reader.ReadUInt32();
			DebugBaml = reader.ReadBoolean();
		}

		public override void Write(BamlBinaryWriter writer) {
			writer.Write(LoadAsync);
			writer.Write(MaxAsyncRecords);
			writer.Write(DebugBaml);
		}
	}

	internal class DocumentEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.DocumentEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class ElementStartRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.ElementStart;

		public ushort TypeId { get; set; }
		public byte Flags { get; set; }

		public override void Read(BamlBinaryReader reader) {
			TypeId = reader.ReadUInt16();
			Flags = reader.ReadByte();
		}

		public override void Write(BamlBinaryWriter writer) {
			writer.Write(TypeId);
			writer.Write(Flags);
		}
	}

	internal class ElementEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.ElementEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class KeyElementStartRecord : DefAttributeKeyTypeRecord {
		public override BamlRecordType Type => BamlRecordType.KeyElementStart;
	}

	internal class KeyElementEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.KeyElementEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class ConnectionIdRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.ConnectionId;

		public uint ConnectionId { get; set; }

		public override void Read(BamlBinaryReader reader) => ConnectionId = reader.ReadUInt32();

		public override void Write(BamlBinaryWriter writer) => writer.Write(ConnectionId);
	}

	internal class PropertyWithExtensionRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyWithExtension;

		public ushort AttributeId { get; set; }
		public ushort Flags { get; set; }
		public ushort ValueId { get; set; }

		public override void Read(BamlBinaryReader reader) {
			AttributeId = reader.ReadUInt16();
			Flags = reader.ReadUInt16();
			ValueId = reader.ReadUInt16();
		}

		public override void Write(BamlBinaryWriter writer) {
			writer.Write(AttributeId);
			writer.Write(Flags);
			writer.Write(ValueId);
		}
	}

	internal class PropertyTypeReferenceRecord : PropertyComplexStartRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyTypeReference;

		public ushort TypeId { get; set; }

		public override void Read(BamlBinaryReader reader) {
			base.Read(reader);
			TypeId = reader.ReadUInt16();
		}

		public override void Write(BamlBinaryWriter writer) {
			base.Write(writer);
			writer.Write(TypeId);
		}
	}

	internal class PropertyStringReferenceRecord : PropertyComplexStartRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyStringReference;

		public ushort StringId { get; set; }

		public override void Read(BamlBinaryReader reader) {
			base.Read(reader);
			StringId = reader.ReadUInt16();
		}

		public override void Write(BamlBinaryWriter writer) {
			base.Write(writer);
			writer.Write(StringId);
		}
	}

	internal class PropertyWithStaticResourceIdRecord : StaticResourceIdRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyWithStaticResourceId;

		public ushort AttributeId { get; set; }

		public override void Read(BamlBinaryReader reader) {
			AttributeId = reader.ReadUInt16();
			base.Read(reader);
		}

		public override void Write(BamlBinaryWriter writer) {
			writer.Write(AttributeId);
			base.Write(writer);
		}
	}

	internal class ContentPropertyRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.ContentProperty;

		public ushort AttributeId { get; set; }

		public override void Read(BamlBinaryReader reader) => AttributeId = reader.ReadUInt16();

		public override void Write(BamlBinaryWriter writer) => writer.Write(AttributeId);
	}

	internal class DefAttributeKeyTypeRecord : ElementStartRecord, IBamlDeferRecord {
		internal uint pos = 0xffffffff;

		public override BamlRecordType Type => BamlRecordType.DefAttributeKeyType;

		public bool Shared { get; set; }
		public bool SharedSet { get; set; }

		public BamlRecord Record { get; set; }

		public void ReadDefer(BamlDocument doc, int index, Func<long, BamlRecord> resolve) {
			bool keys = true;
			do {
				switch (doc[index].Type) {
					case BamlRecordType.DefAttributeKeyString:
					case BamlRecordType.DefAttributeKeyType:
					case BamlRecordType.OptimizedStaticResource:
						keys = true;
						break;
					case BamlRecordType.StaticResourceStart:
						NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index);
						keys = true;
						break;
					case BamlRecordType.KeyElementStart:
						NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index);
						keys = true;
						break;
					default:
						keys = false;
						index--;
						break;
				}
				index++;
			} while (keys);
			Record = resolve(doc[index].Position + pos);
		}

		public void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr) {
			bool keys = true;
			do {
				switch (doc[index].Type) {
					case BamlRecordType.DefAttributeKeyString:
					case BamlRecordType.DefAttributeKeyType:
					case BamlRecordType.OptimizedStaticResource:
						keys = true;
						break;
					case BamlRecordType.StaticResourceStart:
						NavigateTree(doc, BamlRecordType.StaticResourceStart, BamlRecordType.StaticResourceEnd, ref index);
						keys = true;
						break;
					case BamlRecordType.KeyElementStart:
						NavigateTree(doc, BamlRecordType.KeyElementStart, BamlRecordType.KeyElementEnd, ref index);
						keys = true;
						break;
					default:
						keys = false;
						index--;
						break;
				}
				index++;
			} while (keys);
			wtr.BaseStream.Seek(pos, SeekOrigin.Begin);
			wtr.Write((uint)(Record.Position - doc[index].Position));
		}

		public override void Read(BamlBinaryReader reader) {
			base.Read(reader);
			pos = reader.ReadUInt32();
			Shared = reader.ReadBoolean();
			SharedSet = reader.ReadBoolean();
		}

		public override void Write(BamlBinaryWriter writer) {
			base.Write(writer);
			pos = (uint)writer.BaseStream.Position;
			writer.Write((uint)0);
			writer.Write(Shared);
			writer.Write(SharedSet);
		}

		static void NavigateTree(BamlDocument doc, BamlRecordType start, BamlRecordType end, ref int index) {
			index++;
			while (true) {
				if (doc[index].Type == start)
					NavigateTree(doc, start, end, ref index);
				else if (doc[index].Type == end)
					return;
				index++;
			}
		}
	}

	internal class PropertyListStartRecord : PropertyComplexStartRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyListStart;
	}

	internal class PropertyListEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyListEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class PropertyDictionaryStartRecord : PropertyComplexStartRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyDictionaryStart;
	}

	internal class PropertyDictionaryEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyDictionaryEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class PropertyArrayStartRecord : PropertyComplexStartRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyArrayStart;
	}

	internal class PropertyArrayEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyArrayEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class PropertyComplexStartRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyComplexStart;

		public ushort AttributeId { get; set; }

		public override void Read(BamlBinaryReader reader) => AttributeId = reader.ReadUInt16();

		public override void Write(BamlBinaryWriter writer) => writer.Write(AttributeId);
	}

	internal class PropertyComplexEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.PropertyComplexEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class ConstructorParametersStartRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.ConstructorParametersStart;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class ConstructorParametersEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.ConstructorParametersEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class ConstructorParameterTypeRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.ConstructorParameterType;

		public ushort TypeId { get; set; }

		public override void Read(BamlBinaryReader reader) => TypeId = reader.ReadUInt16();

		public override void Write(BamlBinaryWriter writer) => writer.Write(TypeId);
	}

	internal class DeferableContentStartRecord : BamlRecord, IBamlDeferRecord {
		long pos;
		internal uint size = 0xffffffff;

		public override BamlRecordType Type => BamlRecordType.DeferableContentStart;

		public BamlRecord Record { get; set; }

		public void ReadDefer(BamlDocument doc, int index, Func<long, BamlRecord> resolve) => Record = resolve(pos + size);

		public void WriteDefer(BamlDocument doc, int index, BinaryWriter wtr) {
			wtr.BaseStream.Seek(pos, SeekOrigin.Begin);
			wtr.Write((uint)(Record.Position - (pos + 4)));
		}

		public override void Read(BamlBinaryReader reader) {
			size = reader.ReadUInt32();
			pos = reader.BaseStream.Position;
		}

		public override void Write(BamlBinaryWriter writer) {
			pos = writer.BaseStream.Position;
			writer.Write((uint)0);
		}
	}

	internal class StaticResourceStartRecord : ElementStartRecord {
		public override BamlRecordType Type => BamlRecordType.StaticResourceStart;
	}

	internal class StaticResourceEndRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.StaticResourceEnd;

		public override void Read(BamlBinaryReader reader) {
		}

		public override void Write(BamlBinaryWriter writer) {
		}
	}

	internal class StaticResourceIdRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.StaticResourceId;

		public ushort StaticResourceId { get; set; }

		public override void Read(BamlBinaryReader reader) => StaticResourceId = reader.ReadUInt16();

		public override void Write(BamlBinaryWriter writer) => writer.Write(StaticResourceId);
	}

	internal class OptimizedStaticResourceRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.OptimizedStaticResource;

		public byte Flags { get; set; }
		public ushort ValueId { get; set; }

		public bool IsType => (Flags & 1) != 0;

		public bool IsStatic => (Flags & 2) != 0;

		public override void Read(BamlBinaryReader reader) {
			Flags = reader.ReadByte();
			ValueId = reader.ReadUInt16();
		}

		public override void Write(BamlBinaryWriter writer) {
			writer.Write(Flags);
			writer.Write(ValueId);
		}
	}

	internal class LineNumberAndPositionRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.LineNumberAndPosition;

		public uint LineNumber { get; set; }
		public uint LinePosition { get; set; }

		public override void Read(BamlBinaryReader reader) {
			LineNumber = reader.ReadUInt32();
			LinePosition = reader.ReadUInt32();
		}

		public override void Write(BamlBinaryWriter writer) {
			writer.Write(LineNumber);
			writer.Write(LinePosition);
		}
	}

	internal class LinePositionRecord : BamlRecord {
		public override BamlRecordType Type => BamlRecordType.LinePosition;

		public uint LinePosition { get; set; }

		public override void Read(BamlBinaryReader reader) => LinePosition = reader.ReadUInt32();

		public override void Write(BamlBinaryWriter writer) => writer.Write(LinePosition);
	}

	internal class NamedElementStartRecord : ElementStartRecord {
		public override BamlRecordType Type => BamlRecordType.NamedElementStart;

		public string RuntimeName { get; set; }

		public override void Read(BamlBinaryReader reader) {
			TypeId = reader.ReadUInt16();
			RuntimeName = reader.ReadString();
		}

		public override void Write(BamlBinaryWriter writer) {
			writer.Write(TypeId);
			if (RuntimeName != null) {
				writer.Write(RuntimeName);
			}
		}
	}
}