Imports DevExpress.Utils
Imports DevExpress.XtraEditors
Imports DevExpress.XtraEditors.Repository
Imports DevExpress.XtraGrid.Columns
Imports DevExpress.XtraGrid.Views.Grid
Imports DigitalData.Controls.LookupGrid
Imports DigitalData.Modules.EDMI.API.Constants
Imports DigitalData.Modules.EDMI.API.DatabaseWithFallback
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Base
Imports System.ComponentModel
Imports DevExpress.XtraEditors.Controls
Imports DevExpress.XtraGrid.Views.Base
Imports System.Text.RegularExpressions
Imports System.Globalization
Imports DevExpress.Xpo.Helpers.AssociatedCollectionCriteriaHelper
Imports DevExpress.XtraEditors.Mask

Namespace ControlCreator
    Public Class GridControl
        Private ReadOnly _LogConfig As LogConfig
        Private ReadOnly _Logger As Logger
        Private ReadOnly _GridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem))

        Private newRowModified As Boolean

        Public Sub New(pLogConfig As LogConfig, pGridTables As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem)))
            _LogConfig = pLogConfig
            _Logger = pLogConfig.GetLogger()
            _GridTables = pGridTables
        End Sub

        Public Function CreateGridColumns(pColumnTable As DataTable) As DataTable
            Dim oDataTable As New DataTable

            For Each oRow As DataRow In pColumnTable.Rows
                ' Create Columns in Datatable

                Dim oColumn = New DataColumn() With {
                    .ColumnName = oRow.Item("SPALTENNAME"),
                    .Caption = oRow.Item("SPALTEN_HEADER_LANG"),
                    .ReadOnly = False
                }
                Select Case oRow.Item("TYPE_COLUMN")
                    Case Constants.CONTROL_TYPE_TEXT
                        oColumn.DataType = GetType(String)
                    Case Constants.CONTROL_TYPE_INTEGER
                        oColumn.DataType = GetType(Integer)
                    Case Constants.CONTROL_TYPE_DOUBLE
                        oColumn.DataType = GetType(Double)
                    Case Constants.CONTROL_TYPE_CURRENCY
                        oColumn.DataType = GetType(Double)
                    Case Constants.CONTROL_TYPE_BOOLEAN
                        oColumn.DataType = GetType(Boolean)
                    Case Else
                        oColumn.DataType = GetType(String)
                End Select

                oDataTable.Columns.Add(oColumn)
            Next

            Return oDataTable
        End Function

        Public Function FillGridTables(pColumnTable As DataTable, pControlId As Integer, pControlName As String) As Dictionary(Of Integer, Dictionary(Of String, RepositoryItem))
            For Each oRow As DataRow In pColumnTable.Rows
                ' Fetch and cache Combobox results
                Dim oConnectionId As Integer = oRow.ItemEx("CONNECTION_ID", 0)
                Dim oSqlCommand As String = oRow.ItemEx("SQL_COMMAND", "")

                If oConnectionId > 0 And oSqlCommand <> "" Then
                    Try
                        Dim oComboboxDataTable As DataTable = Nothing
                        Dim oColumnName As String = oRow.Item("SPALTENNAME")
                        _Logger.Debug("Working on SQL for Column[{0}]...", oColumnName)
                        If Not clsPatterns.HasComplexPatterns(oSqlCommand) Then
                            _Logger.Debug("SQL has no complex patterns!")
                            'oComboboxDataTable = ClassDatabase.Return_Datatable_ConId(oSqlCommand, oConnectionId)
                            oComboboxDataTable = DatabaseFallback.GetDatatable(New GetDatatableOptions(oSqlCommand, DatabaseType.ECM) With {
                                .ConnectionId = oConnectionId
                            })
                        Else
                            _Logger.Debug("...has complex patterns!!")
                        End If

                        Dim oRepositoryItem = GridTables_GetRepositoryItemForColumn(oColumnName, oComboboxDataTable, oRow.Item("ADVANCED_LOOKUP"))

                        If _GridTables.Item(pControlId).ContainsKey(oColumnName) Then
                            _GridTables.Item(pControlId).Item(oColumnName) = oRepositoryItem
                        Else
                            _GridTables.Item(pControlId).Add(oColumnName, oRepositoryItem)
                        End If
                    Catch ex As Exception
                        _Logger.Warn("Could not load data for column {0} in control {1}", oRow.Item("SPALTENNAME"), pControlName)
                        _Logger.Error(ex)
                    End Try
                End If
            Next

            Return _GridTables
        End Function

        Private Function GridTables_GetRepositoryItemForColumn(pColumnName As String, pDataTable As DataTable, pIsAdvancedLookup As Boolean) As RepositoryItem
            If pIsAdvancedLookup Then

                Dim oEditor = New RepositoryItemLookupControl3

                If pDataTable IsNot Nothing Then
                    oEditor.DisplayMember = pDataTable.Columns.Item(0).ColumnName
                    oEditor.ValueMember = pDataTable.Columns.Item(0).ColumnName
                    oEditor.DataSource = pDataTable
                End If

                Return oEditor
            Else
                Dim oEditor = New RepositoryItemComboBox()
                Dim oItems As New List(Of String)

                AddHandler oEditor.Validating, Sub(_sender As ComboBoxEdit, _e As CancelEventArgs)
                                                   If oItems.Contains(_sender.EditValue) Then
                                                       _e.Cancel = False
                                                   Else
                                                       _e.Cancel = True
                                                   End If

                                               End Sub

                If pDataTable IsNot Nothing Then
                    For Each oRow2 As DataRow In pDataTable.Rows
                        Dim oValue = oRow2.Item(0)

                        Try
                            If oRow2.ItemArray.Length > 1 Then
                                oValue &= $" | {oRow2.Item(1)}"
                            End If
                        Catch ex As Exception
                        End Try

                        oEditor.Items.Add(oValue)
                        oItems.Add(oValue)
                    Next
                End If

                Return oEditor
            End If
        End Function

        Public Sub ConfigureViewColumns(pColumnTable As DataTable, pGridView As GridView, pGrid As DevExpress.XtraGrid.GridControl)
            Dim oShouldDisplayFooter As Boolean = False



            For Each oCol As GridColumn In pGridView.Columns
                Dim oColumnData As DataRow = pColumnTable.
                    Select($"SPALTENNAME = '{oCol.FieldName}'").
                    FirstOrDefault()

                If oColumnData Is Nothing Then
                    Continue For
                End If

                Dim oSequence As Integer = oColumnData.Item("SEQUENCE")
                oCol.VisibleIndex = oSequence

                Dim oColumnType As String = oColumnData.Item("TYPE_COLUMN")

                Select Case oColumnType
                    Case "INTEGER"
                        oCol.DisplayFormat.FormatType = FormatType.Custom
                        oCol.DisplayFormat.FormatString = "N0"

                    Case "DOUBLE"
                        oCol.DisplayFormat.FormatType = FormatType.Custom
                        oCol.DisplayFormat.FormatString = "N2"

                    Case "CURRENCY"
                        oCol.DisplayFormat.FormatType = FormatType.Custom
                        oCol.DisplayFormat.FormatString = "C2"
                End Select

                Dim oSummaryFunction As String = oColumnData.Item("SUMMARY_FUNCTION")

                Select Case oSummaryFunction
                    Case Constants.AGGREGATE_TOTAL_INTEGER
                        oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum
                        oCol.SummaryItem.DisplayFormat = "SUM: {0:N0}"
                        oShouldDisplayFooter = True

                    Case Constants.AGGREGATE_TOTAL_FLOAT
                        oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum
                        oCol.SummaryItem.DisplayFormat = "SUM: {0:N2}"
                        oShouldDisplayFooter = True

                    Case Constants.AGGREGATE_TOTAL_CURRENCY
                        oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Sum
                        oCol.SummaryItem.DisplayFormat = "SUM: {0:C2}"
                        oShouldDisplayFooter = True

                    Case Constants.AGGREGATE_TOTAL_AVG
                        oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Average
                        oCol.SummaryItem.DisplayFormat = "AVG: {0}"
                        oShouldDisplayFooter = True

                    Case Constants.AGGREGATE_TOTAL_MAX
                        oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Max
                        oCol.SummaryItem.DisplayFormat = "MAX: {0}"
                        oShouldDisplayFooter = True

                    Case Constants.AGGREGATE_TOTAL_MIN
                        oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Min
                        oCol.SummaryItem.DisplayFormat = "MIN: {0}"
                        oShouldDisplayFooter = True

                    Case Constants.AGGREGATE_TOTAL_COUNT
                        oCol.SummaryItem.SummaryType = DevExpress.Data.SummaryItemType.Count
                        oCol.SummaryItem.DisplayFormat = "NUM: {0}"
                        oShouldDisplayFooter = True

                End Select
            Next

            pGridView.OptionsView.ShowFooter = oShouldDisplayFooter
        End Sub
        Public Sub ConfigureViewColumnsCurrency(pColumnTable As DataTable, pGridView As GridView, pGrid As DevExpress.XtraGrid.GridControl, pCurrency As String)

            Dim oCultureInfo As CultureInfo = New CultureInfo("de-DE")
            oCultureInfo.NumberFormat.CurrencySymbol = pCurrency
            Dim riTextEdit As RepositoryItemTextEdit = New RepositoryItemTextEdit()
            riTextEdit.MaskSettings.Configure(Of MaskSettings.Numeric)(Sub(settings)
                                                                           settings.MaskExpression = "c"
                                                                           settings.Culture = oCultureInfo
                                                                       End Sub)
            riTextEdit.UseMaskAsDisplayFormat = True 'Optional
            pGrid.RepositoryItems.Add(riTextEdit)


            For Each oCol As GridColumn In pGridView.Columns
                Dim oColumnData As DataRow = pColumnTable.
                    Select($"SPALTENNAME = '{oCol.FieldName}'").
                    FirstOrDefault()

                If oColumnData Is Nothing Then
                    Continue For
                End If

                Dim oColumnType As String = oColumnData.Item("TYPE_COLUMN")

                Select Case oColumnType
                    Case "CURRENCY"
                        oCol.DisplayFormat.FormatType = FormatType.Custom
                        oCol.ColumnEdit = riTextEdit
                End Select

            Next
        End Sub
        Public Sub ConfigureViewEvents(pColumnTable As DataTable, pGridView As GridView, pControl As Windows.Forms.Control, pControlId As Integer)
            AddHandler pGridView.InitNewRow, Sub(sender As Object, e As InitNewRowEventArgs)
                                                 Try
                                                     _Logger.Debug("Initialzing new row")

                                                     For Each oColumnData As DataRow In pColumnTable.Rows
                                                         For Each oGridColumn As GridColumn In pGridView.Columns
                                                             If oGridColumn.FieldName <> oColumnData.Item("SPALTENNAME") Then
                                                                 Continue For
                                                             End If

                                                             Dim oDefaultValue = ObjectEx.NotNull(oColumnData.Item("DEFAULT_VALUE"), String.Empty)

                                                             If oDefaultValue <> String.Empty Then
                                                                 _Logger.Debug("Setting default value [{0}] for column [{1}]", oDefaultValue, oGridColumn.FieldName)
                                                                 pGridView.SetRowCellValue(e.RowHandle, oGridColumn.FieldName, oDefaultValue)
                                                             End If
                                                         Next
                                                     Next

                                                 Catch ex As Exception
                                                     _Logger.Error(ex)
                                                 Finally
                                                     newRowModified = False
                                                 End Try
                                             End Sub

            AddHandler pGridView.CustomRowCellEdit, Sub(sender As Object, e As CustomRowCellEditEventArgs)
                                                        Try
                                                            For Each oRow As DataRow In pColumnTable.Rows
                                                                Dim oColumnName = oRow.Item("SPALTENNAME")
                                                                Dim oEditorExists = GridTables_TestEditorExistsByControlAndColumn(pControlId, oColumnName)
                                                                If oColumnName <> e.Column.FieldName Then
                                                                    Continue For
                                                                End If

                                                                If oEditorExists Then
                                                                    Dim oEditor = _GridTables.Item(pControlId).Item(oColumnName)
                                                                    _Logger.Debug("Assigning Editor to Column [{0}]", oColumnName)
                                                                    e.RepositoryItem = oEditor
                                                                Else
                                                                    _Logger.Debug("Editor for Column [{0}] does not exist", oColumnName)
                                                                End If
                                                            Next
                                                        Catch ex As Exception
                                                            _Logger.Warn("Error in CustomRowCellEdit for [{0}]", e.CellValue)
                                                            _Logger.Error(ex)
                                                        End Try
                                                    End Sub

            AddHandler pGridView.ValidatingEditor, Sub(sender As Object, e As BaseContainerValidateEditorEventArgs)
                                                       Dim oRow As DataRowView = pGridView.GetRow(pGridView.FocusedRowHandle)
                                                       Dim oColumnName = pGridView.FocusedColumn.FieldName
                                                       _Logger.Debug("Validating Editor for Column [{0}]", oColumnName)
                                                       GridTables_ValidateColumn(pGridView, pColumnTable, oColumnName, e.Value, e.Valid, e.ErrorText)
                                                   End Sub

            AddHandler pGridView.PopupMenuShowing, AddressOf View_PopupMenuShowing

            AddHandler pGridView.InvalidRowException, AddressOf View_InvalidRowException
            AddHandler pGridView.ValidatingEditor, AddressOf View_ValidatingEditor
            '  AddHandler pGridView.CustomColumnDisplayText, AddressOf View_CustomColumnDisplayText

            ' These handlers are all used for the custom DefaultValue functionality, additionally some code in the 'InitNewRow' event.
            ' https://supportcenter.devexpress.com/ticket/details/t1035580/how-to-default-a-value-in-a-column-when-add-new-row-in-data-grid
            AddHandler pGridView.ShowingEditor, AddressOf View_ShowingEditor
            AddHandler pGridView.ShownEditor, AddressOf View_ShownEditor
            AddHandler pGridView.ValidateRow, AddressOf View_ValidateRow
            AddHandler pControl.LostFocus, AddressOf Control_LostFocus
        End Sub
        Private Sub View_CustomColumnDisplayText(ByVal eSender As Object, ByVal e As CustomColumnDisplayTextEventArgs)
            If IsNothing(e.Value) Then
                Exit Sub
            End If
            Dim view As GridView = eSender
            'Dim view As GridView = TryCast(GridView1, GridView)
            If e.Column.FieldName = "SpalteCurrency" Then
                ' e.DisplayText = e.Value.ToString().Replace("€", "CHF")
            End If
        End Sub
        Private Sub View_PopupMenuShowing(sender As Object, e As PopupMenuShowingEventArgs)
            Dim view As GridView = TryCast(sender, GridView)
            Dim oFocusedColumn As GridColumn = view.FocusedColumn
            Dim oColumnType As Type = oFocusedColumn.ColumnType
            Dim oColumnName As String = oFocusedColumn.FieldName

            If e.MenuType = GridMenuType.Column AndAlso oColumnType Is GetType(Boolean) Then
                e.Menu.Items.Add(New Menu.DXMenuItem(
                    "Alle Zeilen anhaken",
                    Sub(_sender As Object, _e As EventArgs)
                        SetCellValuesForBooleanColumn(view, oColumnName, True)
                    End Sub,
                    My.Resources.itemtypechecked,
                    Menu.DXMenuItemPriority.Normal))

                e.Menu.Items.Add(New Menu.DXMenuItem(
                    "Alle Zeilen abhaken",
                    Sub(_sender As Object, _e As EventArgs)
                        SetCellValuesForBooleanColumn(view, oColumnName, False)
                    End Sub,
                    My.Resources.itemtypechecked,
                    Menu.DXMenuItemPriority.Normal))
            End If
        End Sub

        Private Sub SetCellValuesForBooleanColumn(pView As GridView, pColumnName As String, pValue As Boolean)
            Dim oRowHandle = 0
            While (pView.IsValidRowHandle(oRowHandle))
                Dim oRow = pView.GetDataRow(oRowHandle)
                Dim oValue = oRow.ItemEx(pColumnName, False)
                oRow.Item(pColumnName) = pValue

                oRowHandle += 1
            End While
        End Sub


        Private Sub Control_LostFocus(sender As DevExpress.XtraGrid.GridControl, e As EventArgs)
            Dim oView2 As GridView = sender.FocusedView

            ' 19.08.2022: 
            ' Calling UpdateCurrentRow when newRowModified Is true
            ' leads to some weird jumping of focus in the current cell.
            ' This seems to fix it.
            If newRowModified = False Then
                oView2.UpdateCurrentRow()
            End If
        End Sub

        Private Sub View_ShowingEditor(sender As Object, e As CancelEventArgs)
            Dim view As GridView = TryCast(sender, GridView)
            _Logger.Debug("Showing editor.")
            If view.IsNewItemRow(view.FocusedRowHandle) AndAlso Not newRowModified Then
                _Logger.Debug("Adding new row.")
                view.AddNewRow()
            End If
        End Sub

        Private Sub View_ShownEditor(sender As Object, e As EventArgs)
            Dim view As GridView = TryCast(sender, GridView)
            If view.IsNewItemRow(view.FocusedRowHandle) Then
                _Logger.Debug("Attaching Modified Handler.")
                AddHandler view.ActiveEditor.Modified, Sub()
                                                           _Logger.Debug("Row was modified.")
                                                           newRowModified = True
                                                       End Sub
            End If
        End Sub

        Private Sub View_ValidateRow(sender As Object, e As ValidateRowEventArgs)
            Dim view As GridView = TryCast(sender, GridView)
            If view.IsNewItemRow(e.RowHandle) AndAlso Not newRowModified Then
                _Logger.Debug("Deleting unused row")
                view.DeleteRow(e.RowHandle)
            End If

            _Logger.Debug("Validating row. Resetting Modified.")
            newRowModified = False
        End Sub

        Private Sub View_ValidatingEditor(sender As Object, e As BaseContainerValidateEditorEventArgs)
            Dim oValue As String = ObjectEx.NotNull(e.Value, "")

            If oValue.Contains(" | ") Then
                oValue = oValue.Split(" | ").ToList().First()
                e.Value = oValue
            End If
        End Sub

        Private Sub View_InvalidRowException(sender As Object, e As InvalidRowExceptionEventArgs)
            e.ExceptionMode = ExceptionMode.NoAction
        End Sub

        Private Function GridTables_TestEditorExistsByControlAndColumn(oControlId As Integer, pColumn As String) As Boolean
            If _GridTables.ContainsKey(oControlId) Then
                Dim oContainsKey = _GridTables.Item(oControlId).ContainsKey(pColumn)
                If oContainsKey AndAlso _GridTables.Item(oControlId).Item(pColumn) IsNot Nothing Then
                    Return True
                Else
                    Return False
                End If
            Else
                Return False
            End If
        End Function

        Private Function GridTables_ValidateColumn(pView As GridView, pColumnDefinition As DataTable, ColumnName As String, pValue As Object, ByRef pIsValid As Boolean, ByRef pErrorText As String) As Boolean
            Dim oColumn As DataRow = (From r As DataRow In pColumnDefinition.Rows
                                      Where r.Item("SPALTENNAME") = ColumnName
                                      Select r).FirstOrDefault()

            Dim oGridColumn As GridColumn = (From c As GridColumn In pView.Columns
                                             Where c.FieldName = ColumnName
                                             Select c).FirstOrDefault()

            Dim oIsRequired = oColumn.Item("VALIDATION")

            Try
                Dim oRegex = ObjectEx.NotNull(oColumn.Item("REGEX_MATCH"), String.Empty)
                Dim oRegexMessage = ObjectEx.NotNull(oColumn.Item("REGEX_MESSAGE_DE"), String.Empty)
                If oRegex <> String.Empty Then
                    Dim oMatch = New Regex(oRegex).IsMatch(pValue.ToString)
                    Dim oDefaultMessage = "Wert entspricht nicht dem gefordertem Format!"
                    Dim oMessage = IIf(oRegexMessage <> String.Empty, oRegexMessage, oDefaultMessage)

                    If oMatch = False Then
                        pErrorText = oMessage
                        pIsValid = False
                        Return False
                    End If
                End If
            Catch ex As Exception
                _Logger.Error(ex)
            End Try

            If oIsRequired And (pValue IsNot Nothing AndAlso pValue.ToString = "") Then
                pErrorText = "Spalte muss ausgefüllt werden!"
                pIsValid = False
                Return False

            End If

            Return True
        End Function
    End Class
End Namespace
