Imports System.Runtime.CompilerServices
Imports System.Data.Objects.DataClasses
Imports System.Runtime.Serialization
Imports System.IO
Imports System.Reflection
Imports System.Runtime.Serialization.Formatters.Binary

Namespace Extensiones
    Public Module EntityExtensions
        ''' <summary> 
        ''' Extension method to Enitity Object.  
        ''' Deeply clone the Object. 
        ''' </summary> 
        ''' <param name="source">Entity Object need to be cloned </param> 
        ''' <returns>The cloned object</returns> 
        <Extension()>
        Public Function Clone(Of T As EntityObject)(ByVal source As T) As T
            Dim ser As New DataContractSerializer(GetType(T))
            Using stream As MemoryStream = New MemoryStream
                ser.WriteObject(stream, source)
                stream.Seek(0, SeekOrigin.Begin)
                Return DirectCast(ser.ReadObject(stream), T)
            End Using
        End Function
        <Extension()>
        Public Function ClearEntityObject(Of T As Class)(ByVal source As T, ByVal bCheckHierarchy As Boolean) As T
            If (source Is Nothing) Then
                Throw New Exception("Null Object cannot be cloned")
            End If
            Dim tObj As Type = source.GetType
            If (Not tObj.GetProperty("EntityKey") Is Nothing) Then
                tObj.GetProperty("EntityKey").SetValue(source, Nothing, Nothing)
            End If
            If bCheckHierarchy Then
                Dim PropertyList As List(Of PropertyInfo) = Enumerable.ToList(Of PropertyInfo)((From a In source.GetType.GetProperties
                                                                                                Where a.PropertyType.Name.Equals("ENTITYCOLLECTION`1", StringComparison.OrdinalIgnoreCase)
                                                                                                Select a))
                Dim prop As PropertyInfo
                For Each prop In PropertyList
                    Dim keys As IEnumerable = DirectCast(tObj.GetProperty(prop.Name).GetValue(source, Nothing), IEnumerable)
                    Dim key As Object
                    For Each key In keys
                        'Dim kk = ((From a In key.GetType.GetProperties
                        'Where (a.PropertyType.Name.Equals("EntityReference`1", StringComparison.OrdinalIgnoreCase))
                        '    Select a))

                        'Dim ochildprop = (From a In key.[GetType]().GetProperties() Where a.PropertyType.Name = "EntityReference`1").SingleOrDefault()

                        Dim childProp As EntityReference = Enumerable.FirstOrDefault(Of PropertyInfo)((From a In key.GetType.GetProperties
                                                                                                       Where (a.PropertyType.Name.Equals("EntityReference`1", StringComparison.OrdinalIgnoreCase))
                                                                                                       Select a)).GetValue(key, Nothing)

                        ClearEntityObject(childProp, False)
                        ClearEntityObject(key, True)
                    Next
                Next
            End If
            Return source
        End Function


        ''' <summary> 
        '''  Clear the entity of object and all related child objects  
        ''' </summary> 
        ''' <param name="source">Entity Object need to be cleared</param> 
        ''' <param name="bcheckHierarchy">This parameter is used to determine whether to clear all the child object</param> 
        ''' <returns></returns> 
        <Extension()>
        Public Function ClearEntityReference(ByVal source As EntityObject, ByVal bCheckHierarchy As Boolean) As EntityObject
            Return ClearEntityObject(source, bCheckHierarchy)
        End Function

        <Extension>
        Public Function LoadAllChild(source As EntityObject) As EntityObject
            Dim PropList As List(Of PropertyInfo) = (From a In source.[GetType]().GetProperties() Where a.PropertyType.Name = "EntityCollection`1").ToList()
            For Each prop As PropertyInfo In PropList
                Dim instance As Object = prop.GetValue(source, Nothing)
                Dim isLoad As Boolean = CBool(instance.[GetType]().GetProperty("IsLoaded").GetValue(instance, Nothing))
                If Not isLoad Then
                    Dim mi As MethodInfo = (From a In instance.[GetType]().GetMethods() Where a.Name = "Load" AndAlso a.GetParameters().Length = 0).FirstOrDefault()
                    mi.Invoke(instance, Nothing)
                End If
            Next
            Return DirectCast(source, EntityObject)
        End Function

        <Extension>
        Public Function DeepClone(Of T As EntityObject)(ByVal source As T) As T
            Using ms = New MemoryStream()
                Dim formatter = New BinaryFormatter()
                formatter.Serialize(ms, source)
                ms.Position = 0
                Return DirectCast(formatter.Deserialize(ms), T)
            End Using
        End Function
        '<Extension>
        'Public Function ToDataTable(Of T As Class)(ByVal Lista As List(Of T)) As DataTable
        '    Dim dt As New DataTable(GetType(T).Name)
        '    Dim props = GetType(T).GetProperties(BindingFlags.Public Or BindingFlags.Instance)
        '    For Each p In props

        '    Next
        'End Function
        <Extension()>
        Public Function ObtieneContexto(entity As Objects.DataClasses.EntityObject) As Objects.ObjectContext
            Dim relationshipManager = DirectCast(entity, Objects.DataClasses.IEntityWithRelationships).RelationshipManager
            Dim wrappedOwnerProperty = relationshipManager.GetType.GetProperty("WrappedOwner", Reflection.BindingFlags.Instance Or BindingFlags.NonPublic)
            Return wrappedOwnerProperty.GetValue(relationshipManager).Context
        End Function
    End Module
End Namespace