﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FreecraftCore.Serializer.Internal;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace FreecraftCore.Serializer
{
	public sealed class RegularSerializerImplementationCompilationUnitEmitter : BaseSerializerImplementationCompilationUnitEmitter
	{
		public RootSerializationMethodBlockEmitter SerializationMethodEmitter { get; }

		public RegularSerializerImplementationCompilationUnitEmitter([NotNull] INamedTypeSymbol typeSymbol)
			: base(typeSymbol)
		{
			SerializationMethodEmitter = new RootSerializationMethodBlockEmitter(typeSymbol);
		}

		private BlockSyntax CreateWriteBlock()
		{
			SerializationMethodEmitter.Mode = SerializationMode.Write;
			return SerializationMethodEmitter
				.CreateBlock();
		}

		private BlockSyntax CreateReadBlock()
		{
			SerializationMethodEmitter.Mode = SerializationMode.Read;
			return SerializationMethodEmitter
				.CreateBlock();
		}

		protected override MemberDeclarationSyntax CreateSerializerImplementationNamespaceMember()
		{ 
			return NamespaceDeclaration
					(
						QualifiedName
						(
							IdentifierName("FreecraftCore"),
							IdentifierName
							(
								Identifier
								(
									TriviaList(),
									"Serializer",
									TriviaList
									(
										CarriageReturnLineFeed
									)
								)
							)
						)
					)
					.WithNamespaceKeyword
					(
						Token
						(
							TriviaList
							(
								CarriageReturnLineFeed
							),
							SyntaxKind.NamespaceKeyword,
							TriviaList
							(
								Space
							)
						)
					)
					.WithOpenBraceToken
					(
						Token
						(
							TriviaList(),
							SyntaxKind.OpenBraceToken,
							TriviaList
							(
								CarriageReturnLineFeed
							)
						)
					)
					.WithMembers
					(
						SingletonList<MemberDeclarationSyntax>
						(
							BuildClassDefinition()
								.WithAttributeLists(List<AttributeListSyntax>
									(
										new AttributeListSyntax[]
										{
											AttributeList
											(
												SingletonSeparatedList<AttributeSyntax>
												(
													new HideTypeAttributeBuilder().Create()
												)
											)
										}
									)
								)
						)
					)
					.WithCloseBraceToken
					(
						Token
						(
							TriviaList(),
							SyntaxKind.CloseBraceToken,
							TriviaList
							(
								CarriageReturnLineFeed
							)
						)
					);
			}

		private ClassDeclarationSyntax BuildClassDefinition()
		{
			return ClassDeclaration
				(
					Identifier
					(
						TriviaList(),
						SerializerTypeName,
						TriviaList
						(
							new[]
							{
								Space,
								CarriageReturnLineFeed
							}
						)
					)
				)
				.WithModifiers
				(
					TokenList
					(
						new[]
						{
							Token
							(
								TriviaList
								(
									new[]
									{
										Tab,
										Comment("//THIS CODE IS FOR AUTO-GENERATED SERIALIZERS! DO NOT MODIFY UNLESS YOU KNOW WELL!"),
										CarriageReturnLineFeed,
										Tab,
										Trivia
										(
											DocumentationCommentTrivia
											(
												SyntaxKind.SingleLineDocumentationCommentTrivia,
												List<XmlNodeSyntax>
												(
													new XmlNodeSyntax[]
													{
														XmlText()
															.WithTextTokens
															(
																TokenList
																(
																	XmlTextLiteral
																	(
																		TriviaList
																		(
																			DocumentationCommentExterior("///")
																		),
																		" ",
																		" ",
																		TriviaList()
																	)
																)
															),
														XmlExampleElement
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			new[]
																			{
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("	///")
																					),
																					" FreecraftCore.Serializer's AUTO-GENERATED (do not edit) serialization",
																					" FreecraftCore.Serializer's AUTO-GENERATED (do not edit) serialization",
																					TriviaList()
																				),
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("	///")
																					),
																					" code for the Type: ",
																					" code for the Type: ",
																					TriviaList()
																				)
																			}
																		)
																	),
																XmlNullKeywordElement()
																	.WithAttributes
																	(
																		SingletonList<XmlAttributeSyntax>
																		(
																			XmlCrefAttribute
																			(
																				NameMemberCref
																				(
																					IdentifierName(SerializableTypeName)
																				)
																			)
																		)
																	),
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			new[]
																			{
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("	///")
																					),
																					" ",
																					" ",
																					TriviaList()
																				)
																			}
																		)
																	)
															)
															.WithStartTag
															(
																XmlElementStartTag
																(
																	XmlName
																	(
																		Identifier("summary")
																	)
																)
															)
															.WithEndTag
															(
																XmlElementEndTag
																(
																	XmlName
																	(
																		Identifier("summary")
																	)
																)
															),
														XmlText()
															.WithTextTokens
															(
																TokenList
																(
																	XmlTextNewLine
																	(
																		TriviaList(),
																		Environment.NewLine,
																		Environment.NewLine,
																		TriviaList()
																	)
																)
															)
													}
												)
											)
										),
										Tab
									}
								),
								SyntaxKind.PublicKeyword,
								TriviaList
								(
									Space
								)
							),
							Token
							(
								TriviaList(),
								SyntaxKind.SealedKeyword,
								TriviaList
								(
									Space
								)
							),
							Token
							(
								TriviaList(),
								SyntaxKind.PartialKeyword,
								TriviaList
								(
									Space
								)
							)
						}
					)
				)
				.WithKeyword
				(
					Token
					(
						TriviaList(),
						SyntaxKind.ClassKeyword,
						TriviaList
						(
							Space
						)
					)
				)
				.WithBaseList
				(
					BaseList
						(
							SingletonSeparatedList<BaseTypeSyntax>
							(
								SimpleBaseType
								(
									GenericName
										(
											CreateBaseSerializerTypeName()
										)
										.WithTypeArgumentList
										(
											TypeArgumentList
												(
													SeparatedList<TypeSyntax>
													(
														new SyntaxNodeOrToken[]
														{
															IdentifierName(SerializerTypeName),
															Token
															(
																TriviaList(),
																SyntaxKind.CommaToken,
																TriviaList
																(
																	Space
																)
															),
															IdentifierName(SerializableTypeName)
														}
													)
												)
												.WithGreaterThanToken
												(
													Token
													(
														TriviaList(),
														SyntaxKind.GreaterThanToken,
														TriviaList
														(
															CarriageReturnLineFeed
														)
													)
												)
										)
								)
							)
						)
						.WithColonToken
						(
							Token
							(
								TriviaList
								(
									Whitespace("		")
								),
								SyntaxKind.ColonToken,
								TriviaList
								(
									Space
								)
							)
						)
				)
				.WithOpenBraceToken
				(
					Token
					(
						TriviaList
						(
							Tab
						),
						SyntaxKind.OpenBraceToken,
						TriviaList
						(
							CarriageReturnLineFeed
						)
					)
				)
				.WithMembers
				(
					List<MemberDeclarationSyntax>
					(
						new MemberDeclarationSyntax[]
							{
								CreateReadMethod(),
								CreateWriteMethod()
							}
							.Concat(SerializationMethodEmitter
								.CreateClasses()) //This embeds our required classes into the Type
					)
				)
				.WithCloseBraceToken
				(
					Token
					(
						TriviaList
						(
							Tab
						),
						SyntaxKind.CloseBraceToken,
						TriviaList
						(
							CarriageReturnLineFeed
						)
					)
				);
		}

		private SyntaxToken CreateBaseSerializerTypeName()
		{
			//Records use a different base serializer
			if (TypeSymbol.IsRecord)
				return Identifier("BaseAutoGeneratedRecordSerializerStrategy");

			return Identifier("BaseAutoGeneratedSerializerStrategy");
		}

		private MethodDeclarationSyntax CreateWriteMethod()
		{
			return MethodDeclaration
				(
					ComputeWriteMethodReturnType(),
					TypeSymbol.IsRecord ? Identifier("Write") : Identifier("InternalWrite")
				)
				.WithModifiers
				(
					TokenList
					(
						Token
						(
							TriviaList
							(
								new[]
								{
									CarriageReturnLineFeed,
									Trivia
									(
										DocumentationCommentTrivia
										(
											SyntaxKind.SingleLineDocumentationCommentTrivia,
											List<XmlNodeSyntax>
											(
												new XmlNodeSyntax[]
												{
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																XmlTextLiteral
																(
																	TriviaList
																	(
																		DocumentationCommentExterior("///")
																	),
																	" ",
																	" ",
																	TriviaList()
																)
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			new[]
																			{
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("		///")
																					),
																					" Auto-generated serialization/write method.",
																					" Auto-generated serialization/write method.",
																					TriviaList()
																				),
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("		///")
																					),
																					" Partial method implemented from shared partial definition.",
																					" Partial method implemented from shared partial definition.",
																					TriviaList()
																				),
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("		///")
																					),
																					" ",
																					" ",
																					TriviaList()
																				)
																			}
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
															(
																XmlName
																(
																	Identifier("summary")
																)
															)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("summary")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																new[]
																{
																	XmlTextNewLine
																	(
																		TriviaList(),
																		Environment.NewLine,
																		Environment.NewLine,
																		TriviaList()
																	),
																	XmlTextLiteral
																	(
																		TriviaList
																		(
																			DocumentationCommentExterior("		///")
																		),
																		" ",
																		" ",
																		TriviaList()
																	)
																}
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			XmlTextLiteral
																			(
																				TriviaList(),
																				"See external doc.",
																				"See external doc.",
																				TriviaList()
																			)
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
																(
																	XmlName
																	(
																		Identifier("param")
																	)
																)
																.WithAttributes
																(
																	SingletonList<XmlAttributeSyntax>
																	(
																		XmlNameAttribute
																		(
																			XmlName
																			(
																				Identifier
																				(
																					TriviaList
																					(
																						Space
																					),
																					"name",
																					TriviaList()
																				)
																			),
																			Token(SyntaxKind.DoubleQuoteToken),
																			IdentifierName("value"),
																			Token(SyntaxKind.DoubleQuoteToken)
																		)
																	)
																)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("param")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																new[]
																{
																	XmlTextNewLine
																	(
																		TriviaList(),
																		Environment.NewLine,
																		Environment.NewLine,
																		TriviaList()
																	),
																	XmlTextLiteral
																	(
																		TriviaList
																		(
																			DocumentationCommentExterior("		///")
																		),
																		" ",
																		" ",
																		TriviaList()
																	)
																}
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			XmlTextLiteral
																			(
																				TriviaList(),
																				"See external doc.",
																				"See external doc.",
																				TriviaList()
																			)
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
																(
																	XmlName
																	(
																		Identifier("param")
																	)
																)
																.WithAttributes
																(
																	SingletonList<XmlAttributeSyntax>
																	(
																		XmlNameAttribute
																		(
																			XmlName
																			(
																				Identifier
																				(
																					TriviaList
																					(
																						Space
																					),
																					"name",
																					TriviaList()
																				)
																			),
																			Token(SyntaxKind.DoubleQuoteToken),
																			IdentifierName("buffer"),
																			Token(SyntaxKind.DoubleQuoteToken)
																		)
																	)
																)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("param")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																new[]
																{
																	XmlTextNewLine
																	(
																		TriviaList(),
																		Environment.NewLine,
																		Environment.NewLine,
																		TriviaList()
																	),
																	XmlTextLiteral
																	(
																		TriviaList
																		(
																			DocumentationCommentExterior("		///")
																		),
																		" ",
																		" ",
																		TriviaList()
																	)
																}
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			XmlTextLiteral
																			(
																				TriviaList(),
																				"See external doc.",
																				"See external doc.",
																				TriviaList()
																			)
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
																(
																	XmlName
																	(
																		Identifier("param")
																	)
																)
																.WithAttributes
																(
																	SingletonList<XmlAttributeSyntax>
																	(
																		XmlNameAttribute
																		(
																			XmlName
																			(
																				Identifier
																				(
																					TriviaList
																					(
																						Space
																					),
																					"name",
																					TriviaList()
																				)
																			),
																			Token(SyntaxKind.DoubleQuoteToken),
																			IdentifierName("offset"),
																			Token(SyntaxKind.DoubleQuoteToken)
																		)
																	)
																)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("param")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																XmlTextNewLine
																(
																	TriviaList(),
																	Environment.NewLine,
																	Environment.NewLine,
																	TriviaList()
																)
															)
														)
												}
											)
										)
									),
									Whitespace("		")
								}
							),
							SyntaxKind.PublicKeyword,
							TriviaList
							(
								Space
							)
						),
						Token
						(
							TriviaList(),
							SyntaxKind.OverrideKeyword,
							TriviaList
							(
								Space
							)
						)
					)
				)
				.WithParameterList
				(
					ParameterList
						(
							SeparatedList<ParameterSyntax>
							(
								new SyntaxNodeOrToken[]
								{
									Parameter
										(
											Identifier("value")
										)
										.WithType
										(
											IdentifierName
											(
												Identifier
												(
													TriviaList(),
													SerializableTypeName,
													TriviaList
													(
														Space
													)
												)
											)
										),
									Token
									(
										TriviaList(),
										SyntaxKind.CommaToken,
										TriviaList
										(
											Space
										)
									),
									Parameter
										(
											Identifier("buffer")
										)
										.WithType
										(
											GenericName
												(
													Identifier("Span")
												)
												.WithTypeArgumentList
												(
													TypeArgumentList
														(
															SingletonSeparatedList<TypeSyntax>
															(
																PredefinedType
																(
																	Token(SyntaxKind.ByteKeyword)
																)
															)
														)
														.WithGreaterThanToken
														(
															Token
															(
																TriviaList(),
																SyntaxKind.GreaterThanToken,
																TriviaList
																(
																	Space
																)
															)
														)
												)
										),
									Token
									(
										TriviaList(),
										SyntaxKind.CommaToken,
										TriviaList
										(
											Space
										)
									),
									Parameter
										(
											Identifier("offset")
										)
										.WithModifiers
										(
											TokenList
											(
												Token
												(
													TriviaList(),
													SyntaxKind.RefKeyword,
													TriviaList
													(
														Space
													)
												)
											)
										)
										.WithType
										(
											PredefinedType
											(
												Token
												(
													TriviaList(),
													SyntaxKind.IntKeyword,
													TriviaList
													(
														Space
													)
												)
											)
										)
								}
							)
						)
						.WithCloseParenToken
						(
							Token
							(
								TriviaList(),
								SyntaxKind.CloseParenToken,
								TriviaList
								(
									CarriageReturnLineFeed
								)
							)
						)
				)
				.WithBody
				(
					CreateWriteBlock()
						.WithOpenBraceToken
						(
							Token
							(
								TriviaList
								(
									Whitespace("		")
								),
								SyntaxKind.OpenBraceToken,
								TriviaList
								(
									CarriageReturnLineFeed
								)
							)
						)
						.WithCloseBraceToken
						(
							Token
							(
								TriviaList
								(
									CarriageReturnLineFeed, //Custom added linebreak!
									Whitespace("		")
								),
								SyntaxKind.CloseBraceToken,
								TriviaList
								(
									CarriageReturnLineFeed
								)
							)
						)
				);
		}

		private TypeSyntax ComputeWriteMethodReturnType()
		{
			return PredefinedType
			(
				Token
				(
					TriviaList(),
					SyntaxKind.VoidKeyword,
					TriviaList
					(
						Space
					)
				)
			);
		}

		private TypeSyntax ComputeReadMethodReturnType()
		{
			if(TypeSymbol.IsRecord)
				return IdentifierName(SerializableTypeName);

			return PredefinedType
			(
				Token
				(
					TriviaList(),
					SyntaxKind.VoidKeyword,
					TriviaList
					(
						Space
					)
				)
			);
		}

		private MethodDeclarationSyntax CreateReadMethod()
		{
			return MethodDeclaration
				(
					ComputeReadMethodReturnType(),
					TypeSymbol.IsRecord ? Identifier("Read") : Identifier("InternalRead")
				)
				.WithModifiers
				(
					TokenList
					(
						Token
						(
							TriviaList
							(
								new[]
								{
									Whitespace("		"),
									Trivia
									(
										DocumentationCommentTrivia
										(
											SyntaxKind.SingleLineDocumentationCommentTrivia,
											List<XmlNodeSyntax>
											(
												new XmlNodeSyntax[]
												{
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																XmlTextLiteral
																(
																	TriviaList
																	(
																		DocumentationCommentExterior("///")
																	),
																	" ",
																	" ",
																	TriviaList()
																)
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			new[]
																			{
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("		///")
																					),
																					" Auto-generated deserialization/read method.",
																					" Auto-generated deserialization/read method.",
																					TriviaList()
																				),
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("		///")
																					),
																					" Partial method implemented from shared partial definition.",
																					" Partial method implemented from shared partial definition.",
																					TriviaList()
																				),
																				XmlTextNewLine
																				(
																					TriviaList(),
																					Environment.NewLine,
																					Environment.NewLine,
																					TriviaList()
																				),
																				XmlTextLiteral
																				(
																					TriviaList
																					(
																						DocumentationCommentExterior("		///")
																					),
																					" ",
																					" ",
																					TriviaList()
																				)
																			}
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
															(
																XmlName
																(
																	Identifier("summary")
																)
															)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("summary")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																new[]
																{
																	XmlTextNewLine
																	(
																		TriviaList(),
																		Environment.NewLine,
																		Environment.NewLine,
																		TriviaList()
																	),
																	XmlTextLiteral
																	(
																		TriviaList
																		(
																			DocumentationCommentExterior("		///")
																		),
																		" ",
																		" ",
																		TriviaList()
																	)
																}
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			XmlTextLiteral
																			(
																				TriviaList(),
																				"See external doc.",
																				"See external doc.",
																				TriviaList()
																			)
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
																(
																	XmlName
																	(
																		Identifier("param")
																	)
																)
																.WithAttributes
																(
																	SingletonList<XmlAttributeSyntax>
																	(
																		XmlNameAttribute
																		(
																			XmlName
																			(
																				Identifier
																				(
																					TriviaList
																					(
																						Space
																					),
																					"name",
																					TriviaList()
																				)
																			),
																			Token(SyntaxKind.DoubleQuoteToken),
																			IdentifierName("value"),
																			Token(SyntaxKind.DoubleQuoteToken)
																		)
																	)
																)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("param")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																new[]
																{
																	XmlTextNewLine
																	(
																		TriviaList(),
																		Environment.NewLine,
																		Environment.NewLine,
																		TriviaList()
																	),
																	XmlTextLiteral
																	(
																		TriviaList
																		(
																			DocumentationCommentExterior("		///")
																		),
																		" ",
																		" ",
																		TriviaList()
																	)
																}
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			XmlTextLiteral
																			(
																				TriviaList(),
																				"See external doc.",
																				"See external doc.",
																				TriviaList()
																			)
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
																(
																	XmlName
																	(
																		Identifier("param")
																	)
																)
																.WithAttributes
																(
																	SingletonList<XmlAttributeSyntax>
																	(
																		XmlNameAttribute
																		(
																			XmlName
																			(
																				Identifier
																				(
																					TriviaList
																					(
																						Space
																					),
																					"name",
																					TriviaList()
																				)
																			),
																			Token(SyntaxKind.DoubleQuoteToken),
																			IdentifierName("buffer"),
																			Token(SyntaxKind.DoubleQuoteToken)
																		)
																	)
																)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("param")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																new[]
																{
																	XmlTextNewLine
																	(
																		TriviaList(),
																		Environment.NewLine,
																		Environment.NewLine,
																		TriviaList()
																	),
																	XmlTextLiteral
																	(
																		TriviaList
																		(
																			DocumentationCommentExterior("		///")
																		),
																		" ",
																		" ",
																		TriviaList()
																	)
																}
															)
														),
													XmlExampleElement
														(
															SingletonList<XmlNodeSyntax>
															(
																XmlText()
																	.WithTextTokens
																	(
																		TokenList
																		(
																			XmlTextLiteral
																			(
																				TriviaList(),
																				"See external doc.",
																				"See external doc.",
																				TriviaList()
																			)
																		)
																	)
															)
														)
														.WithStartTag
														(
															XmlElementStartTag
																(
																	XmlName
																	(
																		Identifier("param")
																	)
																)
																.WithAttributes
																(
																	SingletonList<XmlAttributeSyntax>
																	(
																		XmlNameAttribute
																		(
																			XmlName
																			(
																				Identifier
																				(
																					TriviaList
																					(
																						Space
																					),
																					"name",
																					TriviaList()
																				)
																			),
																			Token(SyntaxKind.DoubleQuoteToken),
																			IdentifierName("offset"),
																			Token(SyntaxKind.DoubleQuoteToken)
																		)
																	)
																)
														)
														.WithEndTag
														(
															XmlElementEndTag
															(
																XmlName
																(
																	Identifier("param")
																)
															)
														),
													XmlText()
														.WithTextTokens
														(
															TokenList
															(
																XmlTextNewLine
																(
																	TriviaList(),
																	Environment.NewLine,
																	Environment.NewLine,
																	TriviaList()
																)
															)
														)
												}
											)
										)
									),
									Whitespace("		")
								}
							),
							SyntaxKind.PublicKeyword,
							TriviaList
							(
								Space
							)
						),
						Token
						(
							TriviaList(),
							SyntaxKind.OverrideKeyword,
							TriviaList
							(
								Space
							)
						)
					)
				)
				.WithParameterList
				(
					ParameterList
						(
							SeparatedList<ParameterSyntax>
							(
								ComputeReadMethodParameterList()
							)
						)
						.WithCloseParenToken
						(
							Token
							(
								TriviaList(),
								SyntaxKind.CloseParenToken,
								TriviaList
								(
									CarriageReturnLineFeed
								)
							)
						)
				)
				.WithBody
				(
					CreateReadBlock()
						.WithOpenBraceToken
						(
							Token
							(
								TriviaList
								(
									Whitespace("		")
								),
								SyntaxKind.OpenBraceToken,
								TriviaList
								(
									CarriageReturnLineFeed
								)
							)
						)
						.WithCloseBraceToken
						(
							Token
							(
								TriviaList
								(
									Whitespace("		")
								),
								SyntaxKind.CloseBraceToken,
								TriviaList
								(
									CarriageReturnLineFeed
								)
							)
						)
				);
		}

		private IEnumerable<SyntaxNodeOrToken> ComputeReadMethodParameterList()
		{
			if(!TypeSymbol.IsRecord)
			{
				yield return Parameter
					(
						Identifier("value")
					)
					.WithType
					(
						IdentifierName
						(
							Identifier
							(
								TriviaList(),
								SerializableTypeName,
								TriviaList
								(
									Space
								)
							)
						)
					);

				yield return Token
				(
					TriviaList(),
					SyntaxKind.CommaToken,
					TriviaList
					(
						Space
					)
				);
			}


			foreach(var node in new SyntaxNodeOrToken[]
			{
				Parameter
					(
						Identifier("buffer")
					)
					.WithType
					(
						GenericName
							(
								Identifier("Span")
							)
							.WithTypeArgumentList
							(
								TypeArgumentList
									(
										SingletonSeparatedList<TypeSyntax>
										(
											PredefinedType
											(
												Token(SyntaxKind.ByteKeyword)
											)
										)
									)
									.WithGreaterThanToken
									(
										Token
										(
											TriviaList(),
											SyntaxKind.GreaterThanToken,
											TriviaList
											(
												Space
											)
										)
									)
							)
					),
				Token
				(
					TriviaList(),
					SyntaxKind.CommaToken,
					TriviaList
					(
						Space
					)
				),
				Parameter
					(
						Identifier("offset")
					)
					.WithModifiers
					(
						TokenList
						(
							Token
							(
								TriviaList(),
								SyntaxKind.RefKeyword,
								TriviaList
								(
									Space
								)
							)
						)
					)
					.WithType
					(
						PredefinedType
						(
							Token
							(
								TriviaList(),
								SyntaxKind.IntKeyword,
								TriviaList
								(
									Space
								)
							)
						)
					)
			})
			{
				yield return node;
			}
		}

		public override IEnumerable<ITypeSymbol> GetRequestedGenericTypes()
		{
			return this.SerializationMethodEmitter.RequestedGenericTypes;
		}
	}
}
