Imports System.ComponentModel
Imports System.Threading
Imports System.Globalization
Imports DevExpress.XtraSplashScreen
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Filesystem
Imports DigitalData.Modules.Language.Utils
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.EDMI.API
Imports DigitalData.Modules.EDMI.API.Constants
Imports DigitalData.Modules.Encryption
Imports DigitalData.GUIs.ZooFlow.ClassConstants
Imports DigitalData.GUIs.ZooFlow.ClassInitLoader
Imports DigitalData.Controls.SQLConfig
Imports System.Data.SqlClient

Public Class ClassInit
    Inherits Base.BaseClass

    Private ReadOnly _MainForm As frmFlowForm
    Private ReadOnly _DataASorDB As ClassDataASorDB
    Private ReadOnly _Database As DatabaseWithFallback
    Private _Loader As ClassInitLoader

    Public Event Completed As EventHandler

    Public Sub New(LogConfig As LogConfig, ParentForm As frmFlowForm)
        MyBase.New(LogConfig)
        _MainForm = ParentForm

        'TODO: Remove when Globix uses DatabaseWithFallback
        clsDataASorDB = New ClassDataASorDB(LogConfig)
    End Sub

    Public Sub InitializeApplication()
        If Not SetupDatabase() Then
            MsgBox("Keine Verbindungs-Informationen hinterlegt. Anwendung wird beendet.", MsgBoxStyle.Critical, _MainForm.Text)
            Application.Exit()
        Else
            _Loader = New ClassInitLoader(LogConfig)

            ' === Init Schritte definieren
            _Loader.AddStep("Initializing Base", AddressOf InitializeBase, True)
            _Loader.AddStep("Initializing Database (1/2)", AddressOf InitializeDatabase, True)
            _Loader.AddStep("Initializing EDMI Service", AddressOf InitializeService, True)
            _Loader.AddStep("Initializing Database (2/2)", AddressOf InitializeDatabaseWithFallback, True)
            _Loader.AddStep("Initializing User", AddressOf InitializeUser, True)
            _Loader.AddStep("Initializing Language", AddressOf InitializeLanguage, False)
            _Loader.AddStep("Initializing 3rd-party licenses", AddressOf Initialize3rdParty, False)
            _Loader.AddStep("Initializing Basic Config", AddressOf InitBasicConfig, False)
            ' === Init Schritte definieren

            AddHandler _Loader.ProgressChanged, AddressOf ProgressChanged
            AddHandler _Loader.InitCompleted, AddressOf InitCompleted

            _Loader.Run()
        End If
    End Sub


#Region "=== Init Steps ==="
    Private Sub InitializeBase(MyApplication As My.MyApplication)
        My.Helpers = New ClassHelpers(My.LogConfig)
    End Sub

    Private Sub InitializeDatabase(MyApplication As My.MyApplication)
        Dim oConnectionString = MSSQLServer.DecryptConnectionString(My.SystemConfig.ConnectionString)
        My.DatabaseECM = New MSSQLServer(My.LogConfig, oConnectionString)

        If My.DatabaseECM.DBInitialized = False Then
            Logger.Warn("Could not initialize DD_ECM-Database!")
            Throw New InitException("Could not initialize ECM-Database!")

        Else
            Dim oSQl = "SELECT * FROM TBDD_CONNECTION WHERE BEZEICHNUNG = 'IDB' AND AKTIV = 1"
            Dim oDatatable As DataTable = My.DatabaseECM.GetDatatable(oSQl)

            If IsNothing(oDatatable) OrElse oDatatable.Rows.Count = 0 Then

                Dim oForm As New frmSQLConfig(My.LogConfig) With {.FormTitle = "IDB Datenbank"}
                Dim oResult = oForm.ShowDialog()

                If oResult = DialogResult.OK Then
                    Dim oConnectionStringSaved = SaveConnectionString(oForm.ConnectionString)

                    If oConnectionStringSaved = False Then
                        Throw New InitException("Fehler beim Laden der IDB Verbindungsdaten!")
                    End If

                    oDatatable = My.DatabaseECM.GetDatatable(oSQl)
                End If

            End If

            If oDatatable.Rows.Count > 1 Then
                Logger.Warn("Multiple IDB connection entries in TBDD_CONNECTION found!")
                Throw New InitException("Fehler beim Laden der IDB Verbindungsdaten!")
            End If

            Dim oDataRow As DataRow = oDatatable.Rows.Item(0)
            Dim oConString = My.DatabaseECM.GetConnectionString(
                oDataRow.Item("SERVER").ToString,
                oDataRow.Item("DATENBANK").ToString,
                oDataRow.Item("USERNAME").ToString,
                oDataRow.Item("PASSWORD").ToString
            )

            Dim oDecryptedConnectionString = MSSQLServer.DecryptConnectionString(oConString)
            My.DatabaseIDB = New MSSQLServer(My.LogConfig, oDecryptedConnectionString)
        End If

        If My.DatabaseIDB.DBInitialized = False Then
            Logger.Warn("Could not initialize IDB-Database!")
            Throw New InitException("Could not initialize IDB-Database!")
        End If
    End Sub
    Private Sub InitializeService(MyApplication As My.MyApplication)
        Try
            If My.SystemConfig.AppServerConfig = String.Empty Then
                Dim oForm As New frmServiceConfig()
                Dim oResult = oForm.ShowDialog()

                If oResult <> DialogResult.OK Then
                    Throw New InitException("Could not initialize IDB-Service!")
                End If
            End If

            MyApplication.Service.Address = My.SystemConfig.AppServerConfig
            Dim oServerData = Client.ParseServiceAddress(My.SystemConfig.AppServerConfig)

            My.Application.Service.Client = New Client(LogConfig, oServerData.Item1, oServerData.Item2)

            If Not IsNothing(My.Application.Service.Client) Then
                If My.Application.Service.Client.Connect() Then
                    MyApplication.Service.IsActive = True
                End If
            End If
        Catch ex As Exception
            Logger.Error(ex)
            Throw New InitException("Error in InitializeService", ex)
        End Try
    End Sub
    Private Sub InitializeDatabaseWithFallback(MyApplication As My.MyApplication)
        Try
            My.Database = New DatabaseWithFallback(LogConfig, My.Application.Service.Client, My.DatabaseECM, My.DatabaseIDB)
        Catch ex As Exception
            Logger.Error(ex)
            Throw New InitException("Error InitializeDatabaseWithFallback!", ex)
        End Try
    End Sub
    Private Sub InitializeUser(MyApplication As My.MyApplication)
        Try
            Dim oSql As String = My.Queries.Common.FNDD_MODULE_INIT(Environment.UserName)
            Dim oDatatable As DataTable = My.DatabaseECM.GetDatatable(oSql)

            If oDatatable Is Nothing Then
                Throw New InitException("Benutzer konnte nicht geladen werden!")
            End If

            If oDatatable.Rows.Count <= 1 Then
                Throw New InitException("Benutzer konnte nicht gefunden werden!")
            End If

            For Each oRow As DataRow In oDatatable.Rows
                Dim oType As String = oRow("TYPE").ToString

                Select Case oType
                    Case "USER"
                        HandleUserInfo(MyApplication, oRow)

                    Case MODULE_CLIPBOARDWATCHER
                        HandleModuleInfo(MyApplication, oType, oRow)
                    Case MODULE_GLOBAL_INDEXER
                        HandleModuleInfo(MyApplication, oType, oRow)
                    Case MODULE_ZOOFLOW
                        HandleModuleInfo(MyApplication, oType, oRow)
                End Select
            Next

        Catch ex As InitException
            Logger.Error(ex)
            Throw ex

        Catch ex As Exception
            Logger.Error(ex)
            Throw New InitException($"Unexpected error while initializing user!", ex)
        End Try
    End Sub
    Private Sub InitializeLanguage(MyApplication As My.MyApplication)
        Dim oLanguage = MyApplication.User.Language
        Dim oDateFormat = MyApplication.User.DateFormat
        Dim oCultureInfo As New CultureInfo(oLanguage)
        oCultureInfo.DateTimeFormat.ShortDatePattern = oDateFormat
        Thread.CurrentThread.CurrentCulture = oCultureInfo
        Thread.CurrentThread.CurrentUICulture = oCultureInfo
        CultureInfo.DefaultThreadCurrentCulture = oCultureInfo
        CultureInfo.DefaultThreadCurrentUICulture = oCultureInfo
    End Sub
    Private Sub Initialize3rdParty(MyApplication As My.MyApplication)
        Try
            Dim oSql = "Select LICENSE FROM TBDD_3RD_PARTY_MODULES WHERE NAME = 'GDPICTURE'"
            Dim oDatatable As DataTable = My.DatabaseECM.GetDatatable(oSql)

            If oDatatable.Rows.Count = 0 Then
                Throw New InitException("Konfiguration konnte nicht geladen werden!")
            End If

            Dim oRow As DataRow = oDatatable.Rows.Item(0)

            MyApplication.Settings.GdPictureKey = NotNull(oRow.Item("LICENSE"), String.Empty)
        Catch ex As Exception
            Logger.Error(ex)
            Throw New InitException("Error Initialize3rdParty!", ex)
        End Try
    End Sub
    Private Sub InitBasicConfig(MyApplication As My.MyApplication)
        Try
            My.Tables.DTIDB_COMMON_SQL = My.DatabaseIDB.GetDatatable("SELECT * FROM TBIDB_COMMON_SQL WHERE ACTIVE = 1")



        Catch ex As Exception
            Logger.Error(ex)
            Throw New InitException("Error in InitBasicData", ex)
        End Try
    End Sub
#End Region

    Private Function SetupDatabase() As Boolean
        If My.SystemConfig.ConnectionString = String.Empty Then
            Dim oConnectionString = My.SystemConfig.ConnectionString
            Dim oForm As New frmSQLConfig(My.LogConfig) With {
                .ConnectionString = oConnectionString,
                .FormTitle = "ECM Datenbank"
            }
            Dim oResult = oForm.ShowDialog()

            If oResult = DialogResult.OK Then
                My.SystemConfig.ConnectionString = oForm.ConnectionString
                My.SystemConfigManager.Save()

                Return True
            Else
                Return False
            End If
        End If

        Return True
    End Function

    Private Function SaveConnectionString(pConnectionString As String) As Boolean
        Try
            Dim oBuilder As New SqlConnectionStringBuilder(pConnectionString)
            Dim oSql = $"
                INSERT INTO TBDD_CONNECTION 
                (BEZEICHNUNG, SQL_PROVIDER, SERVER, DATENBANK, USERNAME, PASSWORD, BEMERKUNG, AKTIV, ERSTELLTWER) 
                VALUES ('IDB', 'MS-SQLServer', '{oBuilder.DataSource}', '{oBuilder.InitialCatalog}', '{oBuilder.UserID}', '{oBuilder.Password}', 'Created by Zooflow', 1, '{Environment.UserName}')"

            Return My.DatabaseECM.ExecuteNonQuery(oSql)
        Catch ex As Exception
            Logger.Error(ex)
            Return False
        End Try
    End Function

    Private Sub ProgressChanged(sender As Object, Progress As InitProgress)
        SplashScreenManager.Default.SendCommand(frmSplash.SplashScreenCommand.SetProgress, Progress.CurrentPercent)
        SplashScreenManager.Default.SendCommand(frmSplash.SplashScreenCommand.SetActionName, Progress.CurrentStep.Name)
    End Sub

    Private Sub InitCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
        If Not IsNothing(e.Error) Then
            MsgBox("Beim Initialisieren des Programms ist folgender Fehler aufgetreten:" & vbNewLine & vbNewLine & e.Error.Message, MsgBoxStyle.Critical, _MainForm.Text)
            Application.ExitThread()
        Else
            ' Copy back state from MyApplication Helper to My.Application
            Dim oMyApplication As My.MyApplication = DirectCast(e.Result, My.MyApplication)

            My.Application.Settings = oMyApplication.Settings
            My.Application.User = oMyApplication.User
            My.Application.Modules = oMyApplication.Modules
            My.Application.ModulesActive = oMyApplication.ModulesActive
            My.Application.ClipboardWatcher = oMyApplication.ClipboardWatcher

            RaiseEvent Completed(sender, Nothing)
        End If
    End Sub

    Private Sub HandleUserInfo(MyApplication As My.MyApplication, Row As DataRow)
        Dim oValue As Object = Row.Item("VALUE")
        Dim oName As String = Row.Item("NAME").ToString

        Select Case oName
            Case "USER_ID"
                MyApplication.User.UserId = CInt(oValue)
            Case "USER_PRENAME"
                MyApplication.User.GivenName = NotNull(oValue.ToString, String.Empty)
            Case "USER_SURNAME"
                MyApplication.User.Surname = NotNull(oValue.ToString, String.Empty)
            Case "USER_SHORTNAME"
                MyApplication.User.ShortName = NotNull(oValue.ToString, String.Empty)
            Case "USER_EMAIL"
                MyApplication.User.Email = NotNull(oValue.ToString, String.Empty)
            Case "USER_DATE_FORMAT"
                MyApplication.User.DateFormat = NotNull(oValue.ToString, "dd.MM.yyyy")
            Case "USER_LANGUAGE"
                MyApplication.User.Language = NotNull(oValue.ToString, "de-DE")
            Case "USER_LANGUAGEID"
                MyApplication.User.LanguageId = CShort(oValue)
        End Select
    End Sub
    Private Sub HandleModuleInfo(MyApplication As My.MyApplication, ModuleName As String, Row As DataRow)
        Dim oValue As Object = Row.Item("VALUE")
        Dim oName As String = Row.Item("NAME").ToString

        If Not MyApplication.Modules.ContainsKey(ModuleName) Then
            MyApplication.Modules.Item(ModuleName) = New DigitalData.Modules.ZooFlow.State.ModuleState()
        End If

        Select Case oName
            Case "MODULE_ACCESS"
                If CBool(oValue) Then
                    SyncLock MyApplication.ModulesActive
                        MyApplication.ModulesActive.Add(ModuleName)
                    End SyncLock
                End If
                MyApplication.Modules.Item(ModuleName).HasAccess = CBool(oValue)

            Case "IS_ADMIN"
                MyApplication.Modules.Item(ModuleName).IsAdmin = CBool(oValue)

            Case "USER_COUNT_LOGGED_IN"
                MyApplication.Modules.Item(ModuleName).LoggedIn = CInt(oValue) + 1

            Case "RESULT"
                Dim oLines = oValue.ToString.Split("|"c)
                Logger.Debug("Access Result for Module {0}", ModuleName)
                For Each oLine In oLines
                    Logger.Debug(oLine.Trim)
                Next
            Case "WORKING_MODE"
                Dim oLines = oValue.ToString.Split("|"c)
                Logger.Debug("WORKING_MODEs for Module {0}", ModuleName)
                For Each oLine In oLines
                    Logger.Debug(oLine.Trim)
                    If oLine = "NO_BASICCONF" Then
                        MyApplication.User.HideBasicConfig = True

                    ElseIf oLine.StartsWith("SEARCH_STRING_ATTRID") Then
                        Try
                            Dim oResult = oLine.Replace("SEARCH_STRING_ATTRID=", "")
                            MyApplication.Search.SelectInStringAttributeIds = oResult
                        Catch ex As Exception
                            MyApplication.Search.SelectInStringAttributeIds = ""
                        End Try
                    ElseIf oLine.StartsWith("SEARCH_INT_ATTRID") Then
                        Try
                            Dim oResult = oLine.Replace("SEARCH_INT_ATTRID=", "")

                            MyApplication.Search.SelectInIntegerAttributeIds = oResult
                        Catch ex As Exception
                            MyApplication.Search.SelectInIntegerAttributeIds = ""
                        End Try
                    Else
                        Logger.Info($"Wrong WorkingMode: {oLine}")
                    End If
                Next

        End Select
    End Sub
End Class
