Imports System.Threading
Imports System.Globalization
Imports System.Drawing
Imports System.Windows.Forms
Imports DevExpress.XtraEditors
Imports DigitalData.GUIs.Common
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Language
Imports DigitalData.Modules.ZooFlow
Imports DigitalData.Modules.ZooFlow.Params
Imports DigitalData.Modules.ZooFlow.Constants

''' <summary>
''' 
''' Selfcontained:
''' - Config (Location of Window)
''' 
''' Environment:
''' - Pattern Replacement Values
''' - User Info (isAdmin, etc)
''' - License Info?
''' - ConnectionString
''' 
''' Parameters:
''' - Matching Profiles as List Of ProfileData
''' - Clipboard Content as String
''' </summary>
Public Class frmMatch
    Private ReadOnly _LogConfig As LogConfig
    Private ReadOnly _Logger As Logger
    Private ReadOnly _Environment As Environment
    Private ReadOnly _Params As ClipboardWatcherParams
    Private ReadOnly _Language As String

    Private ReadOnly PrimaryFont As New Font("Segoe UI", 12, FontStyle.Bold)
    Private ReadOnly SecondaryFont As New Font("Segoe UI", 10)
    Private ReadOnly TileForeColor As Color = Color.Black
    Private ReadOnly TileBackColor As Color = Color.FromArgb(255, 214, 47)

    Private ReadOnly OpenForms As New List(Of IResultForm)
    Private ShouldHideInitially As Boolean = False

    Private Const NO_COUNT_SQL As Integer = 99998
    Private Const INVALID_COUNT_SQL As Integer = 99999

    Private Enum ProfileType
        ANY = 0
        DOCS_ONLY = 1
        DATA_ONLY = 2
    End Enum

    Public Sub New(LogConfig As LogConfig, Environment As Environment, Params As ClipboardWatcherParams)
        ' Dieser Aufruf ist für den Designer erforderlich.
        InitializeComponent()

        ' Fügen Sie Initialisierungen nach dem InitializeComponent()-Aufruf hinzu.
        _LogConfig = LogConfig
        _Logger = LogConfig.GetLogger()
        _Environment = Environment
        _Params = Params

        _Language = Utils.NotNull(_Environment.User.Language, State.UserState.LANG_EN_US)
        Thread.CurrentThread.CurrentUICulture = New CultureInfo(_Language)
    End Sub

    Private Function GetResultString(CreatedTiles, MatchedProfiles, ClipboardContents) As String
        Dim oLanguage = Utils.NotNull(_Environment.User.Language, State.UserState.LANG_EN_US)

        Select Case _Language
            Case State.UserState.LANG_DE_DE
                Dim oResultString = IIf(CreatedTiles = 1, "wurde ein Ergebnis", $"wurden {CreatedTiles} Ergebnisse")

                Dim oProfileString = IIf(MatchedProfiles = 1, "einem Profil", $"{MatchedProfiles} Profilen")
                Dim oBase = "Es {0} in {1} für Ihre Suche nach '{2}' gefunden:"

                Return String.Format(oBase, oResultString, oProfileString, _Params.ClipboardContents)

            Case Else
                Dim oResultString = IIf(CreatedTiles = 1, "One Result was", $"{CreatedTiles} Results were")
                Dim oProfileString = IIf(MatchedProfiles = 1, "one Profile", $"{MatchedProfiles} Profiles")
                Dim oBase = "{0} found in {1} for your search for '{2}':"

                Return String.Format(oBase, oResultString, oProfileString, _Params.ClipboardContents)
        End Select
    End Function

    Private Function GetResultWindowString(ClipboardContents As String) As String
        If _Language = State.UserState.LANG_DE_DE Then
            Return $"Suche Nach '{_Params.ClipboardContents}'"
        Else
            Return $"Search For '{_Params.ClipboardContents}'"
        End If

    End Function

    Private Function GetTileString(Profile As ProfileData, [Type] As ProfileType) As String
        If _Language = State.UserState.LANG_DE_DE Then
            If [Type] = ProfileType.DATA_ONLY Then
                Return $"{Profile.CountData} Datensätze"
            Else
                Return $"{Profile.CountDocs} Dateien"
            End If
        End If

        If [Type] = ProfileType.DATA_ONLY Then
            Return $"{Profile.CountData} Data Records"
        Else
            Return $"{Profile.CountDocs} Files"
        End If
    End Function

    Private Async Sub frmMatch_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim oResult As Tuple(Of Integer, Integer) = CreateTiles()
        Dim oCreatedTiles As Integer = oResult.Item1
        Dim oMatchedProfiles As Integer = oResult.Item2

        Try
            SplashScreenManager1.ShowWaitForm()
            TileControlMatch.Enabled = False

            If oCreatedTiles = -1 Then
                Exit Sub
            End If

            If oCreatedTiles = 0 Then
                _Logger.Warn("No Tiles Created: No Results found for ""{0}""", _Params.ClipboardContents)
                Close()
            End If

            Label1.Text = GetResultString(oCreatedTiles, oMatchedProfiles, _Params.ClipboardContents)
            _Logger.Debug($"Created Tiles: {oCreatedTiles} ")
            _Logger.Debug($"Matched Profiles: {oMatchedProfiles}")

            If oCreatedTiles = 1 Then
                Dim oProfile As ProfileData = _Params.MatchingProfiles.First()
                Dim oProfileSearch As New ProfileSearches(_LogConfig, _Environment, _Params)

                If oProfile.CountDocs > 0 And oProfile.CountData = 0 Then
                    _Logger.Debug($"ONLY Docs")
                    'ONLY DOCS AND NÒ DATA
                    Dim oSearches = Await oProfileSearch.LoadDocumentSearchesAsync(oProfile.Guid)
                    _Logger.Debug($"Searches executed")
                    OpenDocumentResults(oProfile, oSearches)
                    _Logger.Debug($"OpenDocumentResults finished")
                    Hide()

                ElseIf oProfile.CountDocs > 0 And oProfile.CountData > 0 Then
                    _Logger.Debug($"Docs AND Data")

                ElseIf oProfile.CountDocs = 0 And oProfile.CountData > 0 Then
                    _Logger.Debug($"ONLY Data")
                    Dim oSearches = Await oProfileSearch.LoadDataSearchesAsync(oProfile.Guid)
                    OpenDataResults(oProfile, oSearches)
                    Hide()

                End If
            End If
        Catch ex As Exception
            _Logger.Error(ex)
        Finally
            TileControlMatch.Enabled = True
            SplashScreenManager1.CloseWaitForm()
        End Try
    End Sub

    Private Sub frmMatch_Activated(sender As Object, e As EventArgs) Handles Me.Activated
        TopMost = True
    End Sub

    Function CreateTiles() As Tuple(Of Integer, Integer)
        Try
            Dim oCreatedTiles As Integer = 0
            Dim oMatchedProfiles As Integer = 0

            Dim oDocumentGroup = TileControlMatch.Groups.Item("TileGroupDocuments")
            Dim oDataGroup = TileControlMatch.Groups.Item("TileGroupData")
            Dim oDataDocumentsGroup = TileControlMatch.Groups.Item("TileGroupDocumentsData")

            oDocumentGroup.Items.Clear()
            oDataGroup.Items.Clear()

            For Each oProfile As ProfileData In _Params.MatchingProfiles
                'TODO: Add Support for Combined Result Lists
                'If oProfile.ProfileType = ProfileType.ANY Then
                '    If oProfile.CountData > 0 And oProfile.CountDocs > 0 Then
                '        Dim oCountText = oProfile.CountData + oProfile.CountDocs
                '        Dim oItem = CreateTile(oProfile, $"{oCountText} Ergebnisse")
                '        oDataDocumentsGroup.Items.Add(oItem)
                '        oCreatedTiles += 1
                '    End If
                'End If

                Dim oProfileMatch As Boolean = False

                _Logger.NewBlock($"Profile {oProfile.Name}")

                If oProfile.ProfileType = ProfileType.ANY Or oProfile.ProfileType = ProfileType.DOCS_ONLY Then
                    _Logger.Debug("ProfileType: DOCS_ONLY or ANY")

                    If oProfile.CountDocs > 0 Then
                        Dim oItem = CreateTile(oProfile, GetTileString(oProfile, ProfileType.DOCS_ONLY))
                        oDocumentGroup.Items.Add(oItem)
                        oCreatedTiles += 1
                        oProfileMatch = True

                        _Logger.Debug("{0} Doc-Results!", oProfile.CountDocs)
                    Else
                        _Logger.Debug("NO Doc-Results!")
                    End If
                End If

                If oProfile.ProfileType = ProfileType.ANY Or oProfile.ProfileType = ProfileType.DATA_ONLY Then
                    _Logger.Debug("ProfileType: DATA_ONLY or ANY")

                    If oProfile.CountData > 0 Then
                        Dim oItem = CreateTile(oProfile, GetTileString(oProfile, ProfileType.DATA_ONLY))
                        oDataGroup.Items.Add(oItem)
                        oCreatedTiles += 1
                        oProfileMatch = True

                        _Logger.Debug("{0} Data-Results!", oProfile.CountData)
                    Else
                        _Logger.Debug("NO Data-Results!")
                    End If
                End If

                If oProfileMatch Then
                    oMatchedProfiles += 1
                End If
            Next

            Return New Tuple(Of Integer, Integer)(oCreatedTiles, oMatchedProfiles)
        Catch ex As Exception
            _Logger.Error(ex)
            MsgBox("Error while creating profile tiles!" & vbNewLine & ex.Message)
            Return New Tuple(Of Integer, Integer)(-1, -1)
        End Try
    End Function

    Private Function CreateTile(Profile As ProfileData, CountText As String) As TileItem
        Dim oItem As New TileItem() With {.Tag = Profile.Guid}
        oItem.AppearanceItem.Normal.BackColor = TileBackColor
        oItem.Elements.Clear()

        Dim oNameElement = New TileItemElement With {
            .Text = Profile.Name,
            .TextAlignment = TileItemContentAlignment.TopLeft
        }
        oNameElement.Appearance.Normal.Font = PrimaryFont
        oNameElement.Appearance.Normal.ForeColor = TileForeColor
        oItem.Elements.Add(oNameElement)

        Dim oCommentElement = New TileItemElement With {
            .Text = Profile.Comment,
            .TextAlignment = TileItemContentAlignment.MiddleLeft
        }
        oCommentElement.Appearance.Normal.Font = SecondaryFont
        oCommentElement.Appearance.Normal.ForeColor = TileForeColor
        oItem.Elements.Add(oCommentElement)

        Dim oCountElement = New TileItemElement With {
            .Text = GetCountText(Profile, CountText),
            .TextAlignment = TileItemContentAlignment.BottomRight
        }
        oCountElement.Appearance.Normal.Font = SecondaryFont
        oCountElement.Appearance.Normal.ForeColor = TileForeColor
        oItem.Elements.Add(oCountElement)

        Return oItem
    End Function

    Private Function GetCountText(Profile As ProfileData, CountText As String) As String
        Dim oText As String = "No implemented"

        If Profile.CountData = INVALID_COUNT_SQL Then
            oText = "Invalid SQL!"
        ElseIf Profile.CountData = NO_COUNT_SQL Then
            oText = "No SQL!"
        Else
            oText = CountText
        End If

        If Profile.CountDocs = INVALID_COUNT_SQL Then
            oText = "Invalid SQL!"
        ElseIf Profile.CountDocs = NO_COUNT_SQL Then
            oText = "No SQL!"
        Else
            oText = CountText
        End If

        Return oText
    End Function

    Private Sub Label2_Click(sender As Object, e As EventArgs) Handles Label2.Click
        Dim oForm As New frmTreeView(_Params.MatchTreeView.Nodes) With {
            .StartPosition = FormStartPosition.Manual,
            .Left = Left + Width,
            .Top = Top
        }
        oForm.ShowDialog()
    End Sub

    Private Async Sub TileControlMatch_ItemClick(sender As Object, e As TileItemEventArgs) Handles TileControlMatch.ItemClick
        Try
            SplashScreenManager1.ShowWaitForm()
            TileControlMatch.Enabled = False

            Dim oItem As TileItem = e.Item
            Dim oProfileId As Integer = oItem.Tag

            Dim oProfileSearch As New ProfileSearches(_LogConfig, _Environment, _Params)
            Dim oProfile As ProfileData = _Params.MatchingProfiles.
                Where(Function(p) p.Guid = oProfileId).
                ToList().
                First()

            Select Case oItem.Group.Name
                Case TileGroupData.Name
                    Dim oSearches = Await oProfileSearch.LoadDataSearchesAsync(oProfileId)
                    OpenDataResults(oProfile, oSearches)

                Case TileGroupDocuments.Name
                    Dim oSearches = Await oProfileSearch.LoadDocumentSearchesAsync(oProfileId)
                    OpenDocumentResults(oProfile, oSearches)

                Case Else
                    ' TODO: Load combined results
                    'OpenResultForms(oProfileId, ProfileType.ANY)
            End Select

            Hide()
        Catch ex As Exception
            MsgBox($"Error while loading Searches: " & vbNewLine & ex.Message, MsgBoxStyle.Critical, Text)
            _Logger.Error(ex)
        Finally
            SplashScreenManager1.CloseWaitForm()
            TileControlMatch.Enabled = True
        End Try
    End Sub

    Private Sub OpenDocumentResults(Profile As ProfileData, Searches As List(Of ProfileSearches.Search))
        Dim oNameSlug = Utils.ConvertTextToSlug(Profile.Name)
        Dim oSearchGuids = Searches.Select(Function(s) s.Guid).ToArray
        Dim oWindowGuid = $"{Profile.Guid}-{oNameSlug}-{String.Join("-", oSearchGuids)}"
        Dim oParams = New DocumentResultList.Params() With {
            .WindowGuid = oWindowGuid,
            .WindowTitle = GetResultWindowString(_Params.ClipboardContents),
            .OperationModeOverride = _Params.OperationModeOverride
        }

        For Each oSearch In Searches
            oParams.Results.Add(New DocumentResultList.DocumentResult() With {
                .Title = oSearch.TabCaption,
                .Datatable = oSearch.DataTable
            })
        Next

        Dim oForm As New frmDocumentResultList(_LogConfig, _Environment, oParams)
        AddHandler oForm.FormClosed, AddressOf ProfileResultForm_Closed
        OpenForms.Add(oForm)
        oForm.Show()
    End Sub

    Private Sub OpenDataResults(Profile As ProfileData, Searches As List(Of ProfileSearches.Search))
        Dim oNameSlug = Utils.ConvertTextToSlug(Profile.Name)
        Dim oSearchGuids = Searches.Select(Function(s) s.Guid).ToArray
        Dim oWindowGuid = $"{Profile.Guid}-{oNameSlug}-{String.Join("-", oSearchGuids)}"
        Dim oParams = New DataResultParams() With {
            .WindowGuid = oWindowGuid
        }

        For Each oSearch In Searches
            oParams.Results.Add(New DataResult() With {
                .Title = oSearch.TabCaption,
                .Datatable = oSearch.DataTable
            })
        Next

        Dim oForm As New frmDataResultList(_LogConfig, _Environment, oParams)
        AddHandler oForm.FormClosed, AddressOf ProfileResultForm_Closed
        OpenForms.Add(oForm)
        oForm.Show()
    End Sub

    Private Sub ProfileResultForm_Closed(sender As Object, e As FormClosedEventArgs)
        Dim oShouldOpenAgain As Boolean = False
        Dim oThisForm = New List(Of IResultForm) From {sender}

        If TypeOf sender Is frmDataResultList Or TypeOf sender Is frmDocumentResultList Then
            For Each oForm As IResultForm In OpenForms
                ' Determine if frmProfileMatch should be shown
                If oForm.ShouldReturnToPreviousForm Then
                    oShouldOpenAgain = True
                End If
            Next
        End If

        ' If frmProfileMatch should be shown, close all windows of this profile
        If oShouldOpenAgain Then
            For Each oForm As Form In OpenForms.Except(oThisForm)
                ' Remove the Handler to prevent a loop
                RemoveHandler oForm.FormClosed, AddressOf ProfileResultForm_Closed
                oForm.Close()
            Next

            Show()
        End If
    End Sub
End Class
