Option Explicit On

Imports System.Threading
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Language
Imports DigitalData.Modules.Logging
Imports Quartz
Imports Quartz.Listener

Public Class JobListener
    Inherits JobListenerSupport

    Public Overrides ReadOnly Property Name As String = "JobListener"
    Public Property Dataset As DataSet

    Private ReadOnly _Logger As Logger
    Private ReadOnly _LogConfig As LogConfig
    Private ReadOnly _MSSQL As MSSQLServer

    Public Sub New(LogConfig As LogConfig, MSSQL As MSSQLServer, ResultDataSet As DataSet)
        MyBase.New()

        _LogConfig = LogConfig
        _Logger = LogConfig.GetLogger()
        _MSSQL = MSSQL
        Dataset = ResultDataSet
    End Sub

    Public Overrides Function JobWasExecuted(context As IJobExecutionContext, jobException As JobExecutionException, Optional cancellationToken As CancellationToken = Nothing) As Task
        _Logger.Info("Job [{0}] was executed successfully. Saving Data.", context.JobDetail.Key)

        Dim oResult As JobResult = context.Result

        For Each oTableResult As JobResult.ResultTable In oResult.Tables
            Try
                _Logger.Debug("Saving Datatable [{0}]", oTableResult.Table)
                SaveDataTables(oTableResult)

            Catch ex As Exception
                _Logger.Error("Error while executing SaveDataTables for {0}", oTableResult.Table.TableName)
            End Try
        Next

        Return MyBase.JobWasExecuted(context, jobException, cancellationToken)
    End Function

    Public Sub ReplaceExistingTable(Name As String, Table As DataTable, DataSet As DataSet, Optional ChildTable As DataTable = Nothing)
        Try
            Dim oDatatableNameTemp As String = Name & "-TEMP"

            _Logger.Debug("DataTable [{0}] exists, renaming and replacing in DataSet", Name)

            ' Rename the new table, add TEMP suffix
            _Logger.Debug("Renaming new table [{0}] to [{1}]", Name, oDatatableNameTemp)
            Table.TableName = oDatatableNameTemp

            ' If child table exists, remove all connections to it
            If ChildTable IsNot Nothing Then
                _Logger.Debug("Removing relations/constraints on [{0}] and [{1}]", ChildTable.TableName, Name)

                ' Remove relations/constraints on child table
                Dim oChildTable As DataTable = DataSet.Tables.Item(ChildTable.TableName)
                If oChildTable IsNot Nothing Then
                    oChildTable.Constraints.Clear()
                    oChildTable.ChildRelations.Clear()
                End If

                ' Remove relations/constraints on parent table
                Dim oParentTable As DataTable = DataSet.Tables.Item(Name)
                If oParentTable IsNot Nothing Then
                    oParentTable.Constraints.Clear()
                    oParentTable.ChildRelations.Clear()
                End If
            End If

            ' Remove the temp table if it exists
            If DataSet.Tables.Contains(oDatatableNameTemp) Then
                _Logger.Debug("Removing Table [{0}]", oDatatableNameTemp)

                DataSet.Tables.Item(oDatatableNameTemp).ChildRelations.Clear()
                DataSet.Tables.Item(oDatatableNameTemp).Constraints.Clear()
                DataSet.Tables.Remove(oDatatableNameTemp)
            Else
                _Logger.Debug("Table [{0}] does not exist, skipping removal.", oDatatableNameTemp)
            End If

            ' Remove the current Table
            If DataSet.Tables.Contains(Name) Then
                _Logger.Debug("Removing Table [{0}]", Name)

                DataSet.Tables.Item(Name).Constraints.Clear()
                DataSet.Tables.Remove(Name)
            Else
                _Logger.Debug("Table [{0}] does not exist, skipping removal.", Name)
            End If

            ' Add the new table to the dataset
            _Logger.Debug("Adding Table [{0}] to Dataset", Table.TableName)
            DataSet.Tables.Add(Table)

            ' Rename the new table
            _Logger.Debug("Renaming new table [{0}] to [{1}]", oDatatableNameTemp, Name)
            DataSet.Tables.Item(oDatatableNameTemp).TableName = Name
        Catch ex As Exception
            _Logger.Error(ex)
            Throw ex
        End Try
    End Sub

    Public Sub AddNewTable(Name As String, Table As DataTable, DataSet As DataSet)
        _Logger.Debug("DataTable [{0}] does not exist, adding to DataSet", Name)

        DataSet.Tables.Add(Table)
    End Sub

    Public Function GetRelationName(ParentTableName As String, ChildTableName As String) As String
        Return $"{ParentTableName}-{ChildTableName}"
    End Function

    Public Sub AddRelation(ParentTableName As String, ParentColumnName As String, ChildTableName As String, ChildColumnName As String)
        Dim oChild As DataTable = Dataset.Tables.Item(ChildTableName)
        Dim oParent As DataTable = Dataset.Tables.Item(ParentTableName)

        Dim oRelationName As String = GetRelationName(ParentTableName, ChildTableName)
        Dim oParentColumn As DataColumn = oParent.Columns.Item(ParentColumnName)
        Dim oChildColumn As DataColumn = oChild.Columns.Item(ChildColumnName)

        _Logger.Debug("New Relation: {0}/{1} => {2}/{3}", ParentTableName, ParentColumnName, ChildTableName, ChildColumnName)
        Try
            _Logger.Debug("Creating Relation [{0}]", oRelationName)
            _Logger.Debug("ParentColumn exists: {0}", IsNothing(oParentColumn))
            _Logger.Debug("ChildColumn exists: {0}", IsNothing(oChildColumn))
            Dim oRelation As New DataRelation(oRelationName, oParentColumn, oChildColumn)

            _Logger.Debug("Adding Relation [{0}]", oRelationName)
            Dataset.Relations.Add(oRelation)

        Catch ex As ArgumentNullException
            _Logger.Error(ex)
            _Logger.Warn("ArgumentNullException in AddRelation: {0}", ex.Message)

        Catch ex As DuplicateNameException
            _Logger.Error(ex)
            _Logger.Warn("DuplicateNameException in AddRelation: {0}", ex.Message)

        Catch ex As InvalidConstraintException
            _Logger.Error(ex)
            _Logger.Warn("DuplicateNameException in AddRelation: {0}", ex.Message)

        Catch ex As ArgumentException
            _Logger.Error(ex)
            _Logger.Warn("ArgumentException in AddRelation: {0}", ex.Message)

        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("Exception in AddRelation: {0}", ex.Message)

        End Try
    End Sub

    Public Sub SaveDataTables(Result As JobResult.ResultTable)
        Try
            Dim oTable As DataTable = Result.Table

            Dim oName As String = Result.DetailRow.Item("DT_NAME")
            Dim oDetailId As Integer = Result.DetailRow.Item("GUID")

            ' Used for debugging relations and constraints
            '_Logger.Debug("Dataset BEFORE saving datatables")
            'ListTables(Dataset)

            If Dataset.Tables.Contains(oName) Then
                _Logger.Debug("Datatable [{0}] already exists, replacing.", oName)

                ' Replace existing table
                If Result.ChildTable IsNot Nothing Then
                    ReplaceExistingTable(oName, oTable, Dataset, Result.ChildTable)
                    AddRelation(oName, Result.TableRelationColumn, Result.ChildTable.TableName, Result.ChildRelationColumn)
                Else
                    ReplaceExistingTable(oName, oTable, Dataset)
                End If
            Else
                _Logger.Debug("Datatable [{0}] does not exist yet, adding.", oName)

                AddNewTable(oName, oTable, Dataset)

                If Result.ChildTable IsNot Nothing Then
                    AddNewTable(Result.ChildTable.TableName, Result.ChildTable, Dataset)

                    AddRelation(oName, Result.TableRelationColumn, Result.ChildTable.TableName, Result.ChildRelationColumn)
                End If
            End If

            ' Used for debugging relations and constraints
            '_Logger.Debug("Dataset AFTER saving datatables")
            'ListTables(Dataset)

            Dim oMemoryUsage = GC.GetTotalMemory(True)
            Dim oUsageString = Utils.BytesToString(oMemoryUsage)

            _Logger.Debug("Estimated memory usage after Job is [{0}]", oUsageString)

            _MSSQL.ExecuteNonQuery($"INSERT INTO TBAPPSERV_CRON_DETAIL_HISTORY (DETAIL_ID) VALUES ({oDetailId})")
        Catch ex As Exception
            _Logger.Warn("Unexpected error in JobListener: {0}", ex.Message)
            _Logger.Error(ex)
        End Try
    End Sub

    Private Sub ListTables(dataset As DataSet)
        Dim oIndex As Integer = 1

        For Each oTable As DataTable In dataset.Tables
            _Logger.Debug("Table: [{0}] ({1})", oTable.TableName, oIndex)

            _Logger.Debug("-> Listing Constraints for [{0}]:", oTable.TableName)
            For Each oConstraint As Constraint In oTable.Constraints
                _Logger.Debug("---> Constraint: [{0}]", oConstraint.ConstraintName)
            Next

            _Logger.Debug("-> Listing Child-Relations for [{0}]:", oTable.TableName)
            For Each oRelation As DataRelation In oTable.ChildRelations
                _Logger.Debug("---> Relation: [{0}] ", oRelation.RelationName)
            Next

            oIndex += 1
        Next
    End Sub
End Class
