param
(
    [switch]$multibox,
    [Parameter(Mandatory = $false)]
    [string]$relatedFilesDir,
    [Parameter(Mandatory = $false)]
    [string]$targetDirectory,
    [Parameter(Mandatory = $false)]
    [string]$deploymentDir,
    [Parameter(Mandatory = $false, HelpMessage = "The path to the directory in which to write log files.")]
    [string]$LogDir,
    [Parameter(Mandatory = $false, HelpMessage = "Indicate that this is being run in a Service Fabric context.")]
    [switch]$useServiceFabric = $false,
    [Parameter(Mandatory = $false)]
    [string]$webroot,
    [Parameter(Mandatory = $false)]
    [string]$aosPackageDirectory,
    [Parameter(Mandatory = $false)]
    [string]$sourcePackageDirectory,
    [Parameter(Mandatory = $false, HelpMessage = "Indicate that this is being run on a staging environment.")]
    [switch]$useStaging,
    [Parameter(Mandatory = $false)]
    [string]$tempWorkFolder = "",
    [Parameter(Mandatory = $false, HelpMessage = "Indicates that packages should be applied even if they are labeled as Application or Platform packages.")]
    [switch]$skipPackageValidation = $false
)

$Global:installedPackages = @()

enum CleanUpSourceDirectoriesState
{
    NotStarted
    InProgress
    Failed
    Completed
}

<#
.SYNOPSIS
    Updates the execution status of the clean up source directories step during AOS Update on development machines
#>
function Update-CleanUpSourceDirectoriesProgressFile($stepState)
{
    try
    {
        $global:cleanUpSourceDirectoriesProgress[$global:cleanUpSourceDirectoriesStepName] = $stepState
        $global:cleanUpSourceDirectoriesProgress | ConvertTo-Json | Out-File $global:cleanUpSourceDirectoriesProgressFile -Force
    }
    catch [System.Exception]
    {
        Write-Warning "$_"
    }
}

<#
.SYNOPSIS
    Returns a boolean to indicate whether cleaning up of source files directory is required or not
#>
function Get-IsCleanUpRequired
{
    $global:cleanUpSourceDirectoriesStepName = "CleanUpSourceDirectoriesStep"
    $global:cleanUpSourceDirectoriesProgressFile = "$PSScriptRoot\CleanUpSourceDirectoriesProgress.json"
    $global:cleanUpSourceDirectoriesProgress = @{}

    if (-not (Test-Path $global:cleanUpSourceDirectoriesProgressFile) -or -not(Get-Content $global:cleanUpSourceDirectoriesProgressFile))
    {
        "[$(Get-TimestampPrefix)] Creating CleanUpSourceDirectoriesProgressFile: $global:cleanUpSourceDirectoriesProgressFile as it does not exist" >> $log
        $global:cleanUpSourceDirectoriesProgress.Add($global:cleanUpSourceDirectoriesStepName, [CleanUpSourceDirectoriesState]::NotStarted.ToString())
        $cleanUpSourceDirectoriesProgress | ConvertTo-Json | Out-File $global:CleanUpSourceDirectoriesProgressFile -Force >> $log
    }
    else
    {
        "[$(Get-TimestampPrefix)] The CleanUpSourceDirectoriesProgressFile: $global:cleanUpSourceDirectoriesProgressFile already exists. Getting the current step state."  >> $log
        $cleanUpSourceDirectoriesProgressTemp = Get-Content $global:cleanUpSourceDirectoriesProgressFile | ConvertFrom-Json
        $cleanUpSourceDirectoriesProgressTemp.psobject.properties | Foreach-Object { $global:cleanUpSourceDirectoriesProgress[$_.Name] = $_.Value }
    }

    $isCleanUpRequired = ($global:cleanUpSourceDirectoriesProgress[$global:cleanUpSourceDirectoriesStepName] -ne [CleanUpSourceDirectoriesState]::Completed.ToString())
    "[$(Get-TimestampPrefix)] CleanUpSourceDirectoriesProgressState: [$($global:cleanUpSourceDirectoriesProgress[$global:cleanUpSourceDirectoriesStepName])] and IsCleanUpRequired: [$isCleanUpRequired]."  >> $log

    return $isCleanUpRequired
}

function GenerateSymLinkNgen([string]$webroot, [string]$metadataPackagePath)
{
    if ($useServiceFabric)
    {
        $DeveloperBox = $false
    }
    else
    {
        $DeveloperBox = Get-DevToolsInstalled
    }

    if (!$DeveloperBox)
    {
        Write-Output "Updating Symlink and Ngen Assemblies..."
        $datetime = Get-Date -Format "MMddyyyyhhmmss"
        $SymLinkNgenLog = Join-Path -Path $LogDir -ChildPath "update_SymLink_NgenAssemblies.log"
        $argumentList = '–webroot:"$webroot" –packagedir:"$metadataPackagePath" –log:"$($SymLinkNgenLog)"'

        $NgenoutPutLog = Join-Path -Path $LogDir -ChildPath "update_NgenOutput_$datetime.log"

        if (!(Test-Path -Path $NgenoutPutLog))
        {
            New-Item -ItemType File -Path $NgenoutPutLog -Force | Out-Null
        }

        Invoke-Expression "$metadataPackagePath\bin\CreateSymLinkAndNgenAssemblies.ps1 $argumentList" >> $NgenoutPutLog
    }
}

function UpdateAdditionalFiles([string]$webRoot, [string]$packageDir)
{
    $directorys = Get-ChildItem $packageDir -Directory
    foreach ($moduleName in $directorys)
    {
        $modulePath = Join-Path $packageDir $moduleName
        $additionalFilesDir = Join-Path $modulePath "AdditionalFiles"

        if (Test-Path $additionalFilesDir)
        {
            Write-log "Processing additional files for '$moduleName' "
            $filelocationsfile = Join-Path "$modulePath" "FileLocations.xml"
            if (Test-Path "$filelocationsfile")
            {
                [System.Xml.XmlDocument] $xd = New-Object System.Xml.XmlDocument
                $xd.Load($filelocationsfile)
                $files = $xd.SelectNodes("//AdditionalFiles/File")
                foreach ($file in $files)
                {
                    $assembly = [System.IO.Path]::GetFileName($file.Source)
                    $destination = $file.Destination
                    $relativepath = $file.RelativePath
                    $fullassemblypath = Join-Path "$modulePath" "AdditionalFiles\$assembly"

                    # the reason why we need to check for IsNullorEmpty() for the parameters is because the c:\pakages\bin
                    # comes from both the platform and app side. If the app bin package gets installed first
                    # it will leave a FileLocations.xml file at c:\packages\bin which will be processed by the
                    # platform bin package when it gets installed. We want to ensure that we do not throw an exception
                    # even if we don't find the correct set of parameters being passed from the calling function.
                    switch ($destination)
                    {
                        "AOSWeb" #enum for AOS webroot
                        {
                            $target = Join-Path "$webRoot" "$relativepath"
                        }

                        "PackageBin" #enum for \packages\bin\<<relativepath>>
                        {
                            if (-not [string]::IsNullOrEmpty($packageDir))
                            {
                                $target = Join-Path "$packageDir" "bin"
                                # Assemblies like BPExtensions have relativepath configured in FileLocations.xml. Hence considering relativepath for PackageBin similiar to how it is done in Copy-Files.
                                if (-not [string]::IsNullOrEmpty(($relativepath)))
                                {
                                    $target = join-path "$target" "$relativepath"
                                }
                            }
                        }

                        "ModuleBin" #enum for \<<modulename>>\bin
                        {
                            $target = Join-Path "$modulePath" "bin"
                        }

                        "PackageDir" #enum for \packages\<<relativepath>>
                        {
                            if (-not [string]::IsNullOrEmpty($packageDir))
                            {
                                $target = Join-Path "$packageDir" "$relativepath"
                            }
                        }
                    }

                    if ((Test-Path "$fullassemblypath") -and (-not [string]::IsNullOrEmpty($target)))
                    {
                        if (!(Test-Path "$target"))
                        {
                            Write-log "Creating target directory '$target'"
                            New-Item -Path "$target" -ItemType "directory" -Force | Out-Null
                        }

                        $targetfile = Join-Path "$target" $assembly
                        Write-log "Copying '$fullassemblypath' to '$targetfile'"
                        Copy-Item -path:"$fullassemblypath" -destination:"$targetfile" -Force
                    }
                }
            }

            Write-log "Removing '$additionalFilesDir'..."
            Remove-Item -Path $additionalFilesDir -Recurse -Force | Out-Null
        }
    }
}

function Update-PackageReferenceFile([string]$metadataPath, [string]$packageZipPath, [string]$tempdir)
{
    $ErrorActionPreference = "stop"

    $7zip = Join-Path -Path $env:SystemDrive -ChildPath "DynamicsTools\7za.exe"
    $temppackagesdir = Join-Path -Path $tempdir -ChildPath "temp_$(New-Guid)"

    if (Test-Path -Path $packageZipPath)
    {
        $zipFileNoExt = [System.IO.Path]::GetFileNameWithoutExtension($packageZipPath)
        $updateRefLog = Join-Path -Path $LogDir -ChildPath "install-$zipFileNoExt-$datetime.log"
        $unzipLogName = "install-$zipFileNoExt-$datetime-unzip.log"
        $unzipLog = Join-Path -Path $LogDir -ChildPath $unzipLogName

        $start = (Get-Date).ToUniversalTime()
        ("[{0}] Begin: Updating references from '{1}'" -f $start.ToString("o"), $packageZipPath) >> $updateRefLog

        if (!(Test-Path -Path $temppackagesdir))
        {
            New-Item -Path $temppackagesdir -ItemType directory -Force | Out-Null
        }

        "Unzipping $packageZipPath to $temppackagesdir..."  >> $updateRefLog
        $zip = Start-Process $7zip -ArgumentList "x $packageZipPath -o$temppackagesdir -y -mmt" -Wait -WindowStyle Hidden -PassThru -RedirectStandardOutput $unzipLog

        if ($zip.ExitCode -ne "0")
        {
            "7zip failed to unzip $packageZipPath. See '$unzipLogName' for details." >> $updateRefLog
            throw "7Zip failed to extract dynamics packages reference file."
        }

        $directories = Get-ChildItem -Path $temppackagesdir -Directory
        foreach ($directory in $directories)
        {
            $TargetReferenceUpdateDirectory = Join-Path -Path $metadataPath -ChildPath $directory.Name
            if (Test-Path -Path $TargetReferenceUpdateDirectory)
            {
                "Copying '$($directory.FullName)' to '$TargetReferenceUpdateDirectory'..."  >> $updateRefLog
                Copy-Item -Path ([IO.Path]::Combine($directory.FullName, "*")) -Destination $TargetReferenceUpdateDirectory -Force -Recurse
            }
        }

        if (Test-Path -Path $temppackagesdir)
        {
            "Removing temp directory '$temppackagesdir'..." >> $updateRefLog
            Remove-Item -Path $temppackagesdir -Recurse -Force
        }

        $end = (Get-Date).ToUniversalTime()
        ("[{0}] End: Updating references from '{1}'" -f $end.ToString("o"), $packageZipPath) >> $updateRefLog
    }
}

function Install-Package([string]$packageName, [string]$metadataPath, [string]$source, [string]$log)
{
    $ErrorActionPreference = "stop"

    $dynamicstools = "DynamicsTools"
    $installationrecords = Join-Path $metadataPath "InstallationRecords"
    $packageinstallationrecord = Join-Path $installationrecords $packageName

    $nuget = Join-Path $env:SystemDrive "$dynamicstools\nuget.exe"

    "Removing package installation record $packageinstallationrecord.*" >> $log
    Get-ChildItem -path "$installationrecords" -filter "$packageName.*" | Remove-Item -force -recurse

    "Unpacking the Dynamics packages to $installationrecords" >> $log

    "Running command: $nuget install -OutputDirectory `"$installationrecords`" $packageName -Source $source" >> $log
    if ([System.Version]([System.Diagnostics.FileVersionInfo]::GetVersionInfo($nuget).FileVersion) -ge [System.Version]"2.9.0.0")
    {
        & $nuget install -OutputDirectory "$installationrecords" $packageName -Source $source -DependencyVersion highest #nuget version > 2.8 change behaviour and add a new switch to set it back
    }
    else
    {
        & $nuget install -OutputDirectory "$installationrecords" $packageName -Source $source
    }
    # check the last exit code and decide if the package(s) were installed correctly
    if ($LASTEXITCODE -ne 0)
    {
        Throw "Something went wrong when installing the Dynamics package '$packageName'. Make sure the package name is correct and that it exists at the source directory '$source'."
    }

}

function Install-ZipPackage ([string]$clickoncePath, [string]$metadataPath, [string]$frameworkPath, [string]$packageZipPath, [string]$source, [string]$webroot, [string]$log)
{
    $ErrorActionPreference = "stop"

    #install package
    $arguments = 'clickOnceInstallPath="{0}";metadataInstallPath="{1}";frameworkInstallPath="{2}";packageZipDrop="{3}";webroot="{4}";log="{5}"' -f $clickoncePath, $metadataPath, $frameworkPath, $packageZipPath, $webroot, $log
    $arguments
    $env:DynamicsPackageParameters = $arguments
    $dynamicstools = "DynamicsTools"
    $installationrecords = Join-Path $metadataPath "InstallationRecords"
    $packageinstallationrecord = Join-Path $installationrecords $packageName

    # iterate over every installed package and run the custom powershell script
    $packagesdir = [System.IO.Directory]::EnumerateDirectories($installationrecords, "*", [System.IO.SearchOption]::TopDirectoryOnly)
    foreach ($dir in $packagesdir)
    {
        $currentpackagename = [System.IO.Path]::GetFileName($dir)
        $toolsdir = Join-Path $dir "tools"
        $installscript = Join-Path $toolsdir "installpackage.ps1"
        if (Test-Path $installscript)
        {
            $Global:installedPackages += $currentpackagename

        }
    }
    Parallel-Install -packagesName:$Global:installedPackages -installationrecorddir:$installationrecords
}

function Get-PackageName([string] $fileName)
{
    return $fileName.Split("-")[1]
}

function Remove-MetadataSourceDirectory([string] $packageName, [string] $packageInstallPath)
{
    $basePackageName = Get-PackageName $packageName

    if ($packageName.EndsWith('-compile'))
    {
        $packageInstallPath = Join-Path $packageInstallPath $basePackageName
        $packageInstallPath = Join-Path $packageInstallPath 'XppMetadata'
        if (Test-Path $packageInstallPath)
        {
            #powershell bug - Remove-Item comlet doesn't implement -Recurse correctly
            #Remove-Item $packageInstallPath -Force -Recurse
            Get-ChildItem -path "$packageInstallPath" -force -recurse | Remove-Item -force -recurse
        }
    }
    if ($packageName.EndsWith('-develop'))
    {
        $packageInstallPath = Join-Path $packageInstallPath $basePackageName
        $packageInstallPath = Join-Path $packageInstallPath $basePackageName
        if (Test-Path $packageInstallPath)
        {
            #powershell bug - Remove-Item comlet doesn't implement -Recurse correctly
            #Remove-Item $packageInstallPath -Force -Recurse
            Get-ChildItem -path "$packageInstallPath" -force -recurse | Remove-Item -force -recurse
        }
    }
}

function Remove-SourceDirectory([string] $packageName, [string] $packageInstallPath)
{
    $packageDir = Join-Path $packageInstallPath $packageName
    if (Test-path $packageDir\Descriptor)
    {
        $packageDescriptorXmls = Get-ChildItem $packageDir\Descriptor -filter "*.xml"
        # Delete all folders with matching names with the xml filenames in descriptor
        foreach ($packageDescriptorXml in $packageDescriptorXmls)
        {
            $sourceDirectory = Join-Path $packageDir $packageDescriptorXml.BaseName
            if (Test-path $sourceDirectory)
            {
                Get-ChildItem -path "$sourceDirectory" -force -recurse | Remove-Item -force -recurse
            }
        }
    }
}

function Parallel-Install([string[]] $packagesName, [string] $installationrecorddir)
{
    $ErrorActionPreference = "stop"
    foreach ($pkg in $packagesName)
    {
        $dir = Join-Path $installationrecorddir $pkg
        $toolsdir = Join-Path $dir "tools"
        $installscript = Join-Path $toolsdir "installpackage.ps1"
        if (Test-Path $installscript)
        {
            Write-Output "Running script '$installScript'"
            & $installscript
            Move-Item $installscript ($installscript + ".executed") -Force
        }

    }
}

function Get-TimestampPrefix
{
    return $(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
}

$ErrorActionPreference = "Stop"
if (!$useServiceFabric)
{
    Import-Module WebAdministration
}

Import-Module "$PSScriptRoot\CommonRollbackUtilities.psm1" -ArgumentList $useServiceFabric -DisableNameChecking
Import-Module "$PSScriptRoot\AosEnvironmentUtilities.psm1" -ArgumentList $useServiceFabric -Force -DisableNameChecking

if (!$useServiceFabric)
{
    if (Test-Path "$($PSScriptRoot)\NonAdminDevToolsInterject.ps1")
    {
        & "$PSScriptRoot\NonAdminDevToolsInterject.ps1"
    }
}

if ($tempWorkFolder -ne "")
{
    $tempPackagesDir = $tempWorkFolder
}
else
{
    $tempPackagesDir = [System.IO.Path]::GetTempPath()
}

if ($useStaging)
{
    $webroot = Join-Path $(Get-AosServiceStagingPath) "webroot"
    $metadataPackagePath = Join-Path $(Get-AosServiceStagingPath) "PackagesLocalDirectory"
    $frameworkPackagePath = Join-Path $(Get-AosServiceStagingPath) "PackagesLocalDirectory"
    $sourcePath = [IO.Path]::Combine($(Split-Path -parent $PSScriptRoot), "Packages")
}
elseif ($useServiceFabric)
{
    $webroot = (Resolve-Path $webroot).ProviderPath
    $clickOncePackagePath = Join-Path $webroot "apps"
    $sourcePath = $sourcePackageDirectory
    $metadataPackagePath = $aosPackageDirectory
    $frameworkPackagePath = $aosPackageDirectory
    if ($tempWorkFolder -eq "")
    {
        $tempPackagesDir = $sourcePath
    }
}
else
{
    $webroot = Get-AosWebSitePhysicalPath
    $metadataPackagePath = $(Get-AOSPackageDirectory)
    $frameworkPackagePath = $(Get-AOSPackageDirectory)
    $sourcePath = [IO.Path]::Combine($(Split-Path -parent $PSScriptRoot), "Packages")
}

if (!$useServiceFabric)
{
    $clickOncePackagePath = $(Get-InfrastructureClickonceAppsDirectory)
    $clickOncePackagePath = [IO.Path]::Combine($webroot, $clickOncePackagePath)
}

$resourcePath = [IO.Path]::Combine($webroot, "Resources")
$packageZipDrop = [IO.Path]::Combine($sourcePath, "files")

if ((![string]::IsNullOrWhiteSpace($targetDirectory)) -and (Test-Path $targetDirectory))
{
    $metadataPackagePath = $targetDirectory
    $frameworkPackagePath = $targetDirectory
}

if ((![string]::IsNullOrWhiteSpace($deploymentDir)) -and (Test-Path $deploymentDir))
{
    if ($multibox)
    {
        $clickOncePackagePath = [IO.Path]::Combine($deploymentDir, "WebRoot\apps")
        $webroot = [IO.Path]::Combine($deploymentDir, "WebRoot")
        $resourcePath = [IO.Path]::Combine($deploymentDir, "WebRoot\Resources")
    }
    else
    {
        $clickOncePackagePath = [IO.Path]::Combine($deploymentDir, "DObind\Packages\Cloud\AosWebApplication\AosWebApplication.csx\roles\AosWeb\approot\apps")
        $webroot = [IO.Path]::Combine($deploymentDir, "DObind\Packages\Cloud\AosWebApplication\AosWebApplication.csx\roles\AosWeb\approot")
        $resourcePath = [IO.Path]::Combine($deploymentDir, "DObind\Packages\Cloud\AosWebApplication\AosWebApplication.csx\roles\AosWeb\approot\Resources")
    }
}

if ((![string]::IsNullOrWhiteSpace($relatedFilesDir)) -and (Test-Path $relatedFilesDir))
{
    $sourcePath = $relatedFilesDir
    $packageZipDrop = [IO.Path]::Combine($relatedFilesDir, "files")
}

$datetime = Get-Date -Format "MMddyyyyhhmmss"

if (!$LogDir)
{
    $LogDir = $PSScriptRoot
}

$log = Join-Path -Path $LogDir -ChildPath "install-AXpackages_$datetime.log"
if (!(Test-Path -Path $log))
{
    New-Item -Path $log -ItemType File -Force | Out-Null
}

$innerlog = Join-Path -Path $LogDir -ChildPath "update-AXpackages_$datetime.log"
if (!(Test-Path -Path $innerlog))
{
    New-Item -Path $innerlog -ItemType File -Force | Out-Null
}


$startdatetime = Get-Date
"*******************************************************" >> $log
"** Starting the package deployment at $startdatetime **" >> $log
"*******************************************************" >> $log

$installationrecords = Join-Path -Path $metadataPackagePath -ChildPath "InstallationRecords"

if (!(Test-Path -Path $installationrecords))
{
    "[$(Get-TimestampPrefix)] Creating installation record directory '$($installationrecords)' to keep the installation history." >> $log
    New-Item -Path $installationrecords -ItemType Directory -Force | Out-Null
}
else
{
    # clean up prior nuget installation of the previous package that fail to install
    $packagesdir = [System.IO.Directory]::EnumerateDirectories($installationrecords, "*", [System.IO.SearchOption]::TopDirectoryOnly)
    foreach ($dir in $packagesdir)
    {
        $toolsdir = Join-Path -Path $dir -ChildPath "tools"
        $installscript = Join-Path -Path $toolsdir -ChildPath "installpackage.ps1"
        if (Test-Path -Path $installscript)
        {
            Move-Item -Path $installscript -Destination $($installscript + ".executed") -Force
        }
    }
}

if ($useServiceFabric)
{
    $DeveloperBox = $false
}
else
{
    $DeveloperBox = Get-DevToolsInstalled
}

$isAppSealed = Get-IsAppSealed -webroot:$webroot
$isPlatSealed = Get-IsPlatformUpdate3OrLater -webroot:$webroot
"[$(Get-TimestampPrefix)] Evaluating Microsoft packages with SealedPlatform: [$isPlatSealed] and SealedApplication: [$isAppSealed]." >> $log

#Check if this is a binary update package for RunOne environment
#Clean up source directories for Microsoft-owned module packages to support x++ refactoring
if ((Get-IsBinaryPackageTypeFromPackage) -and ($isAppSealed) -and ($DeveloperBox -eq $true) -and (Get-IsCleanUpRequired))
{
    # Get the list of currently installed packages based on installation records
    $installationrecords = Join-Path $metadataPackagePath "InstallationRecords"
    $nugetPackageFiles = Get-ChildItem -Path:$installationrecords -recurse  -filter "*-develop.*.nupkg"
    # Get the list of Microsoft-owned app and plat packages
    $applicationPackageNames = Get-PackageNamesFromDLL -productInfoDLL "bin\ProductInfo\Microsoft.Dynamics.BusinessPlatform.ProductInformation.Application.dll"
    $platformPackageNames = Get-PackageNamesFromDLL -productInfoDLL "bin\ProductInfo\Microsoft.Dynamics.BusinessPlatform.ProductInformation.Platform.dll"
    # Get the set of modules in the package being applied
    $metadataModuleHash = New-Object System.Collections.Generic.HashSet[string]
    Get-MetadataModuleListFromPackage | foreach { $metadataModuleHash.Add($_.Split(".")[0])} | Out-Null

    if (($applicationPackageNames.count -gt 0) -or ($platformPackageNames.count -gt 0))
    {
        Update-CleanUpSourceDirectoriesProgressFile([CleanUpSourceDirectoriesState]::InProgress.ToString())
        "[$(Get-TimestampPrefix)] Cleaning up develop records and directories for [$($nugetPackageFiles.Count)] Microsoft modules..." >> $log
        foreach ($nugetPackageFile in $nugetPackageFiles)
        {
            $packageName = Get-PackageName -fileName $nugetPackageFile.BaseName
            # Check whether module package is present in the deployable package being installed
            if ($metadataModuleHash.Contains($packageName))
            {
                # Check whether package is a Microsoft-owned application package
                if ((Get-IsModulePartOfApplicationAsBinary -PackageNugetFilePath $nugetPackageFile.FullName) -and ($applicationPackageNames -contains $packageName))
                {
                    "[$(Get-TimestampPrefix)] Removing source directories for package: $packageName" >> $log
                    Remove-SourceDirectory -packageName $packageName -packageInstallPath $metadataPackagePath

                    "[$(Get-TimestampPrefix)] Removing package installation record for package: $packageName-develop" >> $log
                    Get-ChildItem -path "$installationrecords" -filter "dynamicsax-$packageName-develop.*" | Remove-Item -force -recurse
                }
                # Check whether package is a Microsoft-owned platform package
                elseif ((Get-IsModulePartOfPlatformAsBinary -packageNugetFile $nugetPackageFile.FullName) -and ($platformPackageNames -contains $packageName))
                {
                    "[$(Get-TimestampPrefix)] Removing source directories for package: $packageName" >> $log
                    Remove-SourceDirectory -packageName $packageName -packageInstallPath $metadataPackagePath

                    "[$(Get-TimestampPrefix)] Removing package installation record for package: $packageName-develop" >> $log
                    Get-ChildItem -path "$installationrecords" -filter "dynamicsax-$packageName-develop.*" | Remove-Item -force -recurse
                }
            }
        }
        Update-CleanUpSourceDirectoriesProgressFile([CleanUpSourceDirectoriesState]::Completed.ToString())
        "[$(Get-TimestampPrefix)] Finished cleaning up develop records and directories for [$($nugetPackageFiles.Count)] Microsoft modules." >> $log
    }
}

#Check if this is a platform update package base on existence of the config file.
#if it's platformUpdate3 or later, also perform the meta package installation for platform binarys
if ((Test-Path -Path "$PSScriptRoot\PlatformUpdatePackages.Config") -or ($isPlatSealed))
{
    if (Test-Path -Path $sourcePath)
    {
        [Void][Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
        if ($DeveloperBox -eq $true)
        {
            $PackageToInstall = "dynamicsax-meta-platform-development"
        }
        else
        {
            $PackageToInstall = "dynamicsax-meta-platform-runtime"
        }
        if (![string]::IsNullOrWhiteSpace($PackageToInstall))
        {
            $zipFile = Get-Item $sourcePath\$PackageToInstall*.nupkg
            if ($null -eq $zipFile)
            {
                #only throw error if it's a dedicated inplace upgrade package,
                #on any other package it's possible that the meta package doesn't existing thus no operation required
                if (Test-Path "$PSScriptRoot\PlatformUpdatePackages.Config")
                {
                    Throw "Unable to get package information"
                }
            }
            else
            {
                $PackFiles = [IO.Compression.ZipFile]::OpenRead($zipFile).Entries
                $PackageSpec = $PackFiles | Where-Object { ($_.Name -like '*.nuspec') }

                if (!($PackageSpec))
                {
                    Throw "Unable to get package information"
                }

                [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
                $XmlDoc.Load($PackageSpec.Open())

                $Dependencies = $xmlDoc.GetElementsByTagName('dependency').id

                if ($Dependencies.Contains("dynamicsax-systemhealth"))
                {
                    #Remove AxPulse due to the name change to SystemHealth in PlatUpdate3
                    $axPulsePath = Join-Path -Path $metadataPackagePath -ChildPath "axpulse"

                    if (Test-Path $axPulsePath)
                    {
                        Remove-Item $axPulsePath -Force -Recurse
                    }
                    if (Test-Path $installationrecords)
                    {
                        Get-ChildItem -path "$installationrecords" -filter "dynamicsax-axpulse.*" | Remove-Item -force -recurse
                    }
                }

                #Install all packages in meta-package definition
                forEach ($Package in $Dependencies)
                {
                    #if it's not appFall or later, install directory package from platform
                    #all other platform package specified in meta package will get installed
                    if (($(Get-PackageName $Package) -ne 'Directory') -or (!$(Get-IsAppFallOrLater -webroot:$webroot)))
                    {
                        "[$(Get-TimestampPrefix)] Removing package installation record $Package.*" >> $log
                        Get-ChildItem -path "$installationrecords" -filter "$Package.*" | Remove-Item -force -recurse

                        #Remove MetaData and Source Directories for the package before Installing
                        Remove-MetadataSourceDirectory -packageName $Package -packageInstallPath $metadataPackagePath
                    }
                }
                "[$(Get-TimestampPrefix)] Installing Platform meta-package '$PackageToInstall'..." >> $log
                Install-Package -packageName:$PackageToInstall -metadataPath:$metadataPackagePath -source:$sourcePath -log:$log >> $innerlog
                "[$(Get-TimestampPrefix)] Done installing Platform meta-package '$PackageToInstall'." >> $log
            }
        }
    }
}

#dependencyaos
#Install App packages if it is sealed
if ($isAppSealed)
{
    if (Test-Path $sourcePath)
    {
        [Void][Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
        if ($useServiceFabric)
        {
            $DeveloperBox = $false
        }
        else
        {
            $DeveloperBox = Get-DevToolsInstalled
        }
        if ($DeveloperBox -eq $true)
        {
            $PackageToInstall = "dynamicsax-meta-application-development"
        }
        else
        {
            $PackageToInstall = "dynamicsax-meta-application-runtime"
        }
        if (![string]::IsNullOrWhiteSpace($PackageToInstall))
        {
            $zipFile = Get-Item $sourcePath\$PackageToInstall*.nupkg

            if ($null -ne $zipFile)
            {
                $PackFiles = [IO.Compression.ZipFile]::OpenRead($zipFile).Entries
                $PackageSpec = $PackFiles | Where-Object { ($_.Name -like "*.nuspec") }

                if (!($PackageSpec))
                {
                    Throw "Unable to get package information"
                }

                [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument
                $XmlDoc.Load($PackageSpec.Open())

                $Dependencies = $xmlDoc.GetElementsByTagName('dependency').id

                #Install all packages in meta-package definition
                forEach ($Package in $Dependencies)
                {
                    "[$(Get-TimestampPrefix)] Removing package installation record $Package.*" >> $log
                    Get-ChildItem -path "$installationrecords" -filter "$Package.*" | Remove-Item -force -recurse

                    #Remove MetaData and Source Directories for the package before Installing
                    Remove-MetadataSourceDirectory -packageName $Package -packageInstallPath $metadataPackagePath
                }
                "[$(Get-TimestampPrefix)] Installing Application meta-package '$PackageToInstall'..." >> $log
                Install-Package -packageName:$PackageToInstall -metadataPath:$metadataPackagePath -source:$sourcePath -log:$log >> $innerlog
                "[$(Get-TimestampPrefix)] Done installing Application meta-package '$PackageToInstall'." >> $log
            }
        }
    }
}

#still need to perform the aot package installation that's not part of platform or app.
if (!(Test-Path "$PSScriptRoot\PlatformUpdatePackages.Config") -and (Test-Path $sourcePath))
{
    $files = Get-ChildItem -Path:$sourcePath *.nupkg
    $customerPackages = @()
    "[$(Get-TimestampPrefix)] Evaluating additional packages to install..." >> $log

    foreach ($packageFile in $files)
    {
        $packageName = ($packageFile.BaseName).Split(".")[0]

        # If the platform is not sealed, install all [Platform Package]
        if (Get-IsModulePartOfPlatformAsBinary -packageNugetFile $packageFile.FullName)
        {
            if (!$isPlatSealed -or $skipPackageValidation)
            {
                "[$(Get-TimestampPrefix)] Installing unsealed Platform package $packageName.*" >> $log
                Install-Package -packageName:$packageName -metadataPath:$metadataPackagePath -source:$sourcePath -log:$log >> $innerlog
            }
        }
        # If app is not sealed, install all [Application Package]
        elseif (Get-IsModulePartOfApplicationAsBinary -PackageNugetFilePath $packageFile.FullName)
        {
            if (!$isAppSealed -or $skipPackageValidation)
            {
                "[$(Get-TimestampPrefix)] Installing unsealed Application package $packageName.*" >> $log
                Install-Package -packageName:$packageName -metadataPath:$metadataPackagePath -source:$sourcePath -log:$log >> $innerlog
            }
        }
        # Allow customer extensions
        else
        {
            # Remove the customer's packages from the installation folder prior to installing them.
            "[$(Get-TimestampPrefix)] Removing package installation record $packageName.*" >> $log
            Get-ChildItem -path "$installationrecords" -filter "$packageName.*" | Remove-Item -force -recurse
            $customerPackages += $packageName
        }
    }

    "[$(Get-TimestampPrefix)] Done evaluating additional packages to install. [$($customerPackages.Count)] packages found." >> $log
        
    # Install the customer's packages.
    if ($customerPackages.Count -gt 0)
    {
        "[$(Get-TimestampPrefix)] Installing [$($customerPackages.Count)] customization packages..." >> $log
    }

    foreach ($customerPackage in $customerPackages)
    {
        Install-Package -packageName:$customerPackage -metadataPath:$metadataPackagePath -source:$sourcePath -log:$log >> $innerlog
    }

    if ($customerPackages.Count -gt 0)
    {
        "[$(Get-TimestampPrefix)] Done installing [$($customerPackages.Count)] customization packages." >> $log
    }
}

"[$(Get-TimestampPrefix)] Installing Zip Packages..." >>$log
Install-ZipPackage -metadataPath:$metadataPackagePath -clickoncePath:$clickOncePackagePath -frameworkPath:$frameworkPackagePath -packageZipPath:$packageZipDrop -source:$sourcePath -webroot:$webroot -log:$log >> $innerlog
"[$(Get-TimestampPrefix)] Done Installing Zip Packages." >>$log

Write-Output "$(Get-TimestampPrefix)] Updating Metadata Resources Files"
$UpdateResourcesLog = Join-Path -Path $LogDir "Update_Resources_$datetime.log"
$ResourceConfig = @{"Common.BinDir" = $metadataPackagePath; "Infrastructure.WebRoot" = $webroot }
$ResourceBase64Config = ConvertTo-Json $ResourceConfig
$ResourceBase64Config = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($ResourceBase64Config))
$argumentList = '–config:"$ResourceBase64Config" –log:"$($UpdateResourcesLog)"'

$Resourceslog = Join-Path -Path $LogDir -ChildPath "Update_Resources_Output_$datetime.log"
if (!(Test-Path -Path $Resourceslog))
{
    New-Item -ItemType File -Path $Resourceslog -Force | Out-Null
}

Invoke-Expression "$PSScriptRoot\DeployResources.ps1 $argumentList" >> $Resourceslog

Write-Output "Updating Metadata Reference File."
Update-PackageReferenceFile -metadataPath:$metadataPackagePath -packageZipPath:$(Join-Path -Path $packageZipDrop -ChildPath "MetadataReferenceApp.zip") -tempdir:$tempPackagesDir
Update-PackageReferenceFile -metadataPath:$metadataPackagePath -packageZipPath:$(Join-Path -Path $packageZipDrop -ChildPath "MetadataReferencePlat.zip") -tempdir:$tempPackagesDir

Write-Output "Updating Additional Files."
UpdateAdditionalFiles -webRoot:$webroot -packageDir:$metadataPackagePath

if (!$useServiceFabric)
{
    try
    {
        $DeveloperBox = Get-DevToolsInstalled
        if (!$DeveloperBox)
        {
            if (Test-Path -Path "$PSScriptRoot\RemoveSymLinkAndNgenAssemblies.ps1")
            {
                Write-Output "Removing SymLink And NgenAssemblies..."
                Invoke-Expression "$PSScriptRoot\RemoveSymLinkAndNgenAssemblies.ps1 -useStaging:`$$($useStaging)"
                Write-Output "Removing SymLink And NgenAssemblies completed."
            }
        }
    }
    catch
    {
        Write-Output "Warning: Failed to remove SymLink And NgenAssemblies: $($_)"
        Write-Output "Generating SymLink And NgenAssemblies..."
        # Always generate symlink point to the non-staging folder of the AOS service.
        GenerateSymLinkNgen -webroot:$webroot -metadataPackagePath:$(Get-AOSPackageDirectory)
        Write-Output "Generating SymLink And NgenAssemblies completed."
    }

    try
    {
        $CommonBin = Get-CommonBinDir
        $AXInstallationInfoPath = Join-Path -Path $CommonBin -ChildPath "bin\Microsoft.Dynamics.AX.AXInstallationInfo.dll"

        # Using Add-Type which will auto load all referenced assemblies.
        Add-Type -Path $AXInstallationInfoPath

        Write-Output "Creating Metadata Module Installation Info..."
        [Microsoft.Dynamics.AX.AXInstallationInfo.AXInstallationInfo]::ScanMetadataModelInRuntimePackage($metadataPackagePath)
        Write-Output "Creating Metadata Module Installation Info completed."
    }
    catch
    {
        Write-Warning "Failed to create metadata module installation record: $($_)"
    }
}

$enddatetime = Get-Date
"******************************************************" >> $log
"** Completed the package deployment at $enddatetime **" >> $log
"******************************************************" >> $log
"" >> $log
$duration = $enddatetime - $startdatetime
"Package deployment duration:" >> $log
"$duration" >> $log

"" >> $log
"******************************************************" >> $log
"Packages installed in this session:" >> $log
"******************************************************" >> $log
foreach ($pkg in $Global:installedPackages)
{
    "$pkg" >> $log
}

""
"******************************************************"
"Packages installed in this session:"
"******************************************************"
foreach ($pkg in $Global:installedPackages)
{
    "$pkg"
}
""
"installation log file: $log"

# SIG # Begin signature block
# MIIr/wYJKoZIhvcNAQcCoIIr8DCCK+wCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwY2VRkCdWiDKw
# A6C9Xh1iSaCB1JwWlZuW391DpoN9EKCCEW4wggh+MIIHZqADAgECAhM2AAACAO38
# jbec3qFIAAIAAAIAMA0GCSqGSIb3DQEBCwUAMEExEzARBgoJkiaJk/IsZAEZFgNH
# QkwxEzARBgoJkiaJk/IsZAEZFgNBTUUxFTATBgNVBAMTDEFNRSBDUyBDQSAwMTAe
# Fw0yNDExMDgxMjQzMjhaFw0yNTExMDgxMjQzMjhaMCQxIjAgBgNVBAMTGU1pY3Jv
# c29mdCBBenVyZSBDb2RlIFNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
# AoIBAQC5L/UPrOpwYjxcoZC0TqqvMF1WUELvwXN+k27SrA5rohJknn7Cgbxg4hGT
# XKqpcdbtsVTN3ZY896SJ20uQ+INL5OVLzpW408nCNTPYg2LtGJbqHUjpNm0hLCJ+
# gO5Jn2T8DDzIJoUijGXj1m+hRLKb2nOIicCED2GuYBmuWXnaY7INmVEaU3peryty
# ZjDuxdyGDuiPURz8lW1SUiDzoszNp1oswVr+WjDvLDUx4HlxPsG8zUjIst0NnJ6o
# z4tNFKaUBDCetcMjQxpCETn29a1CuRddxZLjPHZHfcotr5sh1S6bNQdzVaMNsxV8
# L3wjHb7XJ6ZVm662mHEiPgpyNcLhAgMBAAGjggWKMIIFhjApBgkrBgEEAYI3FQoE
# HDAaMAwGCisGAQQBgjdbAQEwCgYIKwYBBQUHAwMwPQYJKwYBBAGCNxUHBDAwLgYm
# KwYBBAGCNxUIhpDjDYTVtHiE8Ys+hZvdFs6dEoFgg93NZoaUjDICAWQCAQ4wggJ2
# BggrBgEFBQcBAQSCAmgwggJkMGIGCCsGAQUFBzAChlZodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpaW5mcmEvQ2VydHMvQlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1F
# JTIwQ1MlMjBDQSUyMDAxKDIpLmNydDBSBggrBgEFBQcwAoZGaHR0cDovL2NybDEu
# YW1lLmdibC9haWEvQlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1FJTIwQ1MlMjBDQSUy
# MDAxKDIpLmNydDBSBggrBgEFBQcwAoZGaHR0cDovL2NybDIuYW1lLmdibC9haWEv
# QlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNydDBS
# BggrBgEFBQcwAoZGaHR0cDovL2NybDMuYW1lLmdibC9haWEvQlkyUEtJQ1NDQTAx
# LkFNRS5HQkxfQU1FJTIwQ1MlMjBDQSUyMDAxKDIpLmNydDBSBggrBgEFBQcwAoZG
# aHR0cDovL2NybDQuYW1lLmdibC9haWEvQlkyUEtJQ1NDQTAxLkFNRS5HQkxfQU1F
# JTIwQ1MlMjBDQSUyMDAxKDIpLmNydDCBrQYIKwYBBQUHMAKGgaBsZGFwOi8vL0NO
# PUFNRSUyMENTJTIwQ0ElMjAwMSxDTj1BSUEsQ049UHVibGljJTIwS2V5JTIwU2Vy
# dmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1BTUUsREM9R0JM
# P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0
# aG9yaXR5MB0GA1UdDgQWBBST/HE52ZUlmsYqZcZBdrXZ5u4ZnzAOBgNVHQ8BAf8E
# BAMCB4AwRQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEWMBQGA1UEBRMNMjM2MTY3KzUwMzE1NTCCAeYGA1UdHwSCAd0wggHZMIIB
# 1aCCAdGgggHNhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpaW5mcmEvQ1JM
# L0FNRSUyMENTJTIwQ0ElMjAwMSgyKS5jcmyGMWh0dHA6Ly9jcmwxLmFtZS5nYmwv
# Y3JsL0FNRSUyMENTJTIwQ0ElMjAwMSgyKS5jcmyGMWh0dHA6Ly9jcmwyLmFtZS5n
# YmwvY3JsL0FNRSUyMENTJTIwQ0ElMjAwMSgyKS5jcmyGMWh0dHA6Ly9jcmwzLmFt
# ZS5nYmwvY3JsL0FNRSUyMENTJTIwQ0ElMjAwMSgyKS5jcmyGMWh0dHA6Ly9jcmw0
# LmFtZS5nYmwvY3JsL0FNRSUyMENTJTIwQ0ElMjAwMSgyKS5jcmyGgb1sZGFwOi8v
# L0NOPUFNRSUyMENTJTIwQ0ElMjAwMSgyKSxDTj1CWTJQS0lDU0NBMDEsQ049Q0RQ
# LENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZp
# Z3VyYXRpb24sREM9QU1FLERDPUdCTD9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0
# P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwHwYDVR0jBBgw
# FoAUllGE4Gtve/7YBqvD8oXmKa5q+dQwHwYDVR0lBBgwFgYKKwYBBAGCN1sBAQYI
# KwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAEDd8Wf5RkHsB64vgn2slxDtHzSo
# It9xN/Dm3RdFjNZ0diTUPMgSPYQlSk8nIAfudnB9FLavGlvZLlyUpfrPSuikepj3
# i3pqNEFn6fNdNFv/wHMxv7hQTIDCmuoR1v1rX+w3oeleBPMnN3QmH4ff1NsynyV4
# dZdYgN9Cw9sC/S3pWZpJrbOs7YOM3vqyU6DciHhC4D9i2zByHCF2pu9nYfiQf5A2
# iUZenRvyo1E5rC+UP2VZXa4k7g66W20+zAajIKKIqEmRtWahekMkCcOIHFBY4RDA
# ybgPRSGur4VDAiZPjTXS90wQXrX9CwU20cfiCC6e76F4H95KtQjKYpzuNVAwggjo
# MIIG0KADAgECAhMfAAAAUeqP9pxzDKg7AAAAAABRMA0GCSqGSIb3DQEBCwUAMDwx
# EzARBgoJkiaJk/IsZAEZFgNHQkwxEzARBgoJkiaJk/IsZAEZFgNBTUUxEDAOBgNV
# BAMTB2FtZXJvb3QwHhcNMjEwNTIxMTg0NDE0WhcNMjYwNTIxMTg1NDE0WjBBMRMw
# EQYKCZImiZPyLGQBGRYDR0JMMRMwEQYKCZImiZPyLGQBGRYDQU1FMRUwEwYDVQQD
# EwxBTUUgQ1MgQ0EgMDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJ
# mlIJfQGejVbXKpcyFPoFSUllalrinfEV6JMc7i+bZDoL9rNHnHDGfJgeuRIYO1LY
# /1f4oMTrhXbSaYRCS5vGc8145WcTZG908bGDCWr4GFLc411WxA+Pv2rteAcz0eHM
# H36qTQ8L0o3XOb2n+x7KJFLokXV1s6pF/WlSXsUBXGaCIIWBXyEchv+sM9eKDsUO
# LdLTITHYJQNWkiryMSEbxqdQUTVZjEz6eLRLkofDAo8pXirIYOgM770CYOiZrcKH
# K7lYOVblx22pdNawY8Te6a2dfoCaWV1QUuazg5VHiC4p/6fksgEILptOKhx9c+ia
# piNhMrHsAYx9pUtppeaFAgMBAAGjggTcMIIE2DASBgkrBgEEAYI3FQEEBQIDAgAC
# MCMGCSsGAQQBgjcVAgQWBBQSaCRCIUfL1Gu+Mc8gpMALI38/RzAdBgNVHQ4EFgQU
# llGE4Gtve/7YBqvD8oXmKa5q+dQwggEEBgNVHSUEgfwwgfkGBysGAQUCAwUGCCsG
# AQUFBwMBBggrBgEFBQcDAgYKKwYBBAGCNxQCAQYJKwYBBAGCNxUGBgorBgEEAYI3
# CgMMBgkrBgEEAYI3FQYGCCsGAQUFBwMJBggrBgEFBQgCAgYKKwYBBAGCN0ABAQYL
# KwYBBAGCNwoDBAEGCisGAQQBgjcKAwQGCSsGAQQBgjcVBQYKKwYBBAGCNxQCAgYK
# KwYBBAGCNxQCAwYIKwYBBQUHAwMGCisGAQQBgjdbAQEGCisGAQQBgjdbAgEGCisG
# AQQBgjdbAwEGCisGAQQBgjdbBQEGCisGAQQBgjdbBAEGCisGAQQBgjdbBAIwGQYJ
# KwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMBIGA1UdEwEB/wQI
# MAYBAf8CAQAwHwYDVR0jBBgwFoAUKV5RXmSuNLnrrJwNp4x1AdEJCygwggFoBgNV
# HR8EggFfMIIBWzCCAVegggFToIIBT4YxaHR0cDovL2NybC5taWNyb3NvZnQuY29t
# L3BraWluZnJhL2NybC9hbWVyb290LmNybIYjaHR0cDovL2NybDIuYW1lLmdibC9j
# cmwvYW1lcm9vdC5jcmyGI2h0dHA6Ly9jcmwzLmFtZS5nYmwvY3JsL2FtZXJvb3Qu
# Y3JshiNodHRwOi8vY3JsMS5hbWUuZ2JsL2NybC9hbWVyb290LmNybIaBqmxkYXA6
# Ly8vQ049YW1lcm9vdCxDTj1BTUVSb290LENOPUNEUCxDTj1QdWJsaWMlMjBLZXkl
# MjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9uLERDPUFNRSxE
# Qz1HQkw/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP29iamVjdENsYXNz
# PWNSTERpc3RyaWJ1dGlvblBvaW50MIIBqwYIKwYBBQUHAQEEggGdMIIBmTBHBggr
# BgEFBQcwAoY7aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraWluZnJhL2NlcnRz
# L0FNRVJvb3RfYW1lcm9vdC5jcnQwNwYIKwYBBQUHMAKGK2h0dHA6Ly9jcmwyLmFt
# ZS5nYmwvYWlhL0FNRVJvb3RfYW1lcm9vdC5jcnQwNwYIKwYBBQUHMAKGK2h0dHA6
# Ly9jcmwzLmFtZS5nYmwvYWlhL0FNRVJvb3RfYW1lcm9vdC5jcnQwNwYIKwYBBQUH
# MAKGK2h0dHA6Ly9jcmwxLmFtZS5nYmwvYWlhL0FNRVJvb3RfYW1lcm9vdC5jcnQw
# gaIGCCsGAQUFBzAChoGVbGRhcDovLy9DTj1hbWVyb290LENOPUFJQSxDTj1QdWJs
# aWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u
# LERDPUFNRSxEQz1HQkw/Y0FDZXJ0aWZpY2F0ZT9iYXNlP29iamVjdENsYXNzPWNl
# cnRpZmljYXRpb25BdXRob3JpdHkwDQYJKoZIhvcNAQELBQADggIBAFAQI7dPD+jf
# XtGt3vJp2pyzA/HUu8hjKaRpM3opya5G3ocprRd7vdTHb8BDfRN+AD0YEmeDB5HK
# QoG6xHPI5TXuIi5sm/LeADbV3C2q0HQOygS/VT+m1W7a/752hMIn+L4ZuyxVeSBp
# fwf7oQ4YSZPh6+ngZvBHgfBaVz4O9/wcfw91QDZnTgK9zAh9yRKKls2bziPEnxeO
# ZMVNaxyV0v152PY2xjqIafIkUjK6vY9LtVFjJXenVUAmn3WCPWNFC1YTIIHw/mD2
# cTfPy7QA1pT+GPARAKt0bKtq9aCd/Ym0b5tPbpgCiRtzyb7fbNS1dE740re0COE6
# 7YV2wbeo2sXixzvLftH8L7s9xv9wV+G22qyKt6lmKLjFK1yMw4Ni5fMabcgmzRvS
# jAcbqgp3tk4a8emaaH0rz8MuuIP+yrxtREPXSqL/C5bzMzsikuDW9xH10graZzSm
# PjilzpRfRdu20/9UQmC7eVPZ4j1WNa1oqPHfzET3ChIzJ6Q9G3NPCB+7KwX0OQmK
# yv7IDimj8U/GlsHD1z+EF/fYMf8YXG15LamaOAohsw/ywO6SYSreVW+5Y0mzJutn
# BC9Cm9ozj1+/4kqksrlhZgR/CSxhFH3BTweH8gP2FEISRtShDZbuYymynY1un+Ry
# fiK9+iVTLdD1h/SxyxDpZMtimb4CgJQlMYIZ5zCCGeMCAQEwWDBBMRMwEQYKCZIm
# iZPyLGQBGRYDR0JMMRMwEQYKCZImiZPyLGQBGRYDQU1FMRUwEwYDVQQDEwxBTUUg
# Q1MgQ0EgMDECEzYAAAIA7fyNt5zeoUgAAgAAAgAwDQYJYIZIAWUDBAIBBQCgga4w
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFjumx8yXcHBYz814kuWkNKuXg2lhRNH
# b20HKJpoZrTwMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYA
# dKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEA
# AirKQCUkSgva53zidztiDAJWtg82Rtg1sfwgq8jEkcrtEUcbUUALEnghbQky4GXr
# zR7NqECVVOpZ+EGO6eCV9J8FqEG9ZZIn9khVosi82IYFGm6GjCOQQnzrjOEKfs+/
# 5gxxLVfEQmH2d9yNDM29TE/vAZsxLU/92+2ilnJD1svMJgcYOai9nSW2EetdntHH
# FPJ09sxUf2IrBrYkeVQ8IDnjJdFIAhDZIO7+hhCbb7WNf5WSmVzQlS9qvgA7QvC0
# TU4/Yf6M22JpeVsX84umfcPFRdDadO7xLq0TuKUcohEEX6uP8Ba2lEt71OJEnnzf
# PmYv1YQZ4D/0E8/+yNoHHKGCF68wgherBgorBgEEAYI3AwMBMYIXmzCCF5cGCSqG
# SIb3DQEHAqCCF4gwgheEAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsqhkiG9w0B
# CRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUA
# BCCV4HkR6FhtozwrvuLg6YcfNbZcWJFsA0goA4NgmxnlAAIGaBLCZww+GBIyMDI1
# MDUwOTExMjIxOC4zOFowBIACAfSggdmkgdYwgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjJEMUEt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# oIIR/jCCBygwggUQoAMCAQICEzMAAAH9c/loWs0MYe0AAQAAAf0wDQYJKoZIhvcN
# AQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjQwNzI1MTgz
# MTE2WhcNMjUxMDIyMTgzMTE2WjCB0zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9u
# cyBMaW1pdGVkMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046MkQxQS0wNUUwLUQ5
# NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G
# CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChZaz4P467gmNidEdF527QxMVjM0kR
# U+cgvNzTZHepue6O+FmCSGn6n+XKZgvORDIbbOnFhx5OMgXseJBZu3oVbcBGQGu2
# ElTPTlmcqlwXfWWlQRvyBReIPbEimjxgz5IPRL6FM/VMID/B7fzJncES2Zm1xWdo
# tGn8C+yqD7kojQrDpMMmkrBMuXRVbT/bewqKR5YNKcdB5Oms7TMib9u1qBJibdX/
# zNeV/HLuz8RUV1KCUcaxSrwRm6lQ7xdsfPPu1RHKIPeQ7E2fDmjHV5lf9z9eZbgf
# pvjI2ZkXTBNm7DfvIDU8ko7JJKtetYSH4fr75Zvr7WW0wI+gwkdS08/cKfQI1w2+
# s/Im0NpyqOchOsvOuwd04uqOwfbb1mS+d2TQirEENmAyhj4R/t98VE/ak+SsXUX0
# hwGRjPyEv5CNf67jLhSqrhS1PtVGeyq9H/H/5AsTSlxISH9cTXDV9ynomarxGccR
# eKTJwws39r8pjGlI/cV8Vstm5/6oivIUvSAQPK1qkafU42NWSIqlU/a6pUhiPhWI
# KPLmktRx4x6qIqBiqGmZQcITZaywsuF1AEd2mXbz6T5ljqbh08WcSgZwke4xwhmf
# Dhw7CLGiNE6v42rvVwmPtDgvRfA++5MdC3SgftEoxCCazLsJUPu/nl06F0dd1izI
# 7r10B0r6daXJhwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFOkMxcDhlbz7Ivb7e8Dp
# GZTugQqkMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY
# MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p
# Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF
# BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo
# MSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYD
# VR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBj2Fhf5PkCYKtgZof3pN1H
# lPnb8804bvJJh6im/h+WcNZAuEGWtq8CD6mOU2/ldJdmsoa/x7izl0nlZ2F8L3LA
# VCrhOZedR689e2W5tmT7TYFcrr/beEzRNIqzYqWFiKrNtF7xBsx8pcQO28ygdJlP
# uv7AjYiCNhDCRr7c/1VeARHC7jr9zPPwhH9mr687nnbcmV3qyxW7Oz27AismF9xg
# GPnSZdZEFwyHNqMuNYOByKHQO7KQ9wGmhMuU4vwuleiiqev5AtgTgGlR6ncnJIxh
# 8/PaF84veDTZYR+w7GnwA1tx2KozfV2be9KF4SSaMcDbO4z5OCfiPmf4CfLsg4Nh
# CQis1WEt0wvT167V0g+GnbiUW2dZNg1oVM58yoVrcBvwoMqJyanQC2FE1lWDQE8A
# vnz4HRRygEYrNL2OxzA5O7UmY2WKw4qRVRWRInkWj9y18NI90JNVohdcXuXjSTVw
# z9fY7Ql0BL3tPvyViO3D8/Ju7NfmyHEGH9GpM+8LICEjEFUp83+F+zgIigVqpYnS
# v/xIHUIazLIhw98SAyjxx6rXDlmjQl+fIWLoa6j7Pcs8WX97FBpG5sSuwBRN/IFj
# n/mWLK+MCDINicQHy8c7tzsWDa0Z3mEaBiz4A6hbHbj5dzLGlSQBqMOGTL0OX7wl
# lOO2zoFxP2xhOY6h2T9KAjCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAA
# ABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1
# dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWlj
# cm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4IC
# DwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/
# bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNE
# t6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2
# Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOOR
# j7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnop
# N6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0g
# z3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/
# BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8Qmgu
# EOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFd
# XzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y
# 7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQAB
# o4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS
# /mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1Gely
# MFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6
# Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTAT
# BgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+ii
# XGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0y
# My5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNy
# dDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5O
# R2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6
# th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhT
# dSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnD
# vBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvf
# SaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+
# CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5a
# GZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+
# fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmN
# cP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGe
# Pu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNY
# s6FwZvKhggNZMIICQQIBATCCAQGhgdmkgdYwgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjJEMUEt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# oiMKAQEwBwYFKw4DAhoDFQCiPRa1VVBQ1Iqiq2uOKdECwFR2g6CBgzCBgKR+MHwx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p
# Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA68fM
# YTAiGA8yMDI1MDUwOTAwMzU0NVoYDzIwMjUwNTEwMDAzNTQ1WjB3MD0GCisGAQQB
# hFkKBAExLzAtMAoCBQDrx8xhAgEAMAoCAQACAii7AgH/MAcCAQACAhLRMAoCBQDr
# yR3hAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMH
# oSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBAKsaBA64zrvmALoPjGtO
# oDDDwQWX1avfhYEuex63TJCsnFDgycrU6JC9Wo92lrC1Ilzl72Xhdd6HS+Z9wfoB
# lQEcMNhbGieEJBQBxNqBD9Heap1o1A0K54LSgnGYSnfRPUpMWsfmT4JCOdaTPnAe
# ZN/KyMVc4QLhfy3YjjIANtBrPSJQuF8Z6x1yGyBrM5YC1NKkyOv9LmhgUFaPglm6
# yyRouOCY2mmILwYZ5wkDl/636jn4RxMTJKQI+OTxYarVJJiUkdSt8h01rTYO+uN4
# Wz4bJ9AGNr/k/Coi21lnvUPjFGKCIpAu7fOna5wdEi4KbSXkFG7LO5ZmEpflG15r
# KS0xggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAIT
# MwAAAf1z+WhazQxh7QABAAAB/TANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcN
# AQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCC0VXY/wQerc7R4qhRZ
# a3UFHYql4BRlfOvPkxtV2TDnDTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0E
# IIAoSA3JSjC8h/H94N9hK6keR4XWnoYXCMoGzyVEHg1HMIGYMIGApH4wfDELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAH9c/loWs0MYe0AAQAAAf0wIgQg
# qkcOxCqLoOlmLlC0bfKTeXwNwpiWuRvSIjKG6EsApsAwDQYJKoZIhvcNAQELBQAE
# ggIAbTHRt3h7iGvN/PnoHRlb0SJSUnFuPuHyXYPfnxgTCs9SYTx7C8lBNel9TRL6
# ct4TMU+JdA/G/Wx/6NLJGVpC1wXq0x9Py2yVqJHp9b5sh2zVe8DRkbaRA4JN5xa8
# mfCocWcAgeWyWOisjw28RE1+1rAaDIT699hXujN+raqU9MmFA+4zm/jLdJ2I2u0Z
# q0SHhDed7NWQFi5WcPT7WKMqe+DrFT1cjrcyRWRWgkEQ+HBOFCHrUEj7f7sedGsG
# 8d3z3NpG/Fk8R98HiSwiu2/s+CN/Lut0GoN2skadAYnm+gTV05adN15GblcqsDCW
# joz968iUK9UeLmRDeX2lsoRTYo53+IToD220FeLzH08R9rAQj7zzgiCFPG8vnz0H
# Q3wL2ecVntDY9ntBm+stwz/6KqskIC3TdO0tg/ZFe9WC0gJV6xcp4zWtD20OO+WW
# 2WnVlqchOc796c1geakxywcc/1/TU7HPY3bISSAoTTgwrWyhSht/r49mtU4FmWBw
# uRFw/NiYDKv23xTCD3dkfLJ2TqRWcxfAPcY9Ghhl/nOhFVYB0qgLXz0dW5uSfjzE
# D8BsvPbyEOPVUoVDFTmbI1nH9iaZtaqQVq4Xku34UrfSomQtPjn/47K3IYuNImpP
# FTQEfMamgKK5BKMjQJEHBQaw/CKb/FFnkhTkuRVTHH1oUcE=
# SIG # End signature block
