    <#
      .SYNOPSIS
       Video playback energy efficiency workload for the energy efficiency assessment.    

      .DESCRIPTION
       As an Energy Efficency assessment,the MediaAssessment plays a selected video and measures its efficiency while on battery.
       Options are available to control tracing and playback duration.
       
      .PARAMETER StartupTrace
       Flag to enable playback startup ETW tracing.
       
      .PARAMETER SteadyStateTrace
       Flag to enable playback steady state ETW tracing.
       
      .PARAMETER FiniteDuration
       Flag to enable specific workload duration (vs infinite).
       
      .PARAMETER Minutes
       Workload during in minutes.
       
      .PARAMETER StartupTraceDurationInSeconds
       Number of seconds tracing continues before stopping.
       
      .PARAMETER SteadyStateTraceDelayInMinutes
       Number of minutes to delay tracing when the SteadyStateTrace flag is set.
       
      .PARAMETER PlaybackSource
       A PlaybackSourceEnum value, such as -3, or a video resolution, such as 1080.
       
      .PARAMETER CaptureClockResolution
       Flag to to enable/disable clock resolution job.
       
      .EXAMPLE
       MediaWorkload -PlaybackSource 1080    -Minutes 2 -FiniteDuration -CaptureClockResolution
       
    #>

Param( 
    [parameter(Mandatory=$false)][switch]$StartupTrace,
    [parameter(Mandatory=$false)][switch]$SteadyStateTrace,
    [parameter(Mandatory=$false)][switch]$FiniteDuration,
    [parameter(Mandatory=$false)][int32]$Minutes,
    [parameter(Mandatory=$false)][int32]$StartupTraceDurationInSeconds,
    [parameter(Mandatory=$false)][int32]$SteadyStateTraceDelayInMinutes,
    [parameter(Mandatory=$false)][int32]$SteadyStateTraceDurationInSeconds,
    [parameter(Mandatory=$false)][string]$CustomPlaybackSource,
    [parameter(Mandatory=$true)][int32]$PlaybackSource, 
    [parameter(Mandatory=$false)][switch]$CaptureClockResolution
)

try {
    # Import the modules and functions
    try {
        Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'MediaAssessment.psm1') -ErrorAction Stop
    }
    catch {
        throw "Failed to import module MediaAssessment.psm1."
    }

    try {
        . $PSScriptRoot\scripts\AssessmentHelper.ps1
    }
    catch {
        throw "Failed to execut AssessmentHelper.ps1"
    }


    #*****************************************************************
    #
    #                Definitions and imports
    #
    #*****************************************************************

    # Recover the INI configuration file contents.  
    try { 
        $INIFilePath = (Join-Path -Path $PSScriptRoot -ChildPath (Join-Path -Path resources -ChildPath 'MediaAssessment.ini'))
        Get-INI -INIFilePath $INIFilePath -ErrorAction Stop
    }
    catch {throw ("Error reading INI config file: {0}" -f $_)}

    # Need to know where our script is so we can use full path to all our dependent files.
    $Global:InvocationPath = Split-Path -Path $MyInvocation.MyCommand.Path -Parent

    # Tools and resources
    $arch         = Get-ProcessorArchitecture
    $ResourcePath = Join-Path -path $InvocationPath resources
    $ScriptsPath  = Join-Path -path $InvocationPath scripts

    $WPTPath      = Join-Path -path $InvocationPath $arch
    $env:path     = "$WPTPath;" + $env:path


    # Make global, as it needs to be referenced by timer action
    $Global:SteadyStateTraceDurationInSeconds = $SteadyStateTraceDurationInSeconds

    
    # Analysis profiles
    $WPRP_FileName = Get-ConfigValue -Section 'PROFILES' -Key 'WPRP_FILE' -Default 'resources\Media.wprp'
    if (-not(Test-ValidFileName -FileName $WPRP_FileName -Extension '.wprp')) {
        throw ("Invalid WPRP_FileName '{0}'. Must be avlid file name with .wprp extension. Please check config file PROFILES section, WPRP_FILE key." -f $WPRP_FileName)
    }   
    $Global:WPRP_FILE = Get-RootName -FileName $WPRP_FileName
    
    # AXE termination signal handling loop
    $Global:TerminationLoop = $true
    
    # Allowance for the application to stabilize before checking on status.
        $playbackSeconds = 15
        $playbackString = ((Get-ConfigValue -Section WORKLOAD_CONSTANT -Key 'PLAYBACK_STABILIZATION_SECONDS' -Default '15'))
        if (-not ([int]::TryParse($playbackString, [ref]$playbackSeconds))) {
            throw ("Error converting config [WORKLOAD_CONSTANT][PLAYBACK_STABILIZATION_SECONDS] value '{0}' to [int]." -f $playbackString)
        }
    Set-Variable -Name PlaybackStabilizationSeconds -Value $playbackSeconds -Option Constant

    # Netflix pre-load stabilization time in seconds before playing the video
    $stabilizationSeconds = 10
        $stabilizationString = (Get-ConfigValue -Section SCENARIO_AUTOMATION -Key NETFLIX_STABILIZATION_SECONDS -Default '10')
        if (-not ([int]::TryParse($stabilizationString, [ref]$stabilizationSeconds))) {
            throw ("Error converting config [SCENARIO_AUTOMATION][NETFLIX_STABILIZATION_SECONDS] value '{0}' to [int]." -f $stabilizationString)
        }
    Set-Variable -Name NetflixStabilizationSeconds -Value $stabilizationSeconds -Option Constant

    #*****************************************************************
    #
    #               AXE setup
    #
    #*****************************************************************

    # AXE environment and framework
    try {
        $AXEResultsPath = (Get-Item env:\AssessmentResultsPath).value
        $AXETempPath = (Get-Item env:\AssessmentTempPath).value
        $AxeBinPath = (Get-Item env:\AssessmentAxeBinPath).value
        $AxeCoreNet = "$AxeBinPath\Microsoft.Assessments.Core.dll"
    }
    catch {
        throw "This script only works when run from the Axe framework;{0}:{1}" -f ($_.InvocationInfo.ScriptName),($_.InvocationInfo.ScriptLineNumber)
    }
    
    $Global:WorkloadResultsPath = [string] (Resolve-Path -Path "$AXEResultsPath\..\..\..\..\000_EnergyEfficiency\results")
    
    # load up AXE framework from AXE DLL
    # initialize Runtime and create Logger helper object
    #
    "Load AXE..." | Out-File "$Global:WorkloadResultsPath\log.txt" -Force -Append
    $boolRunningUnderAxe = Initialize-AxeLogger($AxeCoreNet)
    
    
    #*****************************************************************
    #
    #                Playback source selection
    #
    #*****************************************************************

    $Playback = Select-PlaybackSource  -PlaybackSource         $PlaybackSource `
                                       -CustomPlaybackSource   $CustomPlaybackSource `
                                       -Architecture           $arch                                       

    # check system resolution and log it
    if ( $PlaybackSource -eq ([int]([PlaybackSourceEnum]::DEFAUL_LOCAL_AUTOSELECT)) ) {
        "SYSRES=$(Get-ScreenResolution)" | Out-File "$Global:WorkloadResultsPath\resolution.log" -ErrorAction SilentlyContinue -Force
    }
    
    #*****************************************************************
    #
    #                Workload preparation
    #
    #*****************************************************************

    "Clear E3 SRUM db..." | Log-Info
    DisableWow64FsRedirection
    Move-E3logs -ResultsPath $WorkloadResultsPath
    RevertWow64FsRedirection

    "Check UWP Video Player Policy..." | Log-Info
    DisableWow64FsRedirection
    Set-UwpVideoPlayerPolicy
    RevertWow64FsRedirection


    "Install applicable playback packages..." | Log-Info
    Install-PlaybackPackage $Playback.Workload $arch
    
    "Launch applicable streaming media to allow time for library sync..." | Log-Info
    Start-StreamingWorkload -PlaybackSource $PlaybackSource
    
    "Stop playback processes..." | Log-Info
    Stop-Workload
    
    #*****************************************************************
    #
    #                Tracing and termination timers
    #
    #*****************************************************************

    # if PlaybackStartup trace is selected then setup timers
    #
    if ($StartupTrace){ 
        # start tracing
        "Start tracing for startup..." | Log-Info

        wpr -start "$Global:WPRP_FILE!MediaPlaybackProfile.verbose" -filemode
        if ($LASTEXITCODE -ne 0) {throw "Error starting wpa profile $WPRP_FILE!MediaPlaybackProfile.verbose"}

        # set timer to stop tracing after X seconds
        "Setting startup:stop timer..." | Log-Info
        $Timer_StartupTrace = New-Object Timers.Timer
        "Set interval ($StartupTraceDurationInSeconds) and autoreset..." | Log-Info
        $Timer_StartupTrace.Interval = $StartupTraceDurationInSeconds*1000
        $Timer_StartupTrace.AutoReset = $false

        # define trigger action to stop tracing
        $Action_StartupTrace = {
            if ((wpr -status) -match "not recording") {

                $Global:axeLogger.LogErrorCode(-1,"Attempting to stop wpr when not running; Action_StartupTrace")
            }
            else {
                wpr -stop "$Global:WorkloadResultsPath\playback_startup.etl" "playback startup"
            }
        }

        # register event/action and start timer
        "Register startup:stop timer..." | Log-Info
        Register-ObjectEvent -InputObject $Timer_StartupTrace -EventName elapsed -SourceIdentifier Timer_StartupTrace -Action $Action_StartupTrace 

        "Start startup:stop timer..." | Log-Info
        $Timer_StartupTrace.start()
    }

    # if PlaybackSteadyState trace is selected then setup timers
    #
    if ($SteadyStateTrace) {    
        "Setting steady:start timer..." | Log-Info
         # set timer to start tracing after X minutes
        $Timer_SSTrace_Start = New-Object Timers.Timer
        "Set interval ($SteadyStateTraceDelayInMinutes) and autoreset..." | Log-Info
        $Timer_SSTrace_Start.Interval = $SteadyStateTraceDelayInMinutes*60*1000
        $Timer_SSTrace_Start.AutoReset = $false

        # define trigger action to start tracing
        $Action_SSTrace_Start = { 

            # start tracing
            # cancel any ETL tracing that might be going on
            if (-not ((wpr -status) -match "not recording")) {
                 wpr -cancel
            }
            
            wpr -start "$Global:WPRP_FILE!MediaPlaybackProfile.verbose" -filemode

            # set timer to stop tracing after X seconds
            $Timer_SSTrace_Stop = New-Object Timers.Timer
            $Timer_SSTrace_Stop.Interval = $Global:SteadyStateTraceDurationInSeconds*1000
            $Timer_SSTrace_Stop.AutoReset = $false

            # define trigger action to stop tracing
            $Action_SSTrace_Stop = {
                
                if ((wpr -status) -match "not recording") {
                    $Global:axeLogger.LogErrorCode(-1,"Attempting to stop wpr when not running; Action_SSTrace_Stop")
                }
                else {
                    wpr -stop "$Global:WorkloadResultsPath\playback_steadystate.etl" "playback steady state"
                }
            }

            # register event/action and start timer
            Register-ObjectEvent -InputObject $Timer_SSTrace_Stop -EventName elapsed -SourceIdentifier Timer_SSTrace_Stop -Action $Action_SSTrace_Stop 
            $Timer_SSTrace_Stop.start()
        } 

        "Register and start steady:start timer..." | Log-Info
        # register event/action and start timer
        Register-ObjectEvent -InputObject $Timer_SSTrace_Start -EventName elapsed -SourceIdentifier Timer_SSTrace_Start -Action $Action_SSTrace_Start 
        $Timer_SSTrace_Start.start()
    }

    if ($FiniteDuration) {
        # set timer to stop workload after X minutes
        $Timer_FiniteDuration = New-Object Timers.Timer
        $Timer_FiniteDuration.Interval = $Minutes*60*1000
        $Timer_FiniteDuration.AutoReset = $false

        # define trigger action to gracefully exit AXE signal processing loop
        $Action_FiniteDuration = {
            $Global:TerminationLoop = $false
        } 
 
        # register event/action and start timer
        Register-ObjectEvent -InputObject $Timer_FiniteDuration -EventName elapsed -SourceIdentifier Timer_FiniteDuration -Action $Action_FiniteDuration 
        $Timer_FiniteDuration.start()
    }
    #*****************************************************************
    #
    #                Actual workload!
    #
    #*****************************************************************

    "Start collecting System Timer Resolution per second..." |log-info
    if ($CaptureClockResolution)
    {
        "Clock resolution job is enabled. Capturing clock resolution" | Log-Info
        $OutputFile = "$WorkloadResultsPath\clockres.csv"
        try {
            $Clock = Start-Job -ScriptBlock $ClockResolutionScriptBlock -ArgumentList @($OutputFile) -Name "ClockResolutionJob"
        }
        catch {throw ("Error starting clock resolution: {0}" -f $_)}
    }
    else
    {
        "Clock resolution job is disbaled" | Log-Info
    }

    # prepare to play Netflix
    if ($Playback.Workload -eq 'NETFLIX') {
        Start-Executable $Playback.ExePath '-appuid "4DF9E0F8.Netflix_mcm4njqhnhss8!Netflix.App"'
        Start-Sleep -Seconds $NetflixStabilizationSeconds
    }
    
    if ($PlaybackSource -eq [PlaybackSourceEnum]::DEFAULT_STREAMING_VIDEO)
    {
        ### Buy in Windows Store the default content to be streamed
        "Adding the default video to be streamed to user's library" | Log-Info
        $SearchString = Get-ConfigValue -Section 'STREAMING' -Key 'DEFAULT_SEARCH_VIDEO' -Default 'Remaking the Legend'
        Add-StreamingContent -VideoSearchString $SearchString -Playback $Playback
    }
	
	if ($PlaybackSource -eq [PlaybackSourceEnum]::DEFAULT_STREAMING_AUDIO) {
        Configure-GrooveRepeat
	}
    
    # start playing the video
    ("Launch playback source: ExePath={0} ArgumentList={1}..." -f $Playback.ExePath, $Playback.ArgumentList[0] ) | Log-Info
    try {
        $OutPut = Start-Executable $Playback.ExePath $Playback.ArgumentList
    }
    catch {throw ("Error starting video playback. Please check Log file.")}
    finally {
        "ScenarioAutomationTool results..." | Log-Info
        $OutPut | Log-Info
    }
 
    # let app stabilize    
    Start-Sleep -Seconds $PlaybackStabilizationSeconds

    #*****************************************************************
    #
    #                AXE termination signal handling
    #
    #*****************************************************************

    # check if AXE tried to cancel the assessment as the job end condition is met
    if ( $boolRunningUnderAxe ) {
        while(($Global:TerminationLoop) -and (-not ($Global:axeSupport.CancelEvent.WaitOne( 5 * 1000 )) )) {}
    }    
    "Playback completed..." | Log-Info
    
    # stop playback handling process
    "Stop playback processes..." | Log-Info
    Stop-Workload
}
catch
{

    #*****************************************************************
    #
    #                Handle exceptions
    #
    #*****************************************************************

    "$_" | Log-Info

    if ( $Global:axeLogger ) {
        $Global:axeLogger.LogErrorCode( $ErrorCode, "$_" )
    }

    # Write the exception details to errors XML file
    $xmlErrorFile = "$AXEResultsPath\ErrorsAndWarnings.xml"
    Log-ToXmlErrorFile -XmlErrorFile $xmlErrorFile -ErrorCode $_.Exception.Hresult -ErrorMessage $_.Exception.Message
    $message = $_.Exception.Message + [Environment]::NewLine + $_.InvocationInfo.PositionMessage
    $message | Log-Info
    
    throw $_        # Revisit to check if rethrow is needed
}
finally {

    #*****************************************************************
    #
    #                Workload cleanup
    #
    #*****************************************************************

    # dump E3 logs
    $_cmdletExist = Get-Command DisableWow64FsRedirection; if ( $_cmdletExist ) { DisableWow64FsRedirection };
    Write-AuxWorkloadLogs -ResultsPath $WorkloadResultsPath
    $_cmdletExist = Get-Command RevertWow64FsRedirection ; if ( $_cmdletExist ) { RevertWow64FsRedirection };

    # cancel all timer events
    #
    Unregister-Event Timer_FiniteDuration -ErrorAction SilentlyContinue
    Unregister-Event Timer_StartupTrace -ErrorAction SilentlyContinue
    Unregister-Event Timer_SSTrace_Start -ErrorAction SilentlyContinue
    Unregister-Event Timer_SSTrace_Stop -ErrorAction SilentlyContinue

    # cancel any ETL tracing that might be going on
    if (-not ((wpr -status) -match "not recording")) { wpr -cancel }
    
    # stop playback handling process
    Stop-Workload

    # stop system timer resolution script
    if ($CaptureClockResolution)
    {
        "Job details" | Log-Info
        Get-Job | Out-String | Log-Info
        "Removing job with ID " + $Clock.Id + " and name " + $Clock.Name | Log-Info
        Remove-Job $Clock.Id -Force -ErrorAction SilentlyContinue
    }

    # We should always properly dispose of this guy.
    #
    if ( $Global:axeSupport ) {
        $Global:axeSupport.Dispose()
    }
}
