SwaggerUtils.psm1

#########################################################################################
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# Licensed under the MIT license.
#
# PSSwagger Module
#
#########################################################################################

Microsoft.PowerShell.Core\Set-StrictMode -Version Latest
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath Utilities.psm1)
Import-Module -Name 'PSSwaggerUtility'
. "$PSScriptRoot\PSSwagger.Constants.ps1" -Force
. "$PSScriptRoot\Trie.ps1" -Force
. "$PSScriptRoot\PSCommandVerbMap.ps1" -Force
Microsoft.PowerShell.Utility\Import-LocalizedData  LocalizedData -filename PSSwagger.Resources.psd1
$script:CmdVerbTrie = $null
$script:CSharpCodeNamer = $null
$script:CSharpCodeNamerLoadAttempted = $false

$script:PluralizationService = $null
# System.Data.Entity.Design.PluralizationServices.PluralizationService is not yet supported on coreclr.
if(-not (Get-OperatingSystemInfo).IsCore)
{
    if(-not ('System.Data.Entity.Design.PluralizationServices.PluralizationService' -as [Type]))
    {
        Add-Type -AssemblyName System.Data.Entity.Design
    }

    $script:PluralizationService = [System.Data.Entity.Design.PluralizationServices.PluralizationService]::CreateService([System.Globalization.CultureInfo]::GetCultureInfo('en-US'))

    $PluralToSingularMapPath = Join-Path -Path $PSScriptRoot -ChildPath 'PluralToSingularMap.json'
    if(Test-Path -Path $PluralToSingularMapPath -PathType Leaf)
    {
        $PluralToSingularMapJsonObject = ConvertFrom-Json -InputObject ((Get-Content -Path $PluralToSingularMapPath) -join [Environment]::NewLine) -ErrorAction Stop
        $PluralToSingularMapJsonObject.CustomPluralToSingularMapping | ForEach-Object {
            $script:PluralizationService.AddWord($_.PSObject.Properties.Value, $_.PSObject.Properties.Name)
        }
    }
}

$script:IgnoredAutoRestParameters = @(@('Modeler', 'm'), @('AddCredentials'), @('CodeGenerator', 'g'))
$script:PSSwaggerDefaultNamespace = "Microsoft.PowerShell"
$script:CSharpReservedWords = @(
    'abstract', 'as', 'async', 'await', 'base',
    'bool', 'break', 'byte', 'case', 'catch',
    'char', 'checked', 'class', 'const', 'continue',
    'decimal', 'default', 'delegate', 'do', 'double',
    'dynamic', 'else', 'enum', 'event', 'explicit',
    'extern', 'false', 'finally', 'fixed', 'float',
    'for', 'foreach', 'from', 'global', 'goto',
    'if', 'implicit', 'in', 'int', 'interface',
    'internal', 'is', 'lock', 'long', 'namespace',
    'new', 'null', 'object', 'operator', 'out',
    'override', 'params', 'private', 'protected', 'public',
    'readonly', 'ref', 'return', 'sbyte', 'sealed',
    'short', 'sizeof', 'stackalloc', 'static', 'string',
    'struct', 'switch', 'this', 'throw', 'true',
    'try', 'typeof', 'uint', 'ulong', 'unchecked',
    'unsafe', 'ushort', 'using', 'virtual', 'void',
    'volatile', 'while', 'yield', 'var'
)


function ConvertTo-SwaggerDictionary {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $SwaggerSpecPath,

        [Parameter(Mandatory=$true)]
        [string[]]
        $SwaggerSpecFilePaths,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails,

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

        [Parameter(Mandatory=$false)]
        [Version]
        $ModuleVersion = '0.0.1',

        [Parameter(Mandatory=$false)]
        [AllowEmptyString()]
        [string]
        $ClientTypeName,

        [Parameter(Mandatory=$false)]
        [AllowEmptyString()]
        [string]
        $ModelsName,

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

        [Parameter(Mandatory = $false)]
        [AllowEmptyString()]
        [string]
        $Header,

        [Parameter(Mandatory = $false)]
        [switch]
        $AzureSpec,

        [Parameter(Mandatory = $false)]
        [switch]
        $DisableVersionSuffix,

        [Parameter(Mandatory = $false)]
        [hashtable]
        $PowerShellCodeGen,

        [Parameter(Mandatory = $false)]
        [PSCustomObject]
        $PSMetaJsonObject
    )
    
    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $swaggerDocObject = ConvertFrom-Json ((Get-Content $SwaggerSpecPath) -join [Environment]::NewLine) -ErrorAction Stop
    $swaggerDict = @{}

    if(-not (Get-Member -InputObject $swaggerDocObject -Name 'info')) {
        Throw $LocalizedData.InvalidSwaggerSpecification
    }

    if ($PowerShellCodeGen -and (Get-Member -InputObject $swaggerDocObject -Name 'securityDefinitions')) {
        $swaggerDict['SecurityDefinitions'] = $swaggerDocObject.securityDefinitions
        if ((Get-Member -InputObject $swaggerDocObject.securityDefinitions -Name 'azure_auth')) {
            $PowerShellCodeGen['ServiceType'] = 'azure'
        }
    }

    if ((Get-Member -InputObject $swaggerDocObject -Name 'security')) {
        $swaggerDict['Security'] = $swaggerDocObject.security
    }

    $GetSwaggerInfo_params = @{
        Info = $swaggerDocObject.info
        ModuleVersion  = $ModuleVersion
    }
    if($ModuleName)
    {
        $GetSwaggerInfo_params['ModuleName'] = $ModuleName
    }
    if($ClientTypeName) {
        $GetSwaggerInfo_params['ClientTypeName'] = $ClientTypeName
    }
    if($ModelsName) {
        $GetSwaggerInfo_params['ModelsName'] = $ModelsName
    }
    $swaggerDict['Info'] = Get-SwaggerInfo @GetSwaggerInfo_params
    $swaggerDict['Info']['DefaultCommandPrefix'] = $DefaultCommandPrefix
    if($Header) {
        $swaggerDict['Info']['Header'] = $Header
    }

    $SwaggerParameters = @{}
    $SwaggerDefinitions = @{}
    $SwaggerPaths = @{}

    $PSMetaParametersJsonObject = $null
    if($PSMetaJsonObject) {
        if(Get-Member -InputObject $PSMetaJsonObject -Name 'parameters'){
            $PSMetaParametersJsonObject = $PSMetaJsonObject.parameters
        }
    }
    foreach($FilePath in $SwaggerSpecFilePaths) {
        $swaggerObject = ConvertFrom-Json ((Get-Content $FilePath) -join [Environment]::NewLine) -ErrorAction Stop
        if(Get-Member -InputObject $swaggerObject -Name 'parameters') {
            
            $GetSwaggerParameters_Params = @{
                Parameters = $swaggerObject.parameters
                Info = $swaggerDict['Info']
                SwaggerParameters = $swaggerParameters
                DefinitionFunctionsDetails = $DefinitionFunctionsDetails
                AzureSpec = $AzureSpec
                PSMetaParametersJsonObject = $PSMetaParametersJsonObject
            }

            Get-SwaggerParameters @GetSwaggerParameters_Params
        }

        if(Get-Member -InputObject $swaggerObject -Name 'definitions') {
            Get-SwaggerDefinitionMultiItemObject -Object $swaggerObject.definitions -SwaggerDictionary $SwaggerDefinitions
        }

        if(-not (Get-Member -InputObject $swaggerObject -Name 'paths') -or 
           -not (Get-HashtableKeyCount -Hashtable $swaggerObject.Paths.PSObject.Properties)) {
            Write-Warning -Message ($LocalizedData.SwaggerPathsMissing -f $FilePath)
        }

        Get-SwaggerPathMultiItemObject -Object $swaggerObject.paths -SwaggerDictionary $SwaggerPaths
    }

    $swaggerDict['Parameters'] = $swaggerParameters
    $swaggerDict['Definitions'] = $swaggerDefinitions
    $swaggerDict['Paths'] = $swaggerPaths

    return $swaggerDict
}

function Get-SwaggerInfo {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Info,

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

        [Parameter(Mandatory=$false)]
        [Version]
        $ModuleVersion = '0.0.1',

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

        [Parameter(Mandatory=$false)]
        [string]
        $ModelsName
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $infoVersion = '1-0-0'
    if((Get-Member -InputObject $Info -Name 'Version') -and $Info.Version) { 
        $infoVersion = $Info.Version
    }

    $infoTitle = $Info.title
    $CodeOutputDirectory = ''
    $infoName = ''
    $NameSpace = ''
    $codeGenFileRequired = $false
    if(-not $ModelsName) {
        $modelsName = 'Models'
    }

    $Header = ''    
    if(Get-Member -InputObject $Info -Name 'x-ms-code-generation-settings') {
        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('ClientName', 'Name')
        if ($prop) {
            $infoName = $Info.'x-ms-code-generation-settings'.$prop
        }

        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('OutputDirectory', 'o', 'output')
        if ($prop) {
            # When OutputDirectory is specified, we'll have to copy the code from here to the module directory later on
            $CodeOutputDirectory = $Info.'x-ms-code-generation-settings'.$prop
            if ((Test-Path -Path $CodeOutputDirectory) -and (Get-ChildItem -Path (Join-Path -Path $CodeOutputDirectory -ChildPath "*.cs") -Recurse)) {
                throw $LocalizedData.OutputDirectoryMustBeEmpty -f ($CodeOutputDirectory)
            } else {
                Write-Warning -Message ($LocalizedData.CodeDirectoryWillBeCreated -f $CodeOutputDirectory)
            }
        }

        if(-not $PSBoundParameters.ContainsKey('ModelsName')) {
            $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('ModelsName', 'mname')
            if ($prop) {
                # When ModelsName is specified, this changes the subnamespace of the models from 'Models' to whatever is specified
                $modelsName = $Info.'x-ms-code-generation-settings'.$prop
            }
        }

        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('Namespace', 'n')
        if ($prop) {
            # When NameSpace is specified, this overrides our namespace
            $NameSpace = $Info.'x-ms-code-generation-settings'.$prop
            # Warn the user that custom namespaces are not recommended
            Write-Warning -Message $LocalizedData.CustomNamespaceNotRecommended
        }

        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('Header', 'h')
        if ($prop) {
            $Header = $Info.'x-ms-code-generation-settings'.$prop
        }

        # When the following values are specified, the property will be overwritten by PSSwagger using a CodeGenSettings file
        foreach ($ignoredParameterAliases in $script:IgnoredAutoRestParameters) {
            $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases $ignoredParameterAliases
            if ($prop) {
                Write-Warning -Message ($LocalizedData.AutoRestParameterIgnored -f ($prop, $Info.'x-ms-code-generation-settings'.$prop))
                $codeGenFileRequired = $true
            }
        }
    }

    if (-not $infoName) {
        # Remove special characters as info name is used as client variable name in the generated commands.
        $infoName = ($infoTitle -replace '[^a-zA-Z0-9_]','')
    }

    $Description = ''
    if((Get-Member -InputObject $Info -Name 'Description') -and $Info.Description) { 
        $Description = $Info.Description
    }

    $ProjectUri = ''
    $ContactEmail = ''
    $ContactName = ''
    if(Get-Member -InputObject $Info -Name 'Contact')
    {
        # The identifying name of the contact person/organization.
        if((Get-Member -InputObject $Info.Contact -Name 'Name') -and
            $Info.Contact.Name)
        { 
            $ContactName = $Info.Contact.Name
        }

        # The URL pointing to the contact information. MUST be in the format of a URL.
        if((Get-Member -InputObject $Info.Contact -Name 'Url') -and
            $Info.Contact.Url)
        { 
            $ProjectUri = $Info.Contact.Url
        }

        # The email address of the contact person/organization. MUST be in the format of an email address.
        if((Get-Member -InputObject $Info.Contact -Name 'Email') -and
            $Info.Contact.Email)
        { 
            $ContactEmail = $Info.Contact.Email
        }        
    }

    $LicenseUri = ''
    $LicenseName = ''
    if(Get-Member -InputObject $Info -Name 'License')
    {
        # A URL to the license used for the API. MUST be in the format of a URL.
        if((Get-Member -InputObject $Info.License -Name 'Url') -and
          $Info.License.Url)
        { 
            $LicenseUri = $Info.License.Url
        }

        # License name.
        if((Get-Member -InputObject $Info.License -Name 'Name') -and
          $Info.License.Name)
        { 
            $LicenseName = $Info.License.Name
        }
    }

    # Using the info name as module name when $ModuleName is not specified.
    # This is required for PSMeta generaration.
    if(-not $ModuleName)
    {
        $ModuleName = $infoName
    }

    if (-not $NameSpace) {
        # Default namespace supports sxs
        $NamespaceVersionSuffix = "v$("$ModuleVersion" -replace '\.','')"
        $NameSpace = "$script:PSSwaggerDefaultNamespace.$ModuleName.$NamespaceVersionSuffix"
    }

    if($ClientTypeName) {        
        # Get the namespace from namespace qualified client type name.
        $LastDotIndex = $ClientTypeName.LastIndexOf('.')        
        if($LastDotIndex -ne -1){
            $NameSpace = $ClientTypeName.Substring(0, $LastDotIndex)
            $ClientTypeName = $ClientTypeName.Substring($LastDotIndex+1)
        }
    }
    else {
        # AutoRest generates client name with 'Client' appended to info title when a NameSpace part is same as the info name.
        if($NameSpace.Split('.', [System.StringSplitOptions]::RemoveEmptyEntries) -contains $infoName) {
            $ClientTypeName = $infoName + 'Client'
        }
        else {
            $ClientTypeName = $infoName
        }
    }

    return @{
        InfoVersion = $infoVersion
        InfoTitle = $infoTitle
        InfoName = $infoName
        ClientTypeName = $ClientTypeName
        Version = $ModuleVersion
        NameSpace = $NameSpace
        ModuleName = $ModuleName
        Description = $Description
        ContactName = $ContactName
        ContactEmail = $ContactEmail
        ProjectUri = $ProjectUri
        LicenseUri = $LicenseUri
        LicenseName = $LicenseName
        CodeOutputDirectory = $CodeOutputDirectory
        CodeGenFileRequired = $codeGenFileRequired
        Models = $modelsName
        Header = $Header
    }
}

function Test-PropertyWithAliases {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $InputObject,

        [Parameter(Mandatory=$true)]
        [string[]]
        $Aliases
    )

    foreach ($alias in $Aliases) {
        if ((Get-Member -InputObject $InputObject -Name $alias) -and $InputObject.$alias) {
            return $alias
        }
    }

    return $null
}

function Get-SwaggerParameters {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Parameters,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Info,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerParameters,

        [Parameter(Mandatory = $false)]
        [switch]
        $AzureSpec,

        [Parameter(Mandatory=$false)]
        [PSCustomObject]
        $PSMetaParametersJsonObject
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    foreach($Parameter in $Parameters.PSObject.Properties.GetEnumerator()) {
        $GlobalParameterName = $Parameter.Name
        $GPJsonValueObject = $Parameter.Value

        if ($SwaggerParameters.ContainsKey($GlobalParameterName))
        {
            Write-Verbose -Message ($LocalizedData.SkippingExistingParameter -f $GlobalParameterName)
            continue
        }

        $IsParamMandatory = '$false'
        $ParameterDescription = ''
        $x_ms_parameter_location = 'client'
        $x_ms_parameter_grouping = ''
        $ConstantValue = ''
        $ReadOnlyGlobalParameter = $false
        if ((Get-Member -InputObject $GPJsonValueObject -Name 'x-ms-client-name') -and $GPJsonValueObject.'x-ms-client-name') {
            $parameterName = Get-PascalCasedString -Name $GPJsonValueObject.'x-ms-client-name'
        } elseif ((Get-Member -InputObject $GPJsonValueObject -Name 'Name') -and $GPJsonValueObject.Name)
        {
            $parameterName = Get-PascalCasedString -Name $GPJsonValueObject.Name
        }

        if(Get-Member -InputObject $GPJsonValueObject -Name 'x-ms-parameter-location')
        {
            $x_ms_parameter_location = $GPJsonValueObject.'x-ms-parameter-location'
        }

        if ($AzureSpec) {
            # Some global parameters have constant values not expressed in the Swagger spec when dealing with Azure
            if ('subscriptionId' -eq $parameterName) {
                # See PSSwagger.Constants.ps1 $functionBodyStr for this variable name
                $ConstantValue = '`$subscriptionId'
            } elseif ('apiversion' -eq $parameterName) {
                $ReadOnlyGlobalParameter = $true
            }       
        }

        if((Get-Member -InputObject $GPJsonValueObject -Name 'Required') -and
            $GPJsonValueObject.Required)
        {
            $IsParamMandatory = '$true'
        }

        if ((Get-Member -InputObject $GPJsonValueObject -Name 'Description') -and
            $GPJsonValueObject.Description)
        {
            $ParameterDescription = $GPJsonValueObject.Description
        }

        $GetParamTypeParams = @{
            ParameterJsonObject = $GPJsonValueObject
            ModelsNameSpace = "$($Info.NameSpace).$($Info.Models)"
            ParameterName = $parameterName
            DefinitionFunctionsDetails = $DefinitionFunctionsDetails
        }

        $paramTypeObject = Get-ParamType @GetParamTypeParams

        if (Get-Member -InputObject $GPJsonValueObject -Name 'x-ms-parameter-grouping') {
            $groupObject = $GPJsonValueObject.'x-ms-parameter-grouping'
            if (Get-Member -InputObject $groupObject -Name 'name') {
                $parsedName = Get-ParameterGroupName -RawName $groupObject.name
            } elseif (Get-Member -InputObject $groupObject -Name 'postfix') {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId -Postfix $groupObject.postfix
            } else {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId
            }

            $x_ms_parameter_grouping = $parsedName
        }

        $FlattenOnPSCmdlet = $false
        if($PSMetaParametersJsonObject -and 
          (Get-Member -InputObject $PSMetaParametersJsonObject -Name $GlobalParameterName) -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$GlobalParameterName -Name 'x-ps-parameter-info') -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$GlobalParameterName.'x-ps-parameter-info' -Name 'flatten')) {
            $FlattenOnPSCmdlet = $PSMetaParametersJsonObject.$GlobalParameterName.'x-ps-parameter-info'.'flatten'
        }

        $SwaggerParameters[$GlobalParameterName] = @{
            Name = $parameterName
            Type = $paramTypeObject.ParamType
            ValidateSet = $paramTypeObject.ValidateSetString
            Mandatory = $IsParamMandatory
            Description = $ParameterDescription
            IsParameter = $paramTypeObject.IsParameter
            x_ms_parameter_location = $x_ms_parameter_location
            x_ms_parameter_grouping = $x_ms_parameter_grouping
            ConstantValue = $ConstantValue
            ReadOnlyGlobalParameter = $ReadOnlyGlobalParameter
            FlattenOnPSCmdlet = $FlattenOnPSCmdlet
        }
    }
}

function Get-SwaggerPathMultiItemObject {
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Object,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerDictionary
    )

    $Object.PSObject.Properties | ForEach-Object {
        if($SwaggerDictionary.ContainsKey($_.name)) {
            Write-Verbose -Message ($LocalizedData.SkippingExistingKeyFromSwaggerMultiItemObject -f $_)
        } else {
            $SwaggerDictionary[$_.name] = $_
        }
    }
}

function Get-SwaggerDefinitionMultiItemObject {
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Object,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerDictionary
    )

    $Object.PSObject.Properties | ForEach-Object {
        $ModelName = Get-CSharpModelName -Name $_.Name
        if($SwaggerDictionary.ContainsKey($ModelName)) {
            Write-Verbose -Message ($LocalizedData.SkippingExistingKeyFromSwaggerMultiItemObject -f $ModelName)
        } else {
            $SwaggerDictionary[$ModelName] = $_
        }
    }
}

function Get-PathParamInfo
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [PSObject]
        $JsonPathItemObject,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $SwaggerDict,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParameterGroupCache,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParametersTable,

        [Parameter(Mandatory=$false)]
        [PSCustomObject]
        $PSMetaParametersJsonObject
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $index = (Get-HashtableKeyCount -Hashtable $ParametersTable)
    $operationId = $null
    if(Get-Member -InputObject $JsonPathItemObject -Name 'OperationId'){
        $operationId = $JsonPathItemObject.operationId
    }
    
    if(Get-Member -InputObject $JsonPathItemObject -Name 'Parameters'){
        $JsonPathItemObject.parameters | ForEach-Object {
            $AllParameterDetails = Get-ParameterDetails -ParameterJsonObject $_ `
                                                        -SwaggerDict $SwaggerDict `
                                                        -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
                                                        -OperationId $operationId `
                                                        -ParameterGroupCache $ParameterGroupCache `
                                                        -PSMetaParametersJsonObject $PSMetaParametersJsonObject
            foreach ($ParameterDetails in $AllParameterDetails) {
                if($ParameterDetails -and ($ParameterDetails.ContainsKey('x_ms_parameter_grouping_group') -or $ParameterDetails.Type))
                {
                    $ParametersTable[$index] = $ParameterDetails
                    $index = $index + 1            
                }
            }
        }
    }
}

function Get-ParameterDetails
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [PSObject]
        $ParameterJsonObject,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $SwaggerDict,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails,

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

        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParameterGroupCache,

        [Parameter(Mandatory=$false)]
        [PSCustomObject]
        $PSMetaParametersJsonObject
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $NameSpace = $SwaggerDict['Info'].NameSpace
    $Models = $SwaggerDict['Info'].Models
    $DefinitionTypeNamePrefix = "$Namespace.$Models."
    $parameterName = ''
    if ((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-name') -and $ParameterJsonObject.'x-ms-client-name') {
        $parameterName = Get-PascalCasedString -Name $ParameterJsonObject.'x-ms-client-name'
    } elseif ((Get-Member -InputObject $ParameterJsonObject -Name 'Name') -and $ParameterJsonObject.Name)
    {
        $parameterName = Get-PascalCasedString -Name $ParameterJsonObject.Name
    }
   
    $GetParamTypeParameters = @{
        ParameterJsonObject = $ParameterJsonObject
        ModelsNamespace = "$NameSpace.$Models"
        ParameterName = $parameterName
        DefinitionFunctionsDetails = $DefinitionFunctionsDetails
        SwaggerDict = $SwaggerDict
    }

    $paramTypeObject = Get-ParamType @GetParamTypeParameters

    # Swagger Path Operations can be defined with reference to the global method based parameters.
    # Add the method based global parameters as a function parameter.
    $AllParameterDetailsArrayTemp = @()
    $x_ms_parameter_grouping = ''
    if($paramTypeObject.GlobalParameterDetails)
    {
        $ParameterDetails = $paramTypeObject.GlobalParameterDetails
        $x_ms_parameter_grouping = $ParameterDetails.'x_ms_parameter_grouping'
    }
    else
    {
        $IsParamMandatory = '$false'
        $ParameterDescription = ''
        $x_ms_parameter_location = 'method'

        if ((Get-Member -InputObject $ParameterJsonObject -Name 'Required') -and 
            $ParameterJsonObject.Required)
        {
            $IsParamMandatory = '$true'
        }

        if ((Get-Member -InputObject $ParameterJsonObject -Name 'Description') -and 
            $ParameterJsonObject.Description)
        {
            $ParameterDescription = $ParameterJsonObject.Description
        }

        if ($OperationId -and (Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-parameter-grouping')) {
            $groupObject = $ParameterJsonObject.'x-ms-parameter-grouping'
            if (Get-Member -InputObject $groupObject -Name 'name') {
                $parsedName = Get-ParameterGroupName -RawName $groupObject.name
            } elseif (Get-Member -InputObject $groupObject -Name 'postfix') {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId -Postfix $groupObject.postfix
            } else {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId
            }

            $x_ms_parameter_grouping = $parsedName
        }

        $FlattenOnPSCmdlet = $false
        if($PSMetaParametersJsonObject -and 
          (Get-Member -InputObject $PSMetaParametersJsonObject -Name $parameterName) -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$parameterName -Name 'x-ps-parameter-info') -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$parameterName.'x-ps-parameter-info' -Name 'flatten')) {
            $FlattenOnPSCmdlet = $PSMetaParametersJsonObject.$parameterName.'x-ps-parameter-info'.'flatten'
        }

        $ParameterDetails = @{
            Name = $parameterName
            Type = $paramTypeObject.ParamType
            ValidateSet = $paramTypeObject.ValidateSetString
            Mandatory = $IsParamMandatory
            Description = $ParameterDescription
            IsParameter = $paramTypeObject.IsParameter
            x_ms_parameter_location = $x_ms_parameter_location
            x_ms_parameter_grouping = $x_ms_parameter_grouping
            OriginalParameterName = $ParameterJsonObject.Name
            FlattenOnPSCmdlet = $FlattenOnPSCmdlet
        }
    }

    if ((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-flatten') -and $ParameterJsonObject.'x-ms-client-flatten') {
        $referenceTypeName = $ParameterDetails.Type.Replace($DefinitionTypeNamePrefix, '')
        # If the parameter should be flattened, return an array of parameter detail objects for each parameter of the referenced definition
        Write-Verbose -Message ($LocalizedData.FlatteningParameterType -f ($parameterName, $referenceTypeName))
        $AllParameterDetails = @{}
        Expand-Parameters -ReferenceTypeName $referenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
        foreach ($expandedParameterDetail in $AllParameterDetails.GetEnumerator()) {
            Write-Verbose -Message ($LocalizedData.ParameterExpandedTo -f ($parameterName, $expandedParameterDetail.Key))
            $AllParameterDetailsArrayTemp += $expandedParameterDetail.Value
        }
    } else {
        # If the parameter shouldn't be flattened, just return the original parameter detail object
        $AllParameterDetailsArrayTemp += $ParameterDetails
    }

    # Loop through the parameters in case they belong to different groups after being expanded
    $AllParameterDetailsArray = @()
    foreach ($expandedParameterDetail in $AllParameterDetailsArrayTemp) {
        # The parent parameter object, wherever it is, set a grouping name
        if ($x_ms_parameter_grouping) {
            $expandedParameterDetail.'x_ms_parameter_grouping' = $x_ms_parameter_grouping
            # An empty parameter details object is created that contains all known parameters in this group
            if ($ParameterGroupCache.ContainsKey($x_ms_parameter_grouping)) {
                $ParameterDetails = $ParameterGroupCache[$x_ms_parameter_grouping]
            } else {
                $ParameterDetails = @{
                    Name = $x_ms_parameter_grouping
                    x_ms_parameter_grouping_group = @{}
                    IsParameter = $true
                }
            }

            if (-not $ParameterDetails.'x_ms_parameter_grouping_group'.ContainsKey($expandedParameterDetail.Name)) {
                $ParameterDetails.'x_ms_parameter_grouping_group'[$expandedParameterDetail.Name] = $expandedParameterDetail
            }

            $AllParameterDetailsArray += $ParameterDetails
            $ParameterGroupCache[$x_ms_parameter_grouping] = $ParameterDetails
        } else {
            $AllParameterDetailsArray += $expandedParameterDetail
        }
    }

    # Properties of ParameterDetails object
    # .x_ms_parameter_grouping - string - non-empty if this is part of a group, contains the group's parsed name (should be the C# Type name)
    # .x_ms_parameter_grouping_group - hashtable - table of parameter names to ParameterDetails, indicates this ParameterDetails object is a grouping
    return $AllParameterDetailsArray
}

function Expand-Parameters {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $ReferenceTypeName,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $AllParameterDetails
    )

    # Expand unexpanded x-ms-client-flatten
    # Leave it unexpanded afterwards
    if ($DefinitionFunctionsDetails[$ReferenceTypeName].ContainsKey('Unexpanded_x_ms_client_flatten_DefinitionNames') -and
        ($DefinitionFunctionsDetails[$ReferenceTypeName].'Unexpanded_x_ms_client_flatten_DefinitionNames'.Count -gt 0)) {
        foreach ($unexpandedDefinitionName in $DefinitionFunctionsDetails[$ReferenceTypeName].'Unexpanded_x_ms_client_flatten_DefinitionNames') {
            if ($DefinitionFunctionsDetails[$unexpandedDefinitionName].ContainsKey('ExpandedParameters') -and -not $DefinitionFunctionsDetails[$unexpandedDefinitionName].ExpandedParameters) {
                Expand-Parameters -ReferenceTypeName $unexpandedDefinitionName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
            }

            Flatten-ParameterTable -ReferenceTypeName $unexpandedDefinitionName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
        }
    }

    Flatten-ParameterTable -ReferenceTypeName $ReferenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
}

<#
.DESCRIPTION
   Flattens the given type's parameter table into cmdlet parameters.
#>

function Flatten-ParameterTable {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $ReferenceTypeName,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $AllParameterDetails
    )
    foreach ($parameterEntry in $DefinitionFunctionsDetails[$ReferenceTypeName]['ParametersTable'].GetEnumerator()) {
        if ($AllParameterDetails.ContainsKey($parameterEntry.Key)) {
            throw $LocalizedData.DuplicateExpandedProperty -f ($parameterEntry.Key)
        }

        $AllParameterDetails[$parameterEntry.Key] = Clone-ParameterDetail -ParameterDetail $parameterEntry.Value -OtherEntries @{'IsParameter'=$true}
    }
}

<#
.DESCRIPTION
   Clones a given parameter detail object by shallow copying all properties. Optionally adds additional entries.
#>

function Clone-ParameterDetail {
    param(
        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParameterDetail,

        [Parameter(Mandatory=$false)]
        [hashtable]
        $OtherEntries
    )

    $clonedParameterDetail = @{}
    foreach ($kvp in $ParameterDetail.GetEnumerator()) {
        $clonedParameterDetail[$kvp.Key] = $kvp.Value
    }

    foreach ($kvp in $OtherEntries.GetEnumerator()) {
        $clonedParameterDetail[$kvp.Key] = $kvp.Value
    }

    return $clonedParameterDetail
}

function Get-ParamType
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [PSObject]
        $ParameterJsonObject,

        [Parameter(Mandatory=$true)]
        [string]
        $ModelsNamespace,

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $ParameterName,

        [Parameter(Mandatory=$false)]
        [hashtable]
        $SwaggerDict,

        [Parameter(Mandatory=$false)]
        [hashtable]
        $DefinitionFunctionsDetails
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $DefinitionTypeNamePrefix = "$ModelsNamespace."
    $paramType = ""
    $ValidateSetString = $null
    $isParameter = $true
    $GlobalParameterDetails = $null
    $ReferenceTypeName = $null
    if((Get-Member -InputObject $ParameterJsonObject -Name 'Type') -and $ParameterJsonObject.Type)
    {
        $paramType = $ParameterJsonObject.Type

        # Use the format as parameter type if that is available as a type in PowerShell
        if ((Get-Member -InputObject $ParameterJsonObject -Name 'Format') -and 
            $ParameterJsonObject.Format -and 
            ($null -ne ($ParameterJsonObject.Format -as [Type]))) 
        {
            $paramType = $ParameterJsonObject.Format
        }
        elseif (($ParameterJsonObject.Type -eq 'array') -and
                (Get-Member -InputObject $ParameterJsonObject -Name 'Items') -and 
                $ParameterJsonObject.Items)
        {
            if((Get-Member -InputObject $ParameterJsonObject.Items -Name '$ref') -and 
                $ParameterJsonObject.Items.'$ref')
            {
                $ReferenceTypeValue = $ParameterJsonObject.Items.'$ref'
                $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceTypeValue.Substring( $( $ReferenceTypeValue.LastIndexOf('/') ) + 1 )
                $ResolveReferenceParameterType_params = @{
                    DefinitionFunctionsDetails = $DefinitionFunctionsDetails
                    ReferenceTypeName          = $ReferenceTypeName
                    DefinitionTypeNamePrefix   = $DefinitionTypeNamePrefix
                }
                $ResolvedResult = Resolve-ReferenceParameterType @ResolveReferenceParameterType_params
                $paramType = $ResolvedResult.ParameterType + '[]'
                if($ResolvedResult.ValidateSetString) {
                    $ValidateSetString = $ResolvedResult.ValidateSetString
                }
            }
            elseif((Get-Member -InputObject $ParameterJsonObject.Items -Name 'Type') -and $ParameterJsonObject.Items.Type)
            {
                $ReferenceTypeName = Get-PSTypeFromSwaggerObject -JsonObject $ParameterJsonObject.Items
                $paramType = "$($ReferenceTypeName)[]"
            }
        }
        elseif ((Get-Member -InputObject $ParameterJsonObject -Name 'AdditionalProperties') -and 
                $ParameterJsonObject.AdditionalProperties)
        {
            # Dictionary
            if($ParameterJsonObject.Type -eq 'object') {
                if((Get-Member -InputObject $ParameterJsonObject.AdditionalProperties -Name 'Type') -and
                $ParameterJsonObject.AdditionalProperties.Type) {
                    $AdditionalPropertiesType = Get-PSTypeFromSwaggerObject -JsonObject $ParameterJsonObject.AdditionalProperties
                    $paramType = "System.Collections.Generic.Dictionary[[$AdditionalPropertiesType],[$AdditionalPropertiesType]]"
                }
                elseif((Get-Member -InputObject $ParameterJsonObject.AdditionalProperties -Name '$ref') -and
                    $ParameterJsonObject.AdditionalProperties.'$ref')
                {
                    $ReferenceTypeValue = $ParameterJsonObject.AdditionalProperties.'$ref'
                    $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceTypeValue.Substring( $( $ReferenceTypeValue.LastIndexOf('/') ) + 1 )
                    $ResolveReferenceParameterType_params = @{
                        DefinitionFunctionsDetails = $DefinitionFunctionsDetails
                        ReferenceTypeName          = $ReferenceTypeName
                        DefinitionTypeNamePrefix   = $DefinitionTypeNamePrefix
                    }
                    $ResolvedResult = Resolve-ReferenceParameterType @ResolveReferenceParameterType_params
                    $paramType = "System.Collections.Generic.Dictionary[[string],[$($ResolvedResult.ParameterType)]]"
                }
                else {
                    $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('ParameterJsonObject', $($ParameterJsonObject | Out-String))
                    Write-Warning -Message $Message
                }
            }
            elseif($ParameterJsonObject.Type -eq 'string') {
                if((Get-Member -InputObject $ParameterJsonObject.AdditionalProperties -Name 'Type') -and
                   ($ParameterJsonObject.AdditionalProperties.Type -eq 'array'))
                {
                    if(Get-Member -InputObject $ParameterJsonObject.AdditionalProperties -Name 'Items')
                    {
                        if((Get-Member -InputObject $ParameterJsonObject.AdditionalProperties.Items -Name 'Type') -and
                           $ParameterJsonObject.AdditionalProperties.Items.Type)
                        { 
                            $ItemsType = Get-PSTypeFromSwaggerObject -JsonObject $ParameterJsonObject.AdditionalProperties.Items
                            $paramType = "System.Collections.Generic.Dictionary[[string],[System.Collections.Generic.List[$ItemsType]]]"
                        }
                        elseif((Get-Member -InputObject $ParameterJsonObject.AdditionalProperties.Items -Name '$ref') -and
                               $ParameterJsonObject.AdditionalProperties.Items.'$ref')
                        {
                            $ReferenceTypeValue = $ParameterJsonObject.AdditionalProperties.Items.'$ref'
                            $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceTypeValue.Substring( $( $ReferenceTypeValue.LastIndexOf('/') ) + 1 )
                            $ResolveReferenceParameterType_params = @{
                                DefinitionFunctionsDetails = $DefinitionFunctionsDetails
                                ReferenceTypeName          = $ReferenceTypeName
                                DefinitionTypeNamePrefix   = $DefinitionTypeNamePrefix
                            }
                            $ResolvedResult = Resolve-ReferenceParameterType @ResolveReferenceParameterType_params
                            $paramType = "System.Collections.Generic.Dictionary[[string],[System.Collections.Generic.List[$($ResolvedResult.ParameterType)]]]"
                        }
                        else
                        {
                            $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('ParameterJsonObject', $($ParameterJsonObject | Out-String))
                            Write-Warning -Message $Message
                        }
                    }
                    else
                    {
                        $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('ParameterJsonObject', $($ParameterJsonObject | Out-String))
                        Write-Warning -Message $Message
                    }
                }
                else
                {
                    $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('ParameterJsonObject', $($ParameterJsonObject | Out-String))
                    Write-Warning -Message $Message
                }
            }
            else {
                $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('ParameterJsonObject', $($ParameterJsonObject | Out-String))
                Write-Warning -Message $Message
            }
        }
    }
    elseif($parameterName -eq 'Properties' -and
           (Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-flatten') -and 
           ($ParameterJsonObject.'x-ms-client-flatten') )
    {
        # 'x-ms-client-flatten' extension allows to flatten deeply nested properties into the current definition.
        # Users often provide feedback that they don't want to create multiple levels of properties to be able to use an operation.
        # By applying the x-ms-client-flatten extension, you move the inner properties to the top level of your definition.

        $ReferenceParameterValue = $ParameterJsonObject.'$ref'
        $x_ms_client_flatten_ReferenceTypeName = Get-CSharpModelName -Name $ReferenceParameterValue.Substring( $( $ReferenceParameterValue.LastIndexOf('/') ) + 1 )
        Set-TypeUsedAsClientFlatten -ReferenceTypeName $x_ms_client_flatten_ReferenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails
    }
    elseif ( (Get-Member -InputObject $ParameterJsonObject -Name '$ref') -and ($ParameterJsonObject.'$ref') )
    {
        <#
            Currently supported parameter references:
                #/parameters/<PARAMETERNAME> or #../../<Parameters>.Json/parameters/<PARAMETERNAME>
                #/definitions/<DEFINITIONNAME> or #../../<Definitions>.Json/definitions/<DEFINITIONNAME>
        #>

        $ReferenceParameterValue = $ParameterJsonObject.'$ref'
        $ReferenceParts = $ReferenceParameterValue -split '/' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
        if($ReferenceParts.Count -ge 3)
        {
            if($ReferenceParts[-2] -eq 'Parameters')
            {
                # #<...>/parameters/<PARAMETERNAME>
                $GlobalParameters = $SwaggerDict['Parameters']
                # Cloning the common parameters object so that some values can be updated without impacting other operations.
                $GlobalParamDetails = $GlobalParameters[$ReferenceParts[-1]].Clone()

                # Get the definition name of the global parameter so that 'New-<DefinitionName>Object' can be generated.
                if($GlobalParamDetails.Type -and $GlobalParamDetails.Type -match '[.]') {
                    $ReferenceTypeName = ($GlobalParamDetails.Type -split '[.]')[-1]
                }

                # Valid values for this extension are: "client", "method".
                $GlobalParameterDetails = $GlobalParamDetails
                if(-not ($GlobalParamDetails -and 
                   $GlobalParamDetails.ContainsKey('x_ms_parameter_location') -and 
                   ($GlobalParamDetails.x_ms_parameter_location -eq 'method')))
                {
                    $isParameter = $false
                }
            }
            elseif($ReferenceParts[-2] -eq 'Definitions')
            {
                # #<...>/definitions/<DEFINITIONNAME>
                $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceParts[-1]
                $ResolveReferenceParameterType_params = @{
                    DefinitionFunctionsDetails = $DefinitionFunctionsDetails
                    ReferenceTypeName          = $ReferenceTypeName
                    DefinitionTypeNamePrefix   = $DefinitionTypeNamePrefix
                }
                $ResolvedResult = Resolve-ReferenceParameterType @ResolveReferenceParameterType_params
                $paramType = $ResolvedResult.ParameterType
                if($ResolvedResult.ValidateSetString) {
                    $ValidateSetString = $ResolvedResult.ValidateSetString
                }
            }
        }
    }
    elseif ((Get-Member -InputObject $ParameterJsonObject -Name 'Schema') -and ($ParameterJsonObject.Schema) -and
            (Get-Member -InputObject $ParameterJsonObject.Schema -Name '$ref') -and ($ParameterJsonObject.Schema.'$ref') )
    {
        $ReferenceParameterValue = $ParameterJsonObject.Schema.'$ref'
        $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceParameterValue.Substring( $( $ReferenceParameterValue.LastIndexOf('/') ) + 1 )

        $ResolveReferenceParameterType_params = @{
            DefinitionFunctionsDetails = $DefinitionFunctionsDetails
            ReferenceTypeName          = $ReferenceTypeName
            DefinitionTypeNamePrefix   = $DefinitionTypeNamePrefix
        }
        $ResolvedResult = Resolve-ReferenceParameterType @ResolveReferenceParameterType_params
        $paramType = $ResolvedResult.ParameterType
        if($ResolvedResult.ValidateSetString) {
            $ValidateSetString = $ResolvedResult.ValidateSetString
        }

        if((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-flatten') -and
           ($ParameterJsonObject.'x-ms-client-flatten'))
        {
            Set-TypeUsedAsClientFlatten -ReferenceTypeName $ReferenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails
            
            # Assigning $null to $ReferenceTypeName so that this referenced definition is not set as UsedAsPathOperationInputType
            $ReferenceTypeName = $null
        }
    }
    else 
    {
        $paramType = 'object'
    }

    $paramType = Get-PSTypeFromSwaggerObject -ParameterType $paramType
    if ((Get-Member -InputObject $ParameterJsonObject -Name 'Enum') -and $ParameterJsonObject.Enum)
    {
        # AutoRest doesn't generate a parameter on Async method for the path operation
        # when a parameter is required and has singly enum value.
        # Also, no enum type gets generated by AutoRest.
        if(($ParameterJsonObject.Enum.Count -eq 1) -and
           (Get-Member -InputObject $ParameterJsonObject -Name 'Required') -and 
           $ParameterJsonObject.Required -eq 'true')
        {
            $paramType = ""
        }
        elseif((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-enum') -and 
            $ParameterJsonObject.'x-ms-enum' -and 
            ($ParameterJsonObject.'x-ms-enum'.modelAsString -eq $false))
        {
            $paramType = $DefinitionTypeNamePrefix + (Get-CSharpModelName -Name $ParameterJsonObject.'x-ms-enum'.Name)
        }
        else
        {
            $ValidateSet = $ParameterJsonObject.Enum | ForEach-Object {$_ -replace "'","''"}
            $ValidateSetString = "'$($ValidateSet -join "', '")'"
        }
    }
    
    if ($ReferenceTypeName) {
        $ReferencedDefinitionDetails = @{}
        if($DefinitionFunctionsDetails.ContainsKey($ReferenceTypeName)) {
            $ReferencedDefinitionDetails = $DefinitionFunctionsDetails[$ReferenceTypeName]
        }
        $ReferencedDefinitionDetails['UsedAsPathOperationInputType'] = $true
    }

    if($paramType -and 
       (-not $paramType.Contains($DefinitionTypeNamePrefix)) -and
       ($null -eq ($paramType -as [Type])))
    {
        Write-Warning -Message ($LocalizedData.InvalidPathParameterType -f $paramType, $ParameterName)
    }

    return @{
        ParamType = $paramType
        ValidateSetString = $ValidateSetString
        IsParameter = $isParameter
        GlobalParameterDetails = $GlobalParameterDetails
    }
}

function Set-TypeUsedAsClientFlatten
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $ReferenceTypeName,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails
    )

    $ReferencedFunctionDetails = @{}
    if($DefinitionFunctionsDetails.ContainsKey($ReferenceTypeName))
    {
        $ReferencedFunctionDetails = $DefinitionFunctionsDetails[$ReferenceTypeName]
    }
    else
    {
        $ReferencedFunctionDetails['Name'] = $ReferenceTypeName 
    }

    $ReferencedFunctionDetails['IsUsedAs_x_ms_client_flatten'] = $true

    $DefinitionFunctionsDetails[$ReferenceTypeName] = $ReferencedFunctionDetails
}

function Get-PSTypeFromSwaggerObject
{
    param(
        [Parameter(Mandatory=$false)]
        [AllowEmptyString()]
        [string]
        $ParameterType,

        [Parameter(Mandatory=$false)]
        [PSObject]
        $JsonObject
    )

    $ParameterFormat = $null

    if($JsonObject) {
        if((Get-Member -InputObject $JsonObject -Name 'Type') -and $JsonObject.Type)
        {
            $ParameterType = $JsonObject.Type
        }

        if((Get-Member -InputObject $JsonObject -Name 'Format') -and
        $JsonObject.Format -and ($null -ne ($JsonObject.Format -as [Type])))
        {
            $ParameterFormat = $JsonObject.Format
        }
    }
    
    switch ($ParameterType) {
        'Boolean' {
            $ParameterType = 'switch'
            break
        }

        'Integer' {
            if($ParameterFormat) {
                $ParameterType = $ParameterFormat
            }
            else {
                $ParameterType = 'int64'
            }
            break
        }

        'Number' {
            if($ParameterFormat) {
                $ParameterType = $ParameterFormat
            }
            else {
                $ParameterType = 'double'
            }
            break
        }
    }

    return $ParameterType
}

function Get-SingularizedValue
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Name
    )
    
    if($script:PluralizationService)
    {
        $Name = $script:PluralizationService.Singularize($Name)
    }

    return Get-PascalCasedString -Name $Name
}

function Get-PathCommandName
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $OperationId
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $opId = $OperationId

    if ($script:CmdVerbTrie -eq $null) {
        $script:CmdVerbTrie = New-Trie
        $script:PSCommandVerbMap.GetEnumerator() | ForEach-Object {
            $script:CmdVerbTrie = Add-WordToTrie -Word $_.Name -Trie $script:CmdVerbTrie
        }

        # Add approved verbs to Command Verb Trie
        Get-Verb | ForEach-Object {
            $script:CmdVerbTrie = Add-WordToTrie -Word $_.Verb -Trie $script:CmdVerbTrie
        }        
    }

    $currentTriePtr = $script:CmdVerbTrie
    
    $opIdValues = $opId  -split "_",2
    
    if($opIdValues -and ($opIdValues.Count -eq 2)) {
        $cmdNoun = (Get-SingularizedValue -Name $opIdValues[0])
        $cmdVerb = $opIdValues[1]
    }
    else {
        # OperationId can be specified without '_' (Underscore), Verb will retrieved by the below logic for non-approved verbs.
        $cmdNoun = ''
        $cmdVerb = Get-SingularizedValue -Name $opId
    }

    if (-not (Get-Verb -Verb $cmdVerb))
    {
        $UnapprovedVerb = $cmdVerb
        $message = $LocalizedData.UnapprovedVerb -f ($UnapprovedVerb)
        Write-Verbose $message
        
        if ($script:PSCommandVerbMap.ContainsKey($cmdVerb))
        {
            # This condition happens when there aren't any suffixes
            $cmdVerb = $script:PSCommandVerbMap[$cmdVerb] -Split ',' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
            $cmdVerb | ForEach-Object {
                $message = $LocalizedData.ReplacedVerb -f ($_, $UnapprovedVerb)
                Write-Verbose -Message $message
            }
        }
        else
        {
            # This condition happens in cases like: CreateSuffix, CreateOrUpdateSuffix
            $longestVerbMatch = $null
            $currentVerbCandidate = ''
            $firstWord = ''
            $firstWordStarted = $false
            $buildFirstWord = $false
            $firstWordEnd = -1
            $verbMatchEnd = -1
            for($i = 0; $i -lt $UnapprovedVerb.Length; $i++) {
                # Add the start condition of the first word so that the end condition is easier
                if (-not $firstWordStarted) {
                    $firstWordStarted = $true
                    $buildFirstWord = $true
                } elseif ($buildFirstWord -and ([int]$UnapprovedVerb[$i] -ge 65) -and ([int]$UnapprovedVerb[$i] -le 90)) {
                    # Stop building the first word when we encounter another capital letter
                    $buildFirstWord = $false
                    $firstWordEnd = $i
                }

                if ($buildFirstWord) {
                    $firstWord += $UnapprovedVerb[$i]
                }

                if ($currentTriePtr) {
                    # If we're still running along the trie just fine, keep checking the next letter
                    $currentVerbCandidate += $UnapprovedVerb[$i]
                    $currentTriePtr = Test-Trie -Trie $currentTriePtr -Letter $UnapprovedVerb[$i]
                    if ($currentTriePtr -and (Test-TrieLeaf -Trie $currentTriePtr)) {
                        # The latest verb match is also the longest verb match
                        $longestVerbMatch = $currentVerbCandidate
                        $verbMatchEnd = $i+1
                    }
                }
            }

            if ($longestVerbMatch) {
                $beginningOfSuffix = $verbMatchEnd
                $cmdVerb = $longestVerbMatch
            } else {
                $beginningOfSuffix = $firstWordEnd
                $cmdVerb = $firstWord
            }

            if ($script:PSCommandVerbMap.ContainsKey($cmdVerb)) { 
                $cmdVerb = $script:PSCommandVerbMap[$cmdVerb] -Split ',' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
            }

            if (-1 -ne $beginningOfSuffix) {
                # This is still empty when a verb match is found that is the entire string, but it might not be worth checking for that case and skipping the below operation
                $cmdNounSuffix = $UnapprovedVerb.Substring($beginningOfSuffix)
                # Add command noun suffix only when the current noun doesn't contain it or vice-versa.
                if(-not $cmdNoun) {
                    $cmdNoun = Get-PascalCasedString -Name $cmdNounSuffix
                }                
                elseif(-not $cmdNounSuffix.StartsWith('By', [System.StringComparison]::OrdinalIgnoreCase)) {
                    if(($cmdNoun -notmatch $cmdNounSuffix) -and ($cmdNounSuffix -notmatch $cmdNoun)) {
                        $cmdNoun = $cmdNoun + (Get-PascalCasedString -Name $cmdNounSuffix)
                    }
                    elseif($cmdNounSuffix -match $cmdNoun) {
                        $cmdNoun = $cmdNounSuffix
                    }
                }
            }
        }
    }

    # Singularize command noun
    if($cmdNoun) {
        $cmdNoun = Get-SingularizedValue -Name $cmdNoun
    }

    $cmdletInfos = $cmdVerb | ForEach-Object {
        $Verb = Get-PascalCasedString -Name $_
        if($cmdNoun){
            $CommandName = "$Verb-$cmdNoun"
        } else {
            $CommandName = Get-SingularizedValue -Name $Verb
        }
        $cmdletInfo = @{}
        $cmdletInfo['name'] = $CommandName
        $cmdletInfo
        Write-Verbose -Message ($LocalizedData.UsingCmdletNameForSwaggerPathOperation -f ($CommandName, $OperationId))
    }

    return $cmdletInfos
}

function Get-PathFunctionBody
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [PSCustomObject[]]
        $ParameterSetDetails,

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $ODataExpressionBlock,

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $ParameterGroupsExpressionBlock,

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

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerDict,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerMetaDict,

        [Parameter(Mandatory=$false)]
        [switch]
        $AddHttpClientHandler,

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

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

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

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $FlattenedParametersOnPSCmdlet
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $outputTypeBlock = $null
    $Info = $swaggerDict['Info']
    $DefinitionList = $swaggerDict['Definitions']
    $UseAzureCsharpGenerator = $SwaggerMetaDict['UseAzureCsharpGenerator']
    $infoVersion = $Info['infoVersion']
    $clientName = '$' + $Info['ClientTypeName']
    $NameSpace = $info.namespace
    $FullClientTypeName = $Namespace + '.' + $Info['ClientTypeName']
    $SubscriptionId = $null
    $BaseUri = $null
    $GetServiceCredentialStr = ''
    $AdvancedFunctionEndCodeBlock = ''
    $GetServiceCredentialStr = 'Get-AzServiceCredential'

    $parameterSetBasedMethodStr = ''
    foreach ($parameterSetDetail in $ParameterSetDetails) {
        # Responses isn't actually used right now, but keeping this when we need to handle responses per parameter set
        $Responses = $parameterSetDetail.Responses
        $operationId = $parameterSetDetail.OperationId
        $methodName = $parameterSetDetail.MethodName
        $operations = $parameterSetDetail.Operations
        $ParamList = $parameterSetDetail.ExpandedParamList
        $Cmdlet = ''
        if ($parameterSetDetail.ContainsKey('Cmdlet') -and $parameterSetDetail.Cmdlet) {
            $Cmdlet = $parameterSetDetail.Cmdlet
        }

        $CmdletArgs = ''
        if ($parameterSetDetail.ContainsKey('CmdletArgs') -and $parameterSetDetail.CmdletArgs) {
            $CmdletArgs = $parameterSetDetail.CmdletArgs
        }

        $CmdletParameter = ''
        if ($parameterSetDetail.ContainsKey('CmdletParameter') -and $parameterSetDetail.CmdletParameter) {
            $CmdletParameter = $parameterSetDetail.CmdletParameter
        }

        if ($Responses) {
            $responseBodyParams = @{
                                    responses = $Responses.PSObject.Properties
                                    namespace = $Namespace
                                    definitionList = $DefinitionList
                                    Models = $Info.Models
                                }

            $responseBody, $currentOutputTypeBlock = Get-Response @responseBodyParams

            # For now, use the first non-empty output type
            if ((-not $outputTypeBlock) -and $currentOutputTypeBlock) {
                $outputTypeBlock = $currentOutputTypeBlock
            }
        }

        if ($methodName) {
            $methodBlock = $executionContext.InvokeCommand.ExpandString($methodBlockFunctionCall)
        } else {
            $methodBlock = $executionContext.InvokeCommand.ExpandString($methodBlockCmdletCall)
        }
        $additionalConditionStart = ''
        $additionalConditionEnd = ''
        if ($parameterSetDetail.ContainsKey('AdditionalConditions') -and $parameterSetDetail.AdditionalConditions) {
            if ($parameterSetDetail.AdditionalConditions.Count -eq 1) {
                $additionalConditionStart = " if ($($parameterSetDetail.AdditionalConditions[0])) {$([Environment]::NewLine)"
                $additionalConditionEnd = "$([Environment]::NewLine) } else { `$taskResult = `$null }"
            } elseif ($parameterSetDetail.AdditionalConditions.Count -gt 1) {
                $additionalConditionStart = " if ("
                foreach ($condition in $parameterSetDetail.AdditionalConditions) {
                    $additionalConditionStart += "($condition) -and"
                }
                $additionalConditionStart = $additionalConditionStart.Substring(0, $additionalConditionStart.Length-5)
                $additionalConditionStart = ") {$([Environment]::NewLine)"
                $additionalConditionEnd = "$([Environment]::NewLine) } else { `$taskResult = `$null }"
            }
        }

        if ($parameterSetBasedMethodStr) {
            # Add the elseif condition
            $parameterSetBasedMethodStr += $executionContext.InvokeCommand.ExpandString($parameterSetBasedMethodStrElseIfCase)
        } else {
            # Add the beginning if condition
            $parameterSetBasedMethodStr += $executionContext.InvokeCommand.ExpandString($parameterSetBasedMethodStrIfCase)
        }
    }

    # Prepare the code block for constructing the actual operation parameters which got flattened on the generated cmdlet.
    $FlattenedParametersBlock = ''
    $FlattenedParametersOnPSCmdlet.GetEnumerator() | ForEach-Object {
        $SwaggerOperationParameterName = $_.Name        
        $DefinitionDetails = $_.Value
        $FlattenedParamType = $DefinitionDetails.Name

        $FlattenedParametersList = $DefinitionDetails.ParametersTable.GetEnumerator() | ForEach-Object { $_.Name }
        $FlattenedParametersListStr = ''
        if($FlattenedParametersList) {
            $FlattenedParametersListStr = "@('$($flattenedParametersList -join "', '")')"
        }

        $FlattenedParametersBlock += $executionContext.InvokeCommand.ExpandString($constructFlattenedParameter)
    }

    $body = $executionContext.InvokeCommand.ExpandString($functionBodyStr)

    $bodyObject = @{ OutputTypeBlock = $outputTypeBlock;
                     Body = $body;
                    }

    $result = @{
        BodyObject = $bodyObject
        ParameterSetDetails = $ParameterSetDetails
    }

    return $result
}

function Test-OperationNameInDefinitionList
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Name,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerDict
    )

    return $SwaggerDict['Definitions'].ContainsKey($Name)
}

function Get-OutputType
{
    param
    (
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Schema,

        [Parameter(Mandatory=$true)]
        [string]
        $ModelsNamespace,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $DefinitionList
    )

    $outputType = ""
    if(Get-member -inputobject $schema -name '$ref')
    {
        $ref = $schema.'$ref'
        $RefParts = $ref -split '/' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
        if(($RefParts.Count -ge 3) -and ($RefParts[-2] -eq 'definitions'))
        {
            $key = Get-CSharpModelName -Name $RefParts[-1]
            if ($definitionList.ContainsKey($key))
            {
                $definition = ($definitionList[$key]).Value
                if(Get-Member -InputObject $definition -name 'properties')
                {
                    $fullPathDataType = ""
                    if(Get-HashtableKeyCount -Hashtable $definition.properties.PSObject.Properties)
                    {
                        $defProperties = $definition.properties

                        # If this data type is actually a collection of another $ref
                        if(Get-member -InputObject $defProperties -Name 'value')
                        {
                            $defValue = $defProperties.value
                            $outputValueType = ""
                            
                            # Iff the value has items with $ref nested properties,
                            # this is a collection and hence we need to find the type of collection

                            if((Get-Member -InputObject $defValue -Name 'items') -and 
                                (Get-Member -InputObject $defValue.items -Name '$ref'))
                            {
                                $defRef = $defValue.items.'$ref'
                                $DefRefParts = $defRef -split '/' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
                                if(($DefRefParts.Count -ge 3) -and ($DefRefParts[-2] -eq 'definitions'))
                                {
                                    $ReferenceTypeName = $DefRefParts[-1]
                                    $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceTypeName
                                    $fullPathDataType = "$ModelsNamespace.$ReferenceTypeName"
                                }
                                if(Get-member -InputObject $defValue -Name 'type') 
                                {
                                    $defType = $defValue.type
                                    switch ($defType) 
                                    {
                                        "array" { $outputValueType = '[]' }
                                        Default {
                                            $exceptionMessage = $LocalizedData.DataTypeNotImplemented -f ($defType, $ref)
                                            throw ([System.NotImplementedException] $exceptionMessage)
                                        }
                                    }
                                }

                                if($outputValueType -and $fullPathDataType) {$fullPathDataType = $fullPathDataType + " " + $outputValueType}
                            }
                            else
                            { # if this datatype has value, but no $ref and items
                                $fullPathDataType = "$ModelsNamespace.$key"
                            }
                        }
                        else
                        { # if this datatype is not a collection of another $ref
                            $fullPathDataType = "$ModelsNamespace.$key"
                        }
                    }

                    if($fullPathDataType)
                    {
                        $fullPathDataType = $fullPathDataType.Replace('[','').Replace(']','').Trim()
                        $outputType += $executionContext.InvokeCommand.ExpandString($outputTypeStr)
                    }
                }
            }
        }
    }

    return $outputType
}

function Get-Response
{
    param
    (
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Responses,
        
        [Parameter(Mandatory=$true)]
        [string]
        $NameSpace, 

        [Parameter(Mandatory=$true)]
        [string]
        $Models, 

        [Parameter(Mandatory=$true)]        
        [hashtable]
        $DefinitionList
    )

    $outputTypeFlag = $false
    $responseBody = ""
    $outputType = ""
    $failWithDesc = ""

    $failWithDesc = ""
    $responses | ForEach-Object {
        $responseStatusValue = "'" + $_.Name + "'"
        $value = $_.Value

        switch($_.Name) {
            # Handle Success
            {200..299 -contains $_} {
                if(-not $outputTypeFlag -and (Get-member -inputobject $value -name "schema"))
                {
                    # Add the [OutputType] for the function
                    $OutputTypeParams = @{
                        "schema"  = $value.schema
                        "ModelsNamespace" = "$NameSpace.$Models"
                        "definitionList" = $definitionList
                    }

                    $outputType = Get-OutputType @OutputTypeParams
                    $outputTypeFlag = $true
                }
            }
            # Handle Client Error
            {400..499 -contains $_} {
                if($Value.description)
                {
                    $failureDescription = "Write-Error 'CLIENT ERROR: " + $value.description + "'"
                    $failWithDesc += $executionContext.InvokeCommand.ExpandString($failCase)
                }
            }
            # Handle Server Error
            {500..599 -contains $_} {
                if($Value.description)
                {
                    $failureDescription = "Write-Error 'SERVER ERROR: " + $value.description + "'"
                    $failWithDesc += $executionContext.InvokeCommand.ExpandString($failCase)
                }
            }
        }
    }

    $responseBody += $executionContext.InvokeCommand.ExpandString($responseBodySwitchCase)
    
    return $responseBody, $outputType
}
function Get-CSharpModelName
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Name
    )

    # Below logic is as per AutoRest
    $Name = $Name -replace '[[]]','Sequence'
    # Remove special characters
    $Name = $Name -replace '[^a-zA-Z0-9_-]',''

    # AutoRest appends 'Model' to the definition name when it is a C# reserved word.
    if($script:CSharpReservedWords -contains $Name) {        
        $Name += 'Model'
    }

    return Get-PascalCasedString -Name $Name
}

<# Create an object from an external dependency with possible type name changes. Optionally resolve the external dependency using a delegate.
Input to $AssemblyResolver is $AssemblyName.
Doesn't support constructors with args currently. #>

function New-ObjectFromDependency {
    param(
        [Parameter(Mandatory=$true)]
        [string[]]
        $TypeNames,

        [Parameter(Mandatory=$true)]
        [string]
        $AssemblyName,

        [Parameter(Mandatory=$false)]
        [System.Action[string]]
        $AssemblyResolver
    )

    if ($TypeNames.Count -gt 0) {
        $assemblyLoadAttempted = $false
        foreach ($typeName in $TypeNames) {
            if (-not ($typeName -as [Type])) {
                if (-not $assemblyLoadAttempted) {
                    if ($AssemblyResolver -ne $null) {
                        $AssemblyResolver.Invoke($AssemblyName)
                    } else {
                        $null = Add-Type -Path $AssemblyName
                    }

                    $assemblyLoadAttempted = $true
                }
            } else {
                return New-Object -TypeName $typeName
            }
        }
    }

    return $null
}

function Get-PowerShellCodeGenSettings {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Path,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $CodeGenSettings,

        [Parameter(Mandatory=$false)]
        [PSCustomObject]
        $PSMetaJsonObject
    )

    if($PSMetaJsonObject) {
        $swaggerObject = $PSMetaJsonObject
    }
    else {
        $swaggerObject = ConvertFrom-Json ((Get-Content $Path) -join [Environment]::NewLine) -ErrorAction Stop
    }
    if ((Get-Member -InputObject $swaggerObject -Name 'info') -and (Get-Member -InputObject $swaggerObject.'info' -Name 'x-ps-code-generation-settings')) {
        $props = Get-Member -InputObject $swaggerObject.'info'.'x-ps-code-generation-settings' -MemberType NoteProperty
        foreach ($prop in $props) {
            if (-not $CodeGenSettings.ContainsKey($prop.Name)) {                
                Write-Warning -Message ($LocalizedData.UnknownPSMetadataProperty -f ('x-ps-code-generation-settings', $prop.Name))
            } else {
                $CodeGenSettings[$prop.Name] = $swaggerObject.'info'.'x-ps-code-generation-settings'.$($prop.Name)
            }
        }
    }
}

function Resolve-ReferenceParameterType {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $ReferenceTypeName,

        [Parameter(Mandatory = $true)]
        [string]
        $DefinitionTypeNamePrefix,

        [Parameter(Mandatory = $true)]
        [PSCustomObject]
        $DefinitionFunctionsDetails
    )

    $ParameterType = $DefinitionTypeNamePrefix + $ReferenceTypeName
    $ValidateSet = $null
    $ValidateSetString = $null

    # Some referenced definitions can be non-models like enums with validateset.
    if ($DefinitionFunctionsDetails.ContainsKey($ReferenceTypeName) -and
        $DefinitionFunctionsDetails[$ReferenceTypeName].ContainsKey('Type') -and
        $DefinitionFunctionsDetails[$ReferenceTypeName].Type -and
        -not $DefinitionFunctionsDetails[$ReferenceTypeName].IsModel)
    {
        $ParameterType = $DefinitionFunctionsDetails[$ReferenceTypeName].Type
        
        if($DefinitionFunctionsDetails[$ReferenceTypeName].ValidateSet) {
            $ValidateSet = $DefinitionFunctionsDetails[$ReferenceTypeName].ValidateSet
            $ValidateSetString = "'$($ValidateSet -join "', '")'"
        }
    }

    return @{
        ParameterType     = $ParameterType
        ValidateSet       = $ValidateSet
        ValidateSetString = $ValidateSetString
    }
}
# SIG # Begin signature block
# MIIarQYJKoZIhvcNAQcCoIIanjCCGpoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUlQ9myTWq6VMQbOoVgmrPue4d
# rsCgghWAMIIEwjCCA6qgAwIBAgITMwAAALpqNt4arb08HwAAAAAAujANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODQ2
# WhcNMTgwOTA3MTc1ODQ2WjCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046
# RjZGRi0yREE3LUJCNzUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbkc0j1DekYIbe
# VMtJHd3kxvCvQnxVC0dNcc2Tvu9dDBtILt4pNaDkO8xhEFIzOgsVucV5UZaQHtmu
# vcwvG3F1xrgKCTDGgRhdCb1/JGBs67K7emkIkF8dgbmZtITASGSjwzy2jhlK+eMn
# cSBuuoatutTxSS86jJxeN5pml2HV+z648r+rkqVmJpTLR2EnI07QcXt0nZ/g9J/k
# /A7mfSfFGeFHRHsVc/abypJdUNWSGv1RVYJ7FoicxUoXudbiYBdTDfsyvZPrOJ28
# 9S8KoZ7KUxGuSGGDkyfctQgZoZFI+XlD89KmKiNbahq9hG5m5weSGwVePfw/99JH
# GTI+jC25AgMBAAGjggEJMIIBBTAdBgNVHQ4EFgQUW+wPFw2KORr5sOGDFIF72bhI
# HKAwHwYDVR0jBBgwFoAUIzT42VJGcArtQPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJ
# oEegRYZDaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljcm9zb2Z0VGltZVN0YW1wUENBLmNybDBYBggrBgEFBQcBAQRMMEowSAYIKwYB
# BQUHMAKGPGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z
# b2Z0VGltZVN0YW1wUENBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
# 9w0BAQUFAAOCAQEAB4FDLPBU1MrtqkPH4Y0KmjsyoVUgQhQN/HvuW46j0DrKoDVr
# cloJb21SN2CQe9oKarFfjyxfmXZ3ChKmB42MBaHkETqo9LAG22q3cpxbYO8W0uke
# 4RpYA/rV8V+S310vexazwXLDrhddWQzaRJQT3Brq/H6T2LwBqV3fk0mKUXTKoPf2
# RrQu4+tAmZJBv5QOxhmNRiR4EazqERp4QUmIJfiPw9vmyf0K3mn6inBhYFIFj4wz
# hI1CfKpy/JwM1WozANowmalYYrBUcNa/lk2+9ZeywjI8TynnEo9HxtPhiugw2qGV
# mOKyJZIFdcS2lPoGPPkuPiTikd4ipJgIFAc7SzCCBOswggPToAMCAQICEzMAAAF4
# JVq1zSPGX5UAAQAAAXgwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IENvZGUgU2ln
# bmluZyBQQ0EwHhcNMTcwODExMjAxMTE1WhcNMTgwODExMjAxMTE1WjCBgjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEMMAoGA1UECxMDQU9DMR4w
# HAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQCZbh1TVaudsrIbXUPPB9c8S+E+dKSbskHKYlG6SGTH8jhT
# hpuvGiAO87F2b9GHVN+DvszaMkGy/xQgHaGEJLfpnb3kpakic7E0bjDHdG4KnHRb
# no/wfUkGLfS79o+cw//RY8Ck6yE+0czDBcxp0Gbw5JyGP+KFqvzRR/3Tv3nt/5x0
# 5ZnEOHYP+eDVikDvn/DH+oxxtiCfX3tkvtm/yX4eOb47YdmYKQjCgz2+Nil/lupY
# vU0QFIjvke3jshqQINDng/vO9ys2qA0ex/q5hlLKQTST99dGoM86Ge6F723ReToq
# KnGLN8kiCG7uNapOAIQrpCHZq96CVumiaA5ZvxU9AgMBAAGjggFgMIIBXDATBgNV
# HSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUjuhtD3FD7tk/RKloJFX05cpgLjcw
# UQYDVR0RBEowSKRGMEQxDDAKBgNVBAsTA0FPQzE0MDIGA1UEBRMrMjI5ODAzKzFh
# YmY5ZTVmLWNlZDAtNDJlNi1hNjVkLWQ5MzUwOTU5ZmUwZTAfBgNVHSMEGDAWgBTL
# EejK0rQWWAHJNy4zFha5TJoKHzBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js
# Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNDb2RTaWdQQ0FfMDgt
# MzEtMjAxMC5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY0NvZFNpZ1BDQV8wOC0zMS0y
# MDEwLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAYnG/oHG/xgZYR8NAMHZ/vE9GM0e4
# 7YdhuTea2uY7pSGwM707wp8Wan0Fa6evK1PWfcd/XNOh2BpEv5o8RmKDoEsG0ECP
# 13Jug7cklfKreBVHQ+Djg43VVFLZpuo7aOAVK6wjlcnpPUtn+SfH9K0aM2FjDKVJ
# FW6XFKXBat5R+Zp6uOxWTxpSeMTeDC5zF6IY6ogR1uzU+9EQoRlAvkwX6po+exEL
# nMLr4++P+fqOxIU+PODIoB8ijClAqwwKvLlMPa3qlrNHt+LweTMu7lvGC/RA18wU
# zzXAeomuZ03blUw+bkOiVgWOk4S0RN7EnW7zjJV8gd/+G2dbToUi1cB/fTCCBbww
# ggOkoAMCAQICCmEzJhoAAAAAADEwDQYJKoZIhvcNAQEFBQAwXzETMBEGCgmSJomT
# 8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMk
# TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDgzMTIy
# MTkzMloXDTIwMDgzMTIyMjkzMloweTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0Ew
# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCycllcGTBkvx2aYCAgQpl2
# U2w+G9ZvzMvx6mv+lxYQ4N86dIMaty+gMuz/3sJCTiPVcgDbNVcKicquIEn08Gis
# TUuNpb15S3GbRwfa/SXfnXWIz6pzRH/XgdvzvfI2pMlcRdyvrT3gKGiXGqelcnNW
# 8ReU5P01lHKg1nZfHndFg4U4FtBzWwW6Z1KNpbJpL9oZC/6SdCnidi9U3RQwWfjS
# jWL9y8lfRjFQuScT5EAwz3IpECgixzdOPaAyPZDNoTgGhVxOVoIoKgUyt0vXT2Pn
# 0i1i8UU956wIAPZGoZ7RW4wmU+h6qkryRs83PDietHdcpReejcsRj1Y8wawJXwPT
# AgMBAAGjggFeMIIBWjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTLEejK0rQW
# WAHJNy4zFha5TJoKHzALBgNVHQ8EBAMCAYYwEgYJKwYBBAGCNxUBBAUCAwEAATAj
# BgkrBgEEAYI3FQIEFgQU/dExTtMmipXhmGA7qDFvpjy82C0wGQYJKwYBBAGCNxQC
# BAweCgBTAHUAYgBDAEEwHwYDVR0jBBgwFoAUDqyCYEBWJ5flJRP8KuEKU5VZ5KQw
# UAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j
# cmwvcHJvZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQuY3JsMFQGCCsGAQUFBwEBBEgw
# RjBEBggrBgEFBQcwAoY4aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0
# cy9NaWNyb3NvZnRSb290Q2VydC5jcnQwDQYJKoZIhvcNAQEFBQADggIBAFk5Pn8m
# Rq/rb0CxMrVq6w4vbqhJ9+tfde1MOy3XQ60L/svpLTGjI8x8UJiAIV2sPS9MuqKo
# VpzjcLu4tPh5tUly9z7qQX/K4QwXaculnCAt+gtQxFbNLeNK0rxw56gNogOlVuC4
# iktX8pVCnPHz7+7jhh80PLhWmvBTI4UqpIIck+KUBx3y4k74jKHK6BOlkU7IG9KP
# cpUqcW2bGvgc8FPWZ8wi/1wdzaKMvSeyeWNWRKJRzfnpo1hW3ZsCRUQvX/TartSC
# Mm78pJUT5Otp56miLL7IKxAOZY6Z2/Wi+hImCWU4lPF6H0q70eFW6NB4lhhcyTUW
# X92THUmOLb6tNEQc7hAVGgBd3TVbIc6YxwnuhQ6MT20OE049fClInHLR82zKwexw
# o1eSV32UjaAbSANa98+jZwp0pTbtLS8XyOZyNxL0b7E8Z4L5UrKNMxZlHg6K3RDe
# ZPRvzkbU0xfpecQEtNP7LN8fip6sCvsTJ0Ct5PnhqX9GuwdgR2VgQE6wQuxO7bN2
# edgKNAltHIAxH+IOVN3lofvlRxCtZJj/UBYufL8FIXrilUEnacOTj5XJjdibIa4N
# XJzwoq6GaIMMai27dmsAHZat8hZ79haDJLmIz2qoRzEvmtzjcT3XAH5iR9HOiMm4
# GPoOco3Boz2vAkBq/2mbluIQqBC0N1AI1sM9MIIGBzCCA++gAwIBAgIKYRZoNAAA
# AAAAHDANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYK
# CZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBD
# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEwNDAzMTMw
# MzA5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYD
# VQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4kD+7Rp9FM
# rXQwIBHrB9VUlRVJlBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMkh53y9Gcc
# LPx754gd6udOo6HBI1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDlKEYuJ6yG
# T1VSDOQDLPtqkJAwbofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gASkdmEScp
# ZqiX5NMGgUqi+YSnEUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1Un68eeEEx
# d8yb3zuDk6FhArUdDbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIBpzAPBgNV
# HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWzDzALBgNV
# HQ8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAUDqyCYEBW
# J5flJRP8KuEKU5VZ5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
# kiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290IENl
# cnRpZmljYXRlIEF1dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNVHR8ESTBH
# MEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0
# cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQGCCsGAQUF
# BzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jvc29m
# dFJvb3RDZXJ0LmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQUF
# AAOCAgEAEJeKw1wDRDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQSooxtYrhX
# AstOIBNQmd16QOJXu69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBTFd1Pq5Lk
# 541q1YDB5pTyBi+FA+mRKiQicPv2/OR4mS4N9wficLwYTp2OawpylbihOZxnLcVR
# DupiXD8WmIsgP+IHGjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfrTot/xTUr
# XqO/67x9C0J71FNyIe4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWGzFFW6J1w
# lGysOUzU9nm/qhh6YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H2146SodDW4
# TsVxIxImdgs8UoxxWkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4iIdBD6Svp
# u/RIzCzU2DKATCYqSCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2sWo9iaF2
# YbRuoROmv6pH8BJv/YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1sMpjtHhU
# BdRBLlCslLCleKuzoJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/Jmu5J4PcB
# ZW+JC33Iacjmbuqnl84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0xggSXMIIEkwIB
# ATCBkDB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSMwIQYD
# VQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQQITMwAAAXglWrXNI8ZflQAB
# AAABeDAJBgUrDgMCGgUAoIGwMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBTCqZ3S
# aqtlSKrRSkMf4Rckl7sLPjBQBgorBgEEAYI3AgEMMUIwQKAWgBQAUABvAHcAZQBy
# AFMAaABlAGwAbKEmgCRodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUG93ZXJTaGVs
# bCAwDQYJKoZIhvcNAQEBBQAEggEAcqKW+uIOInC7LbPFWBQK81tkITD2QJdTRAjt
# Sh0/xl5J4fpUwz3rRW+gDc2ZZcSpUqN/nOsqKviUEbZYwwhPQRlGaOIGuVk/VUbT
# kjw6gU5M2fw4AqS43azHsabGE1IXkuESsRC/NtEwlv9UD2ffyY/jfAnUEq+lVjjR
# vUfqT1hGWj2FS0xzGP/M7uTElpCuqUcXAnSxTk/rVERPaeylYG/GbtLTniX1QXI+
# mPfqa9+TgVZpgtnxbJ8M1HlSP3nJskT8a6iJXHpEvpGCB69pJBf7uqAHVD8RFqfb
# LVBH2wYsPHh28KFqIE8zNeVP4QIuQ8CPW+bYBwGXSnxA+MznsKGCAigwggIkBgkq
# hkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EC
# EzMAAAC6ajbeGq29PB8AAAAAALowCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE3MTAxMDIwNDc0OFowIwYJKoZI
# hvcNAQkEMRYEFHxgM0bNL0cAuqK9ensxslhIr9ygMA0GCSqGSIb3DQEBBQUABIIB
# ABbA7NYciBGFlJ3O99Raqp9e/t/Y8Zg6ugdK/eeElJdEviG4f2DS6bqPbFJJ4LRO
# CpuAb3JYLPxGlCSj/pS/MqNxWBnvR8lYuc/YH0n9ozpv2jsodNmKxZskxh848M8a
# 8NP7HxKM1imTJdPVjJmX1OlbIavNbiGnQBkJXd5u+o4+lmcBf7n3RQbibQ2eWM2s
# er7WZXVfCVMArn0X9SElTyxs2r0xWHSINKlEulsanN3I0UTrYQ8qgN1kXHlj6XfX
# DOAUdCayHo63xIsbE0u0fHu6NeU0ps3GLGXmYe8xpfi6eaxnixWZAqtk7XQtYGHv
# 64Y3dohiTxK1/QvGRw0MvsA=
# SIG # End signature block