function copyManualOutputFromVM($session, $hostOutputPath)
{
    $outputFolderVM = Read-Host -Prompt "When sequencing is done, enter output folder on the VM and continue (Type 'q' to skip)"
    while ($true)
    {
        if ($outputFolderVM -eq "q")
        {
            return
        }

        $isPathExisting = invoke-command -session $session -argumentlist "$outputFolderVM\*.appv" { Get-Item $args[0] -ErrorAction SilentlyContinue }
        if (!$isPathExisting)
        {
            $outputFolderVM = Read-Host -Prompt "Invalid output folder $outputFolderVM on the VM. Please enter a valid one (Type 'q' to skip)"
        }
        else
        {
            break
        }
    }

    if (!(test-path $hostOutputPath))
    {
        new-item $hostOutputPath -type directory -force
    }

    copy-item -fromsession $session $outputFolderVM $hostOutputPath -recurse -force
}

function WaitForTaskComplete($timeoutInMinutes)
{
    $TASK_NAME = "sequencer_task"
    $TASK_READY = 3
    $TASK_RUNNING = 4

    $POLL_INTERVAL = 20
    $TASK_START_POLL_INTERVAL = 5 

    if ($timeoutInMinutes -gt 0)
    {
        $timeElapsed = 0
        $timeoutInSecs = $timeoutInMinutes * 60
    }

    $task = Get-ScheduledTask $TASK_NAME
    Write-Host -NoNewLine "Waiting for sequencing task to start.."
    while ($task.state -eq $TASK_READY)
    {
        #still in ready state
        start-sleep -s $TASK_START_POLL_INTERVAL
        Write-Host -NoNewLine "."

        $task = Get-ScheduledTask $TASK_NAME
        if ($timeoutInSecs)
        {
            $timeElapsed = $timeElapsed + $TASK_START_POLL_INTERVAL
            if ($timeElapsed -ge $timeoutInSecs)
            {
                Write-Host "."
                Write-host "Timeout $timeoutInMinutes minutes triggered waiting for sequencing task to start" -BackgroundColor Red
                return $false
            }
        }
    }
    Write-Host "."

    if ($timeoutInMinutes -gt 0)
    {
        $timeElapsed = 0
    }

    if ($task.state -eq $TASK_RUNNING)
    {
        Write-Host -NoNewLine "Sequencing task is running.."

        while ($task.state -eq $TASK_RUNNING)
        {
            start-sleep -s $POLL_INTERVAL
            Write-Host -NoNewLine "."
            if ($timeoutInSecs)
            {
               $timeElapsed = $timeElapsed + $POLL_INTERVAL
               if ($timeElapsed -ge $timeoutInSecs)
               {
                    Write-Host "."
                    Write-host "Timeout $timeoutInMinutes minutes triggered for sequencing" -BackgroundColor Red
                    return $false
               }
            }

            $task = Get-ScheduledTask $TASK_NAME
        }
        Write-Host "."
    }

    Write-Host "Sequencing task has finished running"
    Unregister-ScheduledTask -TaskName $TASK_NAME -Confirm:$false
    return $true
}

function foundOutputError($appOutputFolder)
{
    $logFile = "$appOutputFolder\log.txt"
    if (!(Test-Path $logFile))
    {
        Write-host "Failed to get sequencing result from VM" -BackgroundColor Red
        return $true
    }

    $foundError = select-string "error|exception" $logFile -quiet
    if ($foundError)
    {
        Write-host "Error found in $logFile" -BackgroundColor Red
    }

    return $foundError
}

class CVMCheckPoint
{
    [String] $VMName
    CVMCheckPoint([String] $name)
    {
        $this.VMName = $name
    }

    [String] $OriginalCheckpointType
    [void]Initialize()
    {
        $local:vm = Get-VM $this.VMName
        if ($local:vm)
        {
            $this.OriginalCheckpointType = $local:vm.CheckpointType
            Set-VM -Name $this.VMName -CheckpointType Standard
        }
    }
   
    [void]Uninitialize()
    {
        if (!$this.OriginalCheckpointType)
        {
            return;
        }
        Set-VM -Name $this.VMName -CheckpointType $this.OriginalCheckpointType
    }
   
    [String] CreateCheckpoint($appName)
    {
        $date = Get-Date -Format MM-dd-yyyy-HH-mm-ss
        $checkpointName = "$appName-$date"
        Checkpoint-VM -Name $this.VMName -SnapshotName $checkpointName
        return $checkpointName
    }
}

function New-BatchAppVSequencerPackages
{
    [CmdletBinding()] 
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$ConfigFile,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$VMName,
        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [string]$VMCheckpoint,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$OutputPath
    )

    process {
        $Script:AutoSequencingRoot = "$PSScriptRoot\.."
        $WMI_LOGOFF_USER = 4

        Import-module "$Script:AutoSequencingRoot\AutoSequencingUtils.psm1" -Force
        Import-module "$Script:AutoSequencingRoot\AutoSequencingTelemetry.psm1" -Force

        onCmdletStart $MyInvocation.MyCommand
        
        $telemetryProviderLoaded = LoadAutoSequencingTelemetryProvider($Script:AutoSequencingRoot)
        if ($telemetryProviderLoaded -eq $false)
        {
            Write-host "Failed to initialize Auto-Sequencer telemetry" -BackgroundColor Red
            onCmdletStop
        }

        $telemetryId = New-Guid
        LogNewBatchAppVSequencerPackagesStart -TelemetryId $telemetryId

        # the path to "scheduler.ps1" to schedule the sequencing task
        $SCHEDULER_SCRIPT_FILE = "$Script:AutoSequencingRoot\VmSetup\scheduler.ps1"

        $local:cVmCheckPoint = New-Object CVMCheckPoint($VMName)

        try
        {
            if (!(checkHyperV))
            {
                LogNewBatchAppVSequencerPackagesStatus -TelemetryId $telemetryId -Message "Check Hyper-V failed"
                onCmdletStop
            }

            [xml]$ConfigFileXml = Get-Content $ConfigFile
            if (!$ConfigFileXml)
            {
                $loadConfigFileFailed = "Failed to load config XML file $ConfigFile"
                LogNewBatchAppVSequencerPackagesStatus -TelemetryId $telemetryId -Message $loadConfigFileFailed
                Write-host $loadConfigFileFailed -BackgroundColor Red
                onCmdletStop
            }

            $isValidXML = validateConfigXML $ConfigFileXml
            if (!$isValidXML)
            {
                LogNewBatchAppVSequencerPackagesStatus -TelemetryId $telemetryId -Message "Invalid Config Xml"
                onCmdletStop
            }

            $local:cred = RetrieveUserCredential $VMName
            $reportFilePath = createReportFile $MyInvocation.MyCommand $OutputPath

            $local:cVmCheckPoint.Initialize()

            $rootElement = $ConfigFileXml.Applications
            $appCount = $ConfigFileXml.selectnodes("/Applications/Application")
            $appCountMesg = "Number of applications in the batch " + $appCount.Count
            Write-host $appCountMesg
            LogNewBatchAppVSequencerPackagesStatus -TelemetryId $telemetryId -Message $appCountMesg
            foreach ($app in $rootElement.Application)
            {
                $appName = $app.AppName
                $enabled = $app.Enabled
                if ($enabled -eq "false")
                {
                    Write-host "Skipping $appName" -BackgroundColor DarkGray
                    writeToReport $reportFilePath "Skipping $appName"
                    continue
                }

                # Verify that "AppName" is specified if the installer is command line 
                if ($app.Cmdlet -eq "true")
                {
                    # Command Line based installer
                    if (($appName -eq $null) -or (($appName -eq "")))
                    {
                        $errorMessage = "App name must be specified in config xml for command line installer $app.Installer. Skipping entry"
                        Write-Host $errorMessage
                        writeToReport $reportFilePath $errorMessage
                        continue
                    }
                }

                Write-host "-- Start sequencing $appName  --"

                $s = getCleanPSSession $VMName $VMCheckpoint ([REF]$local:cred) $reportFilePath
                if (!$s)
                {
                    break
                }

                $isProvisioned = validateProvisioningStatus $s
                if (!$isProvisioned)
                {
                    break
                }

                Write-host "Copying installer to VM.."
                $installerFolder = $app.InstallerFolder
                $updatePackage = $app.Package
                $customScriptFolder = $app.CustomScriptFolder
                if (!(CopyFilesToVM $s $installerFolder $updatePackage $customScriptFolder))
                {
                    break
                }

                # before scheduling task, we want to make sure the user is logged off
                # since the task will only run when the user logs in. If the user is already logged off
                # we just ignore the exception being thrown
                Invoke-Command -Session $s -argumentlist $WMI_LOGOFF_USER { try { (gwmi win32_operatingsystem ).Win32Shutdown($args[0]) | out-null } catch {} }

                Write-host "Scheduling sequencing task.."
                $installer = $app.Installer
                $installerOptions = $app.InstallerOptions

                # fill in an empty install option if missing
                if (!$installerOptions)
                {
                    $installerOptions = ""
                }

                if ($updatePackage)
                {
                    $isUpdatePackage = $true
                }
                else
                {
                    $isUpdatePackage = $false
                }

                $isCmdlet = $app.Cmdlet
                if ($isCmdlet -eq "false")
                {
                    #Launch Sequencer GUI
                    if (!(printInstructionForGUISequencing $s))
                    {
                        continue
                    }
                    invoke-command -session $s -filepath $SCHEDULER_SCRIPT_FILE -argumentlist $false, $local:cred.UserName
                }
                else
                {
                    [int]$timeoutInMinutes = $app.TimeoutInMinutes
                    if (!$timeoutInMinutes)
                    {
                        $timeoutInMinutes = 0
                    }
                    invoke-command -session $s -filepath $SCHEDULER_SCRIPT_FILE -argumentlist $true, $local:cred.UserName, $appName, $installer, $installerOptions, $isUpdatePackage, $timeoutInMinutes
                }

                # always show the VM
                $dnsResult = dnsResolve $s
                if (!$dnsResult)
                {
                    $dnsResolveError = "Failed to DNS resolve for VM"
                    writeToReport $dnsResolveError $reportFilePath 
                    continue
                }

                $procMstsc = setupAndShowVM $dnsResult $local:cred
                if (!$procMstsc)
                {
                    $vmLaunchError = "Failed to launch VM"
                    LogNewBatchAppVSequencerPackagesStatus -TelemetryId $telemetryId -Message $vmLaunchError
                    writeToReport $vmLaunchError $reportFilePath 
                    continue
                }

                if ($isCmdlet -eq "false")
                {
                    copyManualOutputFromVM $s $OutputPath
                    writeToReport $reportFilePath "Done manual sequencing $appName"
                }
                else
                {
                    $ret = Invoke-Command -session $s -ScriptBlock ${function:WaitForTaskComplete} -argumentlist $timeoutInMinutes

                    # Always try and copy output from VM 
                    if (!(copyAutoOutputFromVM $s $OutputPath))
                    {
                        writeToReport $reportFilePath "Failed to copy output"
                    }

                    if ($ret)
                    {
                        if ((foundOutputError "$OutputPath\$appName"))
                        {
                            writeToReport $reportFilePath "Error found in sequencing $appName"
                        }
                        else
                        {
                            writeToReport $reportFilePath "Done sequencing $appName"
                        }
                    }
                    else
                    {
                        $local:checkpointName = $local:cVmCheckPoint.CreateCheckpoint($appName)
                        Write-Host "Failed to sequence $appName. VM checkpoint $local:checkpointName created for troubleshooting." -BackgroundColor Red
                        writeToReport $reportFilePath "Failed to sequence $appName. VM checkpoint $local:checkpointName created for troubleshooting"
                    }
                }
    
                if (!($procMstsc.HasExited))
                {
                    # close the VM window
                    stop-process -id $procMstsc.id
                }

                # clean up the key store
                $ret = cmdkey /delete:$dnsResult

                remove-pssession -session $s
                $local:VmComputerName = RetrieveVmComputerName $VMName
                RemoveHostFromTrustedHostsList($local:VmComputerName)

                $doneSequencing = "-- Done sequencing $appName --"
                LogNewBatchAppVSequencerPackagesStatus -TelemetryId $telemetryId -Message $doneSequencing
                Write-host $doneSequencing
            }

            $sequencingComplete = "Sequencing complete."
            LogNewBatchAppVSequencerPackagesStatus -TelemetryId $telemetryId -Message $sequencingComplete
            Write-host $sequencingComplete -BackgroundColor DarkGreen

            onCmdletCompletion
        }
        finally
        {
            $local:cVmCheckPoint.Uninitialize() 
        }
    }
}

Export-ModuleMember -Function New-BatchAppVSequencerPackages

# SIG # Begin signature block
# MIIiOwYJKoZIhvcNAQcCoIIiLDCCIigCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCO5NIbr8YJeioz
# JxFkemzoDbsJc6QiHB935/2+g4d43aCCC34wggUGMIID7qADAgECAhMzAAABswyw
# gExVVE3DAAAAAAGzMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTAwHhcNMTcwNzE4MTc1MDQxWhcNMTgwNzEwMTc1MDQxWjB/MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQDEyBNaWNy
# b3NvZnQgV2luZG93cyBLaXRzIFB1Ymxpc2hlcjCCASIwDQYJKoZIhvcNAQEBBQAD
# ggEPADCCAQoCggEBAOP85kiLpaS9v/uqxuT0fO3b0Wc9NPGOUXWJ64gNZsUfo3rt
# F4nqKu39PAhpRvtSFu2dmVO/eMwJ6hKfXoEi7p/HnJiAJsM2KBNuAMr9W0u2U8zU
# mEGwdI86J8xA4NQbLPgRLLiI+5KOhncDPEIg8ejBTCEsp384wIj1kfiQ4JYlHsU7
# QbxyfMaDXhgtD4zKPWB8lQ00nf0bon2uSRJo/eqEMMWVIS8DUtP+0N2F3zSWTXmU
# Av1eLEdBaotG4MS+xL4+1R2288dx3RrxIJ2DANKZFMIxyBX/5j1fE/FmBWFHgT6j
# GNvdM6yo4289niiSZfJ6CSlUZItMp2+Vqhv1q3UCAwEAAaOCAXowggF2MB8GA1Ud
# JQQYMBYGCisGAQQBgjcKAxQGCCsGAQUFBwMDMB0GA1UdDgQWBBQQ638EyhqeAHDv
# ddaB2xCoBuYWQDBRBgNVHREESjBIpEYwRDEMMAoGA1UECxMDQU9DMTQwMgYDVQQF
# EysyMjk5MDMrZmQ2YjllNWQtZWI3My00MTg5LWFiYzItZjdjYWNkYTM4MWFjMB8G
# A1UdIwQYMBaAFOb8X3u7IgBY5HJOtfQhdCMy5u+sMFYGA1UdHwRPME0wS6BJoEeG
# RWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0Nv
# ZFNpZ1BDQV8yMDEwLTA3LTA2LmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUH
# MAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2ln
# UENBXzIwMTAtMDctMDYuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD
# ggEBAJl59WeeQK+g1o1JbJfI95BZ4VvnwGqOd2FSqQJOxmRwAKjXq9U70yxBawxv
# YkheZdLhmeto4TlMBvcj3HrregpzaS4nQvcKhME9eroEFhP5fL4mxZwNSoHajalU
# c6w8Rpu3728wHHqCKVmlLGtSxeFy12f7FVOyxEYSQEewMuFeegHBAW+BmAb889Di
# LemKdr9ilLoQh60EJnLxKqSl0uiN/ft+DHVEDCZLb16LztE4FQaol74OhC7B4Vpz
# 8iBstQ2EgTdHTqVrHd9aK5xqIzEiRurd0rL2+nOJlMVMu0b2gTBca5BEakXyzWE3
# 0nDDShDOiYjQsXmdUzOdTm6e0PwwggZwMIIEWKADAgECAgphDFJMAAAAAAADMA0G
# CSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3Jp
# dHkgMjAxMDAeFw0xMDA3MDYyMDQwMTdaFw0yNTA3MDYyMDUwMTdaMH4xCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29m
# dCBDb2RlIFNpZ25pbmcgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
# ggEKAoIBAQDpDmRQeWe1xOP9CQBMnpSs91Zo6kTYz8VYT6mldnxtRbrTOZK0pB75
# +WWC5BfSj/1EnAjoZZPOLFWEv30I4y4rqEErGLeiS25JTGsVB97R0sKJHnGUzbV/
# S7SvCNjMiNZrF5Q6k84mP+zm/jSYV9UdXUn2siou1YW7WT/4kLQrg3TKK7M7RuPw
# RknBF2ZUyRy9HcRVYldy+Ge5JSA03l2mpZVeqyiAzdWynuUDtWPTshTIwciKJgpZ
# fwfs/w7tgBI1TBKmvlJb9aba4IsLSHfWhUfVELnG6Krui2otBVxgxrQqW5wjHF9F
# 4xoUHm83yxkzgGqJTaNqZmN4k9Uwz5UfAgMBAAGjggHjMIIB3zAQBgkrBgEEAYI3
# FQEEAwIBADAdBgNVHQ4EFgQU5vxfe7siAFjkck619CF0IzLm76wwGQYJKwYBBAGC
# NxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w
# HwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmg
# R4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWlj
# Um9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEF
# BQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29D
# ZXJBdXRfMjAxMC0wNi0yMy5jcnQwgZ0GA1UdIASBlTCBkjCBjwYJKwYBBAGCNy4D
# MIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2Rv
# Y3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABf
# AFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEB
# CwUAA4ICAQAadO9XTyl7xBaFeLhQ0yL8CZ2sgpf4NP8qLJeVEuXkv8+/k8jjNKnb
# gbjcHgC+0jVvr+V/eZV35QLU8evYzU4eG2GiwlojGvCMqGJRRWcI4z88HpP4MIUX
# yDlAptcOsyEp5aWhaYwik8x0mOehR0PyU6zADzBpf/7SJSBtb2HT3wfV2XIALGmG
# dj1R26Y5SMk3YW0H3VMZy6fWYcK/4oOrD+Brm5XWfShRsIlKUaSabMi3H0oaDmmp
# 19zBftFJcKq2rbtyR2MX+qbWoqaG7KgQRJtjtrJpiQbHRoZ6GD/oxR0h1Xv5AiMt
# xUHLvx1MyBbvsZx//CJLSYpuFeOmf3Zb0VN5kYWd1dLbPXM18zyuVLJSR2rAqhOV
# 0o4R2plnXjKM+zeF0dx1hZyHxlpXhcK/3Q2PjJst67TuzyfTtV5p+qQWBAGnJGdz
# z01Ptt4FVpd69+lSTfR3BU+FxtgL8Y7tQgnRDXbjI1Z4IiY2vsqxjG6qHeSF2kcz
# Yo+kyZEzX3EeQK+YZcki6EIhJYocLWDZN4lBiSoWD9dhPJRoYFLv1keZoIBA7hWB
# dz6c4FMYGlAdOJWbHmYzEyc5F3iHNs5Ow1+y9T1HU7bg5dsLYT0q15IszjdaPkBC
# MaQfEAjCVpy/JF1RAp1qedIX09rBlI4HeyVxRKsGaubUxt8jmpZ1xTGCFhMwghYP
# AgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAm
# BgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTACEzMAAAGzDLCA
# TFVUTcMAAAAAAbMwDQYJYIZIAWUDBAIBBQCgggEEMBkGCSqGSIb3DQEJAzEMBgor
# BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3
# DQEJBDEiBCAui/dwMxHJHwXUyNrobPJYTCqJluG9kKZe+d3k4XNiKTA8BgorBgEE
# AYI3CgMcMS4MLEx5VytDYnBNZ1ZDUS8xZWFkNGd6QzAwYmdhc3BHS0R1eGI2b1R6
# UE1UV1k9MFoGCisGAQQBgjcCAQwxTDBKoCSAIgBNAGkAYwByAG8AcwBvAGYAdAAg
# AFcAaQBuAGQAbwB3AHOhIoAgaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3dpbmRv
# d3MwDQYJKoZIhvcNAQEBBQAEggEAtBtAexu9mAvB8Zo26cq3EVv4MP9KeeAGIy+T
# oeeZOWVSWh3iQpS+CfLzMqqpk643fBXG1UWxH07YympJKDjeU+anWPamnKuJsP6B
# laSbuIxu7zMEaOksfC2c9L+Z9ehK13sqwIfA/SeaFtuXrWwp9xLyWBVnZXPovjaS
# 1iYg/LlImc0etlKUpI+JBeZtsO34em4yWb6Gyzh2YS9BdsjvpugpPn9Gz2mfPIGg
# Ns9q8vzqarDBtx7O9TxZfkNAD89Rb7DfGyEa1sSmmewWCPabHF0cC5Ut675sTwzQ
# 9UhJhD8HjHMQzBrp2s3gtsmICpgC1g+jqnREtHCEQZNSlUGeXqGCE0YwghNCBgor
# BgEEAYI3AwMBMYITMjCCEy4GCSqGSIb3DQEHAqCCEx8wghMbAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggE8BgsqhkiG9w0BCRABBKCCASsEggEnMIIBIwIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCB4cqBKqHoJIz0LJV3eYG2FFZd1jXci3d5d
# CnHgZJY+OwIGWc1BKnqJGBMyMDE3MDkyOTAzNDk0MS44OTVaMAcCAQGAAgH0oIG4
# pIG1MIGyMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQwwCgYD
# VQQLEwNBT0MxJzAlBgNVBAsTHm5DaXBoZXIgRFNFIEVTTjoxMkU3LTMwNjQtNjEx
# MjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCDsowggZx
# MIIEWaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVa
# Fw0yNTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIB
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mU
# a3RUENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZ
# sTBED/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4Yy
# hB50YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQ
# YrFd/XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDa
# TgaRtogINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQID
# AQABo4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDz
# Q3t8RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQE
# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQ
# W9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNv
# bS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBa
# BggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNV
# HSABAf8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggr
# BgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQA
# ZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2d
# o6Ehb7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GC
# RBL7uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZ
# eUqRUgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8y
# Sif9Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOc
# o6I8+n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz3
# 9L9+Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSY
# Ighh2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvY
# grRyzR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98is
# TtoouLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8
# l1Bx16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzV
# s341Hgi62jbb01+P3nSISRIwggTZMIIDwaADAgECAhMzAAAArIohvHrSm3L0AAAA
# AACsMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTE2MDkwNzE3NTY1NFoXDTE4MDkwNzE3NTY1NFowgbIxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDDAKBgNVBAsTA0FPQzEnMCUGA1UECxMe
# bkNpcGhlciBEU0UgRVNOOjEyRTctMzA2NC02MTEyMSUwIwYDVQQDExxNaWNyb3Nv
# ZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
# CgKCAQEAocT0PKWOczGhF0Fcq3cIt6nRQ6q739x0+bo1VmqECdipu/tU9xjZxqWz
# Q+j6JgHMPTX1fgvkTJgq04aVJQVc3rs7DS9i7I/ZjHSvN4sXm2s3p0qwNK1yyIn1
# gcyh1T+o87EFqPenB7pB2aiPMUad7WIr4GqbeSy65/UZtiZ/hNIdpSgzsObCLXmY
# q4jjDx9me5rYGF5sgfE4YHQeOqen6zRqXF69daWtVZJXZdImOqAQCkEEsSeQZ+De
# 7ouVo+cA5A2cxgIOp9abTD0zyYS9rqLFvOGI50qf9rLjWqd6joyhGF5qRs72rKua
# sqj8r36n5LDnzQWiZ7X6QvHSAySq5wIDAQABo4IBGzCCARcwHQYDVR0OBBYEFNhH
# cKiFNYsTYa+HkOhFFyTgmKcNMB8GA1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2ha
# hW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w
# a2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggr
# BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2kvY2VydHMvTWljVGltU3RhUENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB
# /wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAEjs
# 6F4nwxsYnF20znZuNwwfXWMcDV2kI9IVtM7QQAnlm1+s8XntcO0yXl0MdSH089QV
# iQH1ZWuxwNaMWtC4hf1H+q30bxBJhph6/cFhG698YhK5ZUxRNlf5nWAUhMHY++Jv
# ECwXhWN+wo8eExXvgAfxUJct5L36ySg2mGE010wBWACqYtAHA03BEl2XLISfzvFG
# bPX69AYKzpuYe6nkNQiTZgzFRKhJULf3BTt3caqwCr9xbv57E+RpAAbvryIm4MV+
# rTZqnHCCFygC4L+8wyGkQAFrV1fS8J5rLQ1GyKfHR/6ElZt7T5wj5awd/gtvLz57
# yXW7SYv8jrhE3fqy5/qhggN0MIICXAIBATCB4qGBuKSBtTCBsjELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQL
# Ex5uQ2lwaGVyIERTRSBFU046MTJFNy0zMDY0LTYxMTIxJTAjBgNVBAMTHE1pY3Jv
# c29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiJQoBATAJBgUrDgMCGgUAAxUAOXAlixTI
# ed1fDF3pi5yzlJn3HLeggcEwgb6kgbswgbgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xDDAKBgNVBAsTA0FPQzEnMCUGA1UECxMebkNpcGhlciBO
# VFMgRVNOOjI2NjUtNEMzRi1DNURFMSswKQYDVQQDEyJNaWNyb3NvZnQgVGltZSBT
# b3VyY2UgTWFzdGVyIENsb2NrMA0GCSqGSIb3DQEBBQUAAgUA3Xg9rzAiGA8yMDE3
# MDkyOTAzMzQwN1oYDzIwMTcwOTMwMDMzNDA3WjB0MDoGCisGAQQBhFkKBAExLDAq
# MAoCBQDdeD2vAgEAMAcCAQACAhkhMAcCAQACAhkSMAoCBQDdeY8vAgEAMDYGCisG
# AQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwGgCjAIAgEAAgMW42ChCjAIAgEAAgMH
# oSAwDQYJKoZIhvcNAQEFBQADggEBACdn4ea1vsjQI3PdSoXjTqTIrJYy6Qq+87XA
# Fc7nZnmBCFs1mGojAAErXrsdJ3T6VsOTR7fkIB/+S3lhB10zSU8adL+o3qTgksbj
# A8Kq7dfC8v2l/gnX631kyEoWzJc8FuuTtnHWrfOCcDsfA5Dzbj5d+3YS8AKZ0Tih
# dtTBvHctfljBbIMzFN6V2ET07rnNhCcvGaiPuu8hgTc72Pkr7G9EPS51dbswxi3v
# mhZeZRChFPe13w0m2tE5HptMaeE8aK0DdhGzCfqMiJsgaKLDVY5t/7dTlYbXNOdq
# 4eibI8aMHlzMB9vkZvDFTMR+vq7YU8KbB7zVQsHxVFNxdiG0xUsxggL1MIIC8QIB
# ATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAKyKIbx60pty
# 9AAAAAAArDANBglghkgBZQMEAgEFAKCCATIwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3
# DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCtwYxeBT7wdSymG425dX3DblBTACkmE8UB
# 7Wi6r+iXkTCB4gYLKoZIhvcNAQkQAgwxgdIwgc8wgcwwgbEEFDlwJYsUyHndXwxd
# 6Yucs5SZ9xy3MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAC
# EzMAAACsiiG8etKbcvQAAAAAAKwwFgQUmIO90ze9QYVmuUyWTTfn3QKIgPwwDQYJ
# KoZIhvcNAQELBQAEggEAkqXlVqOURPCxDto2d7puD52sWNrU8J8n4hqnyNZqn6Wf
# x10+IPxoWjcAGEA36tyjV+W/g/y2E/wkHLmBXQd4B2WGXaahIZ/ogLNc3lWT+PIs
# XeLCziZMNllyarUIHB3apnn1EQ4NrTQbETImKB5BRGPi4QhzMqIH29JqSoxSR7sX
# 5wGC4U7y4y8xbXEucsMdzsyJwn6qjkjbQK0WLK1luzmRrJNtHtNI8U2BUNQ6C3Cz
# cK6EOKqvUhAE3d7OIAlVM1hw2KejF9gzNbMnmB/ytzrfbrxVI5NBfAEAj0cxvmP2
# KpZy2ObR1pUFLyDihfnIlmEkIbDnMUuPFNlUFNV9nA==
# SIG # End signature block
