Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.EDMI.API.EDMIServiceReference
Imports DigitalData.Modules.EDMI.API.Rights
Imports System.ServiceModel
Imports System.IO

Public Class Client
    Private ReadOnly _logger As Logger
    Private ReadOnly _channelFactory As ChannelFactory(Of IEDMIServiceChannel)
    Private _ServiceAddress As String
    Private _IPAddressServer As String
    Private _channel As IEDMIServiceChannel

    Public Class StreamedFile
        Public Stream As MemoryStream
        Public FileName As String
    End Class

    Public Class FileList
        Public Datatable As DataTable
    End Class

    Public Class DocumentInfo
        Public FullPath As String
        Public AccessRight As AccessRight
    End Class

    ''' <summary>
    ''' Creates a new EDMIAPI object
    ''' </summary>
    ''' <param name="LogConfig">LogConfig object</param>
    ''' <param name="ServiceAdress">The full service url to connect to</param>
    Public Sub New(LogConfig As LogConfig, ServiceAdress As String)
        _logger = LogConfig.GetLogger()

        Try
            _ServiceAddress = ServiceAdress
            Dim oBinding = Channel.GetBinding()
            Dim oAddress = New EndpointAddress(ServiceAdress)
            Dim oFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress)

            _channelFactory = oFactory
        Catch ex As Exception
            _logger.Error(ex)
        End Try
    End Sub

    Public Sub New(LogConfig As LogConfig, IPAddress As String, PortNumber As Integer)
        _logger = LogConfig.GetLogger()

        Try
            _IPAddressServer = IPAddress
            Dim oBinding = Channel.GetBinding()
            Dim oAddress = New EndpointAddress($"net.tcp://{IPAddress}:{PortNumber}/DigitalData/Services/Main")
            Dim oFactory = New ChannelFactory(Of IEDMIServiceChannel)(oBinding, oAddress)

            _channelFactory = oFactory
        Catch ex As Exception
            _logger.Error(ex)
        End Try
    End Sub

    ''' <summary>
    ''' Connect to the service
    ''' </summary>
    ''' <returns>True if connection was successful, false otherwise</returns>
    Public Function Connect() As Boolean
        Try
            _channel = GetChannel()

            _logger.Debug("Opening channel..")
            _channel.Open()

            _logger.Info($"Connection to AppService {_IPAddressServer} successfully established!")

            Return True
        Catch ex As Exception
            _logger.Error(ex)
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Imports a file by filename
    ''' </summary>
    ''' <returns>A document object</returns>
    Public Async Function ImportFileAsync(FilePath As String, DocumentType As String, ObjectStoreId As Long, Optional RetentionDays As Integer = 0) As Task(Of Long)
        Try
            Dim oFileInfo As New FileInfo(FilePath)
            If oFileInfo.Exists = False Then
                Throw New FileNotFoundException("Cannot import non-existing file.", FilePath)
            End If

            Using oStream As New FileStream(FilePath, FileMode.Open)
                Dim oContents(oStream.Length) As Byte
                Dim oBytesRead = Await oStream.ReadAsync(oContents, 0, oStream.Length)
                Dim oData As New DocumentImportRequest() With {
                    .FileName = oFileInfo.Name,
                    .Contents = oContents,
                    .DocumentType = DocumentType,
                    .ObjectStoreId = ObjectStoreId,
                    .RetentionDays = RetentionDays
                }

                Dim oResponse = Await _channel.ImportFileAsync(oData)

                Return oResponse.ObjectId
            End Using
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Async Function GetFileByObjectIdAsync(ObjectId As Long) As Task(Of StreamedFile)
        Try
            Dim oData As New DocumentStreamRequest() With {.ObjectId = ObjectId}
            Dim oResponse As DocumentStreamResponse = Await _channel.GetFileByObjectIdAsync(oData)
            Dim oMemoryStream As New MemoryStream()
            oResponse.FileContents.CopyTo(oMemoryStream)
            oMemoryStream.Position = 0

            Return New StreamedFile() With {
                .Stream = oMemoryStream,
                .FileName = oResponse.FileName
            }
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Async Function ListFilesForUserAsync() As Task(Of FileList)
        Try
            Dim oResponse As DocumentListResponse = Await _channel.ListFilesForUserAsync(New ListFilesForUserRequest())
            Return New FileList() With {
                .Datatable = oResponse.Datatable
            }
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Function GetDatatableFromIDB(SQL As String) As TableResult
        Try
            Dim oResponse = _channel.ReturnDatatable_MSSQL_IDB(SQL)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function


    Public Async Function GetDatatableFromIDBAsync(SQL As String) As Task(Of TableResult)
        Try
            Dim oResponse = Await _channel.ReturnDatatable_MSSQL_IDBAsync(SQL)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Function GetScalarValueFromIDB(SQL As String) As ScalarResult
        Try
            Dim oResponse = _channel.ReturnScalar_MSSQL_IDB(SQL)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Async Function GetScalarValueFromIDBAsync(SQL As String) As Task(Of ScalarResult)
        Try
            Dim oResponse = Await _channel.ReturnScalar_MSSQL_IDBAsync(SQL)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Function GetDatatableByName(DatatableName As String, Optional FilterExpression As String = "", Optional SortByColumn As String = "") As TableResult
        Try
            Dim oResponse = _channel.ReturnDatatableFromCache(DatatableName, FilterExpression, SortByColumn)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Async Function GetDatatableByNameAsync(DatatableName As String, Optional FilterExpression As String = "", Optional SortByColumn As String = "") As Task(Of TableResult)
        Try
            Dim oResponse = Await _channel.ReturnDatatableFromCacheAsync(DatatableName, FilterExpression, SortByColumn)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Function GetDocumentInfo(UserId As Long, ObjectId As Long) As DocumentInfo
        Try
            Dim oParams = New DocumentInfoRequest With {
                .ObjectId = ObjectId,
                .UserId = UserId
            }
            Dim oResponse As DocumentInfoResponse = _channel.GetFileInfoByObjectId(oParams)

            Return New DocumentInfo With {
                .AccessRight = oResponse.FileRight,
                .FullPath = oResponse.FullPath
            }

        Catch ex As FaultException(Of ObjectDoesNotExistFault)
            _logger.Error(ex)
            Throw ex

        Catch ex As FaultException
            _logger.Error(ex)
            Throw ex

        Catch ex As Exception
            _logger.Error(ex)
            Throw ex

        End Try
    End Function

    Public Async Function GetDocumentInfoAsync(UserId As Long, ObjectId As Long) As Task(Of DocumentInfo)
        Try
            Dim oParams = New DocumentInfoRequest With {
                .ObjectId = ObjectId,
                .UserId = UserId
            }
            Dim oResponse As DocumentInfoResponse = Await _channel.GetFileInfoByObjectIdAsync(oParams)

            Return New DocumentInfo With {
                .AccessRight = oResponse.FileRight,
                .FullPath = oResponse.FullPath
            }
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    ''' <summary>
    ''' Aborts the channel and creates a new connection
    ''' </summary>
    Public Sub Reconnect()
        _logger.Warn("Connection faulted. Trying to reconnect..")

        Try
            _channel.Abort()
            _channel = GetChannel()
            _channel.Open()
        Catch ex As Exception
            _logger.Error(ex)
        End Try
    End Sub

    ''' <summary>
    ''' Creates a channel and adds a Faulted-Handler
    ''' </summary>
    ''' <returns>A channel object</returns>
    Private Function GetChannel() As IEDMIServiceChannel
        Try
            _logger.Debug("...Creating channel..")
            Dim oChannel = _channelFactory.CreateChannel()

            AddHandler oChannel.Faulted, AddressOf Reconnect

            Return oChannel
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function
    Public Function NewFileStoreObject(IDB_OBJ_ID As Long, pStoreType As String, pDate As String, pExtension As String, pKeepExtension As String) As String
        Try
            If pExtension.StartsWith(".") Then
                pExtension = pExtension.Replace(".", "")
            End If
            Dim oResponse = _channel.New_FileStore_Object(IDB_OBJ_ID, pStoreType, pDate, pExtension, pKeepExtension)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function
    Public Function NewIDB_OBJ_ID(pKindType As String, pWho As String, pBusinessEntity As String) As Long
        Try
            Dim oResponse = _channel.New_IDB_OBJECT(pKindType, pWho, pBusinessEntity)
            Return oResponse
        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function
    Public Async Function ImportIDBFOAsync(pContent As Byte(), pWho As String, pIDB_OBJ_ID As Long, pObjectStoreID As Short, pIDBFOPath As String) As Task(Of Boolean)
        Try
            Dim oData As New DocumentImportIDBFORequest() With {
                     .Contents = pContent,
                     .pWho = pWho,
                    .pIDBFilePath = pIDBFOPath,
                    .pIDB_OBJ_ID = pIDB_OBJ_ID,
                    .pObjectStoreID = pObjectStoreID
                }

            Dim oResponse = Await _channel.ImportNewIDBFOAsync(oData)

            Return oResponse.Result

        Catch ex As Exception
            _logger.Error(ex)
            Return False
        End Try
    End Function
End Class
