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
# MIIiPAYJKoZIhvcNAQcCoIIiLTCCIikCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCO5NIbr8YJeioz
# JxFkemzoDbsJc6QiHB935/2+g4d43aCCC38wggUHMIID76ADAgECAhMzAAABtGsb
# Q7hXc1IAAAAAAAG0MA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTAwHhcNMTcwNzE4MTc1MDQyWhcNMTgwNzEwMTc1MDQyWjB/MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQDEyBNaWNy
# b3NvZnQgV2luZG93cyBLaXRzIFB1Ymxpc2hlcjCCASIwDQYJKoZIhvcNAQEBBQAD
# ggEPADCCAQoCggEBALoNUC4sIGNyRsCwy4Hz1lNmFXull+eDYoV8icPzo7fp48hp
# WfrMVqFCARGABNkFu7NG+9vJw2ZxMGjOF/PPqLUnYr9wOuEqozMtkvKpR8mU0he4
# gzoPoYfVl3D2M/UPVyvfkeLiIIZ0KqrGkjE5Nib/Xcngu6gLGXj9dUXHA8dA40Hg
# BaIls59ygmky4zuMiH59X3ZFQ5PFuT7CLZLt0IDuHUUL6Cv2JgicUoerLEdWrTq9
# vtEtPGlM9ygDgOpM00ws9yRliMxiUhuCQbwJSNKhkDs/rNSrB6KCpKC8MWdIs0Wk
# Oac3y/1LRJB0T/XUvCUsq3Z89eBrAgs6Izqu5QsCAwEAAaOCAXswggF3MB8GA1Ud
# JQQYMBYGCisGAQQBgjcKAxQGCCsGAQUFBwMDMB0GA1UdDgQWBBRn2V9L5fyECUI1
# 7j47Ui+9hpG8pDBSBgNVHREESzBJpEcwRTENMAsGA1UECxMETU9QUjE0MDIGA1UE
# BRMrMjI5OTAzK2ZkNmI5ZTVkLWViNzMtNDE4OS1hYmMyLWY3Y2FjZGEzODFhYzAf
# BgNVHSMEGDAWgBTm/F97uyIAWORyTrX0IXQjMubvrDBWBgNVHR8ETzBNMEugSaBH
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWND
# b2RTaWdQQ0FfMjAxMC0wNy0wNi5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY0NvZFNp
# Z1BDQV8yMDEwLTA3LTA2LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUA
# A4IBAQCir8BXOUv+Q73WyLmTPSVPGlvkmzwOjxOXAiyZAWIdvY1Bz0kGpMv4UMBw
# 6LCcY/niXi2Tq0ji3rTYzs+QSg+hKgAJp6KP8lIM9L5uyPa9jF0F6TK15mH58XSt
# 7ed6evQ+rNqFCcfFacX/KHfEq/SWJ0yiUHhKBzKGv940rzQuzN30TuCYxzKR85hl
# fGn3xz1EN3Knbkm7j5S9HIaaz1i4sDBp1V6qLY2wCgNdaTnyC8vjWfya+91wigW8
# i5EyWwmCgKqP828UHGWUO5J1WWzKZSOmQmpZo0sDRts7M4J8X8laqtbBi2qN5Om4
# PDTVwzIiL04c5IWK3sq4srU+WKBIMIIGcDCCBFigAwIBAgIKYQxSTAAAAAAAAzAN
# BgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9y
# aXR5IDIwMTAwHhcNMTAwNzA2MjA0MDE3WhcNMjUwNzA2MjA1MDE3WjB+MQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3Nv
# ZnQgQ29kZSBTaWduaW5nIFBDQSAyMDEwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
# MIIBCgKCAQEA6Q5kUHlntcTj/QkATJ6UrPdWaOpE2M/FWE+ppXZ8bUW60zmStKQe
# +fllguQX0o/9RJwI6GWTzixVhL99COMuK6hBKxi3oktuSUxrFQfe0dLCiR5xlM21
# f0u0rwjYzIjWaxeUOpPOJj/s5v40mFfVHV1J9rIqLtWFu1k/+JC0K4N0yiuzO0bj
# 8EZJwRdmVMkcvR3EVWJXcvhnuSUgNN5dpqWVXqsogM3Vsp7lA7Vj07IUyMHIiiYK
# WX8H7P8O7YASNUwSpr5SW/Wm2uCLC0h31oVH1RC5xuiq7otqLQVcYMa0KlucIxxf
# ReMaFB5vN8sZM4BqiU2jamZjeJPVMM+VHwIDAQABo4IB4zCCAd8wEAYJKwYBBAGC
# NxUBBAMCAQAwHQYDVR0OBBYEFOb8X3u7IgBY5HJOtfQhdCMy5u+sMBkGCSsGAQQB
# gjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/
# MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJ
# oEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01p
# Y1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYB
# BQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9v
# Q2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGdBgNVHSAEgZUwgZIwgY8GCSsGAQQBgjcu
# AzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL1BLSS9k
# b2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwA
# XwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0B
# AQsFAAOCAgEAGnTvV08pe8QWhXi4UNMi/AmdrIKX+DT/KiyXlRLl5L/Pv5PI4zSp
# 24G43B4AvtI1b6/lf3mVd+UC1PHr2M1OHhthosJaIxrwjKhiUUVnCOM/PB6T+DCF
# F8g5QKbXDrMhKeWloWmMIpPMdJjnoUdD8lOswA8waX/+0iUgbW9h098H1dlyACxp
# hnY9UdumOUjJN2FtB91TGcun1mHCv+KDqw/ga5uV1n0oUbCJSlGkmmzItx9KGg5p
# qdfcwX7RSXCqtq27ckdjF/qm1qKmhuyoEESbY7ayaYkGx0aGehg/6MUdIdV7+QIj
# LcVBy78dTMgW77Gcf/wiS0mKbhXjpn92W9FTeZGFndXS2z1zNfM8rlSyUkdqwKoT
# ldKOEdqZZ14yjPs3hdHcdYWch8ZaV4XCv90Nj4ybLeu07s8n07VeafqkFgQBpyRn
# c89NT7beBVaXevfpUk30dwVPhcbYC/GO7UIJ0Q124yNWeCImNr7KsYxuqh3khdpH
# M2KPpMmRM19xHkCvmGXJIuhCISWKHC1g2TeJQYkqFg/XYTyUaGBS79ZHmaCAQO4V
# gXc+nOBTGBpQHTiVmx5mMxMnORd4hzbOTsNfsvU9R1O24OXbC2E9KteSLM43Wj5A
# QjGkHxAIwlacvyRdUQKdannSF9PawZSOB3slcUSrBmrm1MbfI5qWdcUxghYTMIIW
# DwIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgw
# JgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDEwAhMzAAABtGsb
# Q7hXc1IAAAAAAAG0MA0GCWCGSAFlAwQCAQUAoIIBBDAZBgkqhkiG9w0BCQMxDAYK
# KwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG
# 9w0BCQQxIgQgLov3cDMRyR8F1Mja6GzyWEwqiZbhvZCmXvnd5OFzYikwPAYKKwYB
# BAGCNwoDHDEuDCxMeVcrQ2JwTWdWQ1EvMWVhZDRnekMwMGJnYXNwR0tEdXhiNm9U
# elBNVFdZPTBaBgorBgEEAYI3AgEMMUwwSqAkgCIATQBpAGMAcgBvAHMAbwBmAHQA
# IABXAGkAbgBkAG8AdwBzoSKAIGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS93aW5k
# b3dzMA0GCSqGSIb3DQEBAQUABIIBACQku2yJ6RuaxzY2ZVtJC14Z6sgbecs8bt2i
# /VuWQu3epUv8jR6qfHosAoF6Jn59aDg5nhCentkM7AHAYTzhLn+CoZfI8jyw7yyE
# NIoVgiWc9t6c7zL9ftwca/1769Y37/qgb1Fyvp5AWThI/3tFeSKd+dsBK+7xGfyM
# tcAYf7VtrkhJurfOIWV/CyoxDnaxTdjju0U658EB2dZ8KIU0zsRCEPfLD3msLdHu
# aBb332YUjPKYKynRfeme8G0x9c6yFZdL06F1n2jqazmcKxsvD5wC3RXjuqfI963c
# clJN03xRVurz4+iPo5S6Cy2VzviEpoa/in5cn6fl5lGOpz0wcmKhghNGMIITQgYK
# KwYBBAGCNwMDATGCEzIwghMuBgkqhkiG9w0BBwKgghMfMIITGwIBAzEPMA0GCWCG
# SAFlAwQCAQUAMIIBPAYLKoZIhvcNAQkQAQSgggErBIIBJzCCASMCAQEGCisGAQQB
# hFkKAwEwMTANBglghkgBZQMEAgEFAAQgTuhVo5C3CMHRc7nexYHWbomf5au+YBIv
# +e+EFCbONSgCBlqydPszKxgTMjAxODA0MTExMzQwMjUuNzkxWjAHAgEBgAIB9KCB
# uKSBtTCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEMMAoG
# A1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046RjZGRi0yREE3LUJC
# NzUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wggg7KMIIG
# cTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0
# IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1
# WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC
# ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9p
# lGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEw
# WbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeG
# MoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJ
# UGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw
# 2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0C
# AwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ
# 80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8E
# BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2U
# kFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5j
# b20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmww
# WgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYD
# VR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6
# Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYI
# KwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0
# AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9
# naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtR
# gkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzy
# mXlKkVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCf
# Mkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3D
# nKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs
# 9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110
# mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL
# 2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffI
# rE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxE
# PJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc
# 1bN+NR4Iuto229Nfj950iEkSMIIE2TCCA8GgAwIBAgITMwAAAKVIF3In+XC+YwAA
# AAAApTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MDAeFw0xNjA5MDcxNzU2NTBaFw0xODA5MDcxNzU2NTBaMIGyMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQwwCgYDVQQLEwNBT0MxJzAlBgNVBAsT
# Hm5DaXBoZXIgRFNFIEVTTjpGNkZGLTJEQTctQkI3NTElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
# AQoCggEBALTaktS9TF7w21cH31lhgTomttMYs2nD/fvMI2/SDczTG1YWzFBNvsMS
# +1zWwTSzwjnJtPsh+jLXYWwKCl5uT74Kly/RrQLk4dDvaYGfHzLGTYQm/eJ7qNIi
# LDzxzCs5Mi/+G+yeT2/9i8dU84WdfoJLUhKO7g9jPbY3RPpOW7tf3Y/+5oXXA1IR
# nsGU/zX7hzL2EyCRp5o3ofJPpDJUS+AoFOXnIYbt1sOamTQhJjB1B5igZehTt9CZ
# WpQSZrvbVlY01ESRYYnQuNRfOk7lgDrH8lKbOnXX3HaoIFMgxxm3FFV1fPvKAiIO
# oUuB87DmWcUapQuByhcCPmrFC46/wbcCAwEAAaOCARswggEXMB0GA1UdDgQWBBTi
# E6tL8u2xYLh48YWgUf5y57EvpDAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNo
# WoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMB
# Af8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQA2
# fW15K8YuLyQgyiX1NBWM3PJLRwf0oz7uhngI3nuH1gbFWOn7y/MXTh/pMaDG0MJm
# A5+uzfDsnCtZk4JTupERHAqex2IaWqPVFsstpurA8rT/eX77DvAz6k4brlza9FAu
# 6EuoZxkGq8ffwX1hBSIwYc6lmMDAAih9aTEpmSBupDbn4pTShGzcDRpJyfXjNlVt
# bffVWxHUOboA36bRJMtJMbwlgIJgpOsZ5iGCaS9IkrJQxQ8OnTffHrz+uHLrk0P+
# W8YfG16gaF3eDhTkItRqFVbk6OLnrY/KJzhiZtZs2yYSLwmDxa5wQeI5HoQtC7Hv
# iXAmUgQy6TwMA55sbPhuoYIDdDCCAlwCAQEwgeKhgbikgbUwgbIxCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDDAKBgNVBAsTA0FPQzEnMCUGA1UE
# CxMebkNpcGhlciBEU0UgRVNOOkY2RkYtMkRBNy1CQjc1MSUwIwYDVQQDExxNaWNy
# b3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiUKAQEwCQYFKw4DAhoFAAMVAJvCNd37
# siYrGhQxZVAvHVOUaYJvoIHBMIG+pIG7MIG4MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMQwwCgYDVQQLEwNBT0MxJzAlBgNVBAsTHm5DaXBoZXIg
# TlRTIEVTTjoyNjY1LTRDM0YtQzVERTErMCkGA1UEAxMiTWljcm9zb2Z0IFRpbWUg
# U291cmNlIE1hc3RlciBDbG9jazANBgkqhkiG9w0BAQUFAAIFAN54VJcwIhgPMjAx
# ODA0MTEwOTMyMDdaGA8yMDE4MDQxMjA5MzIwN1owdDA6BgorBgEEAYRZCgQBMSww
# KjAKAgUA3nhUlwIBADAHAgEAAgIh8jAHAgEAAgIYgDAKAgUA3nmmFwIBADA2Bgor
# BgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMBoAowCAIBAAIDFuNgoQowCAIBAAID
# B6EgMA0GCSqGSIb3DQEBBQUAA4IBAQAx54PpjkeeB+piEZY+m8LWjboQ+i4YBAPh
# aoysvd3JOjyEJe+4/CO4fJZ42ZtNKO8xlsE7eyP0sSbXbFhpMGCccXpU8VnUW4+0
# sbWu1RgWArkqnBDlCL/h0gua5UnILuudoQ7XxQyLzKafiQjYFTFTq52SjE78ZlmV
# lB1gzUzr05eb7ydRV1blFfP5KlHV+NAp839AftYLAKjYe/wZ2FzMjWptQz0owfNW
# EFnRZlYRSCamYgwOE1kWnpjhkAE53+zAQgQLEjyFIMgc9s3kqfvuJRdvgwx1kKr4
# o+53QKzQsgPjeZcG5gbz+4GO/INsEL/S707wzdQfdQaqrIaV4AldMYIC9TCCAvEC
# AQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAClSBdyJ/lw
# vmMAAAAAAKUwDQYJYIZIAWUDBAIBBQCgggEyMBoGCSqGSIb3DQEJAzENBgsqhkiG
# 9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgmiLcEb7ZFGw0Oolsa757fndhcYrB/+zk
# s/yRoW/f6KMwgeIGCyqGSIb3DQEJEAIMMYHSMIHPMIHMMIGxBBSbwjXd+7ImKxoU
# MWVQLx1TlGmCbzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# AhMzAAAApUgXcif5cL5jAAAAAAClMBYEFE7VQKZXYfZ2Ak0hmNSVvnEKyvChMA0G
# CSqGSIb3DQEBCwUABIIBAGz2z1ky/betbFKDr2dyD1NxKP6VdatbRaq2NmPgNdL3
# vaSbaUTczWNNs6fcGvkyqbwOi6yPhju4q2SndS+EP6MElcMkSElBVAoxRQWXH62K
# uginfFGRuzKEWFLeW26taD5suD+kUFmhCRfULwi2UXwgVkNn5O2W06FpimI1vnVB
# fznyQ2go2pwaSzO5JWbAXNj3sparuqvmMFfZEuP7WM+Q+m0KOc0G8GdzN6irRilS
# yjdGqpcNs5Xr+FSAzXdINxYUUg7O+NI62obfyZXzMNnWbqXDVjd0KNrd1lnHmSw7
# qLq8s4mS3coyzs0VMRbjBPl6tnFjrC8FbwXHKZVi5Tw=
# SIG # End signature block
