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
# MIInwgYJKoZIhvcNAQcCoIInszCCJ68CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwY2VRkCdWiDKw
# A6C9Xh1iSaCB1JwWlZuW391DpoN9EKCCDXYwggX0MIID3KADAgECAhMzAAACy7d1
# OfsCcUI2AAAAAALLMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NTU5WhcNMjMwNTExMjA0NTU5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC3sN0WcdGpGXPZIb5iNfFB0xZ8rnJvYnxD6Uf2BHXglpbTEfoe+mO//oLWkRxA
# wppditsSVOD0oglKbtnh9Wp2DARLcxbGaW4YanOWSB1LyLRpHnnQ5POlh2U5trg4
# 3gQjvlNZlQB3lL+zrPtbNvMA7E0Wkmo+Z6YFnsf7aek+KGzaGboAeFO4uKZjQXY5
# RmMzE70Bwaz7hvA05jDURdRKH0i/1yK96TDuP7JyRFLOvA3UXNWz00R9w7ppMDcN
# lXtrmbPigv3xE9FfpfmJRtiOZQKd73K72Wujmj6/Su3+DBTpOq7NgdntW2lJfX3X
# a6oe4F9Pk9xRhkwHsk7Ju9E/AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUrg/nt/gj+BBLd1jZWYhok7v5/w4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzQ3MDUyODAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAJL5t6pVjIRlQ8j4dAFJ
# ZnMke3rRHeQDOPFxswM47HRvgQa2E1jea2aYiMk1WmdqWnYw1bal4IzRlSVf4czf
# zx2vjOIOiaGllW2ByHkfKApngOzJmAQ8F15xSHPRvNMmvpC3PFLvKMf3y5SyPJxh
# 922TTq0q5epJv1SgZDWlUlHL/Ex1nX8kzBRhHvc6D6F5la+oAO4A3o/ZC05OOgm4
# EJxZP9MqUi5iid2dw4Jg/HvtDpCcLj1GLIhCDaebKegajCJlMhhxnDXrGFLJfX8j
# 7k7LUvrZDsQniJZ3D66K+3SZTLhvwK7dMGVFuUUJUfDifrlCTjKG9mxsPDllfyck
# 4zGnRZv8Jw9RgE1zAghnU14L0vVUNOzi/4bE7wIsiRyIcCcVoXRneBA3n/frLXvd
# jDsbb2lpGu78+s1zbO5N0bhHWq4j5WMutrspBxEhqG2PSBjC5Ypi+jhtfu3+x76N
# mBvsyKuxx9+Hm/ALnlzKxr4KyMR3/z4IRMzA1QyppNk65Ui+jB14g+w4vole33M1
# pVqVckrmSebUkmjnCshCiH12IFgHZF7gRwE4YZrJ7QjxZeoZqHaKsQLRMp653beB
# fHfeva9zJPhBSdVcCW7x9q0c2HVPLJHX9YCUU714I+qtLpDGrdbZxD9mikPqL/To
# /1lDZ0ch8FtePhME7houuoPcMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGaIwghmeAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAALLt3U5+wJxQjYAAAAAAsswDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFjumx8yXcHBYz814kuWkNKu
# Xg2lhRNHb20HKJpoZrTwMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAKRLXsI71WixznGYeLjchWsuP5u2vTyv4ePdxGr7419stIzquLd14qfkI
# 6aDhz8HpFtP+wPGPGWaXkSKp8iub8ENN+06zTwJqYNqsIT2NaIYgY+We+3Ayg/nH
# vrLheaq1jPJ9dpf5D2aIkolXKx8ADELuky5Y6CiOWmNTJOaP8rUsrgDBTYP0wkYL
# R6Ed/NfyBiwhIUKMu/GZgqRu8oPTYZoQQCs6hbVZGyYXDNICfRq1X0ITqyHZ0nPL
# QccKZzVOKMPvacHkO4Ze1OQ9oX0kByhUG7tHVh6KoGUPbCK+/jl3v83jiH5ppJBj
# OuD7thKHXErb2X/VdHgIyzlHDvFmJKGCFywwghcoBgorBgEEAYI3AwMBMYIXGDCC
# FxQGCSqGSIb3DQEHAqCCFwUwghcBAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq
# hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCC0WttwV37ab8ERpSuuYY01kB47M0VolRnl67bsDciPVgIGZD/R6y40
# GBMyMDIzMDQyNzE5NDYwNi43ODVaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OkEyNDAtNEI4Mi0xMzBFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloIIRezCCBycwggUPoAMCAQICEzMAAAG4CNTBuHngUUkAAQAAAbgwDQYJ
# KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjIw
# OTIwMjAyMjE2WhcNMjMxMjE0MjAyMjE2WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl
# cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpBMjQwLTRC
# ODItMTMwRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJwbsfwRHERn5C95QPGn37tJ
# 5vOiY9aWjeIDxpgaXaYGiqsw0G0cvCK3YulrqemEf2CkGSdcOJAF++EqhOSqrO13
# nGcjqw6hFNnsGwKANyzddwnOO0jz1lfBIIu77TbfNvnaWbwSRu0DTGHA7n7PR0MY
# J9bC/HopStpbFf606LKcTWnwaUuEdAhx6FAqg1rkgugiuuaaxKyxRkdjFZLKFXEX
# L9p01PtwS0fG6vZiRVnEKgeal2TeLvdAIqapBwltPYifgqnp7Z4VJMcPo0TWmRNV
# FOcHRNwWHehN9xg6ugIGXPo7hMpWrPgg4moHO2epc0T36rgm9hlDrl28bG5TakmV
# 7NJ98kbF5lgtlrowT6ecwEVtuLd4a0gzYqhanW7zaFZnDft5yMexy59ifETdzpwA
# rj2nJAyIsiq1PY3XPm2mUMLlACksqelHKfWihK/Fehw/mziovBVwkkr/G0F19OWg
# R+MBUKifwpOyQiLAxrqvVnfCY4QjJCZiHIuS15HCQ/TIt/Qj4x1WvRa1UqjnmpLu
# 4/yBYWZsdvZoq8SXI7iOs7muecAJeEkYlM6iOkMighzEhjQK9ThPpoAtluXbL7qI
# HGrfFlHmX/4soc7jj1j8uB31U34gJlB2XphjMaT+E+O9SImk/6GRV9Sm8C88Fnmm
# 2VdwMluCNAUzPFjfvHx3AgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUxP1HJTeFwzNY
# o1njfucXuUfQaW4wHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYD
# VR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwG
# CCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIw
# MjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
# CDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAJ9uk8miwpMoKw3D
# 996piEzbegAGxkABHYn2vP2hbqnkS9U97s/6QlyZOhGFsVudaiLeRZZTsaG5hR0o
# CuBINZ/lelo5xzHc+mBOpBXpxSaW1hqoxaCLsVH1EBtz7in25Hjy+ejuBcilH6EZ
# 0ZtNxmWGIQz8R0AuS0Tj4VgJXHIlXP9dVOiyGo9Velrk+FGx/BC+iEuCaKd/Isyp
# HPiCUCh52DGc91s2S7ldQx1H4CljOAtanDfbvSejASWLo/s3w0XMAbDurWNns0Xi
# dAF2RnL1PaxoOyz9VYakNGK4F3/uJRZnVgbsCYuwNX1BmSwM1ZbPSnggNSGTZx/F
# Q20Jj/ulrK0ryAbvNbNb4kkaS4a767ifCqvUOFLlUT8PN43hhldxI6yHPMOWItJp
# EHIZBiTNKblBsYbIrghb1Ym9tfSsLa5ZJDzVZNndRfhUqJOyXF+CVm9OtVmFDG9k
# IwM6QAX8Q0if721z4VOzZNvD8ktg1lI+XjXgXDJVs3h47sMu9GXSYzky+7dtgmc3
# iRPkda3YVRdmPJtNFN0NLybcssE7vhFCij75eDGQBFq0A4KVG6uBdr6UTWwE0VKH
# xBz2BpGvn7BCs+5yxnF+HV6CUickDqqPi/II7Zssd9EbP9uzj4luldXDAPrWGtdG
# q+wK0odlGNVuCMxsL3hn8+KiO9UiMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJ
# mQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNh
# dGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1
# WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjK
# NVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhg
# fWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJp
# rx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/d
# vI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka9
# 7aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKR
# Hh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9itu
# qBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyO
# ArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItb
# oKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6
# bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6t
# AgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQW
# BBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacb
# UzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku
# aHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA
# QwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2
# VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwu
# bWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEw
# LTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYt
# MjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/q
# XBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6
# U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVt
# I1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis
# 9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTp
# kbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0
# sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138e
# W0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJ
# sWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7
# Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0
# dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQ
# tB1VM1izoXBm8qGCAtcwggJAAgEBMIIBAKGB2KSB1TCB0jELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxh
# bmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpB
# MjQwLTRCODItMTMwRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAcGteVqFx/IbTKXHLeuXCPRPMD7uggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOf02zYwIhgPMjAyMzA0MjcxOTI5NThaGA8yMDIzMDQyODE5Mjk1OFowdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA5/TbNgIBADAKAgEAAgIgNwIB/zAHAgEAAgISGDAK
# AgUA5/YstgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAFfZyEk0FmEuC242
# B2xArKpkuoGEkRT2uouJKKMg6/Xed3fCEu09uRBVroqnQzZIhBgIqwD3wt0CxxUy
# UrJeKC/5EPAuXaADiwoosr39hdTlszAPIjrfMT0p31YyZMik59pbchCBU+rbB3Wq
# TyBBNWlkeD3hcOXSSVr77jGeU2b+MYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAG4CNTBuHngUUkAAQAAAbgwDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQgZtepbKXOeH2UxaHX9SdRy3rB9dPlDEcHIdv5EemE3JwwgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCAo69Y4oHA7Q4pS+Y1NsBfrpIYTeWsPeGTami0X
# 0PD7HzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# uAjUwbh54FFJAAEAAAG4MCIEIO/Ul+fg/oml1g+i+4OkkFY5ROAzE2USlNO6trMH
# oj0TMA0GCSqGSIb3DQEBCwUABIICAIa45slaRS1BW4MyikyhT6toBkbyTeBKrvPA
# XFeey+8NVegxf+CtVcibfsUGsjKvt0lz2Hx3VXZto+WbZSOleQALPaRmp/VfJb3O
# UuQVGoMvUTCdD3ztBGPUWcktduFFhTHA1U4S7tAX90s4ey3MNLhucnrWDWNaQiHl
# sj9MUXmmByteG2dN0NkYVxMYLjBTIknpum6mNcbX0dczThMnoXSMPxDZ//+nwwo8
# doKGOUDsMFlK01ZX5imovBRXE/XloeeVt3aCj9vFc0wbMU4l0HpzO7321X0y9hjc
# EcFqOhXktFSQG0y1iYCCnLnrlWGUW7rR4pG+8GXg1gTmjb3IIF3H1JakaumaAxJC
# PTHdDfkSuEen9rHw+C8qH0sSKSbS12RKdfVG9C+vaJFjjMhyYSmM4BMfLnVjy/iV
# TvKFt9IgPsZ7FuFVMzE0ivJSh0Rq8hfrUiOoLi0WApHdHgKy8rhIYyGrkVu17aTP
# Vq3AANtjNLnvvnqxgOIIt7bJ0EycZvbOe86kZwRc8YG5Btn6FrC329N+VdA7bFGN
# wQzLqfQTkMS1x1gnbyLpgZxQkjz7+XRLklEo7kTJVcQWaP5uOew/seoqIXjh//kf
# ELa7WluVZEKXBVVdlBRk0t1/iCb1pKJh2F8xo9ryA9r65djmfxA3HNspdX/PQ6Na
# yV9Fp+kE
# SIG # End signature block
