public/New-AzResourceNameGeneratorGUI.ps1

function New-AzResourceNameGeneratorGUI {

<#
.AUTHOR
    MichaƂ Machniak
 
.SYNOPSIS
    Azure Resource Naming Convention Generator GUI
.DESCRIPTION
    This function provides a graphical user interface (GUI) for generating Azure resource names based on naming conventions.
    Users can select resource types from a file, configure naming parameters, and generate resource names with visual feedback.
    The generated names can be displayed in a results window or exported to clipboard.
 
.PARAMETER ResourceNameSchema
    Path to the resource schema JSON file that defines general naming convention.
    Default: https://raw.githubusercontent.com/mimachniak/AzureResources-NameGenerator/refs/heads/main/data/general_naming_shema.json
 
.PARAMETER ResourcesData
    Path to the resource types JSON file. Can be a URL or local file path.
    Default: https://raw.githubusercontent.com/mspnp/AzureNamingTool/refs/heads/main/src/repository/resourcetypes.json
 
.PARAMETER ResourceTypesFile
    Path to a file containing resource types (one per line) to be loaded into the GUI dropdown.
    This allows users to select from predefined resource types.
 
.EXAMPLE
    New-AzResourceNameGeneratorGUI
 
.EXAMPLE
    New-AzResourceNameGeneratorGUI -ResourceTypesFile "C:\resourcetypes.txt"
 
.NOTES
    This GUI requires Windows Forms assembly. It provides an interactive way to:
    - Select multiple resource types from a searchable list
    - Configure environment, region, unique identifier, and other parameters
    - Generate resource names in real-time
    - View results in a grid
    - Export results to clipboard or file
#>


    [CmdletBinding()]
    param(
        [Parameter(HelpMessage = "Path to the resource schema JSON file.")]
        [string]$ResourceNameSchema = "https://raw.githubusercontent.com/mimachniak/AzureResources-NameGenerator/refs/heads/main/data/general_naming_shema.json",

        [Parameter(HelpMessage = "Use default settings to load resource schema from web.")]
        [string]$ResourcesData,

        [Parameter(HelpMessage = "Path to a file containing resource types (one per line).")]
        [string]$ResourceTypesFile
    )

    # Determine the script directory to find resourcetypes.json
    $scriptDir = Split-Path -Parent (Split-Path -Parent $PSCommandPath)
    $localResourcesFile = Join-Path -Path $scriptDir -ChildPath "data\resourcetypes.json"
    
    # If ResourcesData is not specified, try to use local file first, then fall back to URL
    if ([string]::IsNullOrWhiteSpace($ResourcesData)) {
        if (Test-Path $localResourcesFile) {
            $ResourcesData = $localResourcesFile
            Write-Host "Using local resource types file: $ResourcesData" -ForegroundColor Green
        }
        else {
            $ResourcesData = "https://raw.githubusercontent.com/mspnp/AzureNamingTool/refs/heads/main/src/repository/resourcetypes.json"
            Write-Host "Using remote resource types from: $ResourcesData" -ForegroundColor Yellow
        }
    }

    # Load required assemblies
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing

    # Helper function to load resources from JSON file
    function Get-ResourceTypesFromJson {
        param(
            [Parameter(Mandatory = $true)]
            [string]$Path
        )
        
        try {
            $content = $null
            
            # Check if it's a URL or file path
            if ($Path -match '^https?://') {
                # Download from URL
                Write-Verbose "Loading resources from URL: $Path"
                $content = Invoke-WebRequest -Uri $Path -ErrorAction Stop | Select-Object -ExpandProperty Content
            }
            elseif (Test-Path $Path) {
                # Load from file
                Write-Verbose "Loading resources from file: $Path"
                $content = Get-Content -Path $Path -Raw -ErrorAction Stop
            }
            else {
                Write-Warning "Resource file not found: $Path"
                return @()
            }
            
            # Parse JSON and extract resource names
            $jsonData = $content | ConvertFrom-Json -ErrorAction Stop
            
            # Handle both array of objects and simple array
            if ($jsonData -is [System.Collections.IEnumerable] -and -not ($jsonData -is [string])) {
                # If it's an array of objects, extract the 'resource' property
                if ($jsonData[0] -is [PSCustomObject] -and $jsonData[0].PSObject.Properties['resource']) {
                    $resources = @($jsonData | Select-Object -ExpandProperty resource | Sort-Object -Unique)
                    Write-Verbose "Extracted 'resource' property from JSON objects"
                }
                else {
                    # If it's a simple array of strings
                    $resources = @($jsonData | Sort-Object -Unique)
                    Write-Verbose "Using array of strings from JSON"
                }
            }
            else {
                Write-Warning "Unexpected JSON format: Expected array but got $($jsonData.GetType().Name)"
                return @()
            }
            
            if ($resources.Count -gt 0) {
                Write-Verbose "Loaded $($resources.Count) resource types: $($resources[0..4] -join ', ')..."
                Write-Host "Successfully loaded $($resources.Count) resource types from file" -ForegroundColor Green
            }
            return $resources
        }
        catch {
            Write-Error "Error loading resources from $Path : $_" -ErrorAction Continue
            Write-Host "Failed to load resources: $_" -ForegroundColor Red
            return @()
        }
    }

    # Load resource types from file or JSON - use script scope to ensure accessibility in nested scopes
    $script:resourceTypesToLoad = @()
    if ($ResourceTypesFile -and (Test-Path $ResourceTypesFile)) {
        if ($ResourceTypesFile -match '\.json$') {
            # Load from JSON file
            $script:resourceTypesToLoad = @(Get-ResourceTypesFromJson -Path $ResourceTypesFile)
        }
        else {
            # Load from text file (one per line)
            $script:resourceTypesToLoad = @(Get-Content -Path $ResourceTypesFile | Where-Object { $_ -notmatch '^\s*$' })
        }
    }
    else {
        # Try to load from default ResourcesData
        $script:resourceTypesToLoad = @(Get-ResourceTypesFromJson -Path $ResourcesData)
    }

    # Create main form
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "Azure Resource Name Generator"
    $form.Width = 1000
    $form.Height = 700
    $form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
    $form.TopMost = $false
    $form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font

    # Create a TabControl for organizing UI
    $tabControl = New-Object System.Windows.Forms.TabControl
    $tabControl.Dock = [System.Windows.Forms.DockStyle]::Fill
    $tabControl.ItemSize = New-Object System.Drawing.Size(100, 30)

    # Tab 1: Parameters
    $tabParameters = New-Object System.Windows.Forms.TabPage
    $tabParameters.Text = "Parameters"
    $tabParameters.Padding = New-Object System.Windows.Forms.Padding(10)

    # Create GroupBox for parameters
    $groupParams = New-Object System.Windows.Forms.GroupBox
    $groupParams.Text = "Configuration"
    $groupParams.Dock = [System.Windows.Forms.DockStyle]::Top
    $groupParams.Height = 280
    $groupParams.AutoSize = $true
    $groupParams.Padding = New-Object System.Windows.Forms.Padding(10)

    $yPos = 20

    # Environment Label and TextBox
    $labelEnvironment = New-Object System.Windows.Forms.Label
    $labelEnvironment.Text = "Environment (Dev, Test, Prod):"
    $labelEnvironment.Location = New-Object System.Drawing.Point(10, $yPos)
    $labelEnvironment.AutoSize = $true
    $groupParams.Controls.Add($labelEnvironment)

    $textEnvironment = New-Object System.Windows.Forms.TextBox
    $textEnvironment.Text = "Dev"
    $textEnvironment.Location = New-Object System.Drawing.Point(250, $yPos)
    $textEnvironment.Width = 200
    $groupParams.Controls.Add($textEnvironment)

    $yPos += 40

    # Region Label and TextBox
    $labelRegion = New-Object System.Windows.Forms.Label
    $labelRegion.Text = "Region (e.g., westeurope, eastus):"
    $labelRegion.Location = New-Object System.Drawing.Point(10, $yPos)
    $labelRegion.AutoSize = $true
    $groupParams.Controls.Add($labelRegion)

    $textRegion = New-Object System.Windows.Forms.TextBox
    $textRegion.Text = "westeurope"
    $textRegion.Location = New-Object System.Drawing.Point(250, $yPos)
    $textRegion.Width = 200
    $groupParams.Controls.Add($textRegion)

    $yPos += 40

    # Unique Identifier Label and TextBox
    $labelUniqueId = New-Object System.Windows.Forms.Label
    $labelUniqueId.Text = "Unique Identifier (Project Code):"
    $labelUniqueId.Location = New-Object System.Drawing.Point(10, $yPos)
    $labelUniqueId.AutoSize = $true
    $groupParams.Controls.Add($labelUniqueId)

    $textUniqueId = New-Object System.Windows.Forms.TextBox
    $textUniqueId.Text = "MARK"
    $textUniqueId.Location = New-Object System.Drawing.Point(250, $yPos)
    $textUniqueId.Width = 200
    $groupParams.Controls.Add($textUniqueId)

    $yPos += 40

    # Number Label and NumericUpDown
    $labelNumber = New-Object System.Windows.Forms.Label
    $labelNumber.Text = "Number:"
    $labelNumber.Location = New-Object System.Drawing.Point(10, $yPos)
    $labelNumber.AutoSize = $true
    $groupParams.Controls.Add($labelNumber)

    $numericNumber = New-Object System.Windows.Forms.NumericUpDown
    $numericNumber.Value = 1
    $numericNumber.Location = New-Object System.Drawing.Point(250, $yPos)
    $numericNumber.Width = 200
    $numericNumber.Minimum = 1
    $numericNumber.Maximum = 999
    $groupParams.Controls.Add($numericNumber)

    $yPos += 40

    # Separator Label and TextBox
    $labelSeparator = New-Object System.Windows.Forms.Label
    $labelSeparator.Text = "Separator:"
    $labelSeparator.Location = New-Object System.Drawing.Point(10, $yPos)
    $labelSeparator.AutoSize = $true
    $groupParams.Controls.Add($labelSeparator)

    $textSeparator = New-Object System.Windows.Forms.TextBox
    $textSeparator.Text = "-"
    $textSeparator.Location = New-Object System.Drawing.Point(250, $yPos)
    $textSeparator.Width = 200
    $groupParams.Controls.Add($textSeparator)

    $yPos += 40

    # Convert to Lower Checkbox
    $checkLower = New-Object System.Windows.Forms.CheckBox
    $checkLower.Text = "Convert to Lowercase"
    $checkLower.Location = New-Object System.Drawing.Point(250, $yPos)
    $checkLower.AutoSize = $true
    $checkLower.Checked = $true
    $groupParams.Controls.Add($checkLower)

    $tabParameters.Controls.Add($groupParams)

    # Tab 2: Resource Types
    $tabResources = New-Object System.Windows.Forms.TabPage
    $tabResources.Text = "Resources"
    $tabResources.Padding = New-Object System.Windows.Forms.Padding(10)

    # Search Label and TextBox
    $labelSearch = New-Object System.Windows.Forms.Label
    $labelSearch.Text = "Search Resources:"
    $labelSearch.Location = New-Object System.Drawing.Point(10, 10)
    $labelSearch.AutoSize = $true
    $tabResources.Controls.Add($labelSearch)

    $textSearch = New-Object System.Windows.Forms.TextBox
    $textSearch.Location = New-Object System.Drawing.Point(150, 10)
    $textSearch.Width = 300
    $textSearch.Height = 25
    $tabResources.Controls.Add($textSearch)

    # ListBox for resource types
    $labelResourceTypes = New-Object System.Windows.Forms.Label
    $labelResourceTypes.Text = "Available Resource Types:"
    $labelResourceTypes.Location = New-Object System.Drawing.Point(10, 45)
    $labelResourceTypes.AutoSize = $true
    $tabResources.Controls.Add($labelResourceTypes)

    # Load Resources Button
    $buttonLoadResources = New-Object System.Windows.Forms.Button
    $buttonLoadResources.Text = "Load Resources"
    $buttonLoadResources.Location = New-Object System.Drawing.Point(370, 10)
    $buttonLoadResources.Width = 90
    $buttonLoadResources.Height = 25
    $tabResources.Controls.Add($buttonLoadResources)

    $listBoxResources = New-Object System.Windows.Forms.ListBox
    $listBoxResources.SelectionMode = [System.Windows.Forms.SelectionMode]::MultiSimple
    $listBoxResources.Location = New-Object System.Drawing.Point(10, 70)
    $listBoxResources.Width = 450
    $listBoxResources.Height = 500
    $tabResources.Controls.Add($listBoxResources)

    # Selected Resources Label and TextBox
    $labelSelected = New-Object System.Windows.Forms.Label
    $labelSelected.Text = "Selected Resources:"
    $labelSelected.Location = New-Object System.Drawing.Point(480, 45)
    $labelSelected.AutoSize = $true
    $tabResources.Controls.Add($labelSelected)

    $textSelectedResources = New-Object System.Windows.Forms.TextBox
    $textSelectedResources.Multiline = $true
    $textSelectedResources.ScrollBars = [System.Windows.Forms.ScrollBars]::Vertical
    $textSelectedResources.Location = New-Object System.Drawing.Point(480, 70)
    $textSelectedResources.Width = 450
    $textSelectedResources.Height = 500
    $textSelectedResources.ReadOnly = $true
    $tabResources.Controls.Add($textSelectedResources)

    # Search functionality
    $textSearch_TextChanged = {
        $filter = $textSearch.Text.ToLower()
        $listBoxResources.Items.Clear()

        if ([string]::IsNullOrEmpty($filter)) {
            foreach ($item in $script:resourceTypesToLoad) {
                [void]$listBoxResources.Items.Add($item)
            }
        }
        else {
            foreach ($item in $script:resourceTypesToLoad) {
                if ($item.ToLower().Contains($filter)) {
                    [void]$listBoxResources.Items.Add($item)
                }
            }
        }
    }

    $textSearch.Add_TextChanged($textSearch_TextChanged)

    # Load Resources button click event
    $buttonLoadResources.Add_Click({
        $openDialog = New-Object System.Windows.Forms.OpenFileDialog
        $openDialog.Filter = "JSON Files (*.json)|*.json|Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
        $openDialog.Title = "Select Resource File"
        
        if ($openDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
            try {
                $script:resourceTypesToLoad = @()
                
                if ($openDialog.FileName -match '\.json$') {
                    # Load from JSON file
                    $script:resourceTypesToLoad = @(Get-ResourceTypesFromJson -Path $openDialog.FileName)
                }
                else {
                    # Load from text file
                    $script:resourceTypesToLoad = @(Get-Content -Path $openDialog.FileName | Where-Object { $_ -notmatch '^\s*$' })
                }
                
                if ($script:resourceTypesToLoad.Count -gt 0) {
                    # Refresh the listbox
                    $listBoxResources.Items.Clear()
                    foreach ($item in $script:resourceTypesToLoad) {
                        [void]$listBoxResources.Items.Add($item)
                    }
                    
                    [System.Windows.Forms.MessageBox]::Show("Loaded $($script:resourceTypesToLoad.Count) resource types.", "Success", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
                }
                else {
                    [System.Windows.Forms.MessageBox]::Show("No resources found in the selected file.", "No Data", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
                }
            }
            catch {
                [System.Windows.Forms.MessageBox]::Show("Error loading resources:`n`n$($_.Exception.Message)", "Load Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
            }
        }
    })

    # Load initial resource types
    if ($script:resourceTypesToLoad.Count -gt 0) {
        foreach ($item in $script:resourceTypesToLoad) {
            [void]$listBoxResources.Items.Add($item)
        }
    }

    # Update selected resources display
    $updateSelectedResources = {
        $selected = @($listBoxResources.SelectedItems | ForEach-Object { $_ })
        $textSelectedResources.Text = $selected -join [Environment]::NewLine
    }

    $listBoxResources.Add_SelectedIndexChanged($updateSelectedResources)

    # Tab 3: Settings
    $tabSettings = New-Object System.Windows.Forms.TabPage
    $tabSettings.Text = "Settings"
    $tabSettings.Padding = New-Object System.Windows.Forms.Padding(10)
    $tabSettings.AutoScroll = $true

    # Create GroupBox for file settings
    $groupFileSettings = New-Object System.Windows.Forms.GroupBox
    $groupFileSettings.Text = "File Configuration"
    $groupFileSettings.Dock = [System.Windows.Forms.DockStyle]::Top
    $groupFileSettings.Height = 350
    $groupFileSettings.AutoSize = $true
    $groupFileSettings.Padding = New-Object System.Windows.Forms.Padding(10)

    $ySettingsPos = 20

    # Naming Schema File
    $labelSchemaFile = New-Object System.Windows.Forms.Label
    $labelSchemaFile.Text = "Naming Schema File:"
    $labelSchemaFile.Location = New-Object System.Drawing.Point(10, $ySettingsPos)
    $labelSchemaFile.AutoSize = $true
    $groupFileSettings.Controls.Add($labelSchemaFile)

    $textSchemaFile = New-Object System.Windows.Forms.TextBox
    $textSchemaFile.Text = $ResourceNameSchema
    $textSchemaFile.Location = New-Object System.Drawing.Point(10, ($ySettingsPos + 25))
    $textSchemaFile.Width = 650
    $textSchemaFile.Height = 40
    $textSchemaFile.Multiline = $true
    $textSchemaFile.ReadOnly = $true
    $textSchemaFile.ScrollBars = [System.Windows.Forms.ScrollBars]::Vertical
    $groupFileSettings.Controls.Add($textSchemaFile)

    $buttonBrowseSchema = New-Object System.Windows.Forms.Button
    $buttonBrowseSchema.Text = "Browse Schema..."
    $buttonBrowseSchema.Location = New-Object System.Drawing.Point(670, ($ySettingsPos + 25))
    $buttonBrowseSchema.Width = 120
    $buttonBrowseSchema.Height = 25
    $groupFileSettings.Controls.Add($buttonBrowseSchema)

    $buttonResetSchema = New-Object System.Windows.Forms.Button
    $buttonResetSchema.Text = "Reset to Default"
    $buttonResetSchema.Location = New-Object System.Drawing.Point(670, ($ySettingsPos + 55))
    $buttonResetSchema.Width = 120
    $buttonResetSchema.Height = 25
    $groupFileSettings.Controls.Add($buttonResetSchema)

    $ySettingsPos += 100

    # Resources Data File
    $labelResourcesFile = New-Object System.Windows.Forms.Label
    $labelResourcesFile.Text = "Resources Data File:"
    $labelResourcesFile.Location = New-Object System.Drawing.Point(10, $ySettingsPos)
    $labelResourcesFile.AutoSize = $true
    $groupFileSettings.Controls.Add($labelResourcesFile)

    $textResourcesFile = New-Object System.Windows.Forms.TextBox
    $textResourcesFile.Text = $ResourcesData
    $textResourcesFile.Location = New-Object System.Drawing.Point(10, ($ySettingsPos + 25))
    $textResourcesFile.Width = 650
    $textResourcesFile.Height = 40
    $textResourcesFile.Multiline = $true
    $textResourcesFile.ReadOnly = $true
    $textResourcesFile.ScrollBars = [System.Windows.Forms.ScrollBars]::Vertical
    $groupFileSettings.Controls.Add($textResourcesFile)

    $buttonBrowseResources = New-Object System.Windows.Forms.Button
    $buttonBrowseResources.Text = "Browse Resources..."
    $buttonBrowseResources.Location = New-Object System.Drawing.Point(670, ($ySettingsPos + 25))
    $buttonBrowseResources.Width = 120
    $buttonBrowseResources.Height = 25
    $groupFileSettings.Controls.Add($buttonBrowseResources)

    $buttonResetResources = New-Object System.Windows.Forms.Button
    $buttonResetResources.Text = "Reset to Default"
    $buttonResetResources.Location = New-Object System.Drawing.Point(670, ($ySettingsPos + 55))
    $buttonResetResources.Width = 120
    $buttonResetResources.Height = 25
    $groupFileSettings.Controls.Add($buttonResetResources)

    $ySettingsPos += 100

    # Info Label
    $labelSettingsInfo = New-Object System.Windows.Forms.Label
    $labelSettingsInfo.Text = "Note: Changes to files will be applied after reloading. Use 'Browse' buttons to select custom files."
    $labelSettingsInfo.Location = New-Object System.Drawing.Point(10, $ySettingsPos)
    $labelSettingsInfo.Size = New-Object System.Drawing.Size(780, 50)
    $labelSettingsInfo.AutoSize = $false
    $labelSettingsInfo.ForeColor = [System.Drawing.Color]::Blue
    $groupFileSettings.Controls.Add($labelSettingsInfo)

    $tabSettings.Controls.Add($groupFileSettings)

    # Create GroupBox for Bicep settings
    $groupBicepSettings = New-Object System.Windows.Forms.GroupBox
    $groupBicepSettings.Text = "Bicep Export"
    $groupBicepSettings.Location = New-Object System.Drawing.Point(10, 370)
    $groupBicepSettings.Size = New-Object System.Drawing.Size(940, 170)
    $groupBicepSettings.Padding = New-Object System.Windows.Forms.Padding(10)

    # Enable Bicep Export checkbox
    $checkBicepExport = New-Object System.Windows.Forms.CheckBox
    $checkBicepExport.Text = "Enable Bicep Export"
    $checkBicepExport.Location = New-Object System.Drawing.Point(10, 25)
    $checkBicepExport.AutoSize = $true
    $checkBicepExport.Checked = $false
    $groupBicepSettings.Controls.Add($checkBicepExport)

    # Bicep File Type
    $labelBicepType = New-Object System.Windows.Forms.Label
    $labelBicepType.Text = "Bicep File Type:"
    $labelBicepType.Location = New-Object System.Drawing.Point(10, 55)
    $labelBicepType.AutoSize = $true
    $groupBicepSettings.Controls.Add($labelBicepType)

    $comboBicepType = New-Object System.Windows.Forms.ComboBox
    $comboBicepType.Location = New-Object System.Drawing.Point(150, 52)
    $comboBicepType.Width = 200
    $comboBicepType.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    [void]$comboBicepType.Items.Add("Dynamic")
    [void]$comboBicepType.Items.Add("Static")
    $comboBicepType.SelectedIndex = 0
    $comboBicepType.Enabled = $false
    $groupBicepSettings.Controls.Add($comboBicepType)

    # Bicep Output Path
    $labelBicepOutput = New-Object System.Windows.Forms.Label
    $labelBicepOutput.Text = "Bicep Output Path:"
    $labelBicepOutput.Location = New-Object System.Drawing.Point(10, 90)
    $labelBicepOutput.AutoSize = $true
    $groupBicepSettings.Controls.Add($labelBicepOutput)

    $textBicepOutput = New-Object System.Windows.Forms.TextBox
    $textBicepOutput.Location = New-Object System.Drawing.Point(10, 115)
    $textBicepOutput.Width = 650
    $textBicepOutput.Height = 30
    $textBicepOutput.ReadOnly = $true
    $textBicepOutput.Enabled = $false
    $groupBicepSettings.Controls.Add($textBicepOutput)

    $buttonBrowseBicepOutput = New-Object System.Windows.Forms.Button
    $buttonBrowseBicepOutput.Text = "Browse..."
    $buttonBrowseBicepOutput.Location = New-Object System.Drawing.Point(670, 112)
    $buttonBrowseBicepOutput.Width = 120
    $buttonBrowseBicepOutput.Height = 25
    $buttonBrowseBicepOutput.Enabled = $false
    $groupBicepSettings.Controls.Add($buttonBrowseBicepOutput)

    $tabSettings.Controls.Add($groupBicepSettings)

    $tabControl.TabPages.Add($tabParameters)
    $tabControl.TabPages.Add($tabResources)
    $tabControl.TabPages.Add($tabSettings)
    $form.Controls.Add($tabControl)

    # Create buttons at the bottom
    $buttonPanel = New-Object System.Windows.Forms.Panel
    $buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
    $buttonPanel.Height = 60
    $buttonPanel.Padding = New-Object System.Windows.Forms.Padding(10)

    # Generate Button
    $buttonGenerate = New-Object System.Windows.Forms.Button
    $buttonGenerate.Text = "Generate Names"
    $buttonGenerate.Location = New-Object System.Drawing.Point(10, 10)
    $buttonGenerate.Width = 150
    $buttonGenerate.Height = 40
    $buttonPanel.Controls.Add($buttonGenerate)

    # Copy to Clipboard Button
    $buttonCopy = New-Object System.Windows.Forms.Button
    $buttonCopy.Text = "Copy Results"
    $buttonCopy.Location = New-Object System.Drawing.Point(170, 10)
    $buttonCopy.Width = 150
    $buttonCopy.Height = 40
    $buttonCopy.Enabled = $false
    $buttonPanel.Controls.Add($buttonCopy)

    # Export Button
    $buttonExport = New-Object System.Windows.Forms.Button
    $buttonExport.Text = "Export to File"
    $buttonExport.Location = New-Object System.Drawing.Point(330, 10)
    $buttonExport.Width = 150
    $buttonExport.Height = 40
    $buttonExport.Enabled = $false
    $buttonPanel.Controls.Add($buttonExport)

    # Export Bicep Button
    $buttonExportBicep = New-Object System.Windows.Forms.Button
    $buttonExportBicep.Text = "Export to Bicep"
    $buttonExportBicep.Location = New-Object System.Drawing.Point(490, 10)
    $buttonExportBicep.Width = 150
    $buttonExportBicep.Height = 40
    $buttonExportBicep.Enabled = $false
    $buttonPanel.Controls.Add($buttonExportBicep)

    # Close Button
    $buttonClose = New-Object System.Windows.Forms.Button
    $buttonClose.Text = "Close"
    $buttonClose.Location = New-Object System.Drawing.Point(650, 10)
    $buttonClose.Width = 150
    $buttonClose.Height = 40
    $buttonPanel.Controls.Add($buttonClose)

    $buttonClose.Add_Click({ $form.Close() })

    $form.Controls.Add($buttonPanel)

    # Results DataGridView
    $dataGridResults = New-Object System.Windows.Forms.DataGridView
    $dataGridResults.Dock = [System.Windows.Forms.DockStyle]::Fill
    $dataGridResults.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::AllCells
    $dataGridResults.ReadOnly = $true
    $dataGridResults.AllowUserToAddRows = $false
    $dataGridResults.AllowUserToDeleteRows = $false
    $dataGridResults.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::AutoSize

    # Add columns to DataGridView
    $dataGridResults.Columns.Add("ResourceType", "Resource Type") | Out-Null
    $dataGridResults.Columns.Add("GeneratedName", "Generated Name") | Out-Null
    $dataGridResults.Columns.Add("Abbreviation", "Abbreviation") | Out-Null
    $dataGridResults.Columns.Add("RemovedChars", "Removed Characters") | Out-Null

    # Tab 3: Results
    $tabResults = New-Object System.Windows.Forms.TabPage
    $tabResults.Text = "Results"
    $tabResults.Controls.Add($dataGridResults)
    $tabControl.TabPages.Add($tabResults)

    # Generate button click event
    $buttonGenerate.Add_Click({
        $selectedResources = @($listBoxResources.SelectedItems | ForEach-Object { $_ })

        if ($selectedResources.Count -eq 0) {
            [System.Windows.Forms.MessageBox]::Show("Please select at least one resource type.", "No Resources Selected", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        # Validate parameters
        if ([string]::IsNullOrWhiteSpace($textEnvironment.Text)) {
            [System.Windows.Forms.MessageBox]::Show("Environment is required.", "Missing Parameter", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        if ([string]::IsNullOrWhiteSpace($textRegion.Text)) {
            [System.Windows.Forms.MessageBox]::Show("Region is required.", "Missing Parameter", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        if ([string]::IsNullOrWhiteSpace($textUniqueId.Text)) {
            [System.Windows.Forms.MessageBox]::Show("Unique Identifier is required.", "Missing Parameter", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        # Clear previous results
        $dataGridResults.Rows.Clear()

        # Call the name generator function
        try {
            Write-Host "========== GENERATION PARAMETERS ==========" -ForegroundColor Cyan
            Write-Host "Environment: $($textEnvironment.Text)" -ForegroundColor Gray
            Write-Host "Region: $($textRegion.Text)" -ForegroundColor Gray
            Write-Host "Unique ID: $($textUniqueId.Text)" -ForegroundColor Gray
            Write-Host "Number: $([int]$numericNumber.Value)" -ForegroundColor Gray
            Write-Host "Separator: '$($textSeparator.Text)'" -ForegroundColor Gray
            Write-Host "Convert to Lower: $($checkLower.Checked)" -ForegroundColor Gray
            Write-Host "Selected Resources Count: $($selectedResources.Count)" -ForegroundColor Gray
            Write-Host "Selected Resources: $($selectedResources -join ', ')" -ForegroundColor Yellow
            Write-Host "ResourceNameSchema: $ResourceNameSchema" -ForegroundColor Gray
            Write-Host "ResourcesData: $ResourcesData" -ForegroundColor Gray
            Write-Host "==========================================" -ForegroundColor Cyan

            $results = New-AzResourceNameGenerator `
                -environment $textEnvironment.Text `
                -resourceTypeNames $selectedResources `
                -regionName $textRegion.Text `
                -uniqueidentifier $textUniqueId.Text `
                -number ([int]$numericNumber.Value) `
                -separator $textSeparator.Text `
                -convertTolower $checkLower.Checked `
                -ResourceNameSchema $ResourceNameSchema `
                -ResourcesData $ResourcesData

            Write-Host "Results count: $(if ($results) { $results.Count } else { 0 })" -ForegroundColor Cyan

            # Add results to DataGridView
            if ($results) {
                foreach ($result in $results) {
                    $dataGridResults.Rows.Add(
                        $result.resourceTypeName,
                        $result.resourceNameGenerated,
                        $result.abbreviation,
                        $result.removedChars
                    )
                }

                # Enable export buttons
                $buttonCopy.Enabled = $true
                $buttonExport.Enabled = $true
                $buttonExportBicep.Enabled = $checkBicepExport.Checked

                # Switch to Results tab
                $tabControl.SelectedIndex = 2

                [System.Windows.Forms.MessageBox]::Show("Names generated successfully! Check the Results tab.", "Success", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
            }
            else {
                [System.Windows.Forms.MessageBox]::Show("No results returned. Check the PowerShell console for detailed error information.", "No Results", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
            }
        }
        catch {
            Write-Error "Generation Error: $($_.Exception.Message)" -ErrorAction Continue
            Write-Error "Stack Trace: $($_.Exception.StackTrace)" -ErrorAction Continue
            [System.Windows.Forms.MessageBox]::Show("Error generating names:`n`n$($_.Exception.Message)", "Generation Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        }
    })

    # Copy to Clipboard button click event
    $buttonCopy.Add_Click({
        if ($dataGridResults.Rows.Count -gt 0) {
            $clipboardText = @()
            foreach ($row in $dataGridResults.Rows) {
                $clipboardText += "Resource: $($row.Cells[0].Value)"
                $clipboardText += "Generated Name: $($row.Cells[1].Value)"
                $clipboardText += ""
            }

            $clipboardContent = $clipboardText -join [Environment]::NewLine
            [System.Windows.Forms.Clipboard]::SetText($clipboardContent)

            [System.Windows.Forms.MessageBox]::Show("Results copied to clipboard!", "Success", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
        }
    })

    # Export button click event
    $buttonExport.Add_Click({
        if ($dataGridResults.Rows.Count -eq 0) {
            [System.Windows.Forms.MessageBox]::Show("No results to export.", "No Data", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        $saveDialog = New-Object System.Windows.Forms.SaveFileDialog
        $saveDialog.Filter = "CSV Files (*.csv)|*.csv|Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
        $saveDialog.DefaultExt = "csv"

        if ($saveDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
            try {
                $exportData = @()
                foreach ($row in $dataGridResults.Rows) {
                    $exportData += [PSCustomObject]@{
                        ResourceType     = $row.Cells[0].Value
                        GeneratedName    = $row.Cells[1].Value
                        Abbreviation     = $row.Cells[2].Value
                        RemovedChars     = $row.Cells[3].Value
                    }
                }

                if ($saveDialog.FileName.EndsWith(".csv")) {
                    $exportData | Export-Csv -Path $saveDialog.FileName -NoTypeInformation -Force
                }
                else {
                    $exportData | Format-Table -AutoSize | Out-File -Path $saveDialog.FileName -Force
                }

                [System.Windows.Forms.MessageBox]::Show("Results exported successfully to:`n`n$($saveDialog.FileName)", "Export Successful", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
            }
            catch {
                [System.Windows.Forms.MessageBox]::Show("Error exporting results:`n`n$($_.Exception.Message)", "Export Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
            }
        }
    })

    # Bicep settings events
    $checkBicepExport.Add_CheckedChanged({
        $comboBicepType.Enabled = $checkBicepExport.Checked
        $textBicepOutput.Enabled = $checkBicepExport.Checked
        $buttonBrowseBicepOutput.Enabled = $checkBicepExport.Checked
        $buttonExportBicep.Enabled = ($checkBicepExport.Checked -and $dataGridResults.Rows.Count -gt 0)
    })

    $buttonBrowseBicepOutput.Add_Click({
        $saveDialog = New-Object System.Windows.Forms.SaveFileDialog
        if ($comboBicepType.SelectedItem -eq "Dynamic") {
            $saveDialog.Filter = "Bicep Params (*.bicepparam)|*.bicepparam|Bicep Files (*.bicep)|*.bicep|All Files (*.*)|*.*"
            $saveDialog.DefaultExt = "bicepparam"
        }
        else {
            $saveDialog.Filter = "Bicep Files (*.bicep)|*.bicep|All Files (*.*)|*.*"
            $saveDialog.DefaultExt = "bicep"
        }

        if ($saveDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
            $textBicepOutput.Text = $saveDialog.FileName
        }
    })

    # Export to Bicep button click event
    $buttonExportBicep.Add_Click({
        $selectedResources = @($listBoxResources.SelectedItems | ForEach-Object { $_ })

        if ($selectedResources.Count -eq 0) {
            [System.Windows.Forms.MessageBox]::Show("Please select at least one resource type.", "No Resources Selected", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        if (-not $checkBicepExport.Checked) {
            [System.Windows.Forms.MessageBox]::Show("Enable Bicep Export in Settings to continue.", "Bicep Export Disabled", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        # Validate parameters
        if ([string]::IsNullOrWhiteSpace($textEnvironment.Text)) {
            [System.Windows.Forms.MessageBox]::Show("Environment is required.", "Missing Parameter", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        if ([string]::IsNullOrWhiteSpace($textRegion.Text)) {
            [System.Windows.Forms.MessageBox]::Show("Region is required.", "Missing Parameter", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        if ([string]::IsNullOrWhiteSpace($textUniqueId.Text)) {
            [System.Windows.Forms.MessageBox]::Show("Unique Identifier is required.", "Missing Parameter", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
            return
        }

        $bicepOutputPath = $textBicepOutput.Text
        if ([string]::IsNullOrWhiteSpace($bicepOutputPath)) {
            $saveDialog = New-Object System.Windows.Forms.SaveFileDialog
            if ($comboBicepType.SelectedItem -eq "Dynamic") {
                $saveDialog.Filter = "Bicep Params (*.bicepparam)|*.bicepparam|Bicep Files (*.bicep)|*.bicep|All Files (*.*)|*.*"
                $saveDialog.DefaultExt = "bicepparam"
            }
            else {
                $saveDialog.Filter = "Bicep Files (*.bicep)|*.bicep|All Files (*.*)|*.*"
                $saveDialog.DefaultExt = "bicep"
            }

            if ($saveDialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) {
                return
            }
            $bicepOutputPath = $saveDialog.FileName
            $textBicepOutput.Text = $bicepOutputPath
        }

        try {
            $null = New-AzResourceNameGenerator `
                -environment $textEnvironment.Text `
                -resourceTypeNames $selectedResources `
                -regionName $textRegion.Text `
                -uniqueidentifier $textUniqueId.Text `
                -number ([int]$numericNumber.Value) `
                -separator $textSeparator.Text `
                -convertTolower $checkLower.Checked `
                -ResourceNameSchema $ResourceNameSchema `
                -ResourcesData $ResourcesData `
                -bicepFileGeneration `
                -bicepFileType ($comboBicepType.SelectedItem.ToString()) `
                -bicepFileOutputPath $bicepOutputPath

            [System.Windows.Forms.MessageBox]::Show("Bicep file exported successfully to:`n`n$bicepOutputPath", "Export Successful", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
        }
        catch {
            [System.Windows.Forms.MessageBox]::Show("Error exporting Bicep file:`n`n$($_.Exception.Message)", "Export Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        }
    })

    # Browse Schema button click event
    $buttonBrowseSchema.Add_Click({
        $openDialog = New-Object System.Windows.Forms.OpenFileDialog
        $openDialog.Filter = "JSON Files (*.json)|*.json|All Files (*.*)|*.*"
        $openDialog.Title = "Select Naming Schema File"
        
        if ($openDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
            try {
                $content = Get-Content -Path $openDialog.FileName -Raw | ConvertFrom-Json -ErrorAction Stop
                $script:ResourceNameSchema = $openDialog.FileName
                $textSchemaFile.Text = $openDialog.FileName
                [System.Windows.Forms.MessageBox]::Show("Schema file loaded successfully from:`n`n$($openDialog.FileName)", "Success", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
                Write-Host "Naming schema loaded from: $($openDialog.FileName)" -ForegroundColor Green
            }
            catch {
                [System.Windows.Forms.MessageBox]::Show("Error loading schema file:`n`n$($_.Exception.Message)", "Load Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
            }
        }
    })

    # Reset Schema button click event
    $buttonResetSchema.Add_Click({
        $defaultSchema = "https://raw.githubusercontent.com/mimachniak/AzureResources-NameGenerator/refs/heads/main/data/general_naming_shema.json"
        $script:ResourceNameSchema = $defaultSchema
        $textSchemaFile.Text = $defaultSchema
        [System.Windows.Forms.MessageBox]::Show("Schema file reset to default.", "Reset Complete", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
        Write-Host "Naming schema reset to default: $defaultSchema" -ForegroundColor Green
    })

    # Browse Resources button click event
    $buttonBrowseResources.Add_Click({
        $openDialog = New-Object System.Windows.Forms.OpenFileDialog
        $openDialog.Filter = "JSON Files (*.json)|*.json|Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
        $openDialog.Title = "Select Resources Data File"
        
        if ($openDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
            try {
                if ($openDialog.FileName -match '\.json$') {
                    $content = Get-Content -Path $openDialog.FileName -Raw | ConvertFrom-Json -ErrorAction Stop
                }
                else {
                    $content = Get-Content -Path $openDialog.FileName | Where-Object { $_ -notmatch '^\s*$' }
                }
                
                $script:ResourcesData = $openDialog.FileName
                $textResourcesFile.Text = $openDialog.FileName
                
                # Reload the resources in the listbox
                $script:resourceTypesToLoad = @()
                if ($openDialog.FileName -match '\.json$') {
                    $script:resourceTypesToLoad = @(Get-ResourceTypesFromJson -Path $openDialog.FileName)
                }
                else {
                    $script:resourceTypesToLoad = @(Get-Content -Path $openDialog.FileName | Where-Object { $_ -notmatch '^\s*$' })
                }
                
                if ($script:resourceTypesToLoad.Count -gt 0) {
                    $listBoxResources.Items.Clear()
                    foreach ($item in $script:resourceTypesToLoad) {
                        [void]$listBoxResources.Items.Add($item)
                    }
                    [System.Windows.Forms.MessageBox]::Show("Resources file loaded successfully with $($script:resourceTypesToLoad.Count) resources from:`n`n$($openDialog.FileName)", "Success", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
                    Write-Host "Resources loaded from: $($openDialog.FileName) - Total: $($script:resourceTypesToLoad.Count)" -ForegroundColor Green
                }
                else {
                    [System.Windows.Forms.MessageBox]::Show("No resources found in the selected file.", "No Data", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
                }
            }
            catch {
                [System.Windows.Forms.MessageBox]::Show("Error loading resources file:`n`n$($_.Exception.Message)", "Load Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
            }
        }
    })

    # Reset Resources button click event
    $buttonResetResources.Add_Click({
        $scriptDir = Split-Path -Parent (Split-Path -Parent $PSCommandPath)
        $localResourcesFile = Join-Path -Path $scriptDir -ChildPath "data\resourcetypes.json"
        
        $defaultResources = $null
        if (Test-Path $localResourcesFile) {
            $defaultResources = $localResourcesFile
        }
        else {
            $defaultResources = "https://raw.githubusercontent.com/mspnp/AzureNamingTool/refs/heads/main/src/repository/resourcetypes.json"
        }
        
        $script:ResourcesData = $defaultResources
        $textResourcesFile.Text = $defaultResources
        
        # Reload the resources in the listbox
        $script:resourceTypesToLoad = @(Get-ResourceTypesFromJson -Path $defaultResources)
        if ($script:resourceTypesToLoad.Count -gt 0) {
            $listBoxResources.Items.Clear()
            foreach ($item in $script:resourceTypesToLoad) {
                [void]$listBoxResources.Items.Add($item)
            }
        }
        
        [System.Windows.Forms.MessageBox]::Show("Resources file reset to default.", "Reset Complete", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
        Write-Host "Resources reset to default: $defaultResources" -ForegroundColor Green
    })

    # Show the form
    [void]$form.ShowDialog()
}