Imports System.Collections.Specialized
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports Quartz
Imports Quartz.Impl
Imports Quartz.Impl.Matchers
Imports Quartz.Logging

Public Class Scheduler
    Private _Factory As StdSchedulerFactory
    Private _MSSQL As MSSQLServer
    Private _Scheduler As IScheduler
    Private _LogConfig As LogConfig
    Private _Logger As DigitalData.Modules.Logging.Logger
    Private _JobListener As JobListener

    Private _Props = New NameValueCollection From {
        {"quartz.serializer.type", "binary"}
    }

    Private Const JOB_GROUP As String = "DatatableJobs"

    Public Sub New(LogConfig As LogConfig, MSSQL_ECM As MSSQLServer, TableStore As DataSet)
        _LogConfig = LogConfig
        _Logger = LogConfig.GetLogger()

        _Factory = New StdSchedulerFactory(_Props)
        _MSSQL = MSSQL_ECM
        _JobListener = New JobListener(LogConfig, _MSSQL, TableStore)

        Logging.LogProvider.SetCurrentLogProvider(New LogProvider(_Logger))
    End Sub

    Public Async Sub Start()
        ' Get new scheduler
        _Scheduler = Await _Factory.GetScheduler()

        ' configure it
        _Scheduler.ListenerManager.AddJobListener(_JobListener,
                                                  GroupMatcher(Of JobKey).GroupEquals(JOB_GROUP))

        ' start it
        Await _Scheduler.Start()

        Dim oCronjobs As DataTable = Await GetCronJobs()

        Try
            If oCronjobs IsNot Nothing Then
                _Logger.Debug("Loaded {0} cron jobs", oCronjobs.Rows.Count)

                For Each oRow As DataRow In oCronjobs.Rows
                    Dim oDefinition As String = oRow.Item("CRON_DEFINITION")
                    Dim oTitle As String = oRow.Item("TITLE")
                    Dim oGuid As Integer = oRow.Item("GUID")

                    Dim oCronTrigger As ITrigger
                    Dim oInitTrigger As ITrigger

                    Dim oJob As IJobDetail
                    Dim oInitJob As IJobDetail

                    Dim oJobData As New JobDataMap From {
                        {"LogConfig", _LogConfig},
                        {"MSSQL", _MSSQL},
                        {"CronJobId", oGuid},
                        {"CronJobTitle", oTitle}
                    }

                    Dim oIdentity As String = $"CRON-JOB-{oGuid}"

                    oCronTrigger = TriggerBuilder.Create().
                        WithIdentity(oIdentity & "-TRIGGER", JOB_GROUP).
                        WithCronSchedule(oDefinition).
                        Build()

                    oJob = JobBuilder.Create(Of DatatableJob)().
                        WithIdentity(oIdentity, JOB_GROUP).
                        UsingJobData(oJobData).
                        Build()

                    Await _Scheduler.ScheduleJob(oJob, oCronTrigger)

                    ' Also create variant of the job that runs immediately, once

                    Dim oInitIdentity As String = $"CRON-JOB-INIT-{oGuid}"

                    oInitTrigger = TriggerBuilder.Create().
                        WithIdentity(oInitIdentity & "-TRIGGER", JOB_GROUP).
                        StartNow().
                        Build()

                    oInitJob = JobBuilder.Create(Of DatatableJob)().
                        WithIdentity(oInitIdentity, JOB_GROUP).
                        UsingJobData(oJobData).
                        Build()

                    Await _Scheduler.ScheduleJob(oInitJob, oInitTrigger)

                    _Logger.Debug("Scheduled a new job for Cron Job [{0}]", oTitle)
                Next
            Else
                _Logger.Warn("CronJobs could not be fetched!")
            End If
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("Unexpected Error while setting up scheduler: " & ex.Message)
        End Try
    End Sub
    Public Async Function GetCronJobs() As Task(Of DataTable)
        Try
            Dim oSQL As String = "SELECT * FROM TBAPPSERV_CRON_JOB WHERE ACTIVE = 1"
            Dim oDatatable As DataTable = Await _MSSQL.GetDatatableAsync(oSQL)

            Return oDatatable
        Catch ex As Exception
            Return Nothing
        End Try
    End Function

    Public Async Function GetCronJobDetails(CronJobId As Integer) As Task(Of DataTable)
        Try
            Dim oSQL As String = $"SELECT * FROM TBAPPSERV_CRON_DETAIL WHERE CRON_ID = {CronJobId}"
            Dim oDatatable As DataTable = Await _MSSQL.GetDatatableAsync(oSQL)

            Return oDatatable
        Catch ex As Exception
            Return Nothing
        End Try
    End Function

    Public Async Sub [Stop]()
        Await _Scheduler.Shutdown()
    End Sub

    Private Class LogProvider
        Implements ILogProvider

        Private _Logger As Modules.Logging.Logger

        Public Sub New(Logger As DigitalData.Modules.Logging.Logger)
            MyBase.New()
            _Logger = Logger
        End Sub

        Public Function OpenNestedContext(message As String) As IDisposable Implements ILogProvider.OpenNestedContext
            Throw New NotImplementedException()
        End Function

        Public Function OpenMappedContext(key As String, value As Object, Optional destructure As Boolean = False) As IDisposable Implements ILogProvider.OpenMappedContext
            Throw New NotImplementedException()
        End Function

        Private Function GetLogger(name As String) As Logging.Logger Implements ILogProvider.GetLogger
            Return Function(level, func, exception, parameters)
                       If exception IsNot Nothing Then
                           _Logger.Error(exception)
                       ElseIf level >= LogLevel.Debug AndAlso func IsNot Nothing Then
                           _Logger.Debug(func(), parameters)
                       ElseIf level >= LogLevel.Info AndAlso func IsNot Nothing Then
                           _Logger.Info(func(), parameters)
                       End If

                       Return True
                   End Function
        End Function
    End Class
End Class
