Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
Imports System.Xml.XPath
Imports System.Xml.Xsl
Imports DigitalData.Modules.Interfaces.Exceptions
Imports DigitalData.Modules.Logging
Imports GdPicture14

Public Class ZUGFeRDInterface
    Private _logConfig As LogConfig
    Private _logger As Logger

    Public Enum ErrorType
        NoValidFile
        NoZugferd
        NoValidZugferd
        MissingProperties
    End Enum

    Public ReadOnly Property FileGroup As FileGroups
    Public ReadOnly Property PropertyValues As PropertyValues

    Public Sub New(LogConfig As LogConfig, GDPictureKey As String)
        _logConfig = LogConfig
        _logger = _logConfig.GetLogger()

        FileGroup = New FileGroups(_logConfig)
        PropertyValues = New PropertyValues(_logConfig)

        Try
            Dim oLicenseManager As New LicenseManager
            oLicenseManager.RegisterKEY(GDPictureKey)
        Catch ex As Exception
            _logger.Warn("GDPicture License could not be registered!")
            _logger.Error(ex)
        End Try
    End Sub

    ''' <summary>
    ''' Validates a ZUGFeRD File and extracts the XML Document from it
    ''' </summary>
    ''' <param name="Path"></param>
    ''' <exception cref="ZUGFeRDExecption"></exception>
    ''' <returns></returns>
    Public Function ExtractZUGFeRDFileWithGDPicture(Path As String) As Object
        Dim oXmlDocument = ValidateZUGFeRDFileWithGDPicture(Path)

        If IsNothing(oXmlDocument) Then
            Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
        End If

        Return SerializeZUGFeRDDocument(oXmlDocument)
    End Function

    ''' <summary>
    ''' Validates a ZUGFeRD File and extracts the XML Document from it
    ''' </summary>
    ''' <param name="Stream"></param>
    ''' <exception cref="ZUGFeRDExecption"></exception>
    ''' <returns></returns>
    Public Function ExtractZUGFeRDFileWithGDPicture(Stream As Stream) As Object
        Dim oXmlDocument = ValidateZUGFeRDFileWithGDPicture(Stream)

        If IsNothing(oXmlDocument) Then
            Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
        End If

        Return SerializeZUGFeRDDocument(oXmlDocument)
    End Function

    ''' <summary>
    ''' Validates a ZUGFeRD File and extracts the XML Document from it
    ''' </summary>
    ''' <param name="Stream"></param>
    ''' <exception cref="ZUGFeRDExecption"></exception>
    ''' <returns></returns>
    Public Function ValidateZUGFeRDFileWithGDPicture(Stream As Stream) As XPathDocument
        Dim oEmbedExtractor = New PDFEmbeds(_logConfig)
        Dim oAllowedExtensions = New List(Of String) From {"xml"}

        Try
            Dim oFiles = oEmbedExtractor.Extract(Stream, oAllowedExtensions)

            ' Attachments are in this case the files that are embedded into a pdf file,
            ' like for example the zugferd-invoice.xml file
            Return HandleEmbeddedFiles(oFiles)

        Catch ex As ZUGFeRDExecption
            ' Don't log ZUGFeRD Exceptions here, they should be handled by the calling code.
            ' It also produces misleading error messages when checking if an attachment is a zugferd file.
            Throw ex

        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Public Function ValidateZUGFeRDFileWithGDPicture(Path As String) As XPathDocument
        Dim oEmbedExtractor = New PDFEmbeds(_logConfig)
        Dim oAllowedExtensions = New List(Of String) From {"xml"}

        Try
            Dim oFiles = oEmbedExtractor.Extract(Path, oAllowedExtensions)

            ' Attachments are in this case the files that are embedded into a pdf file,
            ' like for example the zugferd-invoice.xml file
            Return HandleEmbeddedFiles(oFiles)

        Catch ex As ZUGFeRDExecption
            ' Don't log ZUGFeRD Exceptions here, they should be handled by the calling code.
            ' It also produces misleading error messages when checking if an attachment is a zugferd file.
            Throw ex

        Catch ex As Exception
            _logger.Error(ex)
            Throw ex
        End Try
    End Function

    Private Function HandleEmbeddedFiles(Results As List(Of PDFEmbeds.EmbeddedFile)) As XPathDocument
        Dim oXmlDocument As XPathDocument

        If Results Is Nothing Then
            Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
        End If

        If Results.Count = 0 Then
            Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
        End If

        Dim oFound As Boolean = False
        Dim oFoundResult As PDFEmbeds.EmbeddedFile = Nothing

        For Each oResult In Results
            If oResult.FileName.ToUpper() = PDFEmbeds.ZUGFERD_XML_FILENAME.ToUpper() Then
                oFound = True
                oFoundResult = oResult
            End If
        Next

        If Not oFound Then
            Throw New ZUGFeRDExecption(ErrorType.NoZugferd, "Datei ist keine ZUGFeRD Datei.")
        End If

        Try
            Using oStream As New MemoryStream(oFoundResult.FileContents)
                oXmlDocument = New XPathDocument(oStream)
            End Using

            Return oXmlDocument
        Catch ex As ZUGFeRDExecption
            ' Don't log ZUGFeRD Exceptions here, they should be handled by the calling code.
            ' It also produces misleading error messages when checking if an attachment is a zugferd file.
            Throw ex

        Catch ex As Exception
            _logger.Error(ex)
            Throw New ZUGFeRDExecption(ErrorType.NoValidZugferd, "Datei ist eine ungültige ZUGFeRD Datei.")
        End Try
    End Function

    Public Function SerializeZUGFeRDDocument(Document As XPathDocument) As Object
        Try
            Dim oNavigator As XPathNavigator = Document.CreateNavigator()
            Dim oReader As XmlReader
            Dim oResult = Nothing

            Dim oTypes As New List(Of Type) From {
                GetType(ZUGFeRD.Version1_0.CrossIndustryDocumentType),
                GetType(ZUGFeRD.Version2_0.CrossIndustryInvoiceType),
                GetType(ZUGFeRD.Version2_1_1.CrossIndustryInvoiceType)
            }

            For Each oType In oTypes
                _logger.Debug("Trying Type [{0}]", oType.FullName)
                Dim oSerializer As New XmlSerializer(oType)

                Try
                    oReader = oNavigator.ReadSubtree()
                    oResult = oSerializer.Deserialize(oReader)
                    _logger.Debug("Serializing with type [{0}] succeeded", oType.FullName)
                    Exit For
                Catch ex As Exception
                    _logger.Debug("Serializing with type [{0}] failed", oType.FullName)
                    _logger.Debug(ex.Message)
                    _logger.Error(ex.InnerException?.Message)
                End Try
            Next

            If oResult Is Nothing Then
                Throw New ApplicationException("No Types matched the given document. Document could not be serialized.")
            End If

            Return oResult

        Catch ex As Exception
            _logger.Error(ex)
            Throw New ZUGFeRDExecption(ErrorType.NoValidZugferd, "Datei ist eine ungültige ZUGFeRD Datei.")
        End Try
    End Function


End Class
