lib/functions/functions.ps1

#######################################################################################################################################################
# WARNING: DO NOT EDIT THIS FILE AS IT IS GENERATED AND WILL BE OVERWRITTEN ON THE NEXT UPDATE! #
# #
# Generated via psake on: 2025-01-13T01:31:24.204Z #
# Version: 2025.1.13.2 #
# Copyright Fortigi (C) 2025 #
#######################################################################################################################################################

#requires -Module OmadaWeb.PS
#requires -Version 7.0

function Clear-Variables {
    try {

        $EndVariables = Get-Variable
        $SkipVariableNames = @("WshShell", "WhatIfPreference", "WarningPreference", "VerbosePreference", "true", "PSItem", "Task")
        foreach ($EndVariable in $EndVariables) {
            if ($EndVariable.Name -notin $StartVariables.Name -and $EndVariable.Name -notin $SkipVariableNames) {
                try {
                    Remove-Variable -Name $EndVariable.Name -Force -ErrorAction SilentlyContinue
                }
                catch {}
            }
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }

}
function Close-SplashScreenForm {
    try {

        "Closing Splash Screen" | Write-LogOutput -LogType DEBUG
        try {
            $SplashScreenForm.Hide()
            $SplashScreenForm.Dispose()
        }
        catch {}

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-AllControls {
    param (
        [System.Windows.DependencyObject]$Parent
    )

    try {

        $Controls = @()
        if ($Parent -is [System.Windows.Controls.Control]) {
            $Controls += $Parent
        }

        for ($i = 0; $i -lt [System.Windows.Media.VisualTreeHelper]::GetChildrenCount($Parent); $i++) {
            $Child = [System.Windows.Media.VisualTreeHelper]::GetChild($Parent, $i)
            $Controls += Get-AllControls -Parent $Child
        }

        return $Controls

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }

}
function Get-GalleryModuleVersion {
    param (
        [string]$ModuleName
    )

    try {
        $ApiEndpoint = "https://www.powershellgallery.com/api/v2/FindPackagesById()?id='{0}'" -f $ModuleName
        $Response = Invoke-RestMethod -Uri $ApiEndpoint -Method Get -Headers @{
            "Accept" = "application/xml"
        } -ConnectionTimeoutSeconds 1

        if ($null -ne $Response) {
            $LatestVersion = $Response | Sort-Object updated -Descending | Select-Object -First 1
            return $LatestVersion.Properties.version
        }
        else {
            return $null
        }
    }
    catch {
        return $null
    }
}
function Get-Icon {
    PARAM(
        [ValidateSet("Wpf", "WinForms", "Base64")]
        [string]$Type = "WinForms"
    )

    try {

        $Base64Icon = "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"

        $IconBytes = [System.Convert]::FromBase64String($Base64Icon)

        $MemoryStream = New-Object System.IO.MemoryStream(, $IconBytes)

        switch ($Type) {
            "Wpf" {
                $Icon = New-Object System.Windows.Media.Imaging.BitmapImage
                $Icon.BeginInit()
                $Icon.StreamSource = $MemoryStream
                $Icon.CacheOption = [System.Windows.Media.Imaging.BitmapCacheOption]::OnLoad
                $Icon.EndInit()
                $Icon.Freeze()  # Freeze to make it thread-safe

                return $Icon
            }
            "WinForms" {
                return [System.Drawing.Icon]::new($MemoryStream)
            }
            Default {
                return $Base64Icon
            }
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-InstalledModuleInfo {
    param (
        [string]$ModuleName
    )

    $Module = Get-Module -ListAvailable -Name $ModuleName | Sort-Object Version -Descending | Select-Object -First 1
    if ($Module) {
        $ModuleInfo = @{
            Name             = $Module.Name
            Version          = $Module.Version
            RepositorySource = $Module.RepositorySourceLocation
        }
        return $ModuleInfo
    }
    else {
        return $null
    }
}
function Get-ModuleBaseFolder {
    [CmdletBinding()]
    PARAM()

    "Return Module Base Folder" | Write-LogOutput -LogType VERBOSE
    return Split-Path -Path ($MyInvocation.MyCommand.Module).Path -Parent
}
function Get-OmadaGetPagingDataObject {
    PARAM(
        [parameter(Mandatory = $True, Position = 0)]
        [string]$DataType,
        [parameter(Mandatory = $True, Position = 1)]
        [hashtable]$DataTypeArgs,
        [parameter(Mandatory = $False, Position = 3)]
        [string]$SearchString = $null,
        [parameter(Mandatory = $false, Position = 4)]
        [int]$Rows = 1000
    )

    try {

        $Script:RunTimeData.RestMethodParam.Body = [ordered]@{
            _search      = $false
            nd           = 1732546553116
            rows         = $Rows
            page         = 1
            sidx         = $(if ([string]::IsNullOrWhiteSpace($SearchString)) { $null }else { "name" })
            sord         = "asc"
            searchField  = $null
            searchString = $(if ([string]::IsNullOrWhiteSpace($SearchString)) { $null }else { $SearchString })
            searchOper   = $null
            filters      = $null
            dataType     = $DataType
            dataTypeArgs = $DataTypeArgs
        }

        $Script:RunTimeData.RestMethodParam.Uri = '{0}/WebService/JQGridPopulationWebService.asmx/GetPagingData' -f $Script:AppConfig.BaseUrl
        $Script:RunTimeData.RestMethodParam.Method = "POST"

        return Invoke-OmadaPSWebRequestWrapper

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-SqlQueryObject {
    try {
        if(!(Test-ConnectionRequirements)){
            "Connection not ready" | Write-LogOutput -LogType DEBUG
            return
        }
        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.CurrentSqlQuery.DoId)) {
            $Script:MainWindowForm.Elements.TextBoxURL.Text.Trim() | Invoke-ConfigSetting -Property "BaseUrl"

            "Retrieve current query for SqlQuery DoId: {0}" -f $Script:AppConfig.CurrentSqlQuery.DoId | Write-LogOutput -LogType DEBUG
            $Script:RunTimeData.RestMethodParam.Uri = "{0}/odata/dataobjects/C_P_SQLTROUBLESHOOTING({1})" -f $Script:AppConfig.BaseUrl, $Script:AppConfig.CurrentSqlQuery.DoId
            "QueryUrl: {0}" -f $Script:RunTimeData.RestMethodParam.Uri | Write-LogOutput -LogType DEBUG

            "Retrieve query {0}" -f $Script:AppConfig.CurrentSqlQuery.DoId | Write-LogOutput

            $Script:RunTimeData.RestMethodParam.Body = $Null
            $Script:RunTimeData.RestMethodParam.Method = "GET"
            try {
                return Invoke-OmadaPSWebRequestWrapper
            }
            catch {
                if ($_.Exception.StatusCode -eq 404) {
                    "Query {0} not found! Clearing current value." -f $Script:AppConfig.CurrentSqlQuery.FullName | Write-LogOutput -LogType WARNING
                    $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem = $Null
                    return $null
                }
                else {
                    $_.Exception.Message | Write-LogOutput -LogType ERROR
                }
            }

            "Retrieved object {0}" -f $Script:RunTimeData.SqlQueryObject | Write-LogOutput -LogType VERBOSE
        }
        else {
            "CurrentSqlQuery DoId is not set! Cannot retrieve Sql query!" | Write-LogOutput -LogType WARNING
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-SqlSchemaObject {
    try {

        if(!(Test-ConnectionRequirements)){
            "Connection not ready" | Write-LogOutput -LogType DEBUG
            return
        }

        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.CurrentDataConnection.DoId)) {
            "Retrieve current SqlSchema for data connection DoId: {0}" -f $Script:AppConfig.CurrentDataConnection.DoId | Write-LogOutput -LogType DEBUG
            $Script:RunTimeData.RestMethodParam.Uri = "{0}/webservice/SyntaxHighlighting.asmx/GetSqlSchema" -f $Script:AppConfig.BaseUrl
            "SqlSchemaUrl: {0}" -f $Script:RunTimeData.RestMethodParam.Uri | Write-LogOutput -LogType DEBUG

            "Retrieve schema {0}" -f $Script:AppConfig.CurrentDataConnection.FullName | Write-LogOutput

            $Script:RunTimeData.RestMethodParam.Body = @{
                connectionId = $Script:AppConfig.CurrentDataConnection.DoId
            }
            $Script:RunTimeData.RestMethodParam.Method = "POST"
            $ReturnValue = Invoke-OmadaPSWebRequestWrapper

            $Script:SqlSchemaWindowForm.Definition.Title = "Sql Schema - {0}" -f $Script:AppConfig.CurrentDataConnection.FullName

            "Retrieved object {0}" -f $Script:RunTimeData.SqlQueryObject | Write-LogOutput -LogType VERBOSE

            $SchemaObjects = @{}
            $Script:TreeViewSqlSchema.Items.Clear()
            $Schemas = (($ReturnValue.d | Get-Member -MemberType NoteProperty).Name) | ForEach-Object { $_.Split(".")[0] } | Select-Object -Unique
            foreach ($Schema in $Schemas) {
                $Tables = $ReturnValue.d | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -like ("{0}.*" -f $Schema) }

                $TreeViewSchemaItem = New-Object System.Windows.Controls.TreeViewItem
                $TreeViewSchemaItem.Header = $Schema
                $TreeViewSchemaItem.FontSize = 14
                $TreeViewSchemaItem.IsExpanded = $true
                $Script:TreeViewSqlSchema.Items.Add($TreeViewSchemaItem) | Out-Null

                $TableObjects = @{}

                foreach ($Table in $Tables) {

                    $TableFullName = $Table.Name
                    $TableName = $TableFullName.Split(".")[1]

                    $TreeViewTableItem = New-Object System.Windows.Controls.TreeViewItem
                    $TreeViewTableItem.Header = $TableName
                    $TreeViewTableItem.FontSize = 14
                    $TreeViewSchemaItem.Items.Add($TreeViewTableItem) | Out-Null

                    $TableObjects.Add($TableName,($ReturnValue.d.$TableFullName | ForEach-Object { $_.Split(" ")[0] }))


                    foreach ($Column in $ReturnValue.d.$TableFullName) {
                        $TreeViewColumnItem = New-Object System.Windows.Controls.TreeViewItem
                        $TreeViewColumnItem.Header = $Column
                        $TreeViewColumnItem.FontSize = 12
                        $TreeViewColumnItem.Font
                        $TreeViewTableItem.Items.Add($TreeViewColumnItem) | Out-Null
                    }
                }
                $SchemaObjects.Add($Schema,$TableObjects)
            }

            $SchemaObjectsJson = $SchemaObjects | ConvertTo-Json -Depth 5

            "Schema for Monaco editor: {0}" -f $SchemaObjectsJson | Write-LogOutput -LogType VERBOSE
            $OnCompletedScriptBlock = {
                try {
                    if (!$Script:Task.Status -eq "RanToCompletion") {
                        "Monaco Editor Task failed: {0}" -f $Script:Task.Status | Write-LogOutput -LogType ERROR
                    }
                    else{
                        "Monaco Editor Task completed successfully." | Write-LogOutput -LogType DEBUG
                    }
                }
                catch {
                    $Script:Task.Exception.Message | Write-LogOutput -LogType ERROR
                }
            }

            "Push schema to Monaco editor." | Write-LogOutput -LogType DEBUG
            Invoke-ExecuteScriptAsync -ScriptToExecute "setSchema($SchemaObjectsJson);" -OnCompletedScriptBlock $OnCompletedScriptBlock

        }
        else {
            "SqlSchema DoID is not set! Cannot retrieve Sql schema!" | Write-LogOutput -LogType WARNING
            return $null
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-SqlTroubleShooterView {

    try {

        $ViewResult = Get-OmadaGetPagingDataObject -SearchString "SQL Troubleshooting" -DataType "Views" -DataTypeArgs @{OwnerShipType = "Both" }
        $View = $null
        if ($null -ne $ViewResult -and $ViewResult.d.Records -gt 0) {
            $View = $ViewResult.d.Rows | Where-Object { $_.Name -eq "SQL Troubleshooting" }
        }
        $Private:Result = $null
        if ($null -ne $View) {
            $DataTypeArgs = [ordered]@{
                viewId          = ("{0}" -f $View.Id)
                pageQueryString = ("{0}/dataobjlst.aspx?view={1}" -f $Script:AppConfig.BaseUrl, $View.Id)
                readOnlyMode    = $false
                countRows       = $false
            }

            $Private:Result = Get-OmadaGetPagingDataObject -DataType "DataObjects" -DataTypeArgs $DataTypeArgs
            $Private:Result = $Private:Result.d.Rows
        }
        return $Private:Result

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-TreeViewItemLevel {
    param (
        [System.Windows.Controls.TreeViewItem]$TreeViewItem
    )
    try {
        $Level = 0
        $Parent = $TreeViewItem.Parent

        while ($null -eq $Parent) {
            if ($Parent -is [System.Windows.Controls.TreeViewItem]) {
                $Level++
            }
            $Parent = $Parent.Parent
        }

        return $Level
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}

function Get-ValidWindowMeasurement {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form,
        [parameter(Mandatory = $true)]
        [string]$Setting
    )

    try {
        if ($Setting -in "Width", "Height") {

            $SettingString = "Min{0}" -f $Setting
            if ($Form.$Setting -lt $Form.$SettingString -and $Form.$Setting -gt 0) {
                return [Int]$Form.$SettingString
            }
            else {
                return [Int]$Form.$Setting
            }
        }


    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }

}
function Get-ValidWindowPosition {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form,
        [parameter(Mandatory = $true)]
        [string]$Setting
    )
    try {
        $ActionId = [guid]::NewGuid().ToString()
        if ($Setting -in "Left", "Top") {
            "{0} setting {1}: {2} (Id:{3})" -f $Form.Name, $Setting, $Form.$Setting, $ActionId | Write-LogOutput -LogType VERBOSE2
            if ($Setting -eq "Left") {
                $PrimaryScreenSetting = [system.windows.systemparameters]::PrimaryScreenWidth
                "PrimaryScreenSetting PrimaryScreenWidth {0}: {1} (Id:{2})" -f $Setting, $PrimaryScreenSetting, $ActionId | Write-LogOutput -LogType VERBOSE2
            }
            elseif ($Setting -eq "Top") {
                $PrimaryScreenSetting = [system.windows.systemparameters]::PrimaryScreenHeight
                "PrimaryScreenSetting PrimaryScreenHeight {0}: {1} (Id:{2})" -f $Setting, $PrimaryScreenSetting, $ActionId | Write-LogOutput -LogType VERBOSE2
            }

            if ($Form.$Setting -gt $PrimaryScreenSetting -or $Form.$Setting -lt 0) {
                $Form.$Setting = ($PrimaryScreenSetting - $Form.$Setting) / 2
                "{0} position from screen height '{1}x{2}'. Setting: '{3}' (Id:{4})" -f $Form.Name, $Form.Left, $Form.Top, $Setting, $ActionId | Write-LogOutput -LogType VERBOSE2
                return [Int]::Abs($Form.$Setting)
            }
            else {
                "{0} setting '{1}' (Id:{2})" -f $Form.Name, $Form.$Setting, $ActionId | Write-LogOutput -LogType VERBOSE2
                return [Int]::Abs($Form.$Setting)
            }
        }
        else {
            "{0} setting '{1}' is not valid. (Id:{2})" -f $Form.Name, $Form.$Setting, $ActionId | Write-LogOutput -LogType VERBOSE2
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-WindowPosition {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form,
        [switch]$AsString
    )

    try {
        if ($AsString) {
            return "{0}x{1}" -f $Form.Left, $Form.Top
        }
        else {
            return [PSCustomObject]@{
                Left = $Form.Left
                Top  = $Form.Top
            }
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-WindowPositionConfig {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form
    )
    try {
        $Property = "{0}Position" -f $Form.Name
        if ($null -ne $Script:AppConfig.$Property -and $Script:AppConfig.$Property -match "\b\d+x\d+\b") {
            return $Script:AppConfig.$Property
        }
        else {
            return $null
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-WindowSize {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form,
        [switch]$AsString
    )
    try {
        "{0}: {1}x{2} AsString: {3}" -f $Form.Name, $Form.Width , $Form.Height, $AsString.IsPresent | Write-LogOutput -LogType VERBOSE2
        if ($AsString) {
            return "{0}x{1}" -f $Form.Width, $Form.Height
        }
        else {
            return [PSCustomObject]@{
                Width  = $Form.Width
                Height = $Form.Height
            }
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Get-WindowSizeConfig {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form
    )
    try {
        $Property = "{0}Size" -f $Form.Name
        if ($null -ne $Script:AppConfig.$Property -and $Script:AppConfig.$Property -match "\d+x\d+") {
            return $Script:AppConfig.$Property
        }
        else {
            return $null
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Initialize-ConfigSettings {
    try {

        Invoke-ConfigSetting -Reset:$Reset.IsPresent

        if ($Script:RunTimeConfig.LogToConsole -or $Script:AppConfig.CheckboxConsoleLog) {
            $Script:RunTimeConfig.LogToConsole = $true
            "Console logging is enabled" | Write-LogOutput -LogType LOG
        }

        if ($null -eq ($Script:MainWindowForm.Definition | Get-WindowPositionConfig)) {
            $Script:MainWindowForm.Definition.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterScreen
        }

        "Pre-set Main Window Components from config" | Write-LogOutput -LogType DEBUG
        $Script:CurrentUrl = $Null
        $Script:MainWindowForm.Elements.TextBoxURL.Text = $Script:AppConfig.BaseUrl
        $Script:MainWindowForm.Elements.TextBoxURL.IsEnabled = $True
        if (![String]::IsNullOrWhiteSpace($Script:MainWindowForm.Elements.TextBoxURL.Text)) {
            $Script:CurrentUrl = $Script:MainWindowForm.Elements.TextBoxURL.Text
            "Config: Current Url: {0}" -f $Script:CurrentUrl | Write-LogOutput -LogType DEBUG
        }

        if ($Script:AppConfig.MyQueriesOnly) {
            "Config: MyQueriesOnly: True" | Write-LogOutput -LogType DEBUG
            $Script:MainWindowForm.Elements.CheckboxMyQueries.IsChecked = $True
        }

        if ($null -ne $Script:RunTimeConfig.Logging.LogLevelSetting) {
            $Script:RunTimeConfig.Logging.LogLevelSetting | Invoke-ConfigSetting -Property "LogLevel"
            "Config: LogLevelSetting: {0}" -f $Script:RunTimeConfig.Logging.LogLevelSetting | Write-LogOutput -LogType DEBUG
        }

        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.CurrentSqlQuery.DoId)) {
            "Config: CurrentSqlQuery.DoId: {0}" -f $Script:AppConfig.CurrentSqlQuery.DoId | Write-LogOutput -LogType DEBUG

            $ComboBoxSelectQueryItem = $null
            $ComboBoxSelectQueryItem = $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items | Where-Object { $_.Content -eq $Script:AppConfig.CurrentSqlQuery.FullName }
            if ($null -eq $ComboBoxSelectQueryItem) {
                "Config: Set CurrentSqlQuery.DoId: {0}" -f $Script:AppConfig.CurrentSqlQuery.DoId | Write-LogOutput -LogType DEBUG
                $ComboBoxSelectQueryItem = New-Object System.Windows.Controls.ComboBoxItem
                $ComboBoxSelectQueryItem.Content = $Script:AppConfig.CurrentSqlQuery.FullName
                $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items.Add($ComboBoxSelectQueryItem) | Out-Null
                $Script:RunTimeData.CurrentSqlQuery.DisplayName = $Script:AppConfig.CurrentSqlQuery.DisplayName
                $Script:MainWindowForm.Elements.TextBoxDisplayName.Text = $Script:RunTimeData.CurrentSqlQuery.DisplayName
            }
            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedValue = $ComboBoxSelectQueryItem
        }

        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.CurrentDataConnection.FullName)) {
            "Config: CurrentDataConnection: {0}" -f $Script:AppConfig.CurrentDataConnection.FullName | Write-LogOutput -LogType DEBUG
            Set-DataConnection
        }

        if ([string]::IsNullOrWhiteSpace($Script:AppConfig.LastAuthentication)) {
            "Config: LastAuthentication: {0}" -f $Script:AppConfig.LastAuthentication | Write-LogOutput -LogType DEBUG
            $Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedValue = $Script:AppConfig.LastAuthentication
        }

        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.UserName)) {
            "Config: UserName: {0}" -f $Script:AppConfig.UserName | Write-LogOutput -LogType DEBUG
            $Script:MainWindowForm.Elements.TextBoxUserName.Text = $Script:AppConfig.UserName
        }
        Set-OmadaUrl
        Set-AuthenticationOption
        Test-ConnectionSettings

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Initialize-OmadaSqlTroubleShooter {

    try {
        "Initializing application..." | Write-LogOutput -LogType DEBUG

        Push-Location $Script:RunTimeConfig.ModuleFolder

        $Script:RunTimeConfig.Logging.AppLogObject.Add("Application log initialized`r`n")
        $Script:RunTimeConfig.ConfigFile.Name = $($Script:RunTimeConfig.ScriptName -replace ".ps1", ""), ".json" -join ""
        If (Test-Path $Script:RunTimeConfig.AppDataFolder -PathType Container) {
            New-Item (Join-Path $Script:RunTimeConfig.AppDataFolder -ChildPath "config") -ItemType Directory -Force | Out-Null
            $Script:RunTimeConfig.ConfigFile.Path = (Join-Path $($Script:RunTimeConfig.AppDataFolder) -ChildPath "config\$($Script:RunTimeConfig.ConfigFile.Name)")
        }
        else {
            $Script:RunTimeConfig.ConfigFile.Path = Join-Path $($Script:RunTimeConfig.ModuleFolder) -ChildPath $($Script:RunTimeConfig.ConfigFile.Name)
        }


        try {
            Remove-Variable "Task" -ErrorAction SilentlyContinue
        }
        catch { $Error.Clear() }

        "Load module OmadaWeb.PS" | Write-LogOutput -LogType DEBUG
        Import-Module OmadaWeb.PS

        "Load Assemblies" | Write-LogOutput -LogType DEBUG
        $Env:Path += ";$($Script:RunTimeConfig.ModuleFolder)\Bin"
        $Env:Path += ";$($Script:RunTimeConfig.ModuleFolder)\Bin\Webview2Dlls"
        $Env:Path += ";$($Script:RunTimeConfig.ModuleFolder)"

("System.Windows.Forms", "System.Drawing", "PresentationFramework", "WindowsBase", "PresentationCore", "PresentationFramework") | ForEach-Object {
            "Load assembly: '{0}'" -f $_ | Write-LogOutput -LogType DEBUG
            Add-Type -AssemblyName $_
        }

        "Microsoft.Web.WebView2.Core.dll", "Microsoft.Web.WebView2.Wpf.dll" | ForEach-Object {
            "Load assembly: '{0}'" -f $_ | Write-LogOutput -LogType DEBUG
            $WebViewDllPath = Join-Path $Script:RunTimeConfig.ModuleFolder -ChildPath "Bin\WebView2Dlls\$_"
            if ((Test-Path $WebViewDllPath -PathType Leaf)) {
                [System.Reflection.Assembly]::LoadFrom($WebViewDllPath) | Out-Null
            }
            else {
                Throw ("The WebView2 Dll '{0}' cannot be found at the '{1}' bin folder!" -f $_, $($Script:RunTimeConfig.ModuleFolder))
                Break
            }
        }
        $WebViewLoaderPath = Join-Path $Script:RunTimeConfig.ModuleFolder -ChildPath "Bin\WebView2Dlls\WebView2Loader.dll"
        "Get 'WebView2Loader.Dll'" | Write-LogOutput -LogType DEBUG
        if (!(Test-Path $WebViewLoaderPath -PathType Leaf)) {
            Throw ("The WebView2Loader Dll '{0}' cannot be found at the '{1}' bin folder!" -f "WebView2Loader.dll", $($Script:RunTimeConfig.ModuleFolder))
            Break
        }

        $Script:AppConfig = $null
        $Script:RunTimeData = [PSCustomObject]@{
            RestMethodParam                = @{
                Uri                = $Null
                Method             = "GET"
                AuthenticationType = $($Script:AppConfig.LastAuthentication)
            }
            QuerySaved                     = $false
            Password                       = $Null
            QueryText                      = $null
            SqlQueryObject                 = $null
            QueryResult                    = $null
            CurrentQueryText               = $null
            CurrentSqlQuery                = [PSCustomObject]@{
                DoId        = $null
                DisplayName = $null
                FullName    = $null
            }
            StopWatch                      = $null
            QueryListCache                 = @{
                QueryList   = $null
                LastRefresh = Get-Date
                TTL         = 300
            }
            DataobjdlgAspxAttributeMapping = [PSCustomObject]@{
                SqlQueryDoId      = "c-13"
                SqlQueryCreatedBy = "c-2"
                SqlQueryChangedBy = "c-4"
            }
        }
        $Script:WebView = @{
            Object                  = $null
            Environment             = $null
            EdgeWebview2RuntimePath = $null
            UserDataFolder          = $null
        }

        [Windows.Forms.Application]::EnableVisualStyles()

    }
    catch {
        Throw $_
    }
}
function Invoke-ConfigSetting {
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'CurrentPoperties', Justification = 'The CurrentPoperties variable is used in a function called from here')]
    PARAM(
        [parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true)]
        $Value,
        [parameter(Mandatory = $false)]
        [string]$Property,
        [string]$JoinString = " - ",
        [switch]$Reset
    )

    begin {
        try {
            $InputObject = @()
            if ($Reset) {
                "Reset configuration!" | Write-LogOutput -LogType DEBUG
                if (Test-Path ($Script:RunTimeConfig.ConfigFile.Path) -PathType Leaf) {
                    Get-Item ($Script:RunTimeConfig.ConfigFile.Path) | Remove-Item -Force
                }
                $Script:AppConfig = $Null
            }

            if ($null -eq $Script:ConfigProperties) {
                "Read schema!" | Write-LogOutput -LogType DEBUG
                $Script:ConfigProperties = Get-Content (Join-Path (Get-ModuleBaseFolder) -ChildPath "lib\schema\appConfigSchema.json") | ConvertFrom-Json
            }

            if ($Null -ne $Script:AppConfig) {
                $Config = $Script:AppConfig | ConvertTo-Json | ConvertFrom-Json

                $Config | Get-Member -MemberType NoteProperty | ForEach-Object {
                    if ($Script:ConfigProperties.Name -notcontains $_.Name) {
                        "Remove obsolete property {0} from config object!" -f $_.Name | Write-LogOutput -LogType VERBOSE
                        $Config.PSObject.Properties.Remove($_.Name)
                    }
                }

                $CurrentPoperties = $Config | Get-Member -MemberType NoteProperty
                $Script:ConfigProperties | ForEach-Object {
                    Set-ConfigProperty
                }
                "Update config object!" | Write-LogOutput -LogType VERBOSE
            }
            else {
                if (Test-Path ($Script:RunTimeConfig.ConfigFile.Path) -PathType Leaf) {
                    "Read config settings {0}!" -f ($Script:RunTimeConfig.ConfigFile.Path) | Write-LogOutput -LogType VERBOSE
                    $Config = Get-Content ($Script:RunTimeConfig.ConfigFile.Path) | ConvertFrom-Json
                    $CurrentPoperties = $Config | Get-Member -MemberType NoteProperty
                    $Script:ConfigProperties | ForEach-Object {
                        Set-ConfigProperty
                    }
                }
                else {
                    "Create new config object!" | Write-LogOutput -LogType DEBUG
                    $Config = [pscustomobject]@{}
                    $Script:ConfigProperties | ForEach-Object {
                        Set-ConfigProperty
                    }
                }
            }
        }
        catch {
            $_.Exception.Message | Write-LogOutput -LogType ERROR
        }
    }
    process {
        $InputObject += $Value
        $Value = $Value
    }
    end {
        try {
            if (![string]::IsNullOrWhiteSpace($Property)) {
                "Set value for property {0} in config object!" -f $Property | Write-LogOutput -LogType VERBOSE

                $PropertyDefinition = $Script:ConfigProperties | Where-Object { $_.Name -eq $Property }

                switch ($PropertyDefinition.Type) {
                    "String" {
                        $Config.$Property = $Value
                    }
                    "Int" {
                        $Config.$Property = [int]$Value
                    }
                    "Bool" {

                        $Config.$Property = [bool]$Value
                    }
                    "PSObject" {
                        if ($InputObject.Count -eq 1) {
                            $InputString = $InputObject[0].ToString()
                            $LastIndex = $InputString.LastIndexOf($JoinString)

                            if ($LastIndex -le -1) {
                                $Config.$Property = [pscustomobject]@{
                                    DoId        = $InputString
                                    DisplayName = $null
                                    FullName    = $null
                                }
                            }
                            else {
                                $Config.$Property = [pscustomobject]@{
                                    DoId        = [int]$InputString.Substring($LastIndex + ($JoinString.Length - 1)).Trim()
                                    DisplayName = $InputString.Substring(0, $LastIndex).Trim()
                                    FullName    = $null
                                }
                            }
                        }
                        else {
                            $Config.$Property = [pscustomobject]@{
                                DoId        = [int]$InputObject[0]
                                DisplayName = $InputObject[1]
                                FullName    = $null
                            }
                        }
                        $Config.$Property.FullName = $Config.$Property.DisplayName, $Config.$Property.DoId -join " - "
                    }
                }
            }
            "Store config object to {0}. Contents`r`n{1}`r`n" -f ($Script:RunTimeConfig.ConfigFile.Path), ($Config | ConvertTo-Json) | Write-LogOutput -LogType VERBOSE
            $Success = $false
            do {
                try {
                    if (!$Success) {
                        $Config | ConvertTo-Json | Set-Content ($Script:RunTimeConfig.ConfigFile.Path) -Force
                        $Success = $true
                    }
                }
                catch {
                    if (!$Success) {
                        $ErrorObject = $_
                        "Error writing to file. Retry in 1 second" | Write-LogOutput -LogType WARNING -SkipDialog
                        Start-Sleep -Seconds 1
                    }
                }
            }
            until($Count -ge 10 -or $Success)

            if (!$Success) {
                $ErrorObject.Exception.Message | Write-LogOutput -LogType ERROR -SkipDialog
            }

            $Script:AppConfig = $Config
        }
        catch {
            $_.Exception.Message | Write-LogOutput -LogType ERROR
        }
    }
}

function Invoke-ExecuteScriptAsync {
    PARAM(
        $ScriptToExecute,
        $OnCompletedScriptBlock
    )
    try {

        if ($null -ne $Script:Webview.Object) {
            if ($Script:Webview.Object.IsLoaded) {
                $Script:Task = $Script:Webview.Object.CoreWebView2.ExecuteScriptAsync($ScriptToExecute)
                $Script:Task.GetAwaiter().OnCompleted($OnCompletedScriptBlock)
            }
            else {
                Write-LogOutput -Message "WebView2 is not loaded yet." -LogType DEBUG
            }
        }
        else {
            Write-LogOutput -Message "WebView2 is not initialized." -LogType ERROR
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Invoke-ExecuteScriptWithResultAsync {
    PARAM(
        $ScriptToExecute,
        $OnCompletedScriptBlock
    )
    try {

        if ($null -ne $Script:Webview.Object) {
            if ($Script:Webview.Object.IsLoaded) {
                $Script:Task = $Script:Webview.Object.CoreWebView2.ExecuteScriptWithResultAsync($ScriptToExecute)

                $Script:Task.GetAwaiter().OnCompleted($OnCompletedScriptBlock)
            }
            else {
                Write-LogOutput -Message "WebView2 is not loaded yet." -LogType DEBUG
            }
        }
        else {
            Write-LogOutput -Message "WebView2 is not initialized." -LogType ERROR
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Invoke-LogWindowScrollToEnd {

    try {






        return $true
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Invoke-OmadaPSWebRequestWrapper {
    try {
        try {
            $Private:Parameters = $Script:RunTimeData.RestMethodParam
            $Private:Parameters.AuthenticationType = $($Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem.Content)
            if ($Null -eq $Private:Parameters.Body) {
                if ($Private:Parameters.ContainsKey("Body")) {
                    $Private:Parameters.Remove("Body")
                }
            }
            else {
                if (!$Private:Parameters.ContainsKey("Body")) {
                    $Private:Parameters.Add("Body", $Null)
                }

                $Private:Parameters.Body = $Private:Parameters.Body | ConvertTo-Json
            }
            "Parameters: {0}" -f ($Private:Parameters | ConvertTo-Json -Depth 15) | Write-LogOutput -LogType VERBOSE
            $Private:Result = Invoke-OmadaRestMethod @Parameters
            if($null -ne $Script:MainWindowForm -and $null -ne $Script:MainWindowForm.Definitions -and $Script:MainWindowForm.Definitions.IsVisible){
                $Script:MainWindowForm.Definitions.TextBlockConnectionStatus | Set-TextBlockText -Text "Connected"
            }
            "Result: {0}" -f ($Private:Result | ConvertTo-Json -Depth 15) | Write-LogOutput -LogType VERBOSE
            return $Private:Result
        }
        catch {
            if (![string]::IsNullOrWhiteSpace($_.ErrorDetails?.Message) -and $_.ErrorDetails.Message -like "*Resource not found for the segment 'C_P_SQLTROUBLESHOOTING'*") {
                $Message = "OData Endpoint for SQL Troubleshooting not enabled at tenant {0}.`n`r`n`rError returned by Omada:`n`r`n`r{1}" -f [system.uri]::New($Script:AppConfig.BaseUrl).Host, $_.ErrorDetails.Message
                $Message | Write-LogOutput -LogType ERROR
                if($null -ne $Script:MainWindowForm -and $null -ne $Script:MainWindowForm.Definitions -and $Script:MainWindowForm.Definitions.IsVisible){
                    $Script:MainWindowForm.Definitions.TextBlockConnectionStatus | Set-TextBlockText -Text "Disconnected"
                }
                else{
                    Throw $_
                }
            }
            else{
                Throw $_
            }
        }
    }
    catch {
        Throw $_
    }
}
function Invoke-OnTreeViewItemShiftClick {
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', 'Sender', Justification = 'The use of the variable is on purpose')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', 'Args', Justification = 'The use of the variable is on purpose')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Args', Justification = 'The variable is declared because the call contains the parameter')]
    PARAM (
        $Sender,
        $Args
    )

    try {
        "Left shift {0}, Right shift {1}" -f [System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::LeftShift), [System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::RightShift) | Write-LogOutput -LogType VERBOSE

        if ([System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::LeftShift) -or
            [System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::RightShift)) {

            if ($Sender.SelectedItem.IsSelected) {

                $ItemValue = $Sender.SelectedValue.Header.ToString()
                [System.Windows.Clipboard]::SetText($ItemValue)
                "Copied to clipboard: {0}" -f $ItemValue | Write-LogOutput -LogType DEBUG
                if ($null -eq $Script:PreviousLevel) {
                    $Script:PreviousLevel = -1
                }

                switch (Get-TreeviewItemLevel -TreeViewItem $Sender.SelectedItem) {
                    "0" {
                        "Tree view level: {0}, previous: {1}" -f $_, $Script:PreviousLevel | Write-LogOutput -LogType VERBOSE
                        $ItemValue = "{0}." -f $ItemValue.Trim()

                        $Script:PreviousLevel = $_

                    }
                    "1" {
                        "Tree view level: {0}, previous: {1}" -f $_, $Script:PreviousLevel | Write-LogOutput -LogType VERBOSE
                        if ($Script:PreviousLevel -eq 0) {
                            $ItemValue = "{0}" -f $ItemValue.Trim()
                        }
                        else {
                            $ItemValue = " {0}" -f $ItemValue.Trim()
                        }
                        $Script:PreviousLevel = $_
                    }
                    "2" {
                        "Tree view level: {0}, previous: {1}" -f $_, $Script:PreviousLevel | Write-LogOutput -LogType VERBOSE
                        if ($Script:PreviousLevel -eq 1) {
                            $ItemValue = ".{0}" -f ($ItemValue.Trim().Split(" ")[0])
                        }
                        else {
                            $ItemValue = " {0}," -f ($ItemValue.Trim().Split(" ")[0])
                        }
                        $Script:PreviousLevel = $_
                    }
                    default {
                        "Tree view level: {0}" -f $_ | Write-LogOutput -LogType VERBOSE
                    }
                }

                $ScriptToExecute = "try {{
editor.focus();
const position = editor.getPosition();
const range = new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column);
console.log('Range:', range);
editor.executeEdits('', [{{ range, text: '{0}', forceMoveMarkers: true }}]);
console.log('Edit executed successfully');
}} catch (error) {{
console.error('Edit failed:', error);
}}"
 -f $ItemValue

                $Script:SenderTest = $Sender
                "Execute script in in Monaco Editor:`r`n{0}" -f $ScriptToExecute | Write-LogOutput -LogType DEBUG
                $OnCompletedScriptBlock = {
                    try {
                        if (!$Script:Task.Status -eq "RanToCompletion") {
                            "Monaco Editor Task failed: {0}" -f $Script:Task.Status | Write-LogOutput -LogType ERROR
                        }
                        else {
                            "Monaco Editor Task completed successfully: {0}" -f $Script:Task.Result | Write-LogOutput -LogType DEBUG
                        }
                    }
                    catch {
                        $Script:Task.Exception.Message | Write-LogOutput -LogType ERROR
                    }
                    if ($null -ne $Script:SenderTest.SelectedItem) {
                        $Script:SenderTest.SelectedItem.IsSelected = $false
                        $Script:MainWindowForm.Definition.Focus()
                        $Script:Webview.Object.Focus()
                    }
                }

                "Set value in Monaco editor." | Write-LogOutput -LogType DEBUG
                Invoke-ExecuteScriptWithResultAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock
            }
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
Function Invoke-SanitizeJsonKeys {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$JsonString
    )

    $ParsedJson = $JsonString | ConvertFrom-Json -ErrorAction Stop -AsHashtable

    $SanitizedObject = Invoke-SanitizeObject -Data $ParsedJson

    return $SanitizedObject | ConvertTo-Json -Depth 10
}
Function Invoke-SanitizeObject {
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [object]$Data
    )

    if ($Data -is [hashtable]) {
        $NewData = @{}
        foreach ($Key in $Data.Keys) {
            $NewKey = $Key -replace '[^A-Za-z0-9_\-]', $ReplacementChar
            if ($Data[$Key] -is [hashtable]) {
                $NewData[$NewKey] = Invoke-SanitizeObject -Data $Data[$Key]
            }
            elseif ($Data[$Key] -is [array]) {
                $NewData[$NewKey] = $Data[$Key] | ForEach-Object { Invoke-SanitizeObject -Data $_ }
            }
            else {
                $NewData[$NewKey] = $Data[$Key]
            }
        }
        return $NewData
    }
    elseif ($Data -is [array]) {
        return $Data | ForEach-Object { Invoke-SanitizeObject -Data $_ }
    }
    else {
        return $Data
    }
}
function Invoke-SaveAndExecuteQuery {


    try {

        if(!(Test-ConnectionRequirements)){
            "Connection not ready" | Write-LogOutput -LogType DEBUG
            return
        }

        $ScriptToExecute = "editor.getValue();"

        $OnCompletedScriptBlock = {
            try {
                if ($Script:Task.Status -eq "RanToCompletion") {
                    $Script:RunTimeData.QueryText = $Script:Task.Result
                    if (![string]::IsNullOrWhiteSpace($Script:RunTimeData.QueryText.ResultAsJson)) {
                        $Script:RunTimeData.QueryText = $Script:RunTimeData.QueryText.ResultAsJson | ConvertFrom-Json
                    }

                    $Private:Result = Get-SqlQueryObject

                    "Executing SQL Query: {0}" -f $Script:RunTimeData.QueryText | Write-LogOutput -LogType DEBUG
                    $Script:RunTimeData.RestMethodParam.Body = @{}
                    $Script:RunTimeData.RestMethodParam.Uri = "{0}/odata/dataobjects/C_P_SQLTROUBLESHOOTING({1})" -f $Script:AppConfig.BaseUrl, $Script:AppConfig.CurrentSqlQuery.DoId
                    if ($Script:RunTimeData.CurrentQueryText -ne $Script:RunTimeData.QueryText -or $Script:RunTimeData.QueryText -ne $Private:Result.C_QUERY) {
                        "Update current query for DODI: {0}" -f $Script:AppConfig.CurrentSqlQuery.DoId | Write-LogOutput -LogType DEBUG
                        $Script:RunTimeData.RestMethodParam.Body.Add("C_QUERY", $Script:RunTimeData.QueryText)
                        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.CurrentDataConnection.DoId)) {
                            $Script:RunTimeData.RestMethodParam.Body.Add("C_SQLTROUBLESHOOTING_DATACONNECTION", @{Id = $Script:AppConfig.CurrentDataConnection.DoId })
                        }
                    }
                    if ($Script:RunTimeData.CurrentSqlQuery.DisplayName -ne $Script:MainWindowForm.Elements.TextBoxDisplayName.Text) {
                        $Script:RunTimeData.RestMethodParam.Body.Add("NAME", $Script:MainWindowForm.Elements.TextBoxDisplayName.Text)
                    }
                    if (($Script:RunTimeData.RestMethodParam.Body.Keys | Measure-Object).Count -le 0) {
                        "No changes detected! Just run query" | Write-LogOutput -LogType DEBUG
                    }
                    else {
                        "Body: {0}" -f ($Script:RunTimeData.RestMethodParam.Body | ConvertTo-Json) | Write-LogOutput -LogType VERBOSE
                        "QueryUrl: {0}" -f $Script:RunTimeData.RestMethodParam.Uri | Write-LogOutput -LogType DEBUG

                        "Save query" | Write-LogOutput
                        $Script:RunTimeData.RestMethodParam.Method = "PUT"
                        $Private:Result = Invoke-OmadaPSWebRequestWrapper
                        "Query saved!" | Write-LogOutput
                    }
                    $Script:RunTimeData.RestMethodParam.Uri = "{0}/webservice/jQGridPopulationWebService.asmx/GetPagingData" -f $Script:AppConfig.BaseUrl

                    $Script:RunTimeData.RestMethodParam.Body = @{
                        "dataType"     = "SqlDataProducer"
                        "dataTypeArgs" = @{
                            "targetId" = $Script:AppConfig.CurrentSqlQuery.DoId
                        }
                        "page"         = 1
                        "rows"         = 100000
                        "sidx"         = $Null
                        "sord"         = "asc"
                        "_search"      = $False
                        "searchField"  = $Null
                        "searchString" = $Null
                        "filters"      = $Null
                        "searchOper"   = $Null
                    }
                    "Body: {0}" -f ($Script:RunTimeData.RestMethodParam.Body | ConvertTo-Json) | Write-LogOutput -LogType VERBOSE
                    "QueryUrl: {0}" -f $Script:RunTimeData.RestMethodParam.Uri | Write-LogOutput -LogType DEBUG

                    "Retrieve query output, please wait..." | Write-LogOutput
                    $Script:RunTimeData.RestMethodParam.Method = "POST"
                    $Script:RunTimeData.QueryResult = $null
                    $Script:RunTimeData.QueryResult = Invoke-OmadaPSWebRequestWrapper


                    if ($null -ne $Script:RunTimeData.QueryResult -and ($Script:RunTimeData.QueryResult.d.Rows | Measure-Object).Count -le 0) {
                        "Query did not return any results!" | Write-LogOutput -LogType WARNING
                        $Script:MainWindowForm.Elements.TextBlockRows | Set-TextBlockText -Text "0 rows"
                    }
                    else {
                        $Script:MainWindowForm.Elements.DataGridQueryResult.AutoGenerateColumns = $true
                        try{
                            $Script:MainWindowForm.Elements.DataGridQueryResult.ItemsSource = @($Script:RunTimeData.QueryResult.d.Rows)
                        }
                        catch{
                            $Script:MainWindowForm.Elements.DataGridQueryResult.ItemsSource = @(($Script:RunTimeData.QueryResult | ConvertTo-Json -Depth 10 | Invoke-SanitizeJsonKeys | ConvertFrom-Json -Depth 10).d.Rows)
                        }
                        "Result:`r`n{0}" -f ($Script:RunTimeData.QueryResult.d.rows | Format-Table -AutoSize | Out-String -Width 10000000 ) | Write-LogOutput
                        $Script:MainWindowForm.Elements.ButtonShowOutput.IsEnabled = $True
                        $Script:MainWindowForm.Elements.ButtonSaveOutputFile.IsEnabled = $True
                        "{0} record(s) retrieved!" -f $Script:RunTimeData.QueryResult.d.Records | Write-LogOutput

                        $Script:MainWindowForm.Elements.TextBlockRows | Set-TextBlockText -Text ("{0:n0} rows" -f [Int]$Script:RunTimeData.QueryResult.d.Records)
                        $Private:Result.Id,$Private:Result.DisplayName | Invoke-ConfigSetting -Property "CurrentSqlQuery"
                        if ($Private:Result.DisplayName -ne $Script:RunTimeData.CurrentSqlQuery.DisplayName) {
                            "New display name, Current: {0}, New: {1}" -f $Script:RunTimeData.CurrentSqlQuery.DisplayName, $Private:Result.DisplayName | Write-LogOutput -LogType DEBUG
                            "Force update query list" | Write-LogOutput -LogType DEBUG
                            Update-QueryList -ForceRefresh
                            $ComboBoxSelectQueryItem = $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items | Where-Object { $_.Content -eq $Script:AppConfig.CurrentSqlQuery.FullName }
                            if ($null -ne $ComboBoxSelectQueryItem) {
                                $ComboBoxSelectQueryItem = New-Object System.Windows.Controls.ComboBoxItem
                                $ComboBoxSelectQueryItem.Content = $Script:AppConfig.CurrentSqlQuery.FullName
                                $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items.Add($ComboBoxSelectQueryItem) | Out-Null
                            }
                            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem = $ComboBoxSelectQueryItem
                        }
                    }
                }
                elseif ($Script:Task.Status -eq "Faulted") {
                    "Task failed: {0}" -f $Script:Task.Status | Write-LogOutput -LogType ERROR
                }
                else {
                    "Task result: {0}" -f $Script:Task.Status | Write-LogOutput -LogType DEBUG
                }
                $Script:MainWindowForm.Elements.ButtonSaveQuery.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonExecuteQuery.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonExecuteQuery | Set-ButtonContent -Content "_Execute Query"
                if($null -ne $Script:PopupWindowExecuteQuery) {
                    $Script:PopupWindowExecuteQuery.Close()
                }

                if ($null -ne $Script:RunTimeData.StopWatch) {
                    $Script:RunTimeData.StopWatch.Stop()
                    "Elapsed time: {0}" -f $Script:RunTimeData.StopWatch.Elapsed.ToString() | Write-LogOutput -Debug
                    $Script:MainWindowForm.Elements.TextBlockQueryTime.Text = $Script:RunTimeData.StopWatch.Elapsed.ToString()
                }
            }
            catch {
                $Script:MainWindowForm.Elements.ButtonSaveQuery.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonExecuteQuery.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonExecuteQuery | Set-ButtonContent -Content "_Execute Query"
                if($null -ne $Script:PopupWindowExecuteQuery) {
                    $Script:PopupWindowExecuteQuery.Close()
                }
                $_.Exception.Message | Write-LogOutput -LogType ERROR
            }
        }
        Invoke-ExecuteScriptWithResultAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock
    }
    catch {
        if ($null -ne $Script:RunTimeData.StopWatch) {
            $Script:RunTimeData.StopWatch.Stop()
            $Script:MainWindowForm.Elements.TextBlockQueryTime.Text = $Script:RunTimeData.StopWatch.Elapsed.ToString()
        }
        $Script:MainWindowForm.Elements.ButtonSaveQuery.IsEnabled = $True
        $Script:MainWindowForm.Elements.ButtonExecuteQuery.IsEnabled = $True
        $Script:MainWindowForm.Elements.ButtonExecuteQuery | Set-ButtonContent -Content "_Execute Query"
        if($null -ne $Script:PopupWindowExecuteQuery) {
            $Script:PopupWindowExecuteQuery.Close()
        }

        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }

}
function Invoke-SaveQuery {
    PARAM(
        [switch]$NewQuery
    )

    try {
        $ScriptToExecute = "editor.getValue();"
        $Script:NewQuery = $NewQuery
        $OnCompletedScriptBlock = {
            try {
                if ($Script:Task.Status -eq "RanToCompletion") {
                    $Script:RunTimeData.QueryText = $Script:Task.Result
                    if (![string]::IsNullOrWhiteSpace($Script:RunTimeData.QueryText.ResultAsJson)) {
                        $Script:RunTimeData.QueryText = $Script:RunTimeData.QueryText.ResultAsJson | ConvertFrom-Json
                    }

                    if ($Script:NewQuery) {
                        "Create new query" | Write-LogOutput -LogType DEBUG

                        $Script:RunTimeData.RestMethodParam.Uri = '{0}/odata/dataobjects/C_P_SQLTROUBLESHOOTING?$filter=Deleted ne true and NAME eq ''{1}''' -f $Script:AppConfig.BaseUrl, $Script:MainWindowForm.Elements.TextBoxDisplayName.Text
                        "QueryUrl: {0}" -f $Script:RunTimeData.RestMethodParam.Uri | Write-LogOutput -LogType DEBUG
                        "Check if a query with this name already exists" | Write-LogOutput -LogType DEBUG
                        $Script:RunTimeData.RestMethodParam.Body = $Null
                        $Script:RunTimeData.RestMethodParam.Method = "GET"
                        $Script:RunTimeData.RestMethodParam.Body = $null
                        $CheckIfExistResult = Invoke-OmadaPSWebRequestWrapper
                        if ($null -eq $CheckIfExistResult -or ($CheckIfExistResult.Value | Measure-Object).Count -le 0) {
                            $Script:RunTimeData.RestMethodParam.Uri = "{0}/odata/dataobjects/C_P_SQLTROUBLESHOOTING" -f $Script:AppConfig.BaseUrl
                            $Script:RunTimeData.RestMethodParam.Method = "POST"
                        }
                        else {
                            $Script:MainWindowForm.Elements.ButtonSaveQuery.IsEnabled = $True
                            $Script:MainWindowForm.Elements.ButtonExecuteQuery.IsEnabled = $True
                            "Query with this name already exists!" | Write-LogOutput -LogType ERROR
                            return
                        }
                    }
                    else {
                        "Save existing query" | Write-LogOutput -LogType DEBUG
                        $Script:RunTimeData.RestMethodParam.Uri = "{0}/odata/dataobjects/C_P_SQLTROUBLESHOOTING({1})" -f $Script:AppConfig.BaseUrl, $Script:AppConfig.CurrentSqlQuery.DoId
                        $private:Result = Get-SqlQueryObject
                        $Script:RunTimeData.RestMethodParam.Method = "PUT"
                    }
                    $Script:RunTimeData.RestMethodParam.Body = @{}
                    if ($Script:NewQuery -or ($Script:RunTimeData.CurrentQueryText -ne $Script:RunTimeData.QueryText -or $Script:RunTimeData.QueryText -ne $private:Result.C_QUERY)) {
                        $Script:RunTimeData.RestMethodParam.Body.Add("C_QUERY", $Script:RunTimeData.QueryText)
                        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.CurrentDataConnection.DoId)) {
                            $Script:RunTimeData.RestMethodParam.Body.Add("C_SQLTROUBLESHOOTING_DATACONNECTION", @{Id = $Script:AppConfig.CurrentDataConnection.DoId })
                        }
                    }
                    if ($Script:RunTimeData.CurrentSqlQuery.DisplayName -ne $Script:MainWindowForm.Elements.TextBoxDisplayName.Text) {
                        $Script:RunTimeData.RestMethodParam.Body.Add("NAME", $Script:MainWindowForm.Elements.TextBoxDisplayName.Text)
                    }
                    if (!$Script:NewQuery -and ($Script:RunTimeData.RestMethodParam.Body.Keys | Measure-Object).Count -le 0) {
                        "No changes detected! Saving not needed." | Write-LogOutput -LogType DEBUG
                    }
                    else {
                        "Saving SQL Query: {0}" -f $Script:RunTimeData.QueryText | Write-LogOutput -LogType DEBUG
                        "Body: {0}" -f ($Script:RunTimeData.RestMethodParam.Body | ConvertTo-Json) | Write-LogOutput -LogType VERBOSE
                        "QueryUrl: {0}" -f $Script:RunTimeData.RestMethodParam.Uri | Write-LogOutput -LogType DEBUG

                        "Save query" | Write-LogOutput
                        $private:Result = Invoke-OmadaPSWebRequestWrapper

                        if ($null -ne $private:Result -and $Script:NewQuery -or $private:Result.DisplayName -ne $Script:RunTimeData.CurrentSqlQuery.DisplayName) {
                            "Query saved!" | Write-LogOutput
                            if ($Script:NewQuery) {
                                $Script:RunTimeData.CurrentSqlQuery.DoId = $private:Result.Id
                                $Script:RunTimeData.CurrentSqlQuery.DisplayName = $private:Result.Name
                                $private:Result.Id, $private:Result.Name | Invoke-ConfigSetting -Property "CurrentSqlQuery"
                                $ComboBoxSelectQueryItem = New-Object System.Windows.Controls.ComboBoxItem
                                $ComboBoxSelectQueryItem.Content = $Script:RunTimeData.CurrentSqlQuery.DoId
                                $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items.Add($ComboBoxSelectQueryItem) | Out-Null
                            }
                            else {
                                "New display name, Current: {0}, New: {1}" -f $Script:RunTimeData.CurrentSqlQuery.DisplayName, $private:Result.DisplayName | Write-LogOutput -LogType VERBOSE
                                "Force update query list" | Write-LogOutput -LogType DEBUG
                                Update-QueryList -ForceRefresh
                                $Script:RunTimeData.CurrentSqlQuery.DoId = $Script:AppConfig.CurrentSqlQuery.DoId
                                $ComboBoxSelectQueryItem = $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items | Where-Object { $_.Content -eq $Script:RunTimeData.CurrentSqlQuery.DoId }
                                if ($null -eq $ComboBoxSelectQueryItem) {
                                    $ComboBoxSelectQueryItem = New-Object System.Windows.Controls.ComboBoxItem
                                    $ComboBoxSelectQueryItem.Content = $Script:RunTimeData.CurrentSqlQuery.DoId
                                    $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items.Add($ComboBoxSelectQueryItem) | Out-Null
                                }
                            }
                            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem = $ComboBoxSelectQueryItem
                            $Script:RunTimeData.CurrentSqlQuery.DisplayName = $private:Result.DisplayName
                        }
                    }
                }
                elseif ($Script:Task.Status -eq "Faulted") {
                    "Task failed: {0}" -f $Script:Task.Status | Write-LogOutput -LogType ERROR
                }
                else {
                    "Task result: {0}" -f $Script:Task.Status | Write-LogOutput -LogType DEBUG
                }
                $Script:MainWindowForm.Elements.ButtonSaveQuery.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonExecuteQuery.IsEnabled = $True
            }
            catch {
                $Script:Task.Exception.Message | Write-LogOutput -LogType ERROR
            }
        }
        Invoke-ExecuteScriptWithResultAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }

}
function New-FormObject {
    PARAM (
        [parameter(Mandatory = $False)]
        [validateScript({ Test-Path $_ -PathType Leaf })]
        $FormPath,
        [parameter(Mandatory = $False)]
        $Xaml,
        [parameter(Mandatory = $False)]
        $ParentForm
    )
    try {
        if ($null -eq $FormPath -and $null -eq $Xaml) {
            "Either FormPath or Xaml must be provided!" | Write-LogOutput -LogType ERROR
            break
        }

        if ($null -ne $FormPath) {
            [xml]$Xaml = Get-Content $FormPath -Raw
        }

        $NamespaceManager = New-Object System.Xml.XmlNamespaceManager($Xaml.NameTable)
        $NamespaceManager.AddNamespace("default", "http://schemas.microsoft.com/winfx/2006/xaml/presentation")
        $NamespaceManager.AddNamespace("x", "http://schemas.microsoft.com/winfx/2006/xaml")
        $NamespaceManager.AddNamespace("Wpf", "clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf")

        $Reader = (New-Object System.Xml.XmlNodeReader $Xaml)
        $Form = [Windows.Markup.XamlReader]::Load($Reader)
        "Create form: {0}" -f $Form.Name | Write-LogOutput -LogType DEBUG
        $Form.Icon = Get-Icon -Type Wpf

        $Elements = @()
        $ElementNames = @("ComboBox", "Label", "TextBox", "Button", "CheckBox", "RadioButton", "PasswordBox", "ComboBoxItem", "WebView2", "DataGrid", "TextBlock", "TreeViewSqlSchema")
        foreach ($ElementName in $ElementNames) {
            "Find element type: {0}" -f $ElementName | Write-LogOutput -LogType DEBUG
            $Xaml.DocumentElement.SelectNodes("//default:$ElementName", $NamespaceManager) | ForEach-Object {
                $_.Name | Select-Object -Unique | ForEach-Object {
                    if (![string]::IsNullOrWhiteSpace($_) -and $null -ne $Form.FindName($_)) {
                        "Add element type: {0}" -f $_ | Write-LogOutput -LogType DEBUG
                        $Elements += @{
                            "$_" = $Form.FindName($_)
                        }
                    }
                }
            }
        }

        if ($null -ne $ParentForm) {
            "Parent form: {0}" -f $ParentForm.Name | Write-LogOutput -LogType DEBUG
            $Form.Owner = $ParentForm
            "Form Height: {0}" -f $Form.Height | Write-LogOutput -LogType DEBUG
            "Parent form Height: {0}" -f $ParentForm.Height | Write-LogOutput -LogType DEBUG
            if([double]::IsNaN($Form.Height)){
                $Form.Height = $ParentForm.Height
            }
            else{
                $Form.Height = [math]::Max($Form.Height, $ParentForm.Height)
            }
            if($Form.Width -eq "NaN"){
                $Form.Width = $Form.MinWidth
            }
        }

        "Form Dimensions: {0}x{1}" -f  $Form.Width,$Form.Height | Write-LogOutput -LogType DEBUG
        "Form Location: {0}x{1}" -f $Form.Left, $Form.Top | Write-LogOutput -LogType DEBUG

        "Return form object for: {0}" -f $Form.Name | Write-LogOutput -LogType DEBUG

        return [PSCustomObject]@{
            Definition      = $Form
            Elements        = $Elements
            Xaml            = $Xaml
            Position        = [PSCustomObject]@{
                Left = $null
                Top  = $null
            }
            Size            = [PSCustomObject]@{
                Width  = $Form.MinWidth
                Height = $Form.MinHeight
            }
            State           = "NotOpenend"
            PositionManager = @{
                Synchronizing       = $false
                PositionOffSetLeft  = 0
                PositionOffSetRight = 0
                PositionOffSetTop   = 0
                MainWindowRight     = 0
                MainWindowBottom    = 0
                ChildWindowLeft     = 0
                ChildWindowRight    = 0
                ChildWindowBottom   = 0
                LastPositionChange  = Get-Date
            }
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Open-LogWindow {
    try {
        "Opening Log window" | Write-LogOutput -LogType DEBUG
        $Script:LogWindowForm = New-FormObject -FormPath (Join-Path $Script:RunTimeConfig.ModuleFolder -ChildPath "lib\ui\LogWindow.xaml") -ParentForm $Script:MainWindowForm.Definition
        [Int]$Script:LogWindowForm.PositionManager.PositionOffSetLeft = 1200

        $true | Invoke-ConfigSetting -Property "LogWindowFormOpen"

        $Script:LogWindowForm.Definition.ShowInTaskbar = $false
        $Script:TextBoxLog = $Script:LogWindowForm.Definition.FindName("TextBoxLog")
        if ($Script:AppConfig.LogWindowWordWrap) {
            $Script:TextBoxLog.TextWrapping = "WrapWithOverflow"
            $Script:LogWindowForm.Elements.CheckboxWordWrap.IsChecked = $true
            "Word wrap is enabled" | Write-LogOutput -LogType LOG
            $true | Invoke-ConfigSetting -Property "LogWindowWordWrap"
        }
        else {
            $Script:TextBoxLog.TextWrapping = "NoWrap"
            $Script:LogWindowForm.Elements.CheckboxWordWrap.IsChecked = $false
            $false | Invoke-ConfigSetting -Property "LogWindowWordWrap"
        }
        if ($Script:RunTimeConfig.LogToConsole) {
            $Script:LogWindowForm.Elements.CheckboxConsoleLog.IsChecked = $true
            "Console logging is enabled" | Write-LogOutput -LogType LOG
            $true | Invoke-ConfigSetting -Property "CheckboxConsoleLog"
        }
        else {
            $Script:LogWindowForm.Elements.CheckboxConsoleLog.IsChecked = $false
            $false | Invoke-ConfigSetting -Property "CheckboxConsoleLog"
        }

        if (![string]::IsNullOrWhiteSpace($Script:AppConfig.LogLevel)) {
            "Set window log level to: {0}" -f $Script:AppConfig.LogLevel | Write-LogOutput -LogType DEBUG
            if (($LogWindowForm.Elements.ComboBoxSelectLogLevel.Items | Measure-Object).count -le 0 -and !$LogWindowForm.Elements.ComboBoxSelectLogLevel.Items.Content.Contains($Script:AppConfig.LogLevel)) {
                $ComboBoxSelectLogLevelItem = New-Object System.Windows.Controls.ComboBoxItem
                $ComboBoxSelectLogLevelItem.Content = $Script:AppConfig.LogLevel
                $LogWindowForm.Elements.ComboBoxSelectLogLevel.Items.Add($ComboBoxSelectLogLevelItem) | Out-Null
            }
            $LogWindowForm.Elements.ComboBoxSelectLogLevel.SelectedValue = $LogWindowForm.Elements.ComboBoxSelectLogLevel.Items | Where-Object { $_.Content -eq $Script:AppConfig.LogLevel }
            $Script:RunTimeConfig.Logging.LogLevelSetting = $Script:AppConfig.LogLevel
        }
        else {
            "Set window log level to default because it was not set: INFO" | Write-LogOutput -LogType DEBUG
            if (($LogWindowForm.Elements.ComboBoxSelectLogLevel.Items | Measure-Object).count -le 0 -and !$LogWindowForm.Elements.ComboBoxSelectLogLevel.Items.Content.Contains("INFO")) {
                $ComboBoxSelectLogLevelItem = New-Object System.Windows.Controls.ComboBoxItem
                $ComboBoxSelectLogLevelItem.Content = "INFO"
                $LogWindowForm.Elements.ComboBoxSelectLogLevel.Items.Add($ComboBoxSelectLogLevelItem) | Out-Null
            }
            $LogWindowForm.Elements.ComboBoxSelectLogLevel.SelectedValue = $LogWindowForm.Elements.ComboBoxSelectLogLevel.Items | Where-Object { $_.Content -eq "INFO" }
            $Script:RunTimeConfig.Logging.LogLevelSetting = $LogWindowForm.Elements.ComboBoxSelectLogLevel.SelectedValue.Content
        }


        $Script:LogWindowForm.Definition.Add_LocationChanged({
                $_ | Show-EventInfo -LogType VERBOSE2
                if (!$Script:LogWindowForm.PositionManager.Synchronizing) {
                    $Script:LogWindowForm.PositionManager.Synchronizing = $true
                    "MainWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:MainWindowForm.Definition.Left, $Script:MainWindowForm.Definition.Top, $Script:MainWindowForm.Definition.Width , $Script:MainWindowForm.Definition.Height | Write-LogOutput -LogType VERBOSE2
                    "LogWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:LogWindowForm.Definition.Left, $Script:LogWindowForm.Definition.Top, $Script:LogWindowForm.Definition.Width , $Script:LogWindowForm.Definition.Height | Write-LogOutput -LogType VERBOSE2
                    $Script:LogWindowForm.Definition.Dispatcher.Invoke({
                            $_ | Show-EventInfo -LogType VERBOSE2
                            $Script:LogWindowForm.PositionManager.PositionOffSetLeft = [Int]::Abs($Script:LogWindowForm.Definition.Left) - [Int]::Abs($Script:MainWindowForm.Definition.Left)
                            "PositionManagerLogWindow PositionOffSetLeft: {0}" -f $Script:LogWindowForm.PositionManager.PositionOffSetLeft | Write-LogOutput -LogType VERBOSE2
                            $Script:LogWindowForm.PositionManager.PositionOffSetTop = [Int]::Abs($Script:LogWindowForm.Definition.Top) - [Int]::Abs($Script:MainWindowForm.Definition.Top)
                            "PositionManagerLogWindow PositionOffSetTop: {0}" -f $Script:LogWindowForm.PositionManager.PositionOffSetTop | Write-LogOutput -LogType VERBOSE2
                            $Script:LogWindowForm.PositionManager.Synchronizing = $false
                        }, [System.Windows.Threading.DispatcherPriority]::Render)
                }
            })

        $Script:LogWindowForm.Definition.Add_SizeChanged({
                $_ | Show-EventInfo -LogType VERBOSE2
                $Script:LogWindowForm.Size = $Script:LogWindowForm.Definition | Get-WindowSize
            })


        if ($null -ne ($Script:LogWindowForm.Definition | Get-WindowPositionConfig)) {
            $Position = $Script:LogWindowForm.Definition | Get-WindowPositionConfig
            "Log window position: {0}" -f $Position | Write-LogOutput -LogType DEBUG
            $Script:LogWindowForm.Definition.Left = [Int]::Abs($Position.Split("x")[0])
            $Script:LogWindowForm.Definition.Top = [Int]::Abs($Position.Split("x")[1])
        }

        $Script:LogWindowForm.Definition.Add_Loaded({
                $_ | Show-EventInfo
                $Script:LogWindowForm.PositionManager.Synchronizing = $true
                $Script:LogWindowForm.Definition.Dispatcher.Invoke({
                        "MainWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:MainWindowForm.Definition.Left, $Script:MainWindowForm.Definition.Top, $Script:MainWindowForm.Definition.Width , $Script:MainWindowForm.Definition.Height | Write-LogOutput -LogType DEBUG
                        $Script:LogWindowForm.Definition.Top = [Int]::Abs($Script:MainWindowForm.Definition.Top)
                        "LogWindowForm Top: {0}" -f $Script:LogWindowForm.Definition.Top | Write-LogOutput -LogType DEBUG
                        $Script:LogWindowForm.Definition.Left = [Int]::Abs($Script:MainWindowForm.Definition.Left) + [Int]::Abs($Script:MainWindowForm.Definition.Width)
                        "LogWindowForm Left: {0}" -f $Script:LogWindowForm.Definition.Left | Write-LogOutput -LogType DEBUG
                        $Script:LogWindowForm.PositionManager.PositionOffSetLeft = [Int]::Abs($Script:LogWindowForm.Definition.Left) - [Int]::Abs($Script:MainWindowForm.Definition.Left)
                        "PositionManagerLogWindow PositionOffSetLeft: {0}" -f $Script:LogWindowForm.PositionManager.PositionOffSetLeft | Write-LogOutput -LogType DEBUG
                        $Script:LogWindowForm.PositionManager.PositionOffSetTop = [Int]::Abs($Script:LogWindowForm.Definition.Top) - [Int]::Abs($Script:MainWindowForm.Definition.Top)
                        "PositionManagerLogWindow PositionOffSetTop: {0}" -f $Script:LogWindowForm.PositionManager.PositionOffSetTop | Write-LogOutput -LogType DEBUG
                        if ($null -ne ($Script:LogWindowForm.Definition | Get-WindowSizeConfig)) {
                            $Size = $Script:LogWindowForm.Definition | Get-WindowSizeConfig
                            "Log window size: {0}" -f $Size | Write-LogOutput -LogType DEBUG
                            $Script:LogWindowForm.Definition.Width = [Int]::Abs($Size.Split("x")[0])
                            "LogWindowForm Width: {0}" -f $Script:LogWindowForm.Definition.Width | Write-LogOutput -LogType DEBUG
                            $Script:LogWindowForm.Definition.Height = [Int]::Abs($Size.Split("x")[1])
                            "LogWindowForm Height: {0}" -f $Script:LogWindowForm.Definition.Height | Write-LogOutput -LogType DEBUG
                        }
                        $Script:LogWindowForm.PositionManager.Synchronizing = $false
                    }, [System.Windows.Threading.DispatcherPriority]::Render)
                $Script:MainWindowForm.Elements.ButtonShowLog | Set-ButtonContent -Content "_Hide Log"
                $Script:TextBoxLog.Text = $Script:RunTimeConfig.Logging.AppLogObject
                $Script:LogWindowForm.PositionManager.PositionOffSetLeft = [Int]::Abs($Script:LogWindowForm.Definition.Left) - [Int]::Abs($Script:MainWindowForm.Definition.Left)
                "PositionManagerLogWindow PositionOffSetLeft: {0}" -f $Script:LogWindowForm.PositionManager.PositionOffSetLeft | Write-LogOutput -LogType DEBUG
                $Script:LogWindowForm.PositionManager.PositionOffSetTop = [Int]::Abs($Script:LogWindowForm.Definition.Top) - [Int]::Abs($Script:MainWindowForm.Definition.Top)
                "PositionManagerLogWindow PositionOffSetTop: {0}" -f $Script:LogWindowForm.PositionManager.PositionOffSetTop | Write-LogOutput -LogType DEBUG
                "LogWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:LogWindowForm.Definition.Left, $Script:LogWindowForm.Definition.Top, $Script:LogWindowForm.Definition.Width , $Script:LogWindowForm.Definition.Height | Write-LogOutput -LogType DEBUG
                $Script:LogWindowForm.State = "Open"
            })

        $Script:LogWindowForm.Definition.Add_Closing({
                $_ | Show-EventInfo
                $Script:LogWindowForm.State = "Closing"
                Save-WindowMeasurements
                if ($Script:MainWindowForm.State -eq "Open") {
                    $false | Invoke-ConfigSetting -Property "LogWindowFormOpen"
                }
            })

        $Script:LogWindowForm.Definition.Add_Closed({
                $_ | Show-EventInfo
                $Script:LogWindowForm.State = "Closed"
                $Script:MainWindowForm.Elements.ButtonShowLog | Set-ButtonContent -Content "Log"
            })

        $Script:LogWindowForm.Elements.ButtonClearLog.Add_Click({
                $_ | Show-EventInfo
                "Clear TextBoxLog" | Write-LogOutput -LogType DEBUG
                $Script:TextBoxLog.Clear()
                "Log cleared" | Write-LogOutput
            })

        $Script:TextBoxLog.remove_TextChanged({
                $_ | Show-EventInfo
                "Clear AppLogObject" | Write-LogOutput -LogType DEBUG
                $Script:RunTimeConfig.Logging.AppLogObject.Clear()
            })

        $Script:LogWindowForm.Elements.ButtonExportLogFile.Add_Click({
                $_ | Show-EventInfo
                $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
                $SaveFileDialog.Filter = "Log files (*.log) | *.log | All files (*.*) | *.*"
                "Dialog Filter: {0}" -f $SaveFileDialog.Filter | Write-LogOutput -LogType DEBUG
                $SaveFileDialog.Title = "Save Log File"
                "Dialog Title: {0}" -f $SaveFileDialog.Title | Write-LogOutput -LogType DEBUG
                $SaveFileDialog.FileName = "OmadaSqlTroubleShooter.log"
                "Dialog Initial FileName: {0}" -f $SaveFileDialog.FileName | Write-LogOutput -LogType DEBUG
                if ($SaveFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
                    if ($Null -eq $SaveFileDialog.FileName) {
                        return
                    }
                    else {
                        $Script:RunTimeConfig.Logging.AppLogObject | Set-Content $SaveFileDialog.FileName -Encoding UTF8
                        "File saved to: {0}" -f $SaveFileDialog.FileName | Write-LogOutput -LogType DEBUG

                    }
                }
                else {
                    "File was not saved!" | Write-LogOutput -LogType DEBUG
                }
            })

        $Script:LogWindowForm.Elements.ComboBoxSelectLogLevel.Add_SelectionChanged({
                $_ | Show-EventInfo
                $Script:LogWindowForm.Elements.ComboBoxSelectLogLevel.SelectedItem.Content | Invoke-ConfigSetting -Property "LogLevel"
                $Script:RunTimeConfig.Logging.LogLevelSetting = $Script:LogWindowForm.Elements.ComboBoxSelectLogLevel.SelectedItem.Content
                "Logging set to {0}!" -f $Script:RunTimeConfig.Logging.LogLevelSetting | Write-LogOutput -LogType LOG
            })


        $Script:LogWindowForm.Elements.CheckboxWordWrap.Add_Checked({
                $_ | Show-EventInfo
                $Script:TextBoxLog.TextWrapping = "WrapWithOverflow"
                "Word wrap is enabled" | Write-LogOutput -LogType LOG
                $true | Invoke-ConfigSetting -Property "LogWindowWordWrap"
            })

        $Script:LogWindowForm.Elements.CheckboxWordWrap.Add_UnChecked({
                $_ | Show-EventInfo
                $Script:TextBoxLog.TextWrapping = "NoWrap"
                "Word wrap is disabled" | Write-LogOutput -LogType LOG
                $false | Invoke-ConfigSetting -Property "LogWindowWordWrap"

            })

        $Script:LogWindowForm.Elements.CheckboxConsoleLog.Add_Checked({
                $_ | Show-EventInfo
                $Script:RunTimeConfig.LogToConsole = $true
                "Console logging is enabled" | Write-LogOutput -LogType LOG
                $true | Invoke-ConfigSetting -Property "CheckboxConsoleLog"
            })

        $Script:LogWindowForm.Elements.CheckboxConsoleLog.Add_UnChecked({
                $_ | Show-EventInfo
                $Script:RunTimeConfig.LogToConsole = $false
                "Console logging is disabled" | Write-LogOutput -LogType LOG
                $false | Invoke-ConfigSetting -Property "CheckboxConsoleLog"

            })

        $Script:LogWindowForm.Definition.Show()
        if ($Script:TextBoxLog.IsLoaded -and (Invoke-LogWindowScrollToEnd)) {
            $Script:TextBoxLog.ScrollToEnd()
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Open-SplashScreenForm {
    try {

        "Loading Splash Screen" | Write-LogOutput -LogType DEBUG
        $SplashScreenForm = New-Object System.Windows.Forms.Form
        $SplashScreenForm.Text = "Loading..."
        $SplashScreenForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None
        $SplashScreenForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
        $SplashScreenForm.Width = 300
        $SplashScreenForm.Height = 250
        $SplashScreenForm.BackColor = [System.Drawing.Color]::White

        $LogoPictureBox = New-Object System.Windows.Forms.PictureBox
        $LogoPictureBox.Image = Get-Icon -Type WinForms
        $LogoPictureBox.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
        $LogoPictureBox.Width = 150
        $LogoPictureBox.Height = 150
        $LogoPictureBox.Location = New-Object System.Drawing.Point(65, 20)
        $SplashScreenForm.Controls.Add($LogoPictureBox)

        $SplashLabel = New-Object System.Windows.Forms.Label
        $SplashLabel.Text = "Initializing application..."
        $SplashLabel.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold)
        $SplashLabel.AutoSize = $True
        $SplashLabel.Location = New-Object System.Drawing.Point(55, 180)
        $SplashScreenForm.Controls.Add($SplashLabel)
        return $SplashScreenForm

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Open-SqlSchemaWindow {
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', 'Sender', Justification = 'The use of the variable is on purpose')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', 'Args', Justification = 'The use of the variable is on purpose')]
    PARAM()
    try {
        "Opening Sql Schema window" | Write-LogOutput -LogType DEBUG
        $Script:SqlSchemaWindowForm = New-FormObject -FormPath (Join-Path $Script:RunTimeConfig.ModuleFolder -ChildPath "lib\ui\SqlSchemaWindow.xaml") -ParentForm $Script:MainWindowForm.Definition
        [Int]$Script:SqlSchemaWindowForm.PositionManager.PositionOffSetRight = 405


        $true | Invoke-ConfigSetting -Property "SqlSchemaWindowFormOpen"

        $Script:SqlSchemaWindowForm.Definition.ShowInTaskbar = $false
        $Script:TreeViewSqlSchema = $Script:SqlSchemaWindowForm.Definition.FindName("TreeViewSqlSchema")

        $Script:TreeViewSqlSchema.Add_SelectedItemChanged({
                param ($Sender, $Args)
                $_ | Show-EventInfo
                Invoke-OnTreeViewItemShiftClick -sender $Sender -args $Args
            })



        $Script:SqlSchemaWindowForm.Definition.Add_LocationChanged({
                $_ | Show-EventInfo -LogType VERBOSE2
                if (!$Script:SqlSchemaWindowForm.PositionManager.Synchronizing) {
                    $Script:SqlSchemaWindowForm.PositionManager.Synchronizing = $true
                    "MainWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:MainWindowForm.Definition.Left, $Script:MainWindowForm.Definition.Top, $Script:MainWindowForm.Definition.Width , $Script:MainWindowForm.Definition.Height | Write-LogOutput -LogType VERBOSE2
                    "SqlSchemaWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:SqlSchemaWindowForm.Definition.Left, $Script:SqlSchemaWindowForm.Definition.Top, $Script:SqlSchemaWindowForm.Definition.Width , $Script:SqlSchemaWindowForm.Definition.Height | Write-LogOutput -LogType VERBOSE2
                    $Script:SqlSchemaWindowForm.Definition.Dispatcher.Invoke({
                            $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetTop = [Int]::Abs($Script:MainWindowForm.Definition.Top) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Top)
                            "PositionManagerSqlSchemaWindow PositionOffSetLeft: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetLeft | Write-LogOutput -LogType VERBOSE2
                            $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetLeft = [Int]::Abs($Script:MainWindowForm.Definition.Left) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Left)
                            "PositionManagerSqlSchemaWindow PositionOffSetTop: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetTop | Write-LogOutput -LogType VERBOSE2
                            $Script:SqlSchemaWindowForm.PositionManager.Synchronizing = $false
                        }, [System.Windows.Threading.DispatcherPriority]::Render)
                }
            })

        $Script:SqlSchemaWindowForm.Definition.Add_SizeChanged({
                $_ | Show-EventInfo -LogType VERBOSE2
                $Script:SqlSchemaWindowForm.Size = $Script:SqlSchemaWindowForm.Definition | Get-WindowSize
            })


        if ($null -ne ($Script:SqlSchemaWindowForm.Definition | Get-WindowPositionConfig)) {
            $Position = $Script:SqlSchemaWindowForm.Definition | Get-WindowPositionConfig
            "Sql Schema window position: {0}" -f $Position | Write-LogOutput -LogType DEBUG
            $Script:SqlSchemaWindowForm.Definition.Left = [Int]::Abs($Position.Split("x")[0])
            $Script:SqlSchemaWindowForm.Definition.Top = [Int]::Abs($Position.Split("x")[1])
        }



        $Script:SqlSchemaWindowForm.Definition.Add_Loaded({
                $_ | Show-EventInfo

                Get-SqlSchemaObject

                $Script:SqlSchemaWindowForm.PositionManager.Synchronizing = $true
                $Script:SqlSchemaWindowForm.Definition.Dispatcher.Invoke({
                        $Script:SqlSchemaWindowForm.Definition.Top = [Int]::Abs($Script:MainWindowForm.Definition.Top)
                        "MainWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:MainWindowForm.Definition.Left, $Script:MainWindowForm.Definition.Top, $Script:MainWindowForm.Definition.Width , $Script:MainWindowForm.Definition.Height | Write-LogOutput -LogType DEBUG
                        $Script:SqlSchemaWindowForm.Definition.Left = [Int]::Abs($Script:MainWindowForm.Definition.Left) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Width)
                        "SqlSchemaWindowForm Left: {0}" -f $Script:SqlSchemaWindowForm.Definition.Left | Write-LogOutput -LogType DEBUG
                        $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetTop = [Int]::Abs($Script:MainWindowForm.Definition.Top) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Top)
                        $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetLeft = [Int]::Abs($Script:MainWindowForm.Definition.Left) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Left)
                        "PositionManagerSqlSchemaWindow PositionOffSetLeft: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetLeft | Write-LogOutput -LogType DEBUG
                        if ($null -ne ($Script:SqlSchemaWindowForm.Definition | Get-WindowSizeConfig)) {
                            $Size = $Script:SqlSchemaWindowForm.Definition | Get-WindowSizeConfig
                            "Sql Schema window size: {0}" -f $Size | Write-LogOutput -LogType DEBUG
                            $Script:SqlSchemaWindowForm.Definition.Width = $Size.Split("x")[0]
                            $Script:SqlSchemaWindowForm.Definition.Height = $Size.Split("x")[1]
                            "SqlSchemaWindowForm Height: {0}" -f $Script:SqlSchemaWindowForm.Definition.Height | Write-LogOutput -LogType DEBUG
                        }
                        $Script:SqlSchemaWindowForm.PositionManager.Synchronizing = $false
                    }, [System.Windows.Threading.DispatcherPriority]::Render)
                $Script:MainWindowForm.Elements.ButtonShowSqlSchema.IsEnabled = $true
                $Script:MainWindowForm.Elements.ButtonShowSqlSchema | Set-ButtonContent -Content "Hide Schema"
                $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetTop = [Int]::Abs($Script:MainWindowForm.Definition.Top) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Top)
                "PositionManagerSqlSchemaWindow PositionOffSetLeft: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetLeft | Write-LogOutput -LogType DEBUG
                $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetLeft = [Int]::Abs($Script:MainWindowForm.Definition.Left) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Left)
                "PositionManagerSqlSchemaWindow PositionOffSetTop: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.PositionOffSetTop | Write-LogOutput -LogType DEBUG
                "SqlSchemaWindowForm Position: {0}x{1}, Dimensions: {2}x{3}" -f $Script:SqlSchemaWindowForm.Definition.Left, $Script:SqlSchemaWindowForm.Definition.Top, $Script:SqlSchemaWindowForm.Definition.Width , $Script:SqlSchemaWindowForm.Definition.Height | Write-LogOutput -LogType DEBUG
                $Script:SqlSchemaWindowForm.State = "Open"
            })

        $Script:SqlSchemaWindowForm.Definition.Add_Closing({
                $_ | Show-EventInfo
                Save-WindowMeasurements
                $Script:SqlSchemaWindowForm.State = "Closing"
                if ($Script:MainWindowForm.State -eq "Open") {
                    $false | Invoke-ConfigSetting -Property "SqlSchemaWindowFormOpen"
                }
            })

        $Script:SqlSchemaWindowForm.Definition.Add_Closed({
                $_ | Show-EventInfo
                $Script:SqlSchemaWindowForm.State = "Closed"
                $Script:MainWindowForm.Elements.ButtonShowSqlSchema | Set-ButtonContent -Content "Schema"
            })

        $Script:SqlSchemaWindowForm.Definition.Show()

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }

}
function Push-ToEditor {
    PARAM(
        [parameter(Mandatory = $true)]
        [string]$ScriptToExecute
    )
    try {
        $OnCompletedScriptBlock = {
            try {
                if ($Script:Task.Status -eq "RanToCompletion") {
                    "Editor value updated!" | Write-LogOutput -LogType DEBUG
                }
                elseif ($Script:Task.Status -eq "Faulted") {
                    "Task failed: {0}" -f $Script:Task.Status | Write-LogOutput -LogType ERROR
                }
                else {
                    "Task result: {0}" -f $Script:Task.Status | Write-LogOutput -LogType DEBUG
                }
            }
            catch {
                $Script:Task.Exception.Message | Write-LogOutput -LogType ERROR
            }
        }

        Invoke-ExecuteScriptAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Reset-Application {
    PARAM(
        [switch]$SkipTextBoxURL,
        [switch]$SkipAuthentication,
        [switch]$ResetEditor
    )

    try {
        if ($null -ne $Script:SqlSchemaWindow -and $null -ne $Script:SqlSchemaWindow.Definitions -and $Script:SqlSchemaWindow.Definitions.IsVisible) {
            $Script:SqlSchemaWindow.Definitions.Close()
        }
        $Script:MainWindowForm.Elements.ButtonExecuteQuery.IsEnabled = $False
        $Script:MainWindowForm.Elements.ButtonSaveOutputFile.IsEnabled = $False
        $Script:MainWindowForm.Elements.ButtonOpenOutputFile.IsEnabled = $False

        if (!$SkipTextBoxURL) {
            $Script:MainWindowForm.Elements.TextBoxURL.Text = $null
            $null | Invoke-ConfigSetting -Property "BaseUrl"
            $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.SelectedItem = $Null
            $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Clear()
            $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.IsEnabled = $false
            $null, $null | Invoke-ConfigSetting -Property "CurrentDataConnection"
            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem = $Null
            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items.Clear()
            $Script:MainWindowForm.Elements.CheckboxMyQueries.IsChecked = $False
            $Script:MainWindowForm.Elements.CheckboxMyQueries.IsEnabled = $False
            $null, $null | Invoke-ConfigSetting -Property "CurrentSqlQuery"
        }
        $Script:MainWindowForm.Elements.TextBoxURL.IsEnabled = $True

        if (!$SkipAuthentication) {
            $Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem = $Null
            $null | Invoke-ConfigSetting -Property "LastAuthentication"
            if ($Script:MainWindowForm.Elements.TextBoxUserName.IsEnabled) {
                $Script:MainWindowForm.Elements.TextBoxUserName.Text = $Null
                $Script:MainWindowForm.Elements.TextBoxUserName.IsEnabled = $false
            }
            if ($Script:MainWindowForm.Elements.TextBoxPassword.IsEnabled) {
                $Script:MainWindowForm.Elements.TextBoxPassword.Password = $Null
                $Script:MainWindowForm.Elements.TextBoxPassword.IsEnabled = $false
            }
        }

        if (!$SkipTextBoxURL -and !$SkipAuthentication) {
            $Script:MainWindowForm.Elements.TextBoxDisplayName.IsEnabled = $False
            $Script:MainWindowForm.Elements.TextBoxDisplayName.Text = $null
        }

        $Script:MainWindowForm.Elements.ButtonShowOutput.IsEnabled = $False
        $Script:MainWindowForm.Elements.ButtonSaveQuery.IsEnabled = $False
        $Script:MainWindowForm.Elements.ButtonShowSqlSchema.IsEnabled = $False

        if ($ResetEditor -and $null -ne $Script:Webview.Object.CoreWebView2) {
            Set-EditorValue
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Save-WindowMeasurements {
    try {
        "Save-WindowMeasurements" | Write-LogOutput -LogType VERBOSE2
        $MinDelta = 500
        $Timestamp = Get-Date
        if ($Script:RunTimeConfig.LastWindowMeasured -gt (Get-Date).AddMilliseconds(-$MinDelta)) {
            "WindowMeasurements save ignored because last measurement was less than {0} milliseconds ago. Current Measurement save timestamp = '{1}', last measurement save timestamp: '{2}'" -f $MinDelta, $Timestamp.ToString("o"), $Script:RunTimeConfig.LastWindowMeasured.ToString("o") | Write-LogOutput -LogType VERBOSE2
            return
        }
        "WindowMeasurements Current Measurement save timestamp = '{0}', last measurement save timestamp: '{1}'" -f $Timestamp.ToString("o"), $Script:RunTimeConfig.LastWindowMeasured.ToString("o") | Write-LogOutput -LogType VERBOSE2
        $Script:RunTimeConfig.LastWindowMeasured = Get-Date

        if ($Script:MainWindowForm.Definition.IsVisible) {
            $ValueSize = "{0}x{1}" -f [Int]::Abs(($Script:MainWindowForm.Definition | Get-ValidWindowMeasurement -Setting "Width")), [Int]::Abs(($Script:MainWindowForm.Definition | Get-ValidWindowMeasurement -Setting "Height"))
            $ValueSize | Invoke-ConfigSetting -Property "MainWindowSize"
            $ValuePosition = "{0}x{1}" -f [Int]::Abs(($Script:MainWindowForm.Definition | Get-ValidWindowPosition -Setting "Left")), [Int]::Abs(($Script:MainWindowForm.Definition | Get-ValidWindowPosition -Setting "Top"))
            $ValuePosition | Invoke-ConfigSetting -Property "MainWindowPosition"
            "MainWindowForm Size:'{0}', Position: '{1}'" -f $ValueSize, $ValuePosition | Write-LogOutput -LogType VERBOSE2
            if ($null -ne $Script:LogWindowForm -and $null -ne $Script:LogWindowForm.Definition -and $Script:LogWindowForm.Definition.IsVisible) {
                $ValueSize = "{0}x{1}" -f [Int]::Abs(($Script:LogWindowForm.Definition | Get-ValidWindowMeasurement -Setting "Width")), [Int]::Abs(($Script:LogWindowForm.Definition | Get-ValidWindowMeasurement -Setting "Height"))
                $ValueSize | Invoke-ConfigSetting -Property "LogWindowSize"
                $ValuePosition = "{0}x{1}" -f [Int]::Abs(($Script:LogWindowForm.Definition | Get-ValidWindowPosition -Setting "Left")), [Int]::Abs(($Script:LogWindowForm.Definition | Get-ValidWindowPosition -Setting "Top"))
                $ValuePosition | Invoke-ConfigSetting -Property "LogWindowPosition"
                "LogWindowForm Size:'{0}', Position: '{1}'" -f $ValueSize, $ValuePosition | Write-LogOutput -LogType VERBOSE2
            }
            else {
                "LogWindowForm is not visible" | Write-LogOutput -LogType VERBOSE2
            }
            if ($null -ne $Script:SqlSchemaWindowForm -and $null -ne $Script:SqlSchemaWindowForm.Definition -and $Script:SqlSchemaWindowForm.Definition.IsVisible) {
                $ValueSize = "{0}x{1}" -f [Int]::Abs(($Script:SqlSchemaWindowForm.Definition | Get-ValidWindowMeasurement -Setting "Width")), [Int]::Abs(($Script:SqlSchemaWindowForm.Definition | Get-ValidWindowMeasurement -Setting "Height"))
                $ValueSize | Invoke-ConfigSetting -Property "SqlSchemaWindowSize"
                $ValuePosition = "{0}x{1}" -f [Int]::Abs(($Script:SqlSchemaWindowForm.Definition | Get-ValidWindowPosition -Setting "Left")), [Int]::Abs(($Script:SqlSchemaWindowForm.Definition | Get-ValidWindowPosition -Setting "Top"))
                $ValuePosition | Invoke-ConfigSetting -Property "SqlSchemaWindowPosition"
                "SqlSchemaWindowForm Size:'{0}', Position: '{1}'" -f $ValueSize, $ValuePosition | Write-LogOutput -LogType VERBOSE2
            }
            else {
                "SqlSchemaWindowForm is not visible" | Write-LogOutput -LogType VERBOSE2
            }
        }
        else {
            "MainWindowForm is not visible" | Write-LogOutput -LogType VERBOSE2
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-AuthenticationOption {

    try {
        if ($Null -ne $Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem.Content) {

            switch ($Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem.Content) {
                { $_ -in @("Basic", "Windows", "OAuth" ) } {

                    if ($_ -eq "OAuth") {
                        $Script:MainWindowForm.Elements.LabelUserName | Set-LabelContent -Content "Client ID:"
                        $Script:MainWindowForm.Elements.LabelPassword | Set-LabelContent -Content "Client Secret:"
                    }
                    else {
                        $Script:MainWindowForm.Elements.LabelUserName | Set-LabelContent -Content "Username:"
                        $Script:MainWindowForm.Elements.LabelPassword | Set-LabelContent -Content "Password:"
                    }
                    $Script:MainWindowForm.Elements.LabelUserName.Visibility = "Visible"
                    $Script:MainWindowForm.Elements.LabelPassword.Visibility = "Visible"
                    $Script:MainWindowForm.Elements.TextBoxUserName.Visibility = "Visible"
                    $Script:MainWindowForm.Elements.TextBoxPassword.Visibility = "Visible"
                    $Script:MainWindowForm.Elements.TextBoxUserName.IsEnabled = $True
                    $Script:MainWindowForm.Elements.TextBoxPassword.IsEnabled = $True
                    if (!$Script:RunTimeData.RestMethodParam.ContainsKey("Credential")) {
                        $Script:RunTimeData.RestMethodParam.Add("Credential", $Null)
                    }
                }

                default {
                    $Script:MainWindowForm.Elements.TextBoxUserName.IsEnabled = $False
                    $Script:MainWindowForm.Elements.TextBoxPassword.IsEnabled = $False
                    $Script:MainWindowForm.Elements.LabelUserName.Visibility = "Hidden"
                    $Script:MainWindowForm.Elements.LabelPassword.Visibility = "Hidden"
                    $Script:MainWindowForm.Elements.TextBoxUserName.Visibility = "Hidden"
                    $Script:MainWindowForm.Elements.TextBoxPassword.Visibility = "Hidden"
                    $Script:MainWindowForm.Elements.TextBoxUserName | Set-TextBlockText -Text $null
                    $Script:MainWindowForm.Elements.TextBoxPassword.Password = ""
                    $Null | Invoke-ConfigSetting -Property "UserName"
                    if ($Script:RunTimeData.RestMethodParam.ContainsKey("Credential")) {
                        $Script:RunTimeData.RestMethodParam.Remove("Credential")
                    }
                }
            }
            $Script:RunTimeConfig.AuthenticationSet = $True
            $Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem.Content | Invoke-ConfigSetting -Property "LastAuthentication"
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }

}
function Set-ButtonContent {
    PARAM(
        [Parameter(Mandatory = $true,ValueFromPipeline = $true)]
        $ButtonObject,
        [Parameter(Mandatory = $true)]
        [string]$Content
    )

    try {
        $CurrentButtonContent = $ButtonObject.Content
        $ButtonObject.Content = $Content
        "{0} set from '{1}' to '{2}'" -f $ButtonObject.Name, $CurrentButtonContent, $ButtonObject.Content | Write-LogOutput -LogType DEBUG

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }


}
function Set-ConfigProperty {
    if ($_.Name -notin $CurrentPoperties.Name) {
        $Value = $Null

        if ($_.Type -eq "Bool") {
            $Value = $false
        }
        elseif ($_.Type -eq "Int") {
            $Value = -1
        }
        elseif ($_.Type -eq "String") {
            $Value = $null
        }
        elseif ($_.Type -eq "PSObject") {
            $Value = [pscustomobject]@{}
            $_.Attributes | ForEach-Object {
                if ($_.Type -eq "Bool") {
                    $Value | Add-Member -MemberType NoteProperty -Name $_.Name -Value $false
                }
                elseif ($_.Type -eq "Int") {
                    $Value | Add-Member -MemberType NoteProperty -Name $_.Name -Value -1
                }
                elseif ($_.Type -eq "String") {
                    $Value | Add-Member -MemberType NoteProperty -Name $_.Name -Value $null
                }
                if ($_.DefaultValue) {
                    $Value.$($_.Name) = $_.DefaultValue
                }
            }
        }
        if ($_.DefaultValue) {
            $Value = $_.DefaultValue
        }
        $Config | Add-Member -MemberType NoteProperty -Name $_.Name -Value $Value
    }
}
function Set-DataConnection {
    try {


        if (!$Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items.Contains($Script:AppConfig.CurrentDataConnection.FullName)) {
            $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items.Add($Script:AppConfig.CurrentDataConnection.FullName) | Out-Null
        }
        $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.SelectedValue = $Script:AppConfig.CurrentDataConnection.FullName
        $Script:MainWindowForm.Elements.TextBlockDatabaseName.Text = $Script:AppConfig.CurrentDataConnection.DisplayName
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-EditorBackground {

    try {

        $OnCompletedScriptBlock = {
            try {
                if (!$Script:Task.Status -eq "RanToCompletion") {
                    "Monaco Editor Task failed: {0}" -f $Script:Task.Status | Write-LogOutput -LogType ERROR
                }
                else{
                    "Monaco Editor Task completed successfully." | Write-LogOutput -LogType DEBUG
                }
            }
            catch {
                $Script:Task.Exception.Message | Write-LogOutput -LogType ERROR
            }
        }
        $ScriptToExecute = "container.style.backgroundImage = url('`${0}');" -f (Get-Icon -Type Base64)
        Invoke-ExecuteScriptAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock
        $ScriptToExecute = "container.style.backgroundSize = `"200px 200px`";"
        Invoke-ExecuteScriptAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock
        $ScriptToExecute = "container.style.backgroundPosition = `"center`";"
        Invoke-ExecuteScriptAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock
        $ScriptToExecute = "container.style.backgroundRepeat = `"no-repeat`";"
        Invoke-ExecuteScriptAsync -ScriptToExecute $ScriptToExecute -OnCompletedScriptBlock $OnCompletedScriptBlock

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-EditorValue {
    try {
        if ($null -ne $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem) {
            "Selected SQL Query object: {0}" -f $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem.Content | Write-LogOutput -LogType DEBUG
            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem.Content | Invoke-ConfigSetting -Property "CurrentSqlQuery"

            if ([string]::IsNullOrWhiteSpace($Script:AppConfig.CurrentSqlQuery.DoId)) {
                "Omada Url not set or Query not selected. Set correct values to execute queries!" | Write-LogOutput -LogType WARNING
                return
            }
            if (!(Test-ConnectionRequirements)) {
                "Connection requirements are not met" | Write-LogOutput -LogType DEBUG
                return
            }

            $Private:Result = Get-SqlQueryObject
            if ($null -ne $Private:Result) {

                $Script:RunTimeData.CurrentSqlQuery.DoId = $Private:Result.Id
                $Script:RunTimeData.CurrentSqlQuery.DisplayName = $Private:Result.DisplayName
                $Script:MainWindowForm.Elements.TextBoxDisplayName.Text = $Private:Result.DisplayName
                $Private:Result.C_SQLTROUBLESHOOTING_DATACONNECTION.Id, $Private:Result.C_SQLTROUBLESHOOTING_DATACONNECTION.DisplayName | Invoke-ConfigSetting -Property "CurrentDataConnection"

                Set-DataConnection

                $Script:MainWindowForm.Elements.ButtonExecuteQuery.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonSaveQuery.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonReset.IsEnabled = $True
                $Script:MainWindowForm.Elements.TextBoxDisplayName.IsEnabled = $True
                $Script:MainWindowForm.Elements.ButtonShowSqlSchema.IsEnabled = $true

                if ($null -ne $Script:Webview.Object.CoreWebView2) {

                    $ScriptToExecute = "editor.setValue('{0}');" -f ($Private:Result.C_QUERY -replace "`n", "\n" -replace "`r", "\r" -replace "`t", "\t" -replace "'", "\'")
                    Push-ToEditor -ScriptToExecute $ScriptToExecute
                    $Script:RunTimeData.CurrentQueryText = $Private:Result.C_QUERY
                    "Query {0} retrieved!" -f $Script:AppConfig.CurrentSqlQuery.DoId | Write-LogOutput
                }
            }
        }
        else {
            "Clear Editor Value because no query is selected!" | Write-LogOutput -LogType DEBUG
            $ScriptToExecute = "editor.setValue('');"
            Push-ToEditor -ScriptToExecute $ScriptToExecute
            Reset-Application -SkipTextBoxURL -SkipAuthentication
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-LabelContent {
    PARAM(
        [Parameter(Mandatory = $true,ValueFromPipeline = $true)]
        $LabelObject,
        [Parameter(Mandatory = $true)]
        [string]$Content
    )

    try {
        $CurrentButtonContent = $LabelObject.Content
        $LabelObject.Content = $Content
        "{0} set from '{1}' to '{2}'" -f $LabelObject.Name, $CurrentButtonContent, $LabelObject.Content | Write-LogOutput -LogType DEBUG

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }


}
function Set-MonacoSchema {
    PARAM(
        $ReturnValue
    )
    try {
        "Add schema to Monaco editor." | Write-LogOutput -LogType DEBUG
        $TableObjects = @()
        foreach ($Table in ($ReturnValue.d | Get-Member -MemberType NoteProperty)) {
            $TableName = $($Table.Name).Split(".")[1]
            $TableObject = [pscustomobject]@{
                $TableName = @()
            }
            foreach ($Column in $ReturnValue.d.$($Table.Name)) {
                $TableObject.$TableName += $Column.Split(" ")[0]
            }
            $TableObjects += $TableObject
        }
        $TableObjectsJson = $TableObjects | ConvertTo-Json -Depth 5

        "Schema for Monaco editor." | Write-LogOutput -LogType VERBOSE
        $OnCompletedScriptBlock = {
            try {
                if (!$Script:Task.Status -eq "RanToCompletion") {
                    "Monaco Editor Task failed: {0}" -f $Script:Task.Status | Write-LogOutput -LogType ERROR
                }
                else{
                    "Monaco Editor Task completed successfully." | Write-LogOutput -LogType DEBUG
                }
            }
            catch {
                $Script:Task.Exception.Message | Write-LogOutput -LogType ERROR
            }
        }

        "Push schema to Monaco editor." | Write-LogOutput -LogType DEBUG
        Invoke-ExecuteScriptAsync -ScriptToExecute "setSchema($TableObjectsJson);" -OnCompletedScriptBlock $OnCompletedScriptBlock

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-OmadaUrl {

    try {

        if (![string]::IsNullOrWhiteSpace($Script:MainWindowForm.Elements.TextBoxURL.Text)) {

            if ($Script:MainWindowForm.Elements.TextBoxURL.Text -notlike "http*") {
                if ($Script:MainWindowForm.Elements.TextBoxURL.Text -notlike "*.*" -and $Script:MainWindowForm.Elements.TextBoxURL.Text -notlike "*.omada.cloud") {
                    $Script:MainWindowForm.Elements.TextBoxURL | Set-TextBlockText -Text "https://$($Script:MainWindowForm.Elements.TextBoxURL.Text).omada.cloud"
                }
                else {
                    $Script:MainWindowForm.Elements.TextBoxURL | Set-TextBlockText -Text "https://$($Script:MainWindowForm.Elements.TextBoxURL.Text)"
                }
            }

            $Uri = [System.Uri]::new($Script:MainWindowForm.Elements.TextBoxURL.Text.Trim())

            if ($Uri.IsAbsoluteUri -and ($Uri.Scheme -eq 'http' -or $Uri.Scheme -eq 'https')) {
("Input Url {0} is valid." -f $Uri.IsAbsoluteUri) | Write-LogOutput -LogType DEBUG
            }
            else {
                $Null | Invoke-ConfigSetting -Property "BaseUrl"
                $Script:MainWindowForm.Elements.TextBoxURL.Text = $Null
                "Input Url {0} is not valid." -f $Script:MainWindowForm.Elements.TextBoxURL.Text.Trim() | Write-LogOutput -LogType ERROR
                return
            }

            try {
                $DnsResult = Resolve-DnsName -Name $Uri.Host -QuickTimeout -ErrorAction SilentlyContinue
                if (($DnsResult | Measure-Object).Count -le 0) {
                    "DNS resolution for {0} failed!" -f $Uri.Host | Write-LogOutput -LogType ERROR
                    return
                }
            }
            catch {
                $Null | Invoke-ConfigSetting -Property "BaseUrl"
                $Script:MainWindowForm.Elements.TextBoxURL.Text = $Null
                $Script:MainWindowForm.Elements.TextBlockUrl.Text = $Null
                "Endpoint {0} not found!" -f $Uri.AbsoluteUri | Write-LogOutput -LogType ERROR
            }

            $Uri.AbsoluteUri.TrimEnd("/") | Invoke-ConfigSetting -Property "BaseUrl"

            if ($Script:CurrentUrl -ne $Script:AppConfig.BaseUrl) {
                "Omada Url set to: {0}" -f $Script:AppConfig.BaseUrl | Write-LogOutput -LogType DEBUG
                $Script:CurrentUrl = $Script:AppConfig.BaseUrl
                if ($Script:RunTimeConfig.AuthenticationSet) {
                    "Authentication is set, force update query list!" | Write-LogOutput -LogType DEBUG
                    Update-QueryList -ForceRefresh
                }
            }
            elseif([string]::IsNullOrEmpty($Script:AppConfig.BaseUrl)) {
                "Omada Url is empty!" | Write-LogOutput -LogType DEBUG
            }
            else{
                "Omada Url maintained: {0}" -f $Script:AppConfig.BaseUrl | Write-LogOutput -LogType DEBUG
            }
        }
        else {
            Reset-Application
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-TextBlockText {
    PARAM(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $TextBlockObject,
        [Parameter(Mandatory = $false)]
        [string]$Text
    )

    try {
        $CurrentButtonContent = $TextBlockObject.Text
        if ([string]::IsNullOrEmpty($Text)) {
            $TextBlockObject.Text = $null
        }
        else {
            $TextBlockObject.Text = $Text
        }
        $TextBlockObject.Text = $Text
        "{0} set from '{1}' to '{2}'" -f $TextBlockObject.Name, $CurrentButtonContent, $TextBlockObject.Text | Write-LogOutput -LogType DEBUG

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }


}
function Set-TextBoxWrapping {
    PARAM(
        $TextBox,
        [bool]$Wrap =$false
    )
    try {
        "Set TextBox wrapping to {0}" -f $Wrap | Write-LogOutput -LogType DEBUG
        if ($Wrap) {
            $TextBox.TextWrapping = "WrapWithOverflow"
        }
        else {
            $TextBox.TextWrapping = "NoWrap"
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-WindowPosition {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form,
        [parameter(Mandatory = $true)]
        [string]$Setting
    )
    try {
        $Form.Left | Write-Host -ForegroundColor DarkYellow
        $Form.Top | Write-Host -ForegroundColor DarkYellow

        $Form.Left = [Int]::Abs($Setting.Split("x")[0])
        $Form.Top = [Int]::Abs($Setting.Split("x")[1])
        $Form.Left | Write-Host -ForegroundColor Yellow
        $Form.Top | Write-Host -ForegroundColor Yellow

        "{0} position setting {1}: {2}x{3}" -f $Form.Name, $Setting, $Form.Left, $Form.Top | Write-LogOutput -LogType VERBOSE2

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Set-WindowSize {
    PARAM(
        [parameter(Mandatory = $true, ValueFromPipeline = $true)]
        $Form,
        [parameter(Mandatory = $true)]
        [string]$Setting
    )
    try {
        $Form.Width = [Int]::Abs($Setting.Split("x")[0])
        $Form.Height = [Int]::Abs($Setting.Split("x")[1])

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Show-EventInfo {
    PARAM(
        [parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
        $Item,
        [validateSet("DEBUG", "VERBOSE", "VERBOSE2")]
        $LogType = "DEBUG"
    )
    try {
        $CallStack = Get-PSCallStack
        if ($Item -is [Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs]) {
            "Webview success: '{0}' Source: '{1}'" -f $Item.IsSuccess, $CallStack[1].Location | Write-LogOutput -LogType $LogType
        }
        elseif ($Item -is [System.Windows.Window]) {
            "Form: '{0}', Event: '{1}', Event Type: '{2}', Source: '{3}'" -f $Item.Source.Title, $Item.RoutedEvent.Name, $Item.RoutedEvent.OwnerType.FullName, $CallStack[1].Location | Write-LogOutput -LogType $LogType
        }
        elseif ($Item -is [System.Windows.Controls.SelectionChangedEventArgs]) {
            "Control: '{0}', Event: '{1}', Event Type: '{2}', Added values: {3}, Removed values: {4}, Source: '{5}'" -f $Item.Source.Name, $Item.RoutedEvent.Name, $Item.RoutedEvent.OwnerType.FullName, ($Item.AddedItems | Measure-Object).Count, ($Item.RemovedItems | Measure-Object).Count, $CallStack[1].Location | Write-LogOutput -LogType $LogType
        }
        elseif ($Item -is [System.Windows.SizeChangedEventArgs]) {
            "Control: '{0}', Event: '{1}', Event Type: '{2}', PreviousSize: {3}, NewSize: {4}, Source: {5}" -f $Item.Source.Name, $Item.RoutedEvent.Name, $Item.RoutedEvent.OwnerType.FullName, $Item.PreviousSize, $Item.NewSize, $CallStack[1].Location | Write-LogOutput -LogType $LogType
        }
        elseif ($Item -is [System.Windows.RoutedEventArgs]) {
            "Event: '{0}', Event Type: '{1}, Source: '{2}'" -f $Item.RoutedEvent.Name, $Item.RoutedEvent.OwnerType.FullName, $CallStack[1].Location | Write-LogOutput -LogType $LogType
        }
        elseif ($Item -is [System.EventArgs]) {
            if ([string]::IsNullOrWhiteSpace($Item.RoutedEvent.Name)) {
                "Event: '{0}', Source: '{1}'" -f $Item, $CallStack[1].Location | Write-LogOutput -LogType $LogType
            }
            else {
                "Event: '{0}', Source: '{1}'" -f $Item.RoutedEvent.Name, $CallStack[1].Location | Write-LogOutput -LogType $LogType
            }
        }
        else {
            "Control: {0}, Event: '{1}', Event Type: '{2}', Source: '{3}'" -f $Item.Source.Name, $Item.RoutedEvent.Name, $Item.RoutedEvent.OwnerType.FullName, $CallStack[1].Location | Write-LogOutput -LogType $LogType
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Show-PopupWindow {
    PARAM(
        $Message
    )
    try {

        if($null -eq $Script:MainWindowForm -or $null -eq $Script:MainWindowForm.Definition -or !$Script:MainWindowForm.Definition.IsVisible) {
            return
        }

        $PopupWindow = New-Object System.Windows.Window
        $PopupWindow.WindowStyle = [System.Windows.WindowStyle]::None
        $PopupWindow.ResizeMode = [System.Windows.ResizeMode]::NoResize
        $PopupWindow.Width = 200
        $PopupWindow.Height = 50
        $PopupWindow.Background = [System.Windows.Media.Brushes]::White
        $PopupWindow.AllowsTransparency = $true
        $PopupWindow.Opacity = 0.8
        $PopupWindow.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterOwner
        $PopupWindow.Owner = $Script:MainWindowForm.Definition
        $PopupWindow.ShowInTaskbar = $false

        $Grid = New-Object System.Windows.Controls.Grid
        $Grid.Margin = '0'

        $PopupWindowBorder = New-Object System.Windows.Controls.Border
        $PopupWindowBorder.Background = [System.Windows.Media.Brushes]::Purple
        $PopupWindowBorder.CornerRadius = '5'
        $PopupWindowBorder.Padding = '5'


        $PopupWindowInsideBorder = New-Object System.Windows.Controls.Border
        $PopupWindowInsideBorder.Background = [System.Windows.Media.Brushes]::LightGray
        $PopupWindowInsideBorder.CornerRadius = '5'
        $PopupWindowInsideBorder.Padding = '5'


        $PopupWindowLabel = New-Object System.Windows.Controls.Label
        $PopupWindowLabel.Content = $Message
        $PopupWindowLabel.FontFamily = "Segoe UI"
        $PopupWindowLabel.FontSize = 12

        $PopupWindowLabel.FontWeight = "Bold"
        $PopupWindowLabel.HorizontalContentAlignment = "Center"
        $PopupWindowLabel.VerticalContentAlignment = "Center"
        $PopupWindowLabel.Foreground = [System.Windows.Media.Brushes]::Black

        $PopupWindowInsideBorder.Child = $PopupWindowLabel
        $PopupWindowBorder.Child = $PopupWindowInsideBorder
        $Grid.Children.Add($PopupWindowBorder) | Out-Null
        $PopupWindow.Content = $Grid

        $PopupWindow.Show() | Out-Null
        return $PopupWindow
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Test-ConnectionRequirements {

    try {

        if ([string]::IsNullOrEmpty($Script:MainWindowForm.Elements.TextBoxURL.Text)) {
            "URL is empty" | Write-LogOutput -LogType DEBUG
            return $false
        }
        if ($null -eq $Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem) {
            "Authentication option is not selected" | Write-LogOutput -LogType DEBUG
            return $false
        }
        if ($Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem.Content -eq "OAuth") {
            "OAuth is selected" | Write-LogOutput -LogType DEBUG
            if ([string]::IsNullOrWhiteSpace($Script:MainWindowForm.Elements.TextBoxUserName.Text)) {
                "Username is empty" | Write-LogOutput -LogType DEBUG
                return $false
            }
            if ([string]::IsNullOrWhiteSpace($Script:MainWindowForm.Elements.TextBoxPassword.Password)) {
                "Password is empty" | Write-LogOutput -LogType DEBUG
                return $false
            }
            "OAuth connection requirements are met" | Write-LogOutput -LogType DEBUG
            return $true
        }
        "Browser connection requirements are met" | Write-LogOutput -LogType DEBUG
        return $true
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Test-ConnectionSettings {

    try {
        if ([string]::IsNullOrEmpty($Script:MainWindowForm.Elements.TextBoxURL.Text) -or $null -eq $Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem) {
            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.IsEnabled = $False
            $Script:MainWindowForm.Elements.ButtonRefreshQueries.IsEnabled = $False
            $Script:MainWindowForm.Elements.ButtonNewQuery.IsEnabled = $False
            $Script:MainWindowForm.Elements.TextBlockConnectionStatus | Set-TextBlockText -Text "Disconnected"
            $Script:MainWindowForm.Elements.TextBlockUrl | Set-TextBlockText -Text "-"
            $Script:MainWindowForm.Elements.TextBoxDisplayName.IsEnabled = $False
            $Script:MainWindowForm.Elements.TextBoxDisplayName.Text = $null
        }
        else {
            if ($Script:MainWindowForm.Elements.ComboBoxSelectAuthenticationOption.SelectedItem.Content -eq "OAuth" -and
([string]::IsNullOrWhiteSpace($Script:MainWindowForm.Elements.TextBoxUserName.Text) -or [string]::IsNullOrWhiteSpace($Script:MainWindowForm.Elements.TextBoxPassword.Password))) {
                $Script:MainWindowForm.Elements.ButtonReset.IsEnabled = $True
                $Script:MainWindowForm.Elements.ComboBoxSelectQuery.IsEnabled = $False
                $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem = $Null
                $Script:MainWindowForm.Elements.CheckboxMyQueries.IsEnabled = $False
                $Script:MainWindowForm.Elements.ButtonRefreshQueries.IsEnabled = $False
                $Script:MainWindowForm.Elements.ButtonNewQuery.IsEnabled = $False
                $Script:MainWindowForm.Elements.TextBlockConnectionStatus | Set-TextBlockText -Text "Disconnected"
                $Script:MainWindowForm.Elements.TextBlockUrl | Set-TextBlockText -Text "-"
                $Script:MainWindowForm.Elements.TextBoxDisplayName.IsEnabled = $False
                $Script:MainWindowForm.Elements.TextBoxDisplayName.Text = $null
            }
            else {
                $Script:MainWindowForm.Elements.ButtonReset.IsEnabled = $True
                $Script:MainWindowForm.Elements.ComboBoxSelectQuery.IsEnabled = $true
                $Script:MainWindowForm.Elements.CheckboxMyQueries.IsEnabled = $true
                $Script:MainWindowForm.Elements.ButtonRefreshQueries.IsEnabled = $true
                $Script:MainWindowForm.Elements.ButtonNewQuery.IsEnabled = $true
                $Script:MainWindowForm.Elements.TextBoxDisplayName.IsEnabled = $true
                $Script:MainWindowForm.Elements.TextBlockConnectionStatus | Set-TextBlockText -Text "Connected"
                $Script:MainWindowForm.Elements.TextBlockUrl.Text = ([System.Uri]::new($Script:MainWindowForm.Elements.TextBoxUrl.Text)).Authority

                if (($Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items | Measure-Object).Count -le 1 -or ($Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items | Measure-Object).Count -le 1) {
                    if ($null -ne $Script:MainWindowForm -and $Script:MainWindowForm.Definition -and $Script:MainWindowForm.Definition.IsVisible) {
                        $ConnectingWindow = Show-PopupWindow -Message "Connecting to Omada..."
                    }
                    if (($Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items | Measure-Object).Count -le 1) {
                        Update-DataConnectionList -NotShowPopupWindow
                    }
                    if (($Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items | Measure-Object).Count -le 1) {
                        Update-QueryList -NotShowPopupWindow
                    }
                    if ($null -ne $ConnectingWindow) {
                        $ConnectingWindow.Close()
                    }
                }
            }
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Test-LogWindowOpen {

    try {
        if ($null -ne $Script:LogWindowForm -and $null -ne $Script:LogWindowForm.Definition -and $Script:LogWindowForm.Definition.IsVisible) {
            "Test-LogWindowOpen: true" | Write-LogOutput -LogType VERBOSE2
            return $true
        }
        else {
            "Test-LogWindowOpen: false" | Write-LogOutput -LogType VERBOSE2
            return $false
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Test-MainWindowOpen {

    try {
        if ($null -ne $Script:MainWindowForm -and $null -ne $Script:MainWindowForm.Definition -and $Script:MainWindowForm.Definition.IsVisible) {
            return $true
        }
        else {
            return $false
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Test-Shortcut {
    $LocalAppDataPath = Join-Path ([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData)) -ChildPath "OmadaSqlTroubleShooter"
    if (-not (Test-Path -Path $LocalAppDataPath)) {
        New-Item -Path $LocalAppDataPath -ItemType Directory -Force | Out-Null
    }
    $PsCallStack = Get-PSCallStack | Where-Object { $_.ScriptName -like "*OmadaSqlTroubleShooter.psm1" }

    $ModulePath = Split-Path -Path $PsCallStack.ScriptName -Parent
    [xml]$MainWindowXaml = Get-Content (Join-Path $ModulePath -ChildPath "lib\ui\MainWindow.xaml")
    $ScriptTitle = $MainWindowXaml.Window.Title
    $WshShell = New-Object -ComObject WScript.Shell
    $ShortcutFullPath = Join-Path $WshShell.SpecialFolders("Programs") -ChildPath ("{0}.lnk" -f $ScriptTitle)

    $RunPath = Join-Path $LocalAppDataPath -ChildPath "Run.ps1"

    if (-not (Test-Path $ShortcutFullPath -PathType Leaf) ) {
        "Start Menu shortcut for this application is not present. Run Set-OmadaSqlTroubleshooterShortcut to create a Start Menu shortcut" | Write-Warning
    }
    else {
        if (-not (Test-Path $RunPath -PathType Leaf) ) {
            "Run.ps1. Start Menu shortcut will not work. Run Set-OmadaSqlTroubleshooterShortcut to fix the shortcut(s)." | Write-Error
        }
    }
}
function Test-SqlSchemaWindowOpen {

    try {
        if ($null -ne $Script:SqlSchemaWindowForm -and $null -ne $Script:SqlSchemaWindowForm.Definition -and $Script:SqlSchemaWindowForm.Definition.IsVisible) {
            "Test-SqlSchemaWindowOpen: true" | Write-LogOutput -LogType VERBOSE2
            return $true
        }
        else {
            "Test-SqlSchemaWindowOpen: false" | Write-LogOutput -LogType VERBOSE2
            return $false
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}

function Test-Variable {
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ExcludeVariable', Justification = 'The variable is used, but script analyzer does not recognize it')]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ExcludeAttribute', Justification = 'The variable is used, but script analyzer does not recognize it')]
    param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [string]$Expression,
        [switch]$ExcludeVariable,
        [switch]$ExcludeAttribute
    )

    function ReturnObject {
        if ($ExcludeVariable -and $ExcludeAttribute) {
            $Return = $false
        }
        elseif ($ExcludeAttribute -and !$ExcludeVariable) {
            $Return.Remove("AttributeExists")
            $Return = $Return.VariableExists
        }
        elseif ($ExcludeVariable -and !$ExcludeAttribute) {
            $Return.Remove("VariableExists")
            $Return = $Return.AttributeExists
        }
        return $Return
    }

    $Parts = $Expression.TrimStart('$').Trim() -split '\.'

    $Root = $Parts[0]

    $ScopePrefix = ''
    if ($Root -match '^(\w+:)') {
        $ScopePrefix = $Matches[1]
        $Root = $Root.Substring($ScopePrefix.Length)
    }

    $SessionState = $ExecutionContext.SessionState
    $Variable = $SessionState.PSVariable.Get($Root)

    $Return = @{
        VariableExists  = $false
        AttributeExists = $false
    }

    if ($null -eq $Variable) {
        $Return.VariableExists = $false
        return ReturnObject
    }

    $CurrentObject = $Variable.Value
    if (($Parts | Measure-Object).Count -eq 1) {
        return ReturnObject
    }
    foreach ($Part in $Parts[1..($Parts.Count - 1)]) {
        if ($null -eq $CurrentObject) {
            $Return.VariableExists = $true
            return ReturnObject
        }

        if ($CurrentObject -is [hashtable]) {
            $Member = $CurrentObject.keys | Where-Object {$_ -eq $Part}
        }
        else {
            $Member = $CurrentObject | Get-Member  -Name $Part -Force -ErrorAction SilentlyContinue | Where-Object {$_.MemberType -in @("Property", "Field", "Method")}
        }

        if ($null -eq $Member) {
            $Return.VariableExists = $true
            return ReturnObject
        }
        $CurrentObject = $CurrentObject.$Part
    }
    return ReturnObject
}
function Update-DataConnectionList {
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'SetInitialConnection', Justification = 'The variable is used, but script analyzer does not recognize it')]
    PARAM(
        [switch]$NotShowPopupWindow
    )

    try {

        if (!(Test-ConnectionRequirements)) {
            "Connection not ready" | Write-LogOutput -LogType DEBUG
            return
        }

        "Retrieve data connections" | Write-LogOutput -LogType DEBUG
        $SqlQueryViewContents = Get-SqlTroubleShooterView
        if ($null -ne $SqlQueryViewContents) {
            $Script:RunTimeData.RestMethodParam.Uri = "{0}/dataobjdlg.aspx?DOID={1}" -f $Script:AppConfig.BaseUrl, $SqlQueryViewContents[0].$($Script:RunTimeData.DataobjdlgAspxAttributeMapping.SqlQueryDoId)
            $Script:RunTimeData.RestMethodParam.Body = $null
            $Script:RunTimeData.RestMethodParam.Method = "GET"
            $Private:Result = Invoke-OmadaPSWebRequestWrapper

            if ($null -eq $Private:Result) {
                "Failed to retrieve data connections! Data connection cannot be changed!" | Write-LogOutput -LogType WARNING
                $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.IsEnabled = $False
                $Script:MainWindowForm.Elements.ButtonShowSqlSchema.IsEnabled = $false
            }
            else {
                if (!$NotShowPopupWindow) {
                    $UpdateDataConnectionsWindow = Show-PopupWindow -Message "Updating Data Connections..."
                }

                $SelectedDataConnection = $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.SelectedItem.Content
                "Stored current selected data connection (if not empty): {0}" -f $SelectedDataConnection | Write-LogOutput -LogType DEBUG
                $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items.Clear()

                if ($null -ne $Private:Result) {


                    $SetInitialConnection = $true
                    $Private:Result -split "`r`n" | ForEach-Object {
                        $Options = [regex]::Matches($Private:Result, '<option.*?value="(\d+).*?data-uid="(.*?)".*?>(.*?)</option>')
                        foreach ($Match in $Options) {
                            $DataConnectionDisplayName = "{0} - {1}" -f $Match.Groups[3].Value, $Match.Groups[1].Value
                            if ($DataConnectionDisplayName -notin $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items.Content) {
                                "Add data connection {0}" -f $DataConnectionDisplayName | Write-LogOutput -LogType DEBUG
                                $ComboBoxDataConnectionItem = New-Object System.Windows.Controls.ComboBoxItem
                                $ComboBoxDataConnectionItem.Content = $DataConnectionDisplayName
                                $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items.Add($ComboBoxDataConnectionItem) | Out-Null
                                if ($null -ne $SelectedDataConnection -and $SelectedDataConnection -eq $DataConnectionDisplayName) {
                                    "Set connection {0} as selected data connection" -f $DataConnectionDisplayName | Write-LogOutput -LogType DEBUG
                                    $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.SelectedItem = $ComboBoxDataConnectionItem
                                    $SetInitialConnection = $false
                                }
                            }
                        }
                    }
                    if ($SetInitialConnection) {
                        "Set initial data connection to OISES" | Write-LogOutput -LogType DEBUG
                        $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.SelectedItem = $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items | Where-Object {$_.Content -like "OISES -*"}
                    }
                    $Script:MainWindowForm.Elements.TextBoxDisplayName.IsEnabled = $true
                    $Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.IsEnabled = $True
                    $Script:MainWindowForm.Elements.ButtonShowSqlSchema.IsEnabled = $true
                }
                "{0} data connections processed!" -f ($Script:MainWindowForm.Elements.ComboBoxSelectDataConnection.Items | Measure-Object).Count | Write-LogOutput

                if ($null -ne $UpdateDataConnectionsWindow) {
                    $UpdateDataConnectionsWindow.Close()
                }
            }
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Update-LogWindow {

    try {
        if ($null -ne $Script:TextBoxLog) {
            $Script:TextBoxLog.Dispatcher.Invoke({
                    $Script:TextBoxLog.AppendText($LogMessage.Text + "`n")
                    if ($Script:TextBoxLog.IsLoaded -and (Invoke-LogWindowScrollToEnd)) {
                        $Script:TextBoxLog.ScrollToEnd()
                    }
                })
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Update-LogWindowPosition {

    try {
        $Script:LogWindowForm.PositionManager.MainWindowRight = [Int]::Abs($Script:MainWindowForm.Definition.Left) + [Int]::Abs($Script:MainWindowForm.Definition.Width)
        "PositionManagerLogWindow MainWindowRight: {0}" -f $Script:LogWindowForm.PositionManager.MainWindowRight | Write-LogOutput -LogType DEBUG
        $Script:LogWindowForm.PositionManager.MainWindowBottom = [Int]::Abs($Script:MainWindowForm.Definition.Top) - [Int]::Abs($Script:MainWindowForm.Definition.Height)
        "PositionManagerLogWindow MainWindowBottom: {0}" -f $Script:LogWindowForm.PositionManager.MainWindowRight | Write-LogOutput -LogType DEBUG
        $Script:LogWindowForm.PositionManager.ChildWindowRight = [Int]::Abs($Script:LogWindowForm.Definition.Left) + [Int]::Abs($Script:LogWindowForm.Definition.Width)
        "PositionManagerLogWindow ChildWindowRight: {0}" -f $Script:LogWindowForm.PositionManager.ChildWindowRight | Write-LogOutput -LogType DEBUG
        $Script:LogWindowForm.PositionManager.ChildWindowBottom = [Int]::Abs($Script:LogWindowForm.Definition.Top) - [Int]::Abs($Script:LogWindowForm.Definition.Height)
        "PositionManagerLogWindow ChildWindowBottom: {0}" -f $Script:LogWindowForm.PositionManager.ChildWindowBottom | Write-LogOutput -LogType DEBUG

        if ([Int]::Abs($Script:LogWindowForm.Definition.Left) -lt [Int]::Abs($Script:MainWindowForm.Definition.Left)) {
            $Script:LogWindowForm.Definition.Left = [Int]::Abs($Script:MainWindowForm.Definition.Left) + [Int]::Abs($Script:MainWindowForm.Definition.Width)
            "LogWindowForm Definition Left: {0}" -f $Script:LogWindowForm.Definition.Left | Write-LogOutput -LogType DEBUG

        }
        elseif ($Script:LogWindowForm.PositionManager.ChildWindowRight -gt $Script:LogWindowForm.PositionManager.MainWindowRight) {
            $Script:LogWindowForm.Definition.Left = [Int]::Abs($Script:MainWindowForm.Definition.Left) - $Script:LogWindowForm.Definition.Width
            "LogWindowForm Definition Left: {0}" -f $Script:LogWindowForm.Definition.Left | Write-LogOutput -LogType DEBUG
        }

        if ($Script:LogWindowForm.Definition.Top -lt [Int]::Abs($Script:MainWindowForm.Definition.Top)) {
            $Script:LogWindowForm.Definition.Top = [Int]::Abs($Script:MainWindowForm.Definition.Top) + [Int]::Abs($Script:MainWindowForm.Definition.Height)
            "LogWindowForm Definition Top: {0}" -f $Script:LogWindowForm.Definition.Top | Write-LogOutput -LogType DEBUG
        }
        elseif ($Script:PositionManager.ChildWindowBottom -gt $MainWindowBottom) {
            $Script:LogWindowForm.Definition.Top = [Int]::Abs($Script:MainWindowForm.Definition.Top) - [Int]::Abs($Script:LogWindowForm.Definition.Height)
            "LogWindowForm Definition Top: {0}" -f $Script:LogWindowForm.Definition.Top | Write-LogOutput -LogType DEBUG
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Update-QueryList {
    PARAM(
        [switch]$ForceRefresh,
        [switch]$NotShowPopupWindow
    )

    try {
        if (!(Test-ConnectionRequirements)) {
            "Connection not ready" | Write-LogOutput -LogType DEBUG
            return
        }

        $CurrentTimestamp = Get-Date

        if (($Script:RunTimeData.QueryListCache.QueryList | Measure-Object).Count -eq 0 -or $ForceRefresh -or $Script:RunTimeData.QueryListCache.LastRefresh -lt $CurrentTimestamp.AddSeconds( - $($Script:RunTimeData.QueryListCache.TTL))) {
            $Script:RunTimeData.QueryListCache.QueryList = $null
            "Cleared query cache!" | Write-LogOutput -LogType DEBUG
        }

        "Queries in cache: {0}" -f ($Script:RunTimeData.QueryListCache.QueryList | Measure-Object).Count | Write-LogOutput -LogType DEBUG

        if (($Script:RunTimeData.QueryListCache.QueryList | Measure-Object).Count -le 0) {
            if (!$NotShowPopupWindow) {
                $Script:PopUpWindowQueryRefresh = Show-PopupWindow -Message "Refreshing queries..."
            }
            $Script:RunTimeData.QueryListCache.QueryList = @()
            if ($Script:AppConfig.MyQueriesOnly -and ![string]::IsNullOrWhiteSpace($Script:AppConfig.IdentityUserName)) {
                $SqlQueryViewContents = Get-SqlTroubleShooterView | Where-Object { $_.$($Script:RunTimeData.DataobjdlgAspxAttributeMapping.SqlQueryCreatedBy) -eq $Script:AppConfig.IdentityUserName -or $_.$($Script:RunTimeData.DataobjdlgAspxAttributeMapping.SqlQueryChangedBy) -eq $Script:AppConfig.IdentityUserName }
            }

            $Script:RunTimeData.RestMethodParam.Uri = '{0}/odata/dataobjects/C_P_SQLTROUBLESHOOTING?$filter=Deleted ne true and NAME ne ''''' -f $Script:AppConfig.BaseUrl
            "QueryUrl: {0}" -f $Script:RunTimeData.RestMethodParam.Uri | Write-LogOutput -LogType DEBUG
            "Refresh queries started" | Write-LogOutput
            $Script:RunTimeData.RestMethodParam.Body = $Null
            $Script:RunTimeData.RestMethodParam.Method = "GET"
            $Script:RunTimeData.RestMethodParam.Body = $null
            $Private:Result = Invoke-OmadaPSWebRequestWrapper

            $SelectedQuery = $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem.Content
            "Stored current selected query (if not empty): {0}" -f $SelectedQuery | Write-LogOutput -LogType DEBUG
            $SelectedQueryDisplayName = $Script:MainWindowForm.Elements.TextBoxDisplayName.Text
            "Stored current selected query display name (if not empty): {0}" -f $SelectedQueryDisplayName | Write-LogOutput -LogType DEBUG
            $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items.Clear()
            $Script:MainWindowForm.Elements.TextBoxDisplayName.Text = $Null

            $ClearQuery = $true
            $Private:Result.value | ForEach-Object {
                $DoIdDisplayName = "{0} - {1}" -f $_.DisplayName, $_.Id
                $Script:RunTimeData.QueryListCache.QueryList += @{
                    $_.Id = $_.DisplayName
                }
                if ($Script:AppConfig.MyQueriesOnly -and $null -ne $SqlQueryViewContents -and $_.Id -notin $SqlQueryViewContents.$($Script:RunTimeData.DataobjdlgAspxAttributeMapping.SqlQueryDoId)) {
                    "Skip query {0} because of 'Filter My Queries' is enabled" -f $DoIdDisplayName | Write-LogOutput -LogType DEBUG
                    if ($null -ne $SelectedQuery -and $SelectedQuery -eq $DoIdDisplayName) {
                        "Selected query {0} is filtered, clear selected query" -f $DoIdDisplayName | Write-LogOutput -LogType DEBUG
                        $SelectedQuery = $null
                    }
                }
                else {
                    if ($Script:AppConfig.MyQueriesOnly -and $null -ne $SqlQueryViewContents -and $_.Id -notin $SqlQueryViewContents.$($Script:RunTimeData.DataobjdlgAspxAttributeMapping.SqlQueryDoId)) {
                        "Add query {0} because of 'Filter My Queries' is enabled" -f $DoIdDisplayName | Write-LogOutput -LogType DEBUG
                    }
                    else {
                        "Add query {0}" -f $DoIdDisplayName | Write-LogOutput -LogType DEBUG
                    }
                    $ComboBoxSelectQueryItem = New-Object System.Windows.Controls.ComboBoxItem
                    $ComboBoxSelectQueryItem.Content = $DoIdDisplayName
                    $Script:MainWindowForm.Elements.ComboBoxSelectQuery.Items.Add($ComboBoxSelectQueryItem) | Out-Null
                }
                if ($ClearQuery -and $null -ne $SelectedQuery -and $SelectedQuery -eq $DoIdDisplayName) {
                    "Set query {0} as selected query" -f $DoIdDisplayName | Write-LogOutput -LogType DEBUG
                    $Script:MainWindowForm.Elements.ComboBoxSelectQuery.SelectedItem = $ComboBoxSelectQueryItem
                    "Set query display name to: {0}" -f $SelectedQueryDisplayName | Write-LogOutput -LogType DEBUG
                    $Script:MainWindowForm.Elements.TextBoxDisplayName.Text = $SelectedQueryDisplayName
                    $ClearQuery = $false
                }
            }
            if ($ClearQuery) {
                "Clear editor window because query is not set" | Write-LogOutput -LogType DEBUG
                Set-EditorValue
            }
            if ($null -ne $Script:PopUpWindowQueryRefresh) {
                $Script:PopUpWindowQueryRefresh.Close()
            }
        }
        else {
            "Query list retrieved from cache! Click `"Refresh Queries`" to refresh queries" | Write-LogOutput -LogType INFO
        }

        $Script:MainWindowForm.Elements.ComboBoxSelectQuery.IsEnabled = $True
        $Script:MainWindowForm.Elements.ButtonRefreshQueries.IsEnabled = $True
        $Script:MainWindowForm.Elements.CheckboxMyQueries.IsEnabled = $True
        $Script:MainWindowForm.Elements.ButtonShowSqlSchema.IsEnabled = $true
        $Script:RunTimeData.QueryListCache.LastRefresh = $CurrentTimestamp
        "{0} queries retrieved!" -f ($Script:RunTimeData.QueryListCache.QueryList | Measure-Object).Count | Write-LogOutput

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Update-SqlSchemaWindow {

    try {
        if ($null -ne $Script:TreeViewSqlSchema) {
            $Script:TreeViewSqlSchema.Dispatcher.Invoke({
                    $null
                })
        }
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Update-SqlSchemaWindowPosition {

    try {
        $Script:SqlSchemaWindowForm.PositionManager.MainWindowRight = [Int]::Abs($Script:MainWindowForm.Definition.Left) + [Int]::Abs($Script:MainWindowForm.Definition.Width)
        "PositionManagerSqlSchemaWindow MainWindowRight: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.MainWindowRight | Write-LogOutput -LogType DEBUG
        $Script:SqlSchemaWindowForm.PositionManager.MainWindowBottom = [Int]::Abs($Script:MainWindowForm.Definition.Top) - [Int]::Abs($Script:MainWindowForm.Definition.Height)
        "PositionManagerSqlSchemaWindow MainWindowBottom: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.MainWindowRight | Write-LogOutput -LogType DEBUG
        $Script:SqlSchemaWindowForm.PositionManager.ChildWindowRight = [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Left) + [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Width)
        "PositionManagerSqlSchemaWindow ChildWindowRight: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.ChildWindowRight | Write-LogOutput -LogType DEBUG
        $Script:SqlSchemaWindowForm.PositionManager.ChildWindowBottom = [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Top) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Height)
        "PositionManagerSqlSchemaWindow ChildWindowBottom: {0}" -f $Script:SqlSchemaWindowForm.PositionManager.ChildWindowBottom | Write-LogOutput -LogType DEBUG

        if ([Int]::Abs($Script:SqlSchemaWindowForm.Definition.Left) -lt [Int]::Abs($Script:MainWindowForm.Definition.Left)) {
            $Script:SqlSchemaWindowForm.Definition.Left = [Int]::Abs($Script:MainWindowForm.Definition.Left) + [Int]::Abs($Script:MainWindowForm.Definition.Width)
            "SqlSchemaWindowForm Definition Left: {0}" -f $Script:SqlSchemaWindowForm.Definition.Left | Write-LogOutput -LogType DEBUG

        }
        elseif ($Script:SqlSchemaWindowForm.PositionManager.ChildWindowRight -gt $Script:SqlSchemaWindowForm.PositionManager.MainWindowRight) {
            $Script:SqlSchemaWindowForm.Definition.Left = [Int]::Abs($Script:MainWindowForm.Definition.Left) - $Script:SqlSchemaWindowForm.Definition.Width
            "SqlSchemaWindowForm Definition Left: {0}" -f $Script:SqlSchemaWindowForm.Definition.Left | Write-LogOutput -LogType DEBUG
        }

        if ($Script:SqlSchemaWindowForm.Definition.Top -lt [Int]::Abs($Script:MainWindowForm.Definition.Top)) {
            $Script:SqlSchemaWindowForm.Definition.Top = [Int]::Abs($Script:MainWindowForm.Definition.Top) + [Int]::Abs($Script:MainWindowForm.Definition.Height)
            "SqlSchemaWindowForm Definition Top: {0}" -f $Script:SqlSchemaWindowForm.Definition.Top | Write-LogOutput -LogType DEBUG
        }
        elseif ($Script:PositionManager.ChildWindowBottom -gt $MainWindowBottom) {
            $Script:SqlSchemaWindowForm.Definition.Top = [Int]::Abs($Script:MainWindowForm.Definition.Top) - [Int]::Abs($Script:SqlSchemaWindowForm.Definition.Height)
            "SqlSchemaWindowForm Definition Top: {0}" -f $Script:SqlSchemaWindowForm.Definition.Top | Write-LogOutput -LogType DEBUG
        }

    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR
    }
}
function Write-LogOutput {
    PARAM(
        [parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $True)]
        [string]$Message,
        [ValidateSet("DEBUG", "INFO", "ERROR", "VERBOSE", "WARNING", "FATAL", "LOG", "VERBOSE2")]
        [string]$LogType = "INFO",
        [switch]$SkipDialog
    )

    try {

        if ($null -eq $Message) {
            $Message = "-"
        }
        $DateTimeObject = Get-Date
        $DateTime = $DateTimeObject.ToString("yyyy-MM-dd HH:mm:ss")
        if ($Script:RunTimeConfig.Logging.LogLevelSetting -in ("VERBOSE", "VERBOSE2")) {
            $DateTime = $DateTimeObject.ToString("o")
        }

        $PSCallStack = Get-PSCallStack
        try {
            $Command = $null
            $Command = $PSCallStack[1]
            if ([string]::IsNullOrWhiteSpace($Command.Command)) {
(Get-PSCallStack) | ForEach-Object {
                    if ([string]::IsNullOrWhiteSpace($Command.Command) -and $_.Command -ne $MyInvocation.MyCommand -and ![string]::IsNullOrWhiteSpace($_.Command)) {
                        $Command = $_
                    }
                }
            }
            $CalledFrom = "{0} ({1})" -f $Command.Command, $Command.ScriptLineNumber
        }
        catch {
            $CalledFrom = $null
        }
        $LogMessage = @{
            Text        = "{0} - {1} - {2}: {3}" -f $DateTime, $LogType, $CalledFrom, $Message
            Show        = $false
            ShowWarning = $false
            ShowError   = $false
            ShowVerbose = $false
            Color       = "White"
        }

        $LogMessageDialog = @{
            Show        = $false
            Text        = $Message
            DialogTitle = $null
            DialogIcon  = $null
        }

        switch ($Script:RunTimeConfig.Logging.LogLevelSetting) {
            { $_ -eq "VERBOSE2" -and $LogType -in @( "DEBUG", "INFO", "ERROR", "VERBOSE", "WARNING", "FATAL", "LOG", "VERBOSE2") } {
                $LogMessage.Show = $true
                $LogMessage.Color = "Gray"
            }
            { $_ -eq "VERBOSE" -and $LogType -in @( "DEBUG", "INFO", "ERROR", "VERBOSE", "WARNING", "FATAL", "LOG") } {
                $LogMessage.Show = $true
                $LogMessage.Color = "Magenta"
            }
            { $_ -eq "DEBUG" -and $LogType -in @( "DEBUG", "INFO", "ERROR", "WARNING", "FATAL", "LOG") } {
                $LogMessage.Show = $true
                $LogMessage.Color = "Cyan"
            }
            { $_ -eq "INFO" -and $LogType -in @( "INFO", "ERROR", "WARNING", "FATAL", "LOG") } {
                $LogMessage.Show = $true
                $LogMessage.Color = "White"
            }
            { $_ -eq "WARNING" -and $LogType -in @(  "ERROR", "WARNING", "FATAL", "LOG") } {
                $LogMessage.Show = $true
                $LogMessage.Color = "Yellow"
            }
            { $_ -in @("ERROR", "FATAL") -and $LogType -in @(  "ERROR", "FATAL", "LOG") } {
                $LogMessage.Show = $true
                $LogMessage.Color = "Red"
            }
            Default {
                $LogMessage.Show = $false
            }
        }

        switch ($LogType) {
            { $_ -eq "VERBOSE2" -and $LogMessage.Show } {
                if (!$Script:RunTimeConfig.VerboseParameterSet -and $Script:RunTimeConfig.LogToConsole) {
                    $LogMessage.ShowVerbose = $true
                }
            }
            { $_ -eq "VERBOSE" -and $LogMessage.Show } {
                if (!$Script:RunTimeConfig.VerboseParameterSet -and $Script:RunTimeConfig.LogToConsole) {
                    $LogMessage.ShowVerbose = $true
                }
            }
            { $_ -eq "DEBUG" -and $LogMessage.Show } {}
            { $_ -eq "INFO" -and $LogMessage.Show } {}
            { $_ -eq "WARNING" -and $LogMessage.Show } {
                $LogMessage.ShowWarning = $true
                $LogMessageDialog.Show = $true
                $LogMessageDialog.Text = "Warning:`r`n`r`n{0}" -f $LogMessageDialog.Text
                $LogMessageDialog.Title = "Warning"
                $LogMessageDialog.Icon = [System.Windows.Forms.MessageBoxIcon]::Warning
            }
            { $_ -in @("ERROR", "FATAL") -and $LogMessage.Show } {
                try {
                    $CallStack = $null # Get-PSCallStack | ConvertTo-Json -Depth 15 -ErrorAction SilentlyContinue
                    "{0}`r`n{1}" -f $LogMessage.Text, $CallStack | Write-Verbose
                }
                catch {}
                $LogMessage.ShowError = $true
                $LogMessageDialog.Show = $true
                $LogMessageDialog.Text = "Failure occurred:`r`n`r`n{0}" -f $LogMessageDialog.Text
                $LogMessageDialog.Title = "Error"
                $LogMessageDialog.Icon = [System.Windows.Forms.MessageBoxIcon]::Error
            }
            { $_ -eq "LOG" -and $LogMessage.Show } {}
            Default {}
        }

        if ($LogMessage.Show) {
            $Script:RunTimeConfig.Logging.AppLogObject.Add(($LogMessage.Text) -join "`r`n")
            if ($Script:RunTimeConfig.LogToConsole) {
                $LogMessage.Text | Write-Host -ForegroundColor $LogMessage.Color
            }
        }
        if ($LogMessage.ShowVerbose) {
            $LogMessage.Text | Write-Verbose
        }
        if ($LogMessageDialog.Show -and !$SkipDialog) {
            if ($null -ne $Script:MainWindowForm -and $null -ne $Script:MainWindowForm.Definition -and $Script:MainWindowForm.Definition.IsVisible) {
                [System.Windows.Forms.MessageBox]::Show($LogMessageDialog.Text, $LogMessageDialog.Title, [System.Windows.Forms.MessageBoxButtons]::OK, $LogMessageDialog.Icon)
            }
            else {
                if ($LogMessage.ShowWarning) {
                    $LogMessage.Text | Write-Warning
                }
                elseif ($LogMessage.ShowError) {
                    $LogMessage.Text | Write-Error
                }
                else {
                    $LogMessage.Text | Write-Host -ForegroundColor $LogMessage.Color
                }
            }
        }
        if ($LogMessage.ShowError) {
            $LogMessage.Text | Write-Error
        }
        if ($null -ne $Script:TextBoxLog -and $Script:TextBoxLog.IsLoaded) {
            if (Invoke-LogWindowScrollToEnd) {
                $Script:TextBoxLog.ScrollToEnd()
            }
        }
    }
    catch {
        $_.Exception.Message | Write-Error
    }
}

<#
.SYNOPSIS
Starts the Omada SQL Troubleshooter application.
 
.DESCRIPTION
The `Invoke-OmadaSqlTroubleshooter` function initializes and starts the Omada SQL Troubleshooter application.
This application is used to manage and execute SQL queries stored in the SQL Troubleshooting section in Omada Identity Suite.
 
.PARAMETER LogLevel
Specifies the log level for the application. Acceptable values are INFO, DEBUG, VERBOSE, WARNING, ERROR, FATAL and VERBOSE2.
 
.PARAMETER Reset
Resets the stored configuration to default.
 
.PARAMETER LogToConsole
Outputs logging to the console.
 
.EXAMPLE
Invoke-OmadaSqlTroubleshooter
 
Starts the Omada SQL Troubleshooter application with default settings.
 
.EXAMPLE
Invoke-OmadaSqlTroubleshooter -LogLevel DEBUG -LogToConsole
 
Starts the Omada SQL Troubleshooter application with log level set to DEBUG and logs output to the console.
 
.EXAMPLE
Invoke-OmadaSqlTroubleshooter -Reset
 
Resets the stored configuration to default and starts the Omada SQL Troubleshooter application.
 
.NOTES
Requires PowerShell 7.0 or higher and the OmadaWeb.PS module.
 
#>


function Invoke-OmadaSqlTroubleshooter {
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'StartVariables', Justification = 'The CurrentPoperties variable is used in a function called from here')]
    [CmdletBinding()]
    PARAM(
        [ValidateSet("INFO", "DEBUG", "VERBOSE", "WARNING", "ERROR", "FATAL", "VERBOSE2")]
        [string]$LogLevel,
        [switch]$Reset,
        [switch]$LogToConsole
    )
    $Error.Clear()

    $StartVariables = Get-Variable
    $ApplicationName = "OmadaSqlTroubleshooter"

    $Script:RunTimeConfig = @{
        ScriptName         = "OmadaSqlTroubleshooter"
        ApplicationTitle   = ""
        ModuleFolder       = Split-Path (Get-Module OmadaSqlTroubleShooter).Path
        AppDataFolder      = Join-Path ([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ApplicationData)) -ChildPath $ApplicationName
        Logging            = [PSCustomObject]@{
            LogToConsole        = $LogToConsole.IsPresent -or $false
            LogLevel            = $null
            VerboseParameterSet = $PSCmdlet.MyInvocation.BoundParameters.Keys.Contains("Verbose")
            LogLevelSetting     = $(if ([string]::IsNullOrWhiteSpace($LogLevel)) { $null }else { $LogLevel })
            AppLogObject        = [System.Collections.ObjectModel.ObservableCollection[string]]::new()
        }
        StopWatch          = $null
        LastWindowMeasured = Get-Date
        ConfigFile         = [PSCustomObject]@{
            Path = $null
            Name = $null
        }
        AuthenticationSet  = $false
        OutputFileName     = $null
    }
    Get-ChildItem -Path (Join-Path $Script:RunTimeConfig.ModuleFolder -ChildPath "Lib\Functions") -Filter *.ps1 | ForEach-Object {
        . $_.FullName
    }
    Initialize-OmadaSqlTroubleShooter

    $SplashScreenForm = Open-SplashScreenForm
    "Loading Main Window Object" | Write-LogOutput -LogType DEBUG
    $Script:MainWindowForm = New-FormObject -FormPath (Join-Path $Script:RunTimeConfig.ModuleFolder -ChildPath "lib\ui\MainWindow.xaml")
    $Script:RunTimeConfig.ApplicationTitle = $Script:MainWindowForm.Definition.Title.ToString()
    "Get WebView" | Write-LogOutput -LogType DEBUG
    $Script:Webview.Object = $Script:MainWindowForm.Definition.FindName("webView21")


    try {
        "Read Events" | Write-LogOutput -LogType DEBUG
        Get-ChildItem -Path (Join-Path $Script:RunTimeConfig.ModuleFolder -ChildPath "Lib\Events") -Filter *.ps1 | ForEach-Object {
            . $_.FullName
        }
    }
    catch {
        if ($_.Exception.Response.StatusCode -eq "NotFound") {
            "SQL Troubleshooting Object not found or OData endpoint for SQL Troubleshooting is not found. Is it enable for OData? Please check the data object type properties!" | Write-LogOutput -LogType ERROR
        }
        else {
            $_.Exception.Message | Write-LogOutput -LogType ERROR
        }
    }

    try {
        "Show Splash Screen" | Write-LogOutput -LogType DEBUG
        [void]$SplashScreenForm.Show()
        [System.Windows.Forms.Application]::DoEvents()

        "Application '{0}': Start initialization..." -f $Script:RunTimeConfig.ApplicationTitle | Write-Host -ForegroundColor Green
        Initialize-ConfigSettings

        Close-SplashScreenForm
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR -SkipDialog
        Close-SplashScreenForm
        Clear-Variables
    }

    try {
        $Message = "Application '{0}': Initialized!" -f $Script:RunTimeConfig.ApplicationTitle
        $Message | Write-Host -ForegroundColor Green
        $Message | Write-LogOutput -LogType DEBUG
        "Loading Main Window with settings:`r`n{0}" -f ($Script:AppConfig | ConvertTo-Json) | Write-LogOutput -LogType DEBUG

        [void]$Script:MainWindowForm.Definition.ShowDialog()
        $Message = "Application '{0}': Closed, cleaning-up!" -f $Script:RunTimeConfig.ApplicationTitle
        $Message | Write-Host -ForegroundColor Green
        $Message | Write-LogOutput -LogType DEBUG
        "Invoke-ConfigSetting" | Write-LogOutput -LogType DEBUG
        Invoke-ConfigSetting
        "Close Main Window" | Write-LogOutput -LogType DEBUG
        $Script:MainWindowForm.Definition.Close() | Out-Null
        $Script:Webview.Object.Dispose() | Out-Null
    }
    catch {
        $_.Exception.Message | Write-LogOutput -LogType ERROR -SkipDialog
        Close-SplashScreenForm
        Clear-Variables
    }

    Pop-Location
    Clear-Variables
    "Application '{0}': Clean-up complete!" -f $Script:RunTimeConfig.ApplicationTitle | Write-Host -ForegroundColor Green
}
<#
.SYNOPSIS
Creates a shortcut for the Omada SQL Troubleshooter application.
 
.DESCRIPTION
The `Set-OmadaSqlTroubleShooterShortcut` function creates a shortcut for the Omada SQL Troubleshooter application in the Start Menu and optionally on the Desktop.
 
.PARAMETER NotCreateDesktopShortcut
If specified, the function will not create a desktop shortcut.
 
.EXAMPLE
Set-OmadaSqlTroubleShooterShortcut
 
Creates a shortcut for the Omada SQL Troubleshooter application in the Start Menu and on the Desktop.
 
.EXAMPLE
Set-OmadaSqlTroubleShooterShortcut -NotCreateDesktopShortcut
 
Creates a shortcut for the Omada SQL Troubleshooter application in the Start Menu only.
 
.NOTES
Requires PowerShell 7.0 or higher.
 
#>


function Set-OmadaSqlTroubleShooterShortcut {
    [CmdletBinding()]
    PARAM(
        [switch]$NotCreateDesktopShortcut
    )
    "Create Start Menu shortcut" | Write-Host
    $LocalAppDataPath = Join-Path ([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData)) -ChildPath "OmadaSqlTroubleShooter"
    if (-not (Test-Path -Path $LocalAppDataPath)) {
        New-Item -Path $LocalAppDataPath -ItemType Directory -Force | Out-Null
    }
    $ModuleInfo = Get-Module OmadaSqlTroubleShooter
    $ModulePath = Split-Path -Path $ModuleInfo.Path -Parent
    [xml]$MainWindowXaml = Get-Content (Join-Path $ModulePath -ChildPath "lib\ui\MainWindow.xaml")
    $ScriptTitle = $MainWindowXaml.Window.Title

    $WshShell = New-Object -ComObject WScript.Shell
    $PowerShellExecPath = (Get-Command "pwsh.exe").Path
    $OmadaSqlTroubleShooterIcoPath = Join-Path $ModulePath -ChildPath "lib\ui\OmadaSqlTroubleShooter.ico"
    $ShortcutFullPath = Join-Path $WshShell.SpecialFolders("Programs") -ChildPath ("{0}.lnk" -f $ScriptTitle)
    $RunPath = Join-Path $LocalAppDataPath -ChildPath "Run.ps1"
    "Push-Location '{0}';
Import-Module -Name 'OmadaSqlTroubleShooter';
Invoke-OmadaSqlTroubleshooter;
Pop-Location;
'Window will automatically close in 5 seconds!' | Write-Host -ForegroundColor Green;
Start-Sleep -Seconds 5"
 -f $LocalAppDataPath | Set-Content $RunPath -Force -Encoding utf8
    $Arguments = ' -File "{0}"' -f $RunPath

    $WshShell = New-Object -ComObject WScript.Shell
    $Shortcut = $WshShell.CreateShortcut($ShortcutFullPath)
    $Shortcut.TargetPath = $PowerShellExecPath
    $Shortcut.WorkingDirectory = $LocalAppDataPath
    $Shortcut.Arguments = $Arguments
    $Shortcut.IconLocation = ("{0},0" -f $OmadaSqlTroubleShooterIcoPath)
    $Shortcut.Save()

    if ($NotCreateDesktopShortcut) {
        "Desktop shortcut not created" | Write-Host
    }
    else {
        Get-Item -Path $ShortcutFullPath | Copy-Item -Destination $WshShell.SpecialFolders("Desktop") -Force
        "Created desktop shortcut. Use parameter -NotCreateDesktopShortcut to skip this part." | Write-Host
    }
}