#!powershell

# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#Requires -Module Ansible.ModuleUtils.Legacy

$ErrorActionPreference = "Stop"

$params = Parse-Args -arguments $args -supports_check_mode $true

$path = Get-AnsibleParam -obj $params -name "path" -type "str" -failifempty $true -aliases "key"
$name = Get-AnsibleParam -obj $params -name "name" -type "str" -aliases "entry","value"

$result = @{
    changed = $false
}

Function Get-PropertyValue {
    param(
        [Parameter(Mandatory=$true)][Microsoft.Win32.RegistryKey]$Key,
        [String]$Name
    )

    $value = $Key.GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::None)
    if ($null -eq $value) {
        # Property does not exist or the key's (Default) is not set
        return $null
    }

    $raw_value = $Key.GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)

    if ($Name -eq "") {
        # The key's (Default) will fail on GetValueKind
        $type = [Microsoft.Win32.RegistryValueKind]::String
    } else {
        $type = $Key.GetValueKind($Name)
    }

    if ($type -in @([Microsoft.Win32.RegistryValueKind]::Binary, [Microsoft.Win32.RegistryValueKind]::None)) {
        $formatted_raw_value = [System.Collections.Generic.List`1[String]]@()
        foreach ($byte in $value) {
            $formatted_raw_value.Add("0x{0:x2}" -f $byte)
        }
        $raw_value = $formatted_raw_value
    } elseif ($type -eq [Microsoft.Win32.RegistryValueKind]::DWord) {
        # .NET returns the value as a signed integer, we need to make it unsigned
        $value = [UInt32]("0x{0:x}" -f $value)
        $raw_value = $value
    } elseif ($type -eq [Microsoft.Win32.RegistryValueKind]::QWord) {
        $value = [UInt64]("0x{0:x}" -f $value)
        $raw_value = $value
    }

    $return_type = switch($type.ToString()) {
        "Binary" { "REG_BINARY" }
        "String" { "REG_SZ" }
        "DWord" { "REG_DWORD" }
        "QWord" { "REG_QWORD" }
        "MultiString" { "REG_MULTI_SZ" }
        "ExpandString" { "REG_EXPAND_SZ" }
        "None" { "REG_NONE" }
        default { "Unknown - $($type.ToString())" }
    }

    return @{
        type = $return_type
        value = $value
        raw_value = $raw_value
    }
}

# Will validate the key parameter to make sure it matches known format
if ($path -notmatch "^HK(CC|CR|CU|LM|U):\\") {
    Fail-Json -obj $result -message "path: $path is not a valid registry path, see module documentation for examples."
}

$registry_path = (Split-Path -Path $path -NoQualifier).Substring(1)  # removes the hive: and leading \
$registry_hive = switch(Split-Path -Path $path -Qualifier) {
    "HKCR:" { [Microsoft.Win32.Registry]::ClassesRoot }
    "HKCC:" { [Microsoft.Win32.Registry]::CurrentConfig }
    "HKCU:" { [Microsoft.Win32.Registry]::CurrentUser }
    "HKLM:" { [Microsoft.Win32.Registry]::LocalMachine }
    "HKU" { [Microsoft.Win32.Registry]::Users }
}

$key = $null
try {
    $key = $registry_hive.OpenSubKey($registry_path, $false)

    if ($null -ne $key) {
        if ($null -eq $name) {
            $property_info = @{}
            foreach ($property in $key.GetValueNames()) {
                $property_info.$property = Get-PropertyValue -Key $key -Name $property
            }

            # Return the key's (Default) property if it has been defined
            $default_value = Get-PropertyValue -Key $key -Name ""
            if ($null -ne $default_value) {
                $property_info."" = $default_value
            }

            $result.exists = $true
            $result.properties = $property_info
            $result.sub_keys = $key.GetSubKeyNames()
        } else {
            $property_value = Get-PropertyValue -Key $key -Name $name
            if ($null -ne $property_value) {
                $result.exists = $true
                $result += $property_value
            } else {
                $result.exists = $false
            }
        }
    } else {
        $result.exists = $false
    }
} finally {
    if ($key) {
        $key.Dispose()
    }
    $registry_hive.Dispose()
}

Exit-Json -obj $result

