Imports DigitalData.GUIs.Common
Imports DigitalData.Modules.Base
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.ZooFlow
Imports System.Threading.Tasks

Namespace Search
    Public Class SearchRunner
        Inherits BaseClass

        Private Const SEARCH_FACT_DATE_DEFAULT As String = "ADDED_WHEN"

        Public Const CREATED_TOMORROW As String = "TOMORROW"
        Public Const CREATED_TODAY As String = "ECM_CREATED_TODAY"
        Public Const CREATED_YESTERDAY As String = "ECM_CREATED_YESTERDAY"
        Public Const CREATED_LAST_7_DAYS As String = "ECM_CREATED_LAST7DAYS"
        Public Const CREATED_YEAR_CURRENT As String = "ECM_CREATED_YEAR_CURRENT"
        Public Const CREATED_YEAR_LAST As String = "ECM_CREATED_YEAR_LAST"
        Public Const CREATED_MONTH_CURR As String = "ECM_CREATED_MONTH_CURRENT"
        Public Const CREATED_MONTH_LAST As String = "ECM_CREATED_MONTH_LAST"

        Public Enum DateConstraint
            Today
            Tomorrow
            Yesterday
            Last7Days
            CurrentMonth
            LastMonth
            CurrentYear
            LastYear
            Undefined
        End Enum

        Public Enum TokenOperator
            [And]
            [Or]
        End Enum

        Public Event NeedsRefresh As EventHandler(Of Integer)
        Public Event NeedsNewSavedSearch As EventHandler
        Public Event Closed As EventHandler(Of Integer)

        Private Property _ActiveDateConstraint As String = String.Empty
        Public ReadOnly Property ActiveDateConstraint As String
            Get
                Return _ActiveDateConstraint
            End Get
        End Property

        Private _ActiveSearchTokens As New List(Of SearchToken.Token)
        Private _ActiveDateAttribute As String = SEARCH_FACT_DATE_DEFAULT
        Private _ActiveTokenOperator As TokenOperator = TokenOperator.And

        Public ReadOnly Property ActiveDateAttribute As String
            Get
                Return _ActiveDateAttribute
            End Get
        End Property

        Public ReadOnly Property ActiveTokens As List(Of SearchToken.Token)
            Get
                Return _ActiveSearchTokens
            End Get
        End Property

        Public Property BaseSearchSQL As String
        Public Property ExplicitDate As Boolean = False

        Private ReadOnly Environment As Environment
        Private ReadOnly SearchId As String
        Private ReadOnly UserId As Integer
        Private ReadOnly UserLanguage As String

        Public Sub New(pLogConfig As LogConfig, pEnvironment As Environment, pSearchId As String)
            MyBase.New(pLogConfig)
            Environment = pEnvironment
            SearchId = pSearchId
            UserId = My.Application.User.UserId
            UserLanguage = My.Application.User.Language
        End Sub

        Public Function RunWithDataTable(pDatatable As DataTable) As SearchResult
            Return RunWithDataTable(pDatatable, "Suche")
        End Function

        Public Function RunWithDataTable(pDatatable As DataTable, pTitle As String) As SearchResult
            Dim oParams = GetParams(pTitle)
            oParams.Results.Add(New DocumentResultList.DocumentResult() With {
                .Title = pTitle,
                .Datatable = pDatatable
            })

            If pDatatable.Rows.Count = 1 Then
                oParams.ShowFileList = False
            End If

            Dim oForm As New frmDocumentResultList(My.LogConfig, Environment, oParams)
            AddEventHandlers(oForm)
            oForm.Show()

            Return New SearchResult(pDatatable.Rows.Count)
        End Function

        ''' <summary>
        ''' Only search for Term. Used for sidebar quicksearch.
        ''' </summary>
        Public Async Function RunWithSearchTerm(pSearchTerm As String) As Task(Of SearchResult)
            Return Await RunWithSearchOptions(New SearchOptions With {.SearchString = pSearchTerm})
        End Function

        Public Async Function RunWithSearchTerm(pSearchTerm As String, pDateFrom As Date, pDateTo As Date, pSearchTitle As String) As Task(Of SearchResult)
            Return Await RunWithSearchOptions(New SearchOptions With {
                .SearchString = pSearchTerm,
                .SearchTitle = pSearchTitle,
                .DateFrom = pDateFrom,
                .DateTo = pDateTo
            })
        End Function

        Public Async Function RunWithTokens(pTokens As IEnumerable(Of Search.SearchToken.Token)) As Task(Of SearchResult)
            Return Await RunWithSearchOptions(New SearchOptions With {.SearchTokens = pTokens})
        End Function

        Public Async Function RunWithTokens(pTokens As IEnumerable(Of Search.SearchToken.Token), pDateFrom As Date, pDateTo As Date, pSearchTitle As String) As Task(Of SearchResult)
            If pTokens.Count = 0 And
            pDateFrom = Date.MinValue And
            pDateTo = Date.MinValue And
            _ActiveDateConstraint = String.Empty Then
                Return New SearchResult(0)
            End If

            Return Await RunWithSearchOptions(New SearchOptions With {
                .SearchTokens = pTokens,
                .DateFrom = pDateFrom,
                .DateTo = pDateTo
            })
        End Function


        'Public Async Function RunWithSearchTerm(pSearchTerm As String, pDateFrom As Date, pDateTo As Date) As Task(Of SearchResult)
        '    Return Await RunWithSearchTerm(pSearchTerm, pDateFrom, pDateTo, Nothing, Nothing)
        'End Function

        Private Async Function RunWithSearchOptions(pOptions As SearchOptions) As Task(Of SearchResult)
            Dim oDateConstraint = Await GetDateConstraint(pOptions.DateFrom, pOptions.DateTo)
            Dim oWindowTitle = GetResultWindowString(pOptions.SearchString, pOptions.SearchTitle)
            Dim oParams = GetParams(oWindowTitle)
            Dim oTokens = pOptions.SearchTokens
            Dim oSearchTerm = pOptions.SearchString

            Dim oSQL = String.Empty

            If oTokens IsNot Nothing AndAlso oTokens.Count > 0 Then
                _ActiveSearchTokens = oTokens
                Await InsertSearchTokens(oTokens)
                oSQL = $"EXEC PRIDB_SEARCH_GET_TOKEN_RESULT_DOCS {UserId}, '{oDateConstraint}', '{UserLanguage}'"
            ElseIf oSearchTerm IsNot Nothing Then
                _ActiveSearchTokens.Clear()
                oSQL = $"EXEC PRIDB_SEARCH_GET_TEXT_RESULTS {UserId},'{pOptions.SearchString}','{oDateConstraint}', '{UserLanguage}'"
            Else
                _ActiveSearchTokens.Clear()
                oSQL = $"EXEC PRIDB_SEARCH_GET_TEXT_RESULTS {UserId},'{String.Empty}','{oDateConstraint}', '{UserLanguage}'"
            End If

            If Await My.Database.ExecuteNonQueryIDBAsync(oSQL) = True Then
                Dim oDTDocResult = Await My.Database.GetDatatableIDBAsync(BaseSearchSQL)

                If oDTDocResult Is Nothing Then
                    Return New SearchResult("Error in Search Query")
                End If

                Dim oRowCount = oDTDocResult.Rows.Count
                If oRowCount > 0 Then
                    oParams.Results.Add(New DocumentResultList.DocumentResult() With {
                        .Title = SearchId,
                        .Datatable = oDTDocResult
                    })

                    If oDTDocResult.Rows.Count = 1 Then
                        oParams.ShowFileList = False
                    End If

                    Dim oForm As New frmDocumentResultList(My.LogConfig, Environment, oParams)
                    AddEventHandlers(oForm)
                    oForm.Show()

                    Return New SearchResult(oRowCount)
                Else
                    Return New SearchResult(oRowCount)

                End If
            Else
                Return New SearchResult("Error in Search Function")

            End If
        End Function

        Private Sub AddEventHandlers(pForm As frmDocumentResultList)
            ' TODO: Implement   
            'AddHandler pForm.NeedsRefresh, AddressOf Form_NeedsRefresh
            AddHandler pForm.FormClosed, AddressOf Form_Closed

            ' Only setup event if it is actually used
            If NeedsNewSavedSearchEvent IsNot Nothing Then
                AddHandler pForm.NeedsNewSavedSearch, AddressOf Form_NeedsNewSavedSearch
            End If

            ' Only setup event if it is actually used
            If NeedsRefreshEvent IsNot Nothing Then
                AddHandler pForm.NeedsRefresh, AddressOf Form_NeedsRefresh
            End If
        End Sub

        Private Sub Form_NeedsNewSavedSearch(sender As Object, e As EventArgs)
            RaiseEvent NeedsNewSavedSearch(sender, e)
        End Sub

        Private Sub Form_NeedsRefresh(sender As Object, e As Integer)
            RaiseEvent NeedsRefresh(sender, e)
        End Sub

        Private Async Function GetDateConstraint(pDateFrom As Date, pDateTo As Date) As Task(Of String)
            Dim oSimpleDateConstraint = $"{_ActiveDateAttribute}~{_ActiveDateConstraint}"
            Dim oExplicitConstraint = Await MaybeSetExplicitDateConstraint(pDateFrom, pDateTo)

            If IsNothing(oExplicitConstraint) Then
                Return oSimpleDateConstraint
            Else
                Return oExplicitConstraint
            End If
        End Function

        Private Async Function InsertSearchTokens(pTokens As IEnumerable(Of Search.SearchToken.Token)) As Task
            Logger.Debug("Deleting previous user tokens..")
            Await My.Database.ExecuteNonQueryIDBAsync($"DELETE FROM TBIDB_SEARCH_INPUT_USER WHERE USR_ID = {UserId}")

            If pTokens Is Nothing Then
                Logger.Warn("Token Object was nothing!")
                Exit Function
            End If

            If pTokens.Count = 0 Then
                Logger.Warn("Token Object was empty!")
                Exit Function
            End If

            Dim oOperatorString

            Select Case _ActiveTokenOperator
                Case TokenOperator.Or
                    oOperatorString = "OR"
                Case Else
                    oOperatorString = "AND"
            End Select


            For Each oToken In pTokens
                Dim oSQLInsert As String = $"
                    INSERT INTO [dbo].[TBIDB_SEARCH_INPUT_USER] ([USR_ID], [ATTR_ID], [ATTR_TITLE], [TERM_ID], [OPERATOR])
                    VALUES ({UserId}, {oToken.AttributeId}, '{oToken.AttributeTitle}', {oToken.TermId}, '{oOperatorString}')"

                Dim oResult = Await My.Database.ExecuteNonQueryIDBAsync(oSQLInsert)
                Logger.Warn("Inserting Tokens failed!")
            Next
        End Function


        Private Async Function MaybeSetExplicitDateConstraint(pDateFrom As Date, pDateTo As Date) As Task(Of String)
            If pDateFrom.Equals(Date.MinValue) = False Then
                ExplicitDate = True

                Dim oDateTo As Date
                If pDateTo.Equals(Date.MinValue) Then
                    oDateTo = pDateFrom
                Else
                    oDateTo = pDateTo
                End If
                Dim oProc = $"EXEC PRIDB_SEARCH_ADD_USR_DATE {UserId},'{pDateFrom}','{oDateTo}'"
                If Await My.Database.ExecuteNonQueryIDBAsync(oProc) = True Then
                    Return $"{_ActiveDateAttribute}~DATEPART"
                Else
                    Return Nothing
                End If
            End If

            Return Nothing
        End Function

        Private Sub Form_Closed(sender As Object, e As EventArgs)
            RaiseEvent Closed(sender, 0)
        End Sub

        Private Function GetParams(pWindowTitle As String) As DocumentResultList.Params
            Dim oParams = New DocumentResultList.Params() With {
            .WindowGuid = SearchId,
            .WindowTitle = pWindowTitle,
            .OperationModeOverride = Modules.ZooFlow.Constants.OperationMode.ZooFlow,
            .ProfileGuid = 35452,
            .ColumnNames = New DocumentResultList.ColumnNames With {
                .ObjectIdColumn = "DocID"
            },
            .ShowBackNavigation = False
        }
            Return oParams
        End Function

        Private Function GetResultWindowString(pSearchContent As String, pWindowTitle As String) As String
            Dim oWindowString

            ' This is the default title
            If UserLanguage = DigitalData.Modules.ZooFlow.State.UserState.LANG_DE_DE Then
                oWindowString = $"Suche"
            Else
                oWindowString = $"Search"
            End If

            ' If a Search String is given, we might take this
            If pSearchContent <> String.Empty Then
                If UserLanguage = DigitalData.Modules.ZooFlow.State.UserState.LANG_DE_DE Then
                    Return $"Suche Nach '{pSearchContent}'"
                Else
                    Return $"Search For '{pSearchContent}'"
                End If
            End If

            ' Window Title overrides everything
            If pWindowTitle IsNot Nothing Then
                oWindowString = pWindowTitle
            End If

            Return oWindowString
        End Function

        Public Sub SetDateConstraint()
            _ActiveDateConstraint = String.Empty
        End Sub

        Public Sub SetDateConstraint(pConstraintName As String)
            _ActiveDateConstraint = pConstraintName
        End Sub

        Public Sub SetDateConstraint(pConstraint As DateConstraint)
            _ActiveDateConstraint = DateConstraintToConstant(pConstraint)
        End Sub

        Public Sub SetTokenOperator(pOperator As TokenOperator)
            _ActiveTokenOperator = pOperator
        End Sub

        Public Function DateConstraintToConstant(pConstraint As DateConstraint) As String
            Select Case pConstraint
                Case DateConstraint.Today
                    Return CREATED_TODAY

                Case DateConstraint.Yesterday
                    Return CREATED_YESTERDAY

                Case DateConstraint.Tomorrow
                    Return CREATED_TOMORROW

                Case DateConstraint.Last7Days
                    Return CREATED_LAST_7_DAYS

                Case DateConstraint.CurrentMonth
                    Return CREATED_MONTH_CURR

                Case DateConstraint.LastMonth
                    Return CREATED_MONTH_LAST

                Case DateConstraint.CurrentYear
                    Return CREATED_YEAR_CURRENT

                Case DateConstraint.LastYear
                    Return CREATED_YEAR_LAST

                Case Else
                    Return String.Empty

            End Select
        End Function
        Public Function ConstantToDateConstraint(pConstant As String) As DateConstraint
            Select Case pConstant
                Case CREATED_TODAY
                    Return DateConstraint.Today
                Case CREATED_YESTERDAY
                    Return DateConstraint.Yesterday
                Case CREATED_TOMORROW
                    Return DateConstraint.Tomorrow
                Case CREATED_LAST_7_DAYS
                    Return DateConstraint.Last7Days
                Case CREATED_MONTH_CURR
                    Return DateConstraint.CurrentMonth
                Case CREATED_MONTH_LAST
                    Return DateConstraint.LastMonth
                Case CREATED_YEAR_CURRENT
                    Return DateConstraint.CurrentYear
                Case CREATED_YEAR_LAST
                    Return DateConstraint.LastYear
                Case Else
                    Return DateConstraint.Undefined
            End Select
        End Function

        Public Sub SetDateAttribute(pAttributeName As String)
            _ActiveDateAttribute = pAttributeName
        End Sub

        Public Sub SetDateAttribute()
            _ActiveDateAttribute = SEARCH_FACT_DATE_DEFAULT
        End Sub

        Public Class SearchOptions
            Public SearchTitle As String

            Public SearchString As String
            Public SearchTokens As IEnumerable(Of Search.SearchToken.Token)

            Public DateFrom As Date
            Public DateTo As Date
        End Class

        Public Class SearchResult
            Public OK As Boolean = True
            Public Count As Integer = 0
            Public ErrorMessage As String = String.Empty

            ''' <summary>
            ''' Returns a positive Search Result
            ''' </summary>
            Public Sub New(pCountResults As Integer)
                Count = pCountResults
            End Sub

            ''' <summary>
            ''' Returns a failed search result with an error message
            ''' </summary>
            ''' <param name="pMessage"></param>
            Public Sub New(pMessage As String)
                OK = False
                ErrorMessage = pMessage
            End Sub
        End Class
    End Class
End Namespace

