param (
	[Parameter(Mandatory=$true)]
	[string]
	$airline = 'ua',

	[Parameter(Mandatory=$true)]
    [ValidateSet('gate', 'checkin')]
	[string]
	$comType,

	[Parameter(Mandatory=$true)]
    [ValidateSet('query', 'import', 'delete', 'generate')]
    [string]
    $action,

	[Parameter(Mandatory=$false)]
    [ValidateSet('HKEY_LOCAL_MACHINE', 'HKEY_CURRENT_USER', 'HKLM', 'HKCU')]
	[string]
	$hive = 'HKEY_LOCAL_MACHINE',

	[Parameter(Mandatory=$false)]
	[string]
	$comDir,

	[Parameter(Mandatory=$false)]
	[string]
	$comFileName,

    [Parameter(Mandatory=$false)]
    [switch]
    $enableDebugging = $false
)

if ($enableDebugging) {
    $DebugPreference = 'Continue'
} else {
    $DebugPreference = 'SilentlyContinue'
}

switch ($airline) {
    'as' {
        Write-Error "Not yet implemented for airline ""$airline"""
        exit 1
    }
    '4n' {
        Write-Error "Not yet implemented for airline ""$airline"""
        exit 1
    }
    'nz' {
        Write-Error "Not yet implemented for airline ""$airline"""
        exit 1
    }
    'ua' {
        switch ($comType) {
            'checkin' {
                $appName = 'uasrdev'
                $clsid = '35578409-F9F5-43e8-A6BD-8B77199ED854'
                $appId = 'C123476B-ABB6-4BCC-B557-2FDC0CDCD6A8'
                $classLabel = 'QSRDev Class'
                $interfaceId = '6B15E61A-381F-41E7-8B65-BA386473FE8C'
                $interfaceName = 'IQSRDev'
                $interfaceStubClsid = '00020424-0000-0000-C000-000000000046'
                $eventInterfaceId = '17DCFB07-18A9-4FFE-B34D-37B91D809C41'
                $eventInterfaceName = '_IQSRDevEvents'
                $eventInterfaceStubClsid = '00020420-0000-0000-C000-000000000046'
                $typeLibId='CD886C7B-B61C-49CF-B3B8-90DB303A502A'
                $typeLibLabel = 'QSRDevice 1.0 Type Library'
                $typeLibVersion = '1.0'
                $versionIndependentProgId = 'QSRDevice.QSRDev'
            }
            'gate' {
                $appName = 'uagrdev'
                $clsid = '3F52625C-F66C-43F5-B538-CDF7331E0B7D'
                $appId = '91D04D72-DCB6-11D3-957D-00409500D5AD'
                $classLabel = 'QWDev Class'
                $interfaceId = 'D178FEAC-4969-45B4-92BF-4582EA5C6F47'
                $interfaceName = 'IQWDev'
                $interfaceStubClsid = '00020424-0000-0000-C000-000000000046'
                $eventInterfaceId = 'FDE206D5-EB3E-4DB1-AC3E-6CF314417443'
                $eventInterfaceName = '_IQWDevEvents'
                $eventInterfaceStubClsid = '00020420-0000-0000-C000-000000000046'
                $typeLibId='FB2C3447-9D8D-4AD0-9234-A163505FC3AE'
                $typeLibLabel = 'QWDevice 1.0 Type Library'
                $typeLibVersion = '1.0'
                $versionIndependentProgId = 'QWDevice.QWDev'
            }
        }
    }
    default {
        Write-Error "Unknown airline: $airline"
        exit 1
    }
}
$progId = "$versionIndependentProgId.1";


switch ($action) {
    'generate' {
    }
    'delete' {
    }
    'query' {
    }
    'import' {
        if (!$comDir) {
            Write-Error "Missing COM server directory"
            exit 1
        }
        if (!$comFileName) {
            Write-Error "Missing COM server file name"
            exit 1
        }
    }
    default {
        Write-Error "Unknown action: $action"
        exit 1
    }
}

switch ($hive) {
    'HKCU' {
        $hiveShort = 'HKCU'
        $hive = 'HKEY_CURRENT_USER'
    }
    'HKLM' {
        $hiveShort = 'HKLM'
        $hive = 'HKEY_LOCAL_MACHINE'
    }
    'HKEY_CURRENT_USER' {
        $hiveShort = 'HKCU'
        $hive = 'HKEY_CURRENT_USER'
    }
    'HKEY_LOCAL_MACHINE' {
        $hiveShort = 'HKLM'
        $hive = 'HKEY_LOCAL_MACHINE'
    }
    default {
        Write-Error "Invalid registry hive: $hive"
        exit 1
    }
}

$comDir = $comDir.TrimEnd('\')
$comPath = "$comDir\$comFileName"
if (![System.IO.Path]::IsPathRooted($comPath)) {
    Write-Error "Provided COM server path is not absolute: $comPath"
    exit 1
}
if (!(Test-Path $comPath)) {
    Write-Warning "COM server path does not exist: $comPath"
}

# COM servers use the network path in some registry keys when self-registering, so attempt to discover it.
$comNetworkPath = $comPath
try {
    $driveLetter = $comPath.Substring(0, 1)
    $networkPathToDrive = (Get-PSDrive | Where-Object {$_.Name -eq $driveLetter}).DisplayRoot
    if ($networkPathToDrive) {
        $rootPathWithoutDrive = $comPath.Substring(2)
        $comNetworkPath = "$networkPathToDrive$rootPathWithoutDrive"
    }
} catch {
    Write-Error "An error occurred trying to discover network path to COM server at $comPath"
}

function Get-ClsidData {
    param(
        [Parameter(Mandatory=$false)]
        [string]
        $architectureNode = '\'
    )
    return @(
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)CLSID\{$clsid}"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $classLabel
                },
                @{
                    Name = 'AppID'
                    Value = "{$appId}"
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)CLSID\{$clsid}\LocalServer32"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $comPath
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)CLSID\{$clsid}\ProgID"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $progId
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)CLSID\{$clsid}\Programmable"
            ValueItems = @()
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)CLSID\{$clsid}\TypeLib"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "{$typeLibId}"
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)CLSID\{$clsid}\VersionIndependentProgID"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $versionIndependentProgId
                }
            )
        }
    )
}

function Get-InterfaceData {
    param(
        [Parameter(Mandatory=$false)]
        [string]
        $architectureNode = '\'
    )
    return @(    
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)Interface\{$interfaceId}"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $interfaceName
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)Interface\{$interfaceId}\ProxyStubClsid32"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "{$interfaceStubClsid}"
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)Interface\{$interfaceId}\TypeLib"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "{$typeLibId}"
                },
                @{
                    Name = 'Version'
                    Value = $typeLibVersion
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)Interface\{$eventInterfaceId}"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $eventInterfaceName
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)Interface\{$eventInterfaceId}\ProxyStubClsid32"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "{$eventInterfaceStubClsid}"
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)Interface\{$eventInterfaceId}\TypeLib"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "{$typeLibId}"
                }
                @{
                    Name = 'Version'
                    Value = $typeLibVersion
                }
            )
        }
    )
}

function Get-ProgIdData {
    return @(
        @{
            Key = "\SOFTWARE\Classes\$versionIndependentProgId"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $classLabel
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes\$versionIndependentProgId\CLSID"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "{$clsid}"
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes\$versionIndependentProgId\CurVer"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $progId
                }
            )
        }
        @{
            Key = "\SOFTWARE\Classes\$progId"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $classLabel
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes\$progId\CLSID"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "{$clsid}"
                }
            )
        }
    )
}

function Get-TypeLibData {
    param(
        [Parameter(Mandatory=$false)]
        [string]
        $architectureNode = '\'
    )
    return @(
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)TypeLib\{$typeLibId}"
            ValueItems = @()
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)TypeLib\{$typeLibId}\$typeLibVersion"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $typeLibLabel
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)TypeLib\{$typeLibId}\$typeLibVersion\0"
            ValueItems = @()
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)TypeLib\{$typeLibId}\$typeLibVersion\0\win32"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = $comNetworkPath
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)TypeLib\{$typeLibId}\$typeLibVersion\FLAGS"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = '0'
                }
            )
        },
        @{
            Key = "\SOFTWARE\Classes$($architectureNode)TypeLib\{$typeLibId}\$typeLibVersion\HELPDIR"
            ValueItems = @(
                @{
                    Name = '(default)'
                    Value = "$comDir\"
                }
            )
        }
    )
}

function Get-Data {
    param(
        [Parameter(Mandatory=$false)]
        [switch]
        $includeWrongArchitecturePaths = $false
    )

    if ($env:PROCESSOR_ARCHITECTURE -eq 'x86') {
        $wow6432Node = '\'
    } else {
        $wow6432Node = '\WOW6432Node\'
    }

    $paths = New-Object System.Collections.Generic.List[System.Object]
    $paths.AddRange((Get-ProgIdData))
    $paths.AddRange((Get-ClsidData -architectureNode $wow6432Node))
    if ($includeWrongArchitecturePaths -and $wow6432Node -ne '\') {
        $paths.AddRange((Get-ClsidData))
    }
    $paths.AddRange((Get-InterfaceData -architectureNode $wow6432Node))
    if ($wow6432Node -ne '\') {
        $paths.AddRange((Get-InterfaceData))
    }
    $paths.AddRange((Get-TypeLibData -architectureNode $wow6432Node))
    if ($wow6432Node -ne '\') {
        $paths.AddRange((Get-TypeLibData))
    }
    return $paths
}

switch ($action) {
    'delete' {
        $data = Get-Data -includeWrongArchitecturePaths
        foreach ($dataEntry in $data) {
            $path = "$($hiveShort):\$($dataEntry.Key)"
            try {
                Remove-Item -Path $path -Recurse -Force -ErrorAction Stop
                Write-Information "Deleted registry key: $path"
            }
            catch {
                Write-Error "Failed to find $path"
                continue
            }
        }
    }
    'import' {
        $data = Get-Data
        foreach ($dataEntry in $data) {
            $path = "$($hiveShort):\$($dataEntry.Key)"    
            try {
                New-Item -Path $path -Force -ErrorAction Stop
                Write-Information "Imported key: $path"
            }
            catch {
                Write-Error "Failed to import key: $path"
                continue
            }
            #Write-Debug $path
            foreach ($valueItem in $dataEntry.ValueItems) {
                try {
                    New-ItemProperty -Path $path -Name $valueItem.Name -Value $valueItem.Value -PropertyType 'String' -Force -ErrorAction Stop
                    Write-Information "Imported value: $($valueItem.Name) in $path"
                }
                catch {
                    Write-Error "Failed to create entry: $($valueItem.Name) in $path"
                    continue
                }
                #Write-Debug "$($valueItem.Name)=$($valueItem.Value)"
            }
        }
    }
    'query' {
        $data = Get-Data -includeWrongArchitecturePaths
        foreach ($dataEntry in $data) {
            $path = "$($hiveShort):\$($dataEntry.Key)"
            try {
                $foundKey = Get-Item -Path $path -ErrorAction Stop
            }
            catch {
                Write-Warning "Failed to find $path"
                continue
            }
            $pathShort = $foundKey.PSPath -replace '^.*::', ''
            Write-Host
            Write-Host $pathShort
            $itemProperty = Get-ItemProperty -Path $path
            if (!$itemProperty) {
                continue
            }
            foreach ($property in $itemProperty.PSObject.Properties) {
                if ($property.Name.StartsWith('PS')) {
                    continue
                }
                Write-Host "$($property.Name)=$($property.Value)"
            }
        }
    }
    'generate' {
        $outFileAdd = "$appName-add.reg"
        $outFileDel = "$appName-del.reg"
        $data = Get-Data
        if (Test-Path $outFileAdd) {
            try {
                Remove-Item -Path $outFileAdd -ErrorAction Stop
            }
            catch {
                Write-Error "Failed to delete existing file: $outFileAdd"
                exit 1
            }
        }
        Write-Output 'Windows Registry Editor Version 5.00' > $outFileAdd
        Write-Output 'Windows Registry Editor Version 5.00' > $outFileDel
        foreach ($dataEntry in $data) {
            $path = "$($hive)$($dataEntry.Key)"
            Write-Output '' >> $outFileAdd
            Write-Output "[$path]" >> $outFileAdd
            Write-Output "[-$path]" >> $outFileDel
            foreach ($valueItem in $dataEntry.ValueItems) {
                if ($valueItem.Name -eq '(default)') {
                    $name = '@'
                } else {
                    $name = """$($valueItem.Name)"""
                }
                $value = $valueItem.Value.Replace('\', '\\').Replace('"', '\"')
                Write-Output "$name=""$value""" >> $outFileAdd
            }
        }
    }
    default {
        Write-Error "Unknown action: $action"
        exit 1
    }
}
