Imports System.Text.RegularExpressions
Imports System.Windows.Forms
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Language.Utils
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Windows
Imports DigitalData.Modules.ZooFlow.Params
Imports DigitalData.Modules.ZooFlow.State
Imports DigitalData.Modules.Patterns

Public Class ProfileFilter
    Private _ProfileMatch As ProfileMatch
    Private _ProfileTable As DataTable
    Private _ProcessTable As DataTable
    Private _WindowTable As DataTable
    Private _ControlTable As DataTable
    Private _Profiles As List(Of ProfileData)
    Private _TreeView As TreeView
    Private _LogConfig As LogConfig
    Private _Logger As Logger

    Public Enum ImageIndex
        Root
        Profile
        Clipboard
        Process
        Window
        Control
    End Enum

    Public Const ERROR_LOADING_COUNT_SQL_FOR_DATA_SEARCH = "ERROR_LOADING_COUNT_SQL_FOR_DATA_SEARCH"
    Public Const ERROR_LOADING_COUNT_SQL_FOR_DOC_SEARCH = "ERROR_LOADING_COUNT_SQL_FOR_DOC_SEARCH"
    Public Const ERROR_EXECUTING_COUNT_SQL_FOR_DATA_SEARCH = "ERROR_EXECUTING_COUNT_SQL_FOR_DATA_SEARCH"
    Public Const ERROR_EXECUTING_COUNT_SQL_FOR_DOC_SEARCH = "ERROR_EXECUTING_COUNT_SQL_FOR_DOC_SEARCH"

    Public Const NODE_ROOT = "ROOT"

    Public Const PROCESS_NAME_CATCHALL = "_CATCHALL_"

    Public ReadOnly Property Profiles As List(Of ProfileData)
        Get
            Return _Profiles
        End Get
    End Property

    Public Sub New(LogConfig As LogConfig, ProfileDatatable As DataTable, ProcessTable As DataTable, WindowDatatable As DataTable, ControlDatatable As DataTable, TreeView As TreeView)
        Try
            _LogConfig = LogConfig
            _Logger = LogConfig.GetLogger()

            _Logger.Debug("Initializing Profile Filter")
            _Logger.Debug("Initializing Profile Data")
            _ProfileTable = ProfileDatatable
            _ProcessTable = ProcessTable
            _WindowTable = WindowDatatable
            _ControlTable = ControlDatatable

            _Logger.Debug("Initializing Profile Debugging")
            _TreeView = TreeView
            _ProfileMatch = New ProfileMatch(LogConfig)

            _Logger.Debug("Transforming Profiles")
            _Profiles = TransformProfiles()
        Catch ex As Exception
            _Logger.Error(ex)
            Throw ex
        End Try
    End Sub

    Public Function ToList() As List(Of ProfileData)
        Return _Profiles
    End Function

    Public Function LogRemainingProfiles(Profiles As List(Of ProfileData), StepDescription As String) As List(Of ProfileData)
        _Logger.Debug("Profiles remaining after Step {0}: {1}", StepDescription, Profiles.Count)
        Return Profiles
    End Function

    Public Function FilterProfilesByClipboardRegex(Profiles As List(Of ProfileData), ClipboardContents As String) As List(Of ProfileData)
        Dim oFilteredProfiles As New List(Of ProfileData)

        Dim oRootNode As TreeNode = New TreeNode() With {
            .Text = $"Suche nach '{ClipboardContents}'",
            .Tag = NODE_ROOT,
            .ImageIndex = ImageIndex.Root
        }
        _TreeView.Nodes.Clear()
        _TreeView.Nodes.Add(oRootNode)

        For Each oProfile In Profiles
            _Logger.Debug("Current Profile: {0}", oProfile.Name)

            Dim oNode = _ProfileMatch.NewProfileNode(oProfile)
            oRootNode.Nodes.Add(oNode)

            Try
                Dim oRegex As New Regex(oProfile.Regex)
                Dim oMatch = oRegex.Match(ClipboardContents)
                If oMatch.Success Then
                    _Logger.Debug("FilterProfilesByClipboardRegex: Clipboard Regex Matched: {0}", ClipboardContents)
                    _Logger.Info("FilterProfilesByClipboardRegex: Profile {0} matched!", oProfile.Name)
                    oFilteredProfiles.Add(oProfile)
                    oProfile.IsMatched = True

                    Dim oSubnode = _ProfileMatch.NewClipboardRegexNode(oProfile, oMatch.Success)
                    oNode.Nodes.Add(oSubnode)
                End If
            Catch ex As Exception
                _Logger.Warn("Regex '{0}' could not be processed for input '{1}'", oProfile.Regex, ClipboardContents)
                _Logger.Error(ex)
            End Try
        Next

        Return oFilteredProfiles
    End Function
    Public Function FilterProfilesByProcess(Profiles As List(Of ProfileData), ProcessName As String) As List(Of ProfileData)
        Dim oFilteredProfiles As New List(Of ProfileData)
        Try
            For Each oProfile As ProfileData In Profiles
                Dim oGuid = oProfile.Guid

                Dim oProcesses As New List(Of ProcessData)
                For Each oProcess As ProcessData In oProfile.Processes
                    If oProcess.PROFILE_ID <> oGuid Then
                        Continue For
                    End If

                    Dim oIsMatch = oProcess.ProcessName.ToLower = ProcessName.ToLower
                    Dim oIsCatchAll As Boolean = False

                    ' Catch-all processname
                    If oProcess.ProcessName.ToLower = PROCESS_NAME_CATCHALL.ToLower Then
                        oIsMatch = True
                        oIsCatchAll = True
                    End If

                    Dim oParent = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oProfile.Name & "-REGEX")
                    If oParent IsNot Nothing Then
                        Dim oNode = _ProfileMatch.NewProcessNode(oProfile, oProcess, oIsMatch, oIsCatchAll)
                        oParent.Nodes.Add(oNode)
                    End If

                    _Logger.Debug($"FilterProfilesByProcess: Checking Profile: {oProfile.Name}")
                    If oIsMatch Then
                        _Logger.Debug($"Processname Matched: {oProcess.ProcessName}")

                        oFilteredProfiles.Add(oProfile)
                        _Logger.Info("FilterProfilesByProcess: Profile {0} matched!", oProfile.Name)

                        ' Set Process matched
                        oProcess.IsMatched = True
                        oProcesses.Add(oProcess)

                        ' Set Profile matched
                        oProfile.IsMatched = True
                        oProfile.MatchedProcessID = oProcess.Guid

                    End If

                    If oIsCatchAll Then
                        oProfile.IsCatchAll = True
                    End If
                Next
                If oFilteredProfiles.Count > 0 Then
                    oProfile.Processes = oProcesses
                End If
            Next

            Return oFilteredProfiles
        Catch ex As Exception
            _Logger.Warn("Unexpected error in FilterProfilesByProcess...")
            _Logger.Error(ex)
            Return Profiles
        End Try
    End Function
    Public Function FilterWindowsByWindowTitleRegex(Profiles As List(Of ProfileData), WindowTitle As String) As List(Of ProfileData)
        Dim oProfiles As New List(Of ProfileData)

        For Each oProfile As ProfileData In Profiles
            _Logger.Debug("Checking WindowDefinition for profile: {0}...", oProfile.Name)

            Dim oFilteredWindows As New List(Of WindowData)

            ' Create list of all controls that match the current process and matched window
            For Each oWindow As WindowData In oProfile.Windows
                If oWindow.WindowProcessID = oProfile.MatchedProcessID Then
                    oFilteredWindows.Add(oWindow)
                End If
            Next

            ' If Profile has no windows at all, it automatically matches
            If oFilteredWindows.Count = 0 Then
                _Logger.Debug("Profile has no Windows assigned, automatic MATCH")

                oProfile.IsMatched = True
                oProfiles.Add(oProfile)

                Continue For
            End If

            Dim oWindows As New List(Of WindowData)

            For Each oWindowDef As WindowData In oFilteredWindows
                If oWindowDef.WindowProcessID <> oProfile.MatchedProcessID Then Continue For
                Try
                    If oWindowDef.Regex = String.Empty Then
                        oProfile.MatchedWindowID = oWindowDef.Guid
                        oWindowDef.IsMatched = True
                        oWindows.Add(oWindowDef)
                        Exit For
                    End If

                    Dim oRegex As New Regex(oWindowDef.Regex)
                    Dim oMatch = oRegex.Match(WindowTitle)

                    If oMatch.Success Then
                        _Logger.Debug("MATCH on WindowTitle: {0}", WindowTitle)
                        oProfile.MatchedWindowID = oWindowDef.Guid
                        oWindowDef.IsMatched = True
                        oWindows.Add(oWindowDef)
                    Else
                        _Logger.Debug("NO MATCH on WindowTitle: {0} - REGEX: {2}", WindowTitle, oWindowDef.Regex)
                    End If

                    Dim oParent = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oWindowDef.WindowProcessID & "-PROCESS")
                    If oParent IsNot Nothing Then
                        Dim oNode = _ProfileMatch.NewWindowNode(oProfile, oWindowDef, oMatch.Success)
                        oParent.Nodes.Add(oNode)
                    End If

                Catch ex As Exception
                    _Logger.Warn("Regex '{0}' could not be processed for input '{1}'", oWindowDef.Regex, WindowTitle)
                    _Logger.Error(ex)
                End Try
            Next

            ' If Profile has windows that matched, the profile matches
            If oWindows.Count > 0 Then
                oProfile.Windows = oWindows
                oProfile.IsMatched = True
                oProfiles.Add(oProfile)

                _Logger.Info("FilterWindowsByWindowTitleRegex: Profile {0} matched!", oProfile.Name)
            End If

            If oProfile.IsCatchAll Then
                oProfile.IsMatched = True
                oProfiles.Add(oProfile)

                _Logger.Info("FilterWindowsByWindowTitleRegex: Profile {0} is marked catchall!", oProfile.Name)
            End If
        Next

        ' Add catch-all profiles to the list
        oProfiles = AddCatchAllProfiles(Profiles, oProfiles)

        Return oProfiles
    End Function

    Public Function FilterProfilesByFocusedControl(Profiles As List(Of ProfileData), ClipboardContents As String, WindowHandle As IntPtr) As List(Of ProfileData)
        Dim oProfiles As New List(Of ProfileData)
        Dim oWindow As New Window(_LogConfig)
        Dim oFocusedWindow As Window.WindowInfo

        Try
            oFocusedWindow = oWindow.GetWindowInfo()
            If oFocusedWindow Is Nothing Then
                Throw New ApplicationException("Window Information is Empty")
            End If
        Catch ex As Exception
            _Logger.Error(ex)
            Return Profiles
        End Try

        For Each oProfile As ProfileData In Profiles
            Dim oFilteredControls As New List(Of ControlData)

            ' Create list of all controls that match the current process and matched window
            For Each oControl As ControlData In oProfile.Controls
                If oProfile.MatchedWindowID = oControl.WindowId And oFocusedWindow.ProcessName = oControl.ProcessName Then
                    oFilteredControls.Add(oControl)
                End If
            Next

            ' If Profile has no controls at all, it automatically matches
            If oFilteredControls.Count = 0 Then
                _Logger.Debug("Profile has no Controls assigned, automatic MATCH")
                oProfiles.Add(oProfile)
                Continue For
            End If

            Dim oMatchingControls As New List(Of ControlData)

            For Each oControl In oFilteredControls
                Dim oFound As Boolean = False

                ' If control name is empty, use coordinates
                If oControl.ControlName Is Nothing OrElse oControl.ControlName = String.Empty Then
                    Dim oControlBounds As Dictionary(Of String, Window.RectangleInfo)

                    Try
                        oControlBounds = oWindow.GetFocusedControlLocation(WindowHandle)

                        For Each oItem As KeyValuePair(Of String, Window.RectangleInfo) In oControlBounds
                            Select Case oItem.Key
                                Case "TopLeft"
                                    If oControl.TopLeft.Top = oItem.Value.Top And oControl.TopLeft.Left = oItem.Value.Left Then
                                        oFound = True
                                    End If
                                Case "TopRight"
                                    If oControl.TopRight.Top = oItem.Value.Top And oControl.TopLeft.Right = oItem.Value.Right Then
                                        oFound = True
                                    End If
                                Case "BottomLeft"
                                    If oControl.BottomLeft.Bottom = oItem.Value.Bottom And oControl.TopLeft.Left = oItem.Value.Left Then
                                        oFound = True
                                    End If
                                Case "BottomRight"
                                    If oControl.BottomRight.Bottom = oItem.Value.Bottom And oControl.TopLeft.Right = oItem.Value.Right Then
                                        oFound = True
                                    End If
                            End Select
                        Next
                    Catch ex As Exception
                        _Logger.Warn("Error while getting focused control location")
                        _Logger.Error(ex)
                    End Try
                Else
                    Dim oControlName As String = oControl.ControlName

                    Try
                        Dim oFocusedControl = oWindow.GetFocusedControl(WindowHandle)

                        If oFocusedControl.ControlName = oControlName Then
                            oFound = True
                        End If
                    Catch ex As Exception
                        _Logger.Warn("Error while getting focused control name")
                        _Logger.Error(ex)
                    End Try
                End If

                If oFound Then
                    _Logger.Debug("Control {0} has MATCH", oControl.ControlName)
                    oMatchingControls.Add(oControl)
                End If

                Dim oParent = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oControl.WindowId & "-WINDOW")
                If oParent IsNot Nothing Then
                    Dim oNode = _ProfileMatch.NewControlNode(oProfile, oControl, oFound)
                    oParent.Nodes.Add(oNode)
                End If
            Next

            ' If Profile has controls that matched, the profile matches
            If oMatchingControls.Count > 0 Then
                _Logger.Debug("Profile has {0} MATCHING Controls", oMatchingControls.Count)
                oProfile.Controls = oMatchingControls
                oProfiles.Add(oProfile)

                _Logger.Info("FilterProfilesByFocusedControl: Profile {0} matched!", oProfile.Name)
            End If
        Next

        ' Add catch-all profiles to the list
        oProfiles = AddCatchAllProfiles(Profiles, oProfiles)

        Return oProfiles
    End Function

    Public Function FilterProfilesBySearchResults(Profiles As List(Of ProfileData), Database As MSSQLServer, User As UserState, ClipboardContents As String) As List(Of ProfileData)
        Dim oProfiles As New List(Of ProfileData)

        For Each oProfile In Profiles
            _Logger.Debug($"Checking SearchResults on ProfileID: {oProfile.Guid}")
            Dim oResultDocs As Integer = 0
            Dim oResultData As Integer = 0

            Dim oPatterns As New ClassPatterns(_LogConfig)

            Dim oDTDataSearches As DataTable
            Dim oDTDocSearches As DataTable

            Try
                oDTDataSearches = Database.GetDatatable($"SELECT COUNT_COMMAND, CONN_ID FROM TBCW_PROF_DATA_SEARCH WHERE ACTIVE = 1 AND PROFILE_ID = {oProfile.Guid}")
            Catch ex As Exception
                oProfile.ErrorMessage = ERROR_LOADING_COUNT_SQL_FOR_DATA_SEARCH
                _Logger.Error(ex)
                Continue For
            End Try

            Try
                oDTDocSearches = Database.GetDatatable($"SELECT COUNT_COMMAND, CONN_ID FROM TBCW_PROF_DOC_SEARCH WHERE ACTIVE = 1 AND PROFILE_ID = {oProfile.Guid}")
            Catch ex As Exception
                oProfile.ErrorMessage = ERROR_LOADING_COUNT_SQL_FOR_DOC_SEARCH
                _Logger.Error(ex)
                Continue For
            End Try

            For Each oRow As DataRow In oDTDataSearches.Rows
                Dim oCountCommand = String.Empty
                Dim oCountConnectionId = String.Empty

                Try
                    oCountCommand = NotNull(oRow.Item("COUNT_COMMAND"), String.Empty)
                    oCountConnectionId = NotNull(oRow.Item("CONN_ID"), 0)

                    If oCountCommand = String.Empty Then
                        _Logger.Debug("Data SQL Query is empty. Skipping.")
                        Continue For
                    End If

                    _Logger.Debug("Count-Command DATA BEFORE Replace: [{0}]", oCountCommand)
                    oCountCommand = oPatterns.ReplaceInternalValues(oCountCommand)
                    oCountCommand = oPatterns.ReplaceUserValues(oCountCommand, User)
                    oCountCommand = oPatterns.ReplaceClipboardContents(oCountCommand, ClipboardContents)

                    _Logger.Debug("Count-Command DATA before execute: [{0}]", oCountCommand)
                    Dim oConnectionString = ProfileUtils.GetConnectionString(Database, oCountConnectionId)

                    oResultData += NotNull(Of Integer)(Database.GetScalarValueWithConnection(oCountCommand, oConnectionString), 0)

                    _Logger.Debug("Datarows returned from search: [{0}]", oResultData)
                Catch ex As Exception
                    oProfile.ErrorMessage = ERROR_EXECUTING_COUNT_SQL_FOR_DATA_SEARCH
                    _Logger.Error(ex)
                    _Logger.Warn("Invalid SQL Query for Counting Data in Profile [{0}]: [{1}]", oProfile.Guid, oCountCommand)
                End Try
            Next

            For Each oRow As DataRow In oDTDocSearches.Rows
                Dim oCountCommand = String.Empty
                Dim oCountConnectionId = String.Empty

                Try
                    oCountCommand = NotNull(oRow.Item("COUNT_COMMAND"), String.Empty)
                    oCountConnectionId = NotNull(oRow.Item("CONN_ID"), 0)

                    If oCountCommand = String.Empty Then
                        _Logger.Debug("Document SQL Query is empty. Skipping.")
                        Continue For
                    End If

                    _Logger.Debug("Count-Command for Documents BEFORE Replace: [{0}]", oCountCommand)
                    oCountCommand = oPatterns.ReplaceInternalValues(oCountCommand)
                    oCountCommand = oPatterns.ReplaceUserValues(oCountCommand, User)
                    oCountCommand = oPatterns.ReplaceClipboardContents(oCountCommand, ClipboardContents)

                    _Logger.Debug("Count-Command for Documents BEFORE execute: [{0}]", oCountCommand)
                    Dim oConnectionString = ProfileUtils.GetConnectionString(Database, oCountConnectionId)

                    oResultDocs += NotNull(Of Integer)(Database.GetScalarValueWithConnection(oCountCommand, oConnectionString), 0)

                    _Logger.Debug("Documents returned from search: [{0}]", oResultDocs)
                Catch ex As Exception
                    oProfile.ErrorMessage = ERROR_EXECUTING_COUNT_SQL_FOR_DOC_SEARCH
                    _Logger.Error(ex)
                    _Logger.Warn("Invalid SQL Query for Counting Data in Profile [{0}]: [{1}]", oProfile.Guid, oCountCommand)
                End Try
            Next

            oProfile.CountData = oResultData
            oProfile.CountDocs = oResultDocs
        Next

        For Each oProfile In Profiles
            Dim oProfileNode = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oProfile.Name & "-PROFILE")
            Dim oCombinedResults = oProfile.CountDocs + oProfile.CountData
            Dim oHasResults = oCombinedResults > 0

            oProfileNode.Text = $"{oProfileNode.Text} ({oCombinedResults} Ergebnisse)"
            oProfileNode.BackColor = _ProfileMatch.GetMatchColor(oHasResults)

            If oProfile.CountData > 0 Or oProfile.CountDocs > 0 Then
                oProfiles.Add(oProfile)
                _Logger.Info("Profile [{0}] matched in FilterProfilesBySearchResults!", oProfile.Name)
            End If
        Next

        Return oProfiles
    End Function

    Public Function FilterWindowsByWindowClipboardRegex(Profiles As List(Of ProfileData), ClipboardContents As String) As List(Of ProfileData)
        Dim oProfiles As New List(Of ProfileData)

        For Each oProfile As ProfileData In Profiles
            _Logger.Debug("Current Profile: {0}", oProfile.Name)

            Dim oWindows As New List(Of WindowData)

            For Each w As WindowData In oProfile.Windows
                Try
                    If w.Regex = String.Empty Then
                        oWindows.Add(w)
                    End If

                    Dim oRegex As New Regex(w.Regex)
                    Dim oMatch = oRegex.Match(ClipboardContents)

                    If oMatch.Success Then
                        _Logger.Debug("Window Clipboard Regex Matched: {0}", ClipboardContents)
                        oWindows.Add(w)
                    End If

                    Dim oResult As TreeNode = _ProfileMatch.FindNodeByTag(_TreeView.Nodes, oProfile.Name & "-REGEX")
                    If Not IsNothing(oResult) Then
                        Dim oNode As New TreeNode($"MATCH on WINDOW Clipboard Regex [{w.Regex}]: {oMatch.Success}")
                        oNode.ImageIndex = 2
                        oNode.Tag = oProfile.Name & "-WINDOW_REGEX"
                        oResult.Nodes.Add(oNode)
                    End If
                Catch ex As Exception
                    _Logger.Warn("Regex '{0}' could not be processed for input '{1}'", w.Regex, ClipboardContents)
                    _Logger.Error(ex)
                End Try
            Next

            If oWindows.Count > 0 Then
                oProfile.Windows = oWindows
                oProfiles.Add(oProfile)

                _Logger.Info("FilterWindowsByWindowClipboardRegex: Profile {0} matched!", oProfile.Name)
            End If
        Next

        ' Add catch-all profiles to the list
        oProfiles = AddCatchAllProfiles(Profiles, oProfiles)

        Return oProfiles
    End Function

    Public Function ClearNotMatchedProfiles(Profiles As List(Of ProfileData)) As List(Of ProfileData)
        Dim oFilteredProfiles As New List(Of ProfileData)
        For Each oProfile In Profiles
            If oProfile.IsMatched Then
                oFilteredProfiles.Add(oProfile)
            End If
        Next
        Return oFilteredProfiles
    End Function

    Public Function ClearDuplicateProfiles(Profiles As List(Of ProfileData)) As List(Of ProfileData)
        Return Profiles.
            GroupBy(Function(Profile) Profile.Guid).
            Select(Function(GroupedProfiles) GroupedProfiles.First).
            ToList()
    End Function

    Private Function AddCatchAllProfiles(AllProfiles As List(Of ProfileData), FilteredProfiles As List(Of ProfileData)) As List(Of ProfileData)
        Dim oCatchAllProfiles As New List(Of ProfileData)

        For Each oProfile As ProfileData In AllProfiles
            If oProfile.IsCatchAll And FilteredProfiles.Contains(oProfile) = False Then
                oProfile.IsMatched = True
                oCatchAllProfiles.Add(oProfile)
            End If
        Next

        Return FilteredProfiles.
            Concat(oCatchAllProfiles).
            ToList()
    End Function

    Private Function TransformProfiles() As List(Of ProfileData)
        Try
            Dim oList As New List(Of ProfileData)

            For Each oRow As DataRow In _ProfileTable.Rows
                Dim oProfileId = oRow.Item("GUID")
                Dim oProcessList As List(Of ProcessData) = TransformProcesses(oProfileId, _ProcessTable)
                Dim oWindowList As List(Of WindowData) = TransformWindows(oProfileId, _WindowTable)
                Dim oControlList As List(Of ControlData) = TransformControls(oProfileId, _ControlTable)

                oList.Add(New ProfileData() With {
                    .Guid = oRow.Item("GUID"),
                    .Regex = oRow.Item("REGEX_EXPRESSION"),
                    .Name = NotNull(oRow.Item("NAME"), String.Empty),
                    .Comment = NotNull(oRow.Item("COMMENT"), String.Empty),
                    .ProfileType = NotNull(oRow.Item("PROFILE_TYPE"), String.Empty),
                    .Processes = oProcessList,
                    .Windows = oWindowList,
                    .Controls = oControlList
                })
            Next

            oList = oList.
                Distinct().
                ToList()

            Return oList
        Catch ex As Exception
            _Logger.Error(ex)
            Throw ex
        End Try
    End Function

    Private Function TransformControls(ProfileId As Integer, ControlDatatable As DataTable) As List(Of ControlData)
        Try
            Dim oControlList As New List(Of ControlData)

            For Each oRow As DataRow In ControlDatatable.Rows
                If oRow.Item("PROFILE_ID") = ProfileId Then
                    oControlList.Add(New ControlData() With {
                        .Guid = oRow.Item("GUID"),
                        .ProcessName = NotNull(oRow.Item("PROCESS_NAME"), String.Empty),
                        .ControlName = NotNull(oRow.Item("CONTROL_NAME"), String.Empty),
                        .Description = NotNull(oRow.Item("DESCRIPTION"), String.Empty),
                        .WindowId = oRow.Item("WINDOW_ID"),
                        .TopLeft = New ControlBounds() With {
                            .Left = oRow.Item("TOPLEFT_LEFT"),
                            .Right = oRow.Item("TOPLEFT_RIGHT"),
                            .Top = oRow.Item("TOPLEFT_TOP"),
                            .Bottom = oRow.Item("TOPLEFT_BOTTOM")
                        },
                        .TopRight = New ControlBounds() With {
                            .Left = oRow.Item("TOPRIGHT_LEFT"),
                            .Right = oRow.Item("TOPRIGHT_RIGHT"),
                            .Top = oRow.Item("TOPRIGHT_TOP"),
                            .Bottom = oRow.Item("TOPRIGHT_BOTTOM")
                        },
                        .BottomLeft = New ControlBounds() With {
                            .Left = oRow.Item("BOTTOMLEFT_LEFT"),
                            .Right = oRow.Item("BOTTOMLEFT_RIGHT"),
                            .Top = oRow.Item("BOTTOMLEFT_TOP"),
                            .Bottom = oRow.Item("BOTTOMLEFT_BOTTOM")
                        },
                        .BottomRight = New ControlBounds() With {
                            .Left = oRow.Item("BOTTOMRIGHT_LEFT"),
                            .Right = oRow.Item("BOTTOMRIGHT_RIGHT"),
                            .Top = oRow.Item("BOTTOMRIGHT_TOP"),
                            .Bottom = oRow.Item("BOTTOMRIGHT_BOTTOM")
                        }
                    })
                End If
            Next

            Return oControlList
        Catch ex As Exception
            _Logger.Error(ex)
            Throw ex
        End Try
    End Function
    Private Function TransformProcesses(ProfileId As Integer, ProcessDatatable As DataTable) As List(Of ProcessData)
        Try
            Dim oProcessList As New List(Of ProcessData)

            For Each oRow As DataRow In ProcessDatatable.Rows
                If oRow.Item("PROFILE_ID") = ProfileId Then
                    oProcessList.Add(New ProcessData() With {
                        .Guid = oRow.Item("GUID"),
                        .PROFILE_ID = oRow.Item("PROFILE_ID"),
                        .ProcessName = NotNull(oRow.Item("PROC_NAME"), String.Empty)
                    })
                End If
            Next

            oProcessList = oProcessList.
                Distinct().
                ToList()

            Return oProcessList
        Catch ex As Exception
            _Logger.Error(ex)
            Throw ex
        End Try
    End Function
    Private Function TransformWindows(ProfileId As Integer, WindowDatatable As DataTable) As List(Of WindowData)
        Try
            Dim oWindowList As New List(Of WindowData)

            For Each oRow As DataRow In WindowDatatable.Rows
                If oRow.Item("PROFILE_ID") = ProfileId Then
                    oWindowList.Add(New WindowData() With {
                        .Guid = oRow.Item("GUID"),
                        .WindowProcessID = oRow.Item("PROCESS_ID"),
                        .Title = NotNull(oRow.Item("DESCRIPTION"), String.Empty),
                        .Regex = NotNull(oRow.Item("REGEX"), String.Empty),
                        .Sequence = NotNull(oRow.Item("SEQUENCE"), 0)
                    })
                End If
            Next

            Return oWindowList
        Catch ex As Exception
            _Logger.Error(ex)
            Throw ex
        End Try
    End Function
End Class
