Common/Wrappers/Wrappers.psm1

function Read-RMUUID {
    param (
        [string] $UserMessage,
        [string] $ParameterName,
        [bool] $IsRequired
    )
    while ($true) {
        $ReadValue = Read-Host $UserMessage
        if ("" -eq $ReadValue) {
            if ($IsRequired) {
                Write-RMError -Message ($ParameterName + " is required, please try again")
                continue
            }
        }
    
        $Tokens = $ReadValue -split ","
        $InvalidUUIDs = @()
        $ResultIDs = @()    
        foreach ($Token in $Tokens) {
            $Token = $Token.Trim()
            try {
                $ResultIDs += [System.Guid] $Token
            } catch {
                $InvalidUUIDs += $Token
            }
        }

        if ($InvalidUUIDs.Count -gt 0) {
            $InvalidUUIDsAsString = $InvalidUUIDs -join ", "
            Write-RMError -Message ("Invalid UUID {0}, please try again" -f $InvalidUUIDsAsString)
            continue
        }
    
        return $ResultIDs
    }
}

function Read-RMString {
    param (
        [Parameter(Mandatory)]
        [string] $UserMessage,
        [string[]] $Options,
        [string] $DefaultValue,
        [Parameter(Mandatory)]
        [string] $ParameterName,
        [Parameter(Mandatory)]
        [bool] $IsRequired
    )

    Confirm-RMInputParameter -InputParameter $PSBoundParameters

    $UserPrompt = $UserMessage
    if ($Options.Count -gt 0) {
        $UserPrompt += " (" + ($Options -join ",") + ")"
    }

    if (![string]::IsNullOrEmpty($DefaultValue)) {
        if ($Options.Count -eq 0) {
            $UserPrompt += " [" + $DefaultValue + "]"
        } else {
            $UserPrompt += "[" + $DefaultValue + "]"
        }
    }

    while ($true) {
        $ReadValue = Read-Host $UserPrompt
        if ("" -eq $ReadValue) {
            if ($IsRequired) {
                Write-RMError -Message ($ParameterName + " is required, please try again")
                continue
            } elseif("None" -ieq $DefaultValue) {
                return ""
            } else {
                return $DefaultValue
            }
        }
        if (0 -ne $Options.Count -and $Options -notcontains $ReadValue) {
            Write-RMError -Message ($ParameterName + " should be " + ($Options -join " or ") + ", please try again")
            continue
        }
        return $ReadValue
    }
}

function Read-RMBoolean {
    param(
        [string] $UserMessage,
        [Parameter(Mandatory)]
        [string] $DefaultValue
    )
    $UserPrompt = $UserMessage
    if (![string]::IsNullOrEmpty($DefaultValue)) {
        $UserPrompt += " (true,false)" + "[" + $DefaultValue + "]"
    }

    while ($true) {
        $ReadValue = Read-Host $UserPrompt
        if ("" -eq $ReadValue) {
            return [System.Convert]::ToBoolean($DefaultValue)
        }
        try {
            return [System.Convert]::ToBoolean($ReadValue)
        } catch {
            Write-RMError -Message "Invalid boolean value '$ReadValue', please try again"
            continue
        }
    }
}

function Read-RMInt {
    param(
        [Parameter(Mandatory)]
        [string] $UserMessage,
        [int[]] $Options,
        [int[]] $OptionsRange,
        [string] $DefaultValue,
        [Parameter(Mandatory)]
        [string] $ParameterName,
        [Parameter(Mandatory)]
        [bool] $IsRequired
    )
    Confirm-RMInputParameter -InputParameter $PSBoundParameters
    if ($OptionsRange.Count -gt 0 -and $OptionsRange.Count -ne 2) {
        throw "The parameter OptionsRange length should be exactly equal to 2, actual length is: " +$OptionsRange.Count
    }

    $UserPrompt = $UserMessage
    if ($Options.Count -eq 1 -and [string]::IsNullOrEmpty($DefaultValue)) {

    }
    if ($Options.Count -gt 0) {
        $UserPrompt += " (" + ($Options -join ",") + ")"
    } elseif ($OptionsRange.Count -gt 0) {
        $UserPrompt += " (" + ($OptionsRange -join "-") + ")"
    }

    if (![string]::IsNullOrEmpty($DefaultValue)) {
        if ($Options.Count -eq 0) {
            $UserPrompt += " [" + $DefaultValue + "]"
        } else {
            $UserPrompt += "[" + $DefaultValue + "]"
        }
    }

    while ($true) {
        $ReadValue = Read-Host $UserPrompt
        if ("" -eq $ReadValue) {
            if ($IsRequired) {
                Write-RMError -Message ($ParameterName + " is required, please try again")
                continue
            } elseif("None" -eq $DefaultValue) {
                return 0
            } else {
                return [int] $DefaultValue
            }
        }

        if (-not($ReadValue -match "^[\d]+$")) {
            Write-RMError -Message "Please enter an integer value only"
            continue
        }

        if (0 -ne $Options.Count -and $Options -notcontains $ReadValue) {
            Write-RMError -Message ($ParameterName + " should be " + ($Options -join " or ") + ", please try again")
            continue
        } elseif ($OptionsRange.Count -gt 0) {
            $ReadValue = [int]$ReadValue
            if(-not($ReadValue -ge $OptionsRange[0] -and $ReadValue -le $OptionsRange[1])) {
                Write-RMError -Message ($ParameterName + " should be in range " + ($OptionsRange -join "-") + ", please try again")
                continue
            }
        }
        return $ReadValue
    }
}

function Read-RMIPAddress {
    param(
        [Parameter(Mandatory)]
        [string] $UserMessage,
        [string] $DefaultValue,
        [Parameter(Mandatory)]
        [string] $ParameterName,
        [Parameter(Mandatory)]
        [bool] $IsRequired
    )
    Confirm-RMInputParameter -InputParameter $PSBoundParameters
    $UserPrompt = $UserMessage
    if (![string]::IsNullOrEmpty($DefaultValue)) {
        $UserPrompt += " [" + $DefaultValue + "]"
    }

    while ($true) {
        $ReadValue = Read-Host $UserPrompt
        if ("" -eq $ReadValue) {
            if ($IsRequired) {
                Write-RMError -Message ($ParameterName + " is required, please try again")
                continue
            } elseif("None" -ieq $DefaultValue) {
                return ""
            } else {
                return $DefaultValue
            }
        }

        $IPPattern = '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
        if ($ReadValue -notmatch $IPPattern) {
            Write-RMError -Message ($ReadValue + " is not a valid IP address, please try again")
            continue
        }

        return $ReadValue
    }
}

function Read-RMDate {
    param(
        [Parameter(Mandatory)]
        [string] $UserMessage,
        [Parameter(Mandatory)]
        [string] $DateFormat,
        [string] $DefaultValue,
        [Parameter(Mandatory)]
        [string] $ParameterName,
        [bool] $IsRequired
    )
    Confirm-RMInputParameter -InputParameter $PSBoundParameters

    $UserPrompt = $UserMessage
    if (![string]::IsNullOrEmpty($DefaultValue)) {
        $UserPrompt += "[" + $DefaultValue + "]"
    }

    while ($true) {
        $ReadValue = Read-Host $UserPrompt
        $ReadValue = $ReadValue.Trim()
        if ("" -eq $ReadValue) {
            if ($IsRequired) {
                Write-RMError -Message ($ParameterName + " is required, please try again")
                continue
            } elseif("None" -ieq $DefaultValue) {
                return ""
            } else {
                return $DefaultValue
            }
        }

        try {
            [datetime]::ParseExact($ReadValue, $DateFormat, $null)
            return $ReadValue
        } catch {
            Write-RMError -Message ("{0} format is incorrect, expected date format is {1}" -f $ParameterName, $DateFormat)
            continue
        }
    }
}

function Read-RMToken {
    param(
        [Parameter(Mandatory)]
        [string] $UserMessage,
        [Parameter(Mandatory)]
        [string] $Separator,
        [Parameter(Mandatory)]
        [string] $ParameterName,
        [string] $DefaultValue,
        [bool] $IsRequired
    )
    Confirm-RMInputParameter -InputParameter $PSBoundParameters

    $UserPrompt = $UserMessage
    if (![string]::IsNullOrEmpty($DefaultValue)) {
        $UserPrompt += "[" + $DefaultValue + "]"
    }

    $Result = @()
    while ($true) {
        $ReadValue = Read-Host $UserPrompt
        $ReadValue = $ReadValue.Trim()
        if ("" -eq $ReadValue) {
            if ($IsRequired) {
                Write-RMError -Message ($ParameterName + " is required, please try again")
                continue
            } elseif("None" -ieq $DefaultValue) {
                return ""
            } else {
                return $DefaultValue
            }
        }
        $Tokens = $ReadValue.Split($Separator)
        foreach ($Token in $Tokens) {
            $Result += $Token.Trim()
        }
        return $Result
    }
}

function Read-RMPair {
    param(
        [Parameter(Mandatory)]
        [string] $UserMessage,
        [string] $Separator,
        [string] $DefaultValue
    )
    $UserPrompt = $UserMessage
    if (![string]::IsNullOrEmpty($DefaultValue)) {
        $UserPrompt += " [" + $DefaultValue + "]"
    }
    while ($true) {
        $ReadValue = Read-Host $UserPrompt
        $ReadValue = $ReadValue.Trim()
        if ("" -eq $ReadValue) {
            if ("none" -ieq $DefaultValue) {
                return ""
            }
            return $DefaultValue
        }

        if (-not(Confirm-RMPair -UserInput $ReadValue -Separator $Separator)) {
            continue
        }
        return $ReadValue
    }
}

function Read-RMSecureString {
    param(
        [string] $UserMessage,
        [string] $ConfirmMessage,
        [string] $ParameterName,
        [string] $ConfirmParameterName,
        [bool] $IsRequired
    )

    while ($true) {
        $ReadValue = Read-Host $UserMessage -AsSecureString

        $SecurePassword = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($ReadValue)
        $Password = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($SecurePassword)
        if ("" -eq $Password) {
            if ($IsRequired) {
                Write-RMError -Message ($ParameterName + " is required, please try again")
                continue
            }
        }
        if ([string]::IsNullOrEmpty($ConfirmMessage)) {
            # No need to confirm the password
            return $Password
        }

        $ReadValue = Read-Host $ConfirmMessage -AsSecureString
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ReadValue)
        $ConfirmPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)    
        if ($Password -ne $ConfirmPassword) {
            Write-RMError -Message ($ParameterName + " and " + $ConfirmParameterName + " do not match, please try again") 
            continue
        }
        
        return $Password
    }
}

function Confirm-RMPair {
    param(
        [string] $UserInput,
        [string] $Separator
    )
    $Tokens = $UserInput -split ","
    $IsValidInput = $true
    foreach ($Token in $Tokens) {
        $Token = $Token.Trim()
        if (!$Token.Contains($Separator)) {
            Write-RMError -Message "'$Token' does not contain '$Separator', please try again"
            $IsValidInput = $false
            continue
        }

        $Pair = $Token -split $Separator
        if ($Pair.Count -ne 2 -or [string]::IsNullOrEmpty($Pair[0]) -or [string]::IsNullOrEmpty($Pair[1])) {
            Write-RMError -Message ("$Token is not in the format key $Separator value, please try again")
            $IsValidInput = $false
            continue
        }
    }

    return $IsValidInput
}

function Confirm-RMInputParameter {
    param(
        [hashtable] $InputParameter
    )
    if ($InputParameter.ContainsKey("DefaultValue") -and $InputParameter.ContainsKey("IsRequired")) {
        if (![string]::IsNullOrEmpty($InputParameter["DefaultValue"]) -and $InputParameter["IsRequired"] -eq $true) {
            throw "Incorrect usage of wrapper method, cannot invoke '{0}' with a default value and IsRequired as true" `
                -f $MyInvocation.MyCommand
        }

        if ($InputParameter["IsRequired"] -eq $false -and [string]::IsNullOrEmpty($InputParameter["DefaultValue"])) {
            throw "Incorrect usage of wrapper method, cannot invoke '{0}' with no default value and IsRequired as false" `
                -f $MyInvocation.MyCommand
        }
    }
}

function Write-RMError {
    param (
       [string] $Message
    )

    [Console]::ForegroundColor = 'red'
    [Console]::Error.WriteLine($message)
    [Console]::ResetColor()
}