Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Xml.Serialization
Imports Mercury.Mobile.IO
Imports System.Threading

Namespace Xml.Serialization

    ''' <summary>
    ''' A helper class for XML serialization
    ''' </summary>
    ''' <remarks></remarks>
    Public Class XmlSerializationHelper

        ''' <summary>
        ''' Static initializer.
        ''' </summary>
        ''' <remarks></remarks>
        Shared Sub New()
            SetEncoding(Encoding.Unicode)
        End Sub

        Private Shared _format As Func(Of Type, String)

#Region "Encoding"
        Private Shared _encoding As Encoding

        ''' <summary>
        ''' Gets the current default encoding.
        ''' </summary>
        ''' <returns>The current default encoding.</returns>
        ''' <remarks></remarks>
        Public Shared ReadOnly Property CurrentEncoding() As Encoding
            Get
                Return _encoding
            End Get
        End Property

        ''' <summary>
        ''' Sets the default encoding scheme to the specified encoding.
        ''' </summary>
        ''' <param name="encoding">The System.Text.Encoding to set.</param>
        ''' <remarks></remarks>
        Public Shared Sub SetEncoding(ByVal encoding As Encoding)
            _encoding = encoding
        End Sub
#End Region

#Region "Serialize"
        ''' <summary>
        ''' Returns the assembly qualified name of the specified System.Type.
        ''' </summary>
        ''' <param name="type">The System.Type to use.</param>
        ''' <returns>The assembly qualified name of the specified System.Type.</returns>
        ''' <remarks></remarks>
        Public Shared Function GetSerializableTypeName(ByVal type As Type) As String
            If (type Is Nothing) Then _
                Throw New ArgumentNullException("type")
            Return XmlSerializationHelper.GetTypeFormatInternal(type)
        End Function

        ''' <summary>
        ''' Serializes the specified object instance to an XML string using the Unicode (little endian) encoding.
        ''' </summary>
        ''' <param name="data">The object instance to serialize.</param>
        ''' <returns>An object serialized to an XML string.</returns>
        ''' <remarks></remarks>
        Public Shared Function Serialize(ByVal data As Object) As String
            Return XmlSerializationHelper.Serialize(data, XmlSerializationHelper.CurrentEncoding)
        End Function

        ''' <summary>
        ''' Serializes the specified object instance to an XML string with the specified encoding.
        ''' </summary>
        ''' <param name="data">The object instance to serialize.</param>
        ''' <param name="encoding">The encoding to use.</param>
        ''' <returns>An object serialized to an XML string with the specified encoding.</returns>
        ''' <remarks></remarks>
        Public Shared Function Serialize(ByVal data As Object, ByVal encoding As Encoding) As String
            ' If data is type, serialize with the type format.
            If (TypeOf data Is Type) Then
                Return SerializeType(TryCast(data, Type), encoding)
            Else    ' Serialize data
                Dim serializer As XmlSerializer = New XmlSerializer(data.GetType())
                Dim writer As StringWriter = New EncodableStringWriter(encoding)
                serializer.Serialize(writer, data)
                writer.Flush()

                Return writer.ToString()
            End If
        End Function

        ''' <summary>
        ''' Serializes the specified object instance using the specified System.Xml.XmlWriter.
        ''' </summary>
        ''' <param name="data">The object instance to serialize.</param>
        ''' <param name="writer">The System.Xml.XmlWriter to use.</param>
        ''' <remarks></remarks>
        Public Shared Sub Serialize(ByVal data As Object, ByVal writer As XmlWriter)
            Dim serializer As XmlSerializer = New XmlSerializer(data.GetType())
            serializer.Serialize(writer, data)
        End Sub

        ''' <summary>
        ''' Serializes the specified System.Type to an XML string using the Unicode (little endian) encoding.
        ''' </summary>
        ''' <param name="type">The System.Type to serialize.</param>
        ''' <returns>An XML string describing the specified System.Type.</returns>
        Public Shared Function SerializeType(ByVal type As Type) As String
            Return XmlSerializationHelper.SerializeType(type, XmlSerializationHelper.CurrentEncoding)
        End Function

        ''' <summary>
        ''' Serializes the specified System.Type to an XML string with the specified encoding.
        ''' </summary>
        ''' <param name="type">The System.Type to serialize.</param>
        ''' <param name="encoding">The encoding to use.</param>
        ''' <returns>An XML string describing the specified System.Type.</returns>
        Public Shared Function SerializeType(ByVal type As Type, ByVal encoding As Encoding) As String
            Using sw As StringWriter = New EncodableStringWriter(encoding)
                Using writer As XmlTextWriter = New XmlTextWriter(sw)
                    SerializeType(type, writer)
                End Using
                Return sw.ToString()
            End Using
        End Function

        ''' <summary>
        ''' Serializes the specified System.Type using the specified System.Xml.XmlWriter.
        ''' </summary>
        ''' <param name="type">The System.Type to serialize.</param>
        ''' <param name="writer">The System.Xml.XmlWriter to use.</param>
        Public Shared Sub SerializeType(ByVal type As Type, ByVal writer As XmlWriter)
            Dim format = XmlSerializationHelper.GetTypeFormatInternal(type)
            writer.WriteElementString("Type", format)
        End Sub

#End Region

#Region "Deserialize"
        ''' <summary>
        ''' Deserializes the specified XML to an instance of the specified type.
        ''' </summary>
        ''' <param name="xml">An XML serialized object.</param>
        ''' <param name="expectedType">The type of the instance to return.</param>
        ''' <returns>An instance of the specified type deserialized from the specified XML or null.</returns>
        ''' <remarks></remarks>
        Public Shared Function Deserialize(ByVal xml As String, ByVal expectedType As Type) As Object
            If (expectedType.Equals(GetType(Type))) Then
                Return XmlSerializationHelper.DeserializeType(xml)
            Else
                Dim serializer As XmlSerializer = New XmlSerializer(expectedType)
                Dim reader As StringReader = New StringReader(xml)
                Return serializer.Deserialize(reader)
            End If
        End Function

        ''' <summary>
        ''' Deserializes the specified XML to an instance of the specified type.
        ''' </summary>
        ''' <typeparam name="T">The type of the instance to return.</typeparam>
        ''' <param name="xml">An XML serialized object.</param>
        ''' <returns>An instance of the specified type deserialized from the specified XML or null.</returns>
        ''' <remarks></remarks>
        Public Shared Function Deserialize(Of T)(ByVal xml As String) As T
            Return CType(XmlSerializationHelper.Deserialize(xml, GetType(T)), T)
        End Function

        ''' <summary>
        ''' Deserializes a System.Type from the specified XML string.
        ''' </summary>
        ''' <param name="xml">An XML serialized type.</param>
        ''' <returns>A System.Type deserialized from the specified XML string or null.</returns>
        Public Shared Function DeserializeType(ByVal xml As String) As Type
            Using sr As New StringReader(xml)
                Using reader As New XmlTextReader(sr)
                    Return XmlSerializationHelper.DeserializeType(reader)
                End Using
            End Using
            Return Nothing
        End Function

        ''' <summary>
        ''' Deserializes a System.Type using the specified System.Xml.XmlReader.
        ''' </summary>
        ''' <param name="reader">The XML reader to use.</param>
        ''' <returns>A System.Type deserialized from the specified System.Xml.XmlReader or null.</returns>
        ''' <remarks></remarks>
        Public Shared Function DeserializeType(ByVal reader As XmlReader) As Type
            If (reader.Read()) Then
                If (reader.NodeType = XmlNodeType.Element AndAlso reader.LocalName = "Type") Then
                    Dim inner As String = reader.ReadInnerXml()
                    If Not (String.IsNullOrEmpty(inner)) Then _
                        Return Type.GetType(inner)
                End If
            End If
            Return Nothing
        End Function
#End Region

        ''' <summary>
        ''' Sets the custom XML serialized type format.  If format is null, then the default will be used.
        ''' </summary>
        ''' <param name="format">A method that can format the serialized type or null to use the default.</param>
        ''' <remarks></remarks>
        Public Shared Sub SetCustomTypeFormat(ByVal format As Func(Of Type, String))
            XmlSerializationHelper._format = format
        End Sub

        Private Shared Function GetTypeFormatInternal(ByVal type As Type) As String
            If XmlSerializationHelper._format IsNot Nothing Then
                Return XmlSerializationHelper._format.Invoke(type)
            Else
                Return type.AssemblyQualifiedName
            End If
        End Function

    End Class

End Namespace