# (c)2018 Microsoft Corporation.  All rights reserved.  This document is provided "as-is." Information and views expressed in this document,
# including URL and other Internet Web site references, may change without notice. You bear the risk of using it. 
# This document does not provide you with any legal rights to any intellectual property in any Microsoft product.
# You may copy and use this document for your internal, reference purposes. You may modify this document for your internal purposes
#
################################################
#  CQD POWERSHELL ##############################
#  JUSTW/SETHHA                                #
#  QUERY CQD DIRECT FROM POWERSHELL AND EXPORT #
#                                              #
################################################

[CmdletBinding()]
param(
  # Non Dynamic params go here #
)
###############################	
#  BEGIN JWT DECODE FUNCTIONS #
###############################
function Convert-64String ([string]$datai)
{
  $datai = $datai.Replace('-','+').Replace('_','/')
  switch ($datai.Length % 4)
  {
    0 { break }
    2 { $datai += '==' }
    3 { $datai += '=' }
    default { throw New-Object ArgumentException ('datai') }
  }
  return [System.Convert]::FromBase64String($datai)
}

function Decode-JWT ([string]$rawToken)
{
  $Tokenparts = $rawToken.Split('.');
  $headers = [System.Text.Encoding]::UTF8.GetString((Convert-64String $Tokenparts[0]))
  $claims = [System.Text.Encoding]::UTF8.GetString((Convert-64String $Tokenparts[1]))
  $signature = (Convert-64String $Tokenparts[2])

  $customObject = [pscustomobject]@{
    headers = ($headers | ConvertFrom-Json)
    claims = ($claims | ConvertFrom-Json)
    signature = $signature
  }

  #Write-Verbose -Message ("JWT`r`n.headers: {0}`r`n.claims: {1}`r`n.signature: {2}`r`n" -f $headers,$claims,[System.BitConverter]::ToString($signature))
  return $customObject
}

function Get-JWTData
{
  [CmdletBinding()]
  param
  (
    # Param1 help description
    [Parameter(Mandatory = $true)]
    [string]$Token,
    [switch]$Recurse
  )

  if ($Recurse)
  {
    $decoded = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Token))
    $DecodedJwt = Decode-JWT -rawToken $decoded
  }
  else
  {
    $DecodedJwt = Decode-JWT -rawToken $Token
  }
  #Write-Host("Token Values") -ForegroundColor Green
  #Write-Host ($DecodedJwt | Select headers,claims | ConvertTo-Json)
  return $DecodedJwt
}
#############################
#  END JWT DECODE FUNCTIONS #
#############################



###############################	
#  BEGIN AAD AUTH FUNCTIONS   #
###############################


####Function to Get the JWT Token VIA OAuth
<#
function Get-CQDToken ([string]$client_id)
{

  if ($CQDVer -eq "V3")
  {
  $CQDUri = "https://cqd.teams.microsoft.com/spd/"
  $V3Token = $true
  }
  else {
    $CQDUri = "https://cqd.lync.com/spd/"
    $V2Token = $true
  }

  Add-Type -AssemblyName System.Web
  $resourceUrl = $WebResource
  $redirectUrl = $CQDUri
  $nonce = [guid]::NewGuid().GUID
  $url = "https://login.microsoftonline.com/common/oauth2/authorize?response_type=token&redirect_uri=" +[System.Web.HttpUtility]::UrlEncode($redirectUrl) +"&client_id=$client_id" +"&prompt=login" + "&nonce=$nonce" + "&resource=" + [System.Web.HttpUtility]::UrlEncode($WebResource)


  Add-Type -AssemblyName System.Windows.Forms

  $form = New-Object -TypeName System.Windows.Forms.Form -Property @{ Width = 440; Height = 640 }
  $web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{ Width = 420; Height = 600; Url = ($url) }
  $DocComp = {
    $Global:uri = $web.Url.AbsoluteUri
    #Write-Host $Global:uri
    if ($Global:Uri -match "error=[^&]*|access_token=[^&]*") { $form.Close() }
  }

  $web.ScriptErrorsSuppressed = $true
  $web.Add_DocumentCompleted($DocComp)
  $form.Controls.Add($web)
  $form.Add_Shown({ $form.Activate() })
  $form.ShowDialog() | Out-Null



  $Script:TokenLifeTime = [Web.HttpUtility]::ParseQueryString(($web.Url -replace '^.*?(expires_in.+)$','$1'))['expires_in']
  #Write-Host $TokenLifeTime
  $Script:Token = [Web.HttpUtility]::ParseQueryString(($web.Url -replace '^.*?(access_token.+)$','$1'))['access_token']
  # Write-Host $Token
  
  
  return ('Bearer {0}' -f $Script:Token)
 
}
#>

#<#
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
$egurkhaPath=($scriptPath.Substring(0,$scriptPath.ToLower().IndexOf('egurkha')+7)).Trim()
$logDir=$egurkhaPath+'\agent\MTM\logs'
if(!(Test-Path -Path $logDir)){
    $null=New-Item -ItemType directory -Path $logDir
}
$LogFile = $logDir+"\MTMCallQlityReport.log"
$LogFile1 = $logDir+"\MTMCallQlityReport1.log"
#>

Function Write-Log {
	Param ([string]$string,[String] $debug)
    
    if($debug -eq 'true'){
        [string]$date = Get-Date -Format G
        ( "[" + $date + "] -  [O365]  -  " + $string ) | Out-File -FilePath $LogFile -Append
        if ([System.IO.File]::Exists($LogFile) -and (Get-Item $LogFile).length -gt 2mb) {  #if the size of file is greater than 1MB 
            if([System.IO.File]::Exists($LogFile1)){  #if logfile1 already exists, delete logfile1 
                Remove-Item $LogFile1 
            } 
            Rename-Item $LogFile $LogFile1   
        }
    }
}

#Function to connect to CQD Via AzureAD
function Connect-CqdOnline {
    Param ([Parameter(Mandatory = $false)]
    [String]$enableDebug)

  if ($CQDVer -eq "V3")
  {
    $UriVar = "https://cqd.teams.microsoft.com/repository/clientconfiguration"
  }
  else {
    $UriVar = "https://repository.cqd.lync.com/clientconfiguration"
  }

 if ($CQDVer -eq "V3")
  {
  $CQDUri = "https://cqd.teams.microsoft.com/spd/"
  $V3Token = $true
  }
  else {
    $CQDUri = "https://cqd.lync.com/spd/"
    $V2Token = $true
  }

  Write-Log -string ("CQDUri --->> "+$CQDUri+" V3Token -->> "+$V3Token+" V2Token -->> "+$V2Token) -debug $enableDebug
  $ClientConfigUri =   $UriVar
  $Script:Configuration = Invoke-RestMethod -Uri $ClientConfigUri -Method Get -SessionVariable WebSession -UserAgent "CQDPowerShell V2.0"
  $client_id=$Configuration.AuthWebAppClientId
  $Script:WebResource = $Configuration.AuthLoginResource
  Add-Type -AssemblyName System.Web
  $resourceUrl = $WebResource
  $redirectUrl = $CQDUri
  $nonce = [guid]::NewGuid().GUID
  $url = "https://login.microsoftonline.com/common/oauth2/authorize?response_type=token&redirect_uri=" +[System.Web.HttpUtility]::UrlEncode($redirectUrl) +"&client_id=$client_id" +"&prompt=login" + "&nonce=$nonce" + "&resource=" + [System.Web.HttpUtility]::UrlEncode($WebResource)
  Write-Log -string ("Request URL1 --->> "+$url) -debug $enableDebug

  $webrequest=Invoke-WebRequest -Uri $url -SessionVariable websession -UseBasicParsing
  $cookies = $websession.Cookies.GetCookies($url) 
  $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
  $session.Cookies.Add($cookies);
  $hpgReqId=$webrequest.Headers.'x-ms-request-id'
  $dev=$webrequest.Content
  Write-Log -string ("Request URL1 response content --->> "+$dev) -debug $enableDebug
  foreach($in in $dev){    
    $flowNum=$in.IndexOf("`"sFT`":`"")+7
    $flowtoken=$in.Substring($flowNum)
    $ftSecNum=$flowtoken.IndexOf("`",`"")
    $flowtoken=$flowtoken.Substring(0,$ftSecNum)
    $ctxNum=$in.IndexOf("`"sCtx`":`"")+8
    $ctxToken=$in.Substring($ctxNum)
    $ctxSecNum=$ctxToken.IndexOf("`",`"")
    $ctxToken=$ctxToken.Substring(0,$ctxSecNum)
    $canaryNum=$in.IndexOf(",`"canary`"")+11
    $canary=$in.Substring($canaryNum)
    $cnrySecNum=$canary.IndexOf("`",`"")
    $canary=$canary.Substring(0,$cnrySecNum)  
  }

  $logUrl='https://login.microsoftonline.com/common/login'
$logParam=@{
"login"=$userName;
"loginfmt"=$userName;
"passwd"=$password;
"canary"=$canary;
"ctx"=$ctxToken;
"hpgrequestid"=$hpgReqId;
"flowToken"=$flowtoken;
}
Write-Log -string ("Request URL2 (logUrl) --->> "+$logUrl) -debug $enableDebug
Write-Log -string ("Request URL2 (logUrl) input params --->> ") -debug $enableDebug
foreach($key in $logParam.Keys){
    if($key -ne 'passwd' -and $key -ne 'login'){
        Write-Log -string ($key+" -- "+$logParam[$key]) -debug $enableDebug
    }
}

  $req1=Invoke-WebRequest -Uri $logUrl -Method POST -Body $logParam  -WebSession $session -UseBasicParsing
  #$req1=Invoke-WebRequest -Uri $logUrl -Method POST -Body $logParam  -Headers @{"Referer"=$url} -WebSession $session -UseBasicParsing
  $psgid=$req1.Headers.'x-ms-request-id'
  $loginDev=$req1.Content
  $tt=$req1.BaseResponse.ResponseUri.Fragment
  Write-Log -string ("URL2 Response - token (logUrl) --->> "+$tt) -debug $enableDebug
  if($tt -eq $null -or $tt -eq ''){
    Write-Log -string ("URL2 Response - Content (logUrl) --->> "+$loginDev) -debug $enableDebug
  foreach($lgn in $loginDev){
    $lgflwtokenNum=$lgn.IndexOf("`"sFT`":`"")+7
    $lgflwtoken=$lgn.Substring($lgflwtokenNum)
    $lgFlwSecNum=$lgflwtoken.IndexOf("`",`"")
    $lgflwtoken=$lgflwtoken.Substring(0,$lgFlwSecNum)
    $ctxtknNum=$lgn.IndexOf("`"sCtx`":`"")+8
    $ctxtkn=$lgn.Substring($ctxtknNum)
    $secNum=$ctxtkn.IndexOf("`",`"")
    $ctxtkn=$ctxtkn.Substring(0,$secNum)
    $canaryNum=$lgn.IndexOf(",`"canary`"")+11
    $canary=$lgn.Substring($canaryNum)
    $CnrySecNum=$canary.IndexOf("`",`"")
    $canarytkn=$canary.Substring(0,$CnrySecNum)
  }

  $kmsiUrl='https://login.microsoftonline.com/kmsi'
$kmsiParam=@{
"ctx"=$ctxtkn;
"hpgrequestid"=$psgid;
"flowToken"=$lgflwtoken;
"canary"=$canarytkn;
}
    Write-Log -string ("Request URL3 (kmsiUrl) --->> "+$kmsiUrl) -debug $enableDebug
    Write-Log -string ("Request URL3 (kmsiUrl) input params --->> ") -debug $enableDebug
    foreach($key in $kmsiParam.Keys){
        Write-Log -string ($key+" -- "+$kmsiParam[$key]) -debug $enableDebug
    }
  $kmsiRes=Invoke-WebRequest -Uri $kmsiUrl -Method POST -Body $kmsiParam  -WebSession $session -UseBasicParsing
  $tt=$kmsiRes.BaseResponse.ResponseUri.Fragment
  }
  $token=$tt.ToString()
  Write-Log -string ("URL3 Response - token (kmsiUrl) --->> "+$token) -debug $enableDebug
  $toknNum=$token.IndexOf("access_token=")+13
  $accessToken=$token.Substring($toknNum)
  $Script:Token=$accessToken.Substring(0,$accessToken.IndexOf("&token_type="))

  $Script:AADBearerToken= ('Bearer {0}' -f $Script:Token)

  #$Script:AADBearerToken = Get-CQDToken $Configuration.AuthWebAppClientId
  $Script:WebSession = $WebSession
  $Script:RepositoryApiBaseUrl = $Script:Configuration.RepositoryApiBaseUrl
  $WebSession.headers.Add('Authorization',$AADBearerToken)
  $ProvisioningStatus = Invoke-RestMethod -Uri ('{0}tenant/provision' -f $RepositoryApiBaseUrl) -Method Get -WebSession $Script:WebSession -UserAgent "CQDPowerShell V2.0"
  if ($ProvisioningStatus.Status -ne 'Provisioned') {
    Write-Log -string ("CQD Not Provisioned for TenantId {0}") -debug $enableDebug
    if($AADBearerToken -eq $null -or $AADBearerToken -eq ''){
        throw ('CQD Not Provisioned for TenantId {0}. Stopping...' -f $ProvisioningStatus.TenantId)
    }
  }
  $Script:DataServiceBaseUrl = Invoke-RestMethod -Uri ('{0}tenant/dataservice' -f $Script:RepositoryApiBaseUrl) -WebSession $Script:WebSession -Method Get -UserAgent "CQDPowerShell V2.0"
  
}
Export-ModuleMember -Function Connect-CqdOnline

#Function to validate if token exists or is valid and unexpired
function CheckToken

{
    Param (
    [Parameter(Mandatory = $false)]
    [String]$setDebug )
  #Check to see if Token already exists
  if ($Script:AADBearerToken -eq $null)
  {
    Connect-CqdOnline -enableDebug $setDebug
  }
  else

  {
    #Check Token
    $JWTDecoded = Get-JWTData $Script:Token
    $JWTI = $JWTDecoded.claims.iat
    $JWTE = $JWTDecoded.claims.exp
    $NowTimeUTC = Get-Date

    $origin = New-Object -Type DateTime -ArgumentList 1970,1,1,0,0,0,0
    $JWTIssuedTime = $origin.AddSeconds($JWTI)
    $JWTExpireTime = $origin.AddSeconds($JWTE)
    #Write-Host $JWTIssuedTime $JWTExpireTime

    if ($CQDVer -eq "V2")
    {

      if ($JWTDecoded.claims.aud -eq "https://cqd.lync.com")
      {  if ($NowTimeUTC.ToUniversalTime() -gt $JWTExpireTime)
        {
          Write-Host "Token Expired"
          Connect-CqdOnline -enableDebug $setDebug
        }
      }
      else 
      {
         write-host "Wrong Token, Expecting V2"
         Connect-CqdOnline -enableDebug $setDebug
      }
    } elseif ($CQDVer -eq "V3")
    {
      if ($JWTDecoded.claims.aud -eq "https://cqd.teams.microsoft.com")
      {  if ($NowTimeUTC.ToUniversalTime() -gt $JWTExpireTime)
        {
          Write-Host "Token Expired"
          Connect-CqdOnline -enableDebug $setDebug
        }
      }
      else {
        write-host "Wrong Token, Expecting V3"
        Connect-CqdOnline -enableDebug $setDebug
      }

    }

  


  }

}

###############################	
#  END AAD AUTH FUNCTIONS     #
###############################

#Function to get Array list of Dimensions available in CQD Cube
function Get-CQDDimensions
{
  $DimensionsCheck = GetParams "Dimensions"
  return $DimensionsCheck

}
Export-ModuleMember -Function Get-CQDDimensions

function Get-CQDMeasures
{
  $MeasuresCheck = GetParams "Measures"
  return $MeasuresCheck
}
Export-ModuleMember -Function Get-CQDMeasures

##Generate Dynamic List of CQD Measures and Dimensions	
function GetParams ([string]$ParamType,[String] $setDebug) {


  CheckToken -setDebug $setDebug

  $CubeRequest = Invoke-WebRequest -Uri ('{0}CubeStructure' -f $DataServiceBaseUrl) -WebSession $WebSession -Method Get -UserAgent "CQDPowerShell V2.0" -UseBasicParsing
  $List = ConvertFrom-Json $CubeRequest

  $D = $List.Dimensions
  $Meas = $List.Measurements
  $Dimensions = @()
  $Measures = @()
  foreach ($i in $D)
  {
    $Dimensions += $i.Category
    $Dimensions += $i.Attributes


  }

  foreach ($a in $Meas)
  {
    $Measures += $a.Category
    $Measures += $a.Attributes

  }

  if ($ParamType -eq "Dimensions")
  {
    $DimensionName = $Dimensions.DataModelName

    $Di = @()
    foreach ($O in $DimensionName)
    {

      $O = $O.Replace("[","")
      $O = $O.Replace("]","")
      $Di += $O
    }
    return $Di
  }
  elseif ($ParamType -eq "Measures")
  {
    $MeasureName = $Measures.DataModelName
    $Mi = @()
    foreach ($M in $MeasureName)
    {
      $M = $M.Replace("[","")
      $M = $M.Replace("]","")
      $Mi += $M
    }
    return $Mi
  }


}


#Function for handling start-end date
function ProcessDateRange
{

  $TS = New-TimeSpan $StartDate $EndDate
  if ($TS.Days -lt 0)
  {
    $DateErrorStartEnd = 'Error in Timerange. Confirm StartDate is BEFORE EndDate'
    Write-Host $DateErrorStartEnd
    throw $DateErrorStartEnd
  }
  $i = $TS.Days
if 
($StartDate -eq $EndDate)
{$DateList = @($EndDate)}
else
{	
$DateList = @($EndDate)
	do 
	{

	$DateList += $EndDate.AddDays(-$i)
	$i --

	}

	while ($i -ne 0)
}



	foreach ($D in $DateList)

	{

        $DateF += "[" +  $D.ToString('yyyy-MM-dd')  + "],"

	}

return $DateF
}
#Function to obtain days of week
function GetDaysofWeek
{

  foreach ($DofWT in $Weekdays)
  {

    $DofW += "[" + $DofWT + "],"

  }
  return $DofW
}



function BuildWeekFilter {
  param(
    [Parameter()] [array]$TimeInput
  )

  # Create list containing "start of weeks'" (Sun) for last 6 month
  $endDay = Get-Date
  $startDay = (Get-Date).AddMonths(-6)
  $cqdLastSixMonthWeeks = @()
  for ($i = $startDay; $i -lt $endDay; $i = $i.AddDays(1)) {
    if (($i.DayOfWeek).ToString() -eq "Sunday") {
      $cqdLastSixMonthWeeks += $i.ToString("yyyy-MM-dd")
    }
  }

  # Initialize variable that will keep final list of "start of weeks'"
  $cqdWeeks = @()

  # There are two cases:
  # 1. Month is provided as an input e.g. "09" for Sep. Must be in this format. length of 2
  # 2. Start of week is provided as an input e.g. "2017-09-18". Must be in this format. length of 10
  $TimeInput | ForEach-Object {
    if ($_.Length -eq 2) {
      $tempMonth = $_
      $cqdLastSixMonthWeeks | ForEach-Object {
        if ($_ -like "*-$($tempMonth)-*") { $cqdWeeks += $_ }
      }
    } elseif ($_.Length -eq 10) {
      $cqdWeeks += $_
    }
  }

  # Create hash table representing CQD Week filter
  $cqdFilterWeek = @{}

  $Caption = ""
  $Value = ""
  $cqdWeeks | ForEach-Object { $Caption += "$($_) | " }
  $cqdWeeks | ForEach-Object { $Value += "[$($_)]," }

  $cqdFilterWeek.Add("DataModelName","[AllStreams].[Week]")
  $cqdFilterWeek.Add("Caption",$Caption)
  $cqdFilterWeek.Add("Value",$Value)
  $cqdFilterWeek.Add("Operand",0)
  $cqdFilterWeek.Add("UnionGroup","")

  # Return Json object representing CQD Week filter
  return ($cqdFilterWeek | ConvertTo-Json)

}

#Function for creating UA Category Filter
function BuildUACategoryFilter {
  param(
    [Parameter()] [array]$UACategory
  )

  # Create hash table representing CQD UA Category filter
  $cqdFilterUACategory = @{}

  $Caption = ""
  $Value = ""
  $UACategory | ForEach-Object { $Caption += "$($_) | " }
  $Caption | ForEach-Object {
    $Length = $Caption.Trim().Length
    $Caption = $Caption.Trim().Substring(0,$Length - 1)
    $Caption = $Caption.Trim()
  }

  $UACategory | ForEach-Object { $Value += "[$($_)]," }
  $Value | ForEach-Object {
    $Length = $Value.Trim().Length
    $Value = $Value.Trim().Substring(0,$Length - 1)
    $Value = $Value.Trim()
  }

  $cqdFilterUACategory.Add("DataModelName","[AllStreams].[Second User Agent Category]")
  $cqdFilterUACategory.Add("Caption",$Caption)
  $cqdFilterUACategory.Add("Value",$Value)
  $cqdFilterUACategory.Add("Operand",0)
  $cqdFilterUACategory.Add("UnionGroup","")

  # Return Json object representing CQD UA Category filter
  return ($cqdFilterUACategory | ConvertTo-Json)
}

#Function for creating Media Type Filter
function BuildMediaTypeFilter {
  param(
    [Parameter()] [array]$MediaType
  )

  # Create hash table representing CQD Media Type filter
  $cqdFilterMediaType = @{}

  $Caption = ""
  $Value = ""
  $MediaType | ForEach-Object { $Caption += "$($_) | " }
  $Caption | ForEach-Object {
    $Length = $Caption.Trim().Length
    $Caption = $Caption.Trim().Substring(0,$Length - 1)
    $Caption = $Caption.Trim()
  }

  $MediaType | ForEach-Object { $Value += "[$($_)]," }
  $Value | ForEach-Object {
    $Length = $Value.Trim().Length
    $Value = $Value.Trim().Substring(0,$Length - 1)
    $Value = $Value.Trim()
  }

  $cqdFilterMediaType.Add("DataModelName","[AllStreams].[Media Type]")
  $cqdFilterMediaType.Add("Caption",$Caption)
  $cqdFilterMediaType.Add("Value",$Value)
  $cqdFilterMediaType.Add("Operand",0)
  $cqdFilterMediaType.Add("UnionGroup","")

  # Return Json object representing CQD Media Type filter
  return ($cqdFilterMediaType | ConvertTo-Json)
}

#Function for Server Pair Filter 
function BuildIsServerPairFilter {
  param(
    [Parameter()] [array]$IsServerPair
  )

  # Create hash table representing CQD Is Server Pair filter
  $cqdFilterIsServerPair = @{}

  $Caption = ""
  $Value = ""
  $IsServerPair | ForEach-Object { $Caption += "$($_) | " }
  $Caption | ForEach-Object {
    $Length = $Caption.Trim().Length
    $Caption = $Caption.Trim().Substring(0,$Length - 1)
    $Caption = $Caption.Trim()
  }

  $IsServerPair | ForEach-Object { $Value += "[$($_)]," }
  $Value | ForEach-Object {
    $Length = $Value.Trim().Length
    $Value = $Value.Trim().Substring(0,$Length - 1)
    $Value = $Value.Trim()
  }

  $cqdFilterIsServerPair.Add("DataModelName","[AllStreams].[Is Server Pair]")
  $cqdFilterIsServerPair.Add("Caption",$Caption)
  $cqdFilterIsServerPair.Add("Value",$Value)
  $cqdFilterIsServerPair.Add("Operand",0)
  $cqdFilterIsServerPair.Add("UnionGroup","")

  # Return Json object representing CQD Is Server Pair filter
  return ($cqdFilterIsServerPair | ConvertTo-Json)
}

#Function for Transport Filter (TCP,UDP)
function BuildTransportFilter {
  param(
    [Parameter()] [array]$Transport
  )

  # Create hash table representing CQD transport filter
  $cqdFilterTransport = @{}

  $Caption = ""
  $Value = ""
  $Transport | ForEach-Object { $Caption += "$($_) | " }
  $Caption | ForEach-Object {
    $Length = $Caption.Trim().Length
    $Caption = $Caption.Trim().Substring(0,$Length - 1)
    $Caption = $Caption.Trim()
  }

  $Transport | ForEach-Object { $Value += "[$($_)]," }
  $Value | ForEach-Object {
    $Length = $Value.Trim().Length
    $Value = $Value.Trim().Substring(0,$Length - 1)
    $Value = $Value.Trim()
  }

  $cqdFilterTransport.Add("DataModelName","[AllStreams].[Transport]")
  $cqdFilterTransport.Add("Caption",$Caption)
  $cqdFilterTransport.Add("Value",$Value)
  $cqdFilterTransport.Add("Operand",0)
  $cqdFilterTransport.Add("UnionGroup","")

  # Return Json object representing CQD transport filter
  return ($cqdFilterTransport | ConvertTo-Json)
}

#Function for building all Filters
function FilterCreation
{

  #Build MonthYear Array From Input

  foreach ($MonthYearI in $MonthYear)
  {
    $MonthYearParams += "[" + $MonthYearI + "],"

  }

  $Filters = New-Object system.Data.DataTable "Filters"
  $FilterName = New-Object system.Data.DataColumn FilterName,([string])
  $FilterValue = New-Object system.Data.DataColumn FilterValue,([string])
  $FilterCaption = New-Object system.Data.DataColumn FilterCaption,([string])
  $FilterOperand = New-Object system.Data.DataColumn FilterOperand,([string])
  $FilterUnionGroup = New-Object system.Data.DataColumn FilterUnionGroup,([string])

  #Add the Columns
  $Filters.columns.Add($FilterName)
  $Filters.columns.Add($FilterValue)
  $Filters.columns.Add($FilterCaption)
  $Filters.columns.Add($FilterOperand)
  $Filters.columns.Add($FilterUnionGroup)


  if ($Weekdays -ne $null)
  {
  $row = $Filters.NewRow()
  $row.FilterName = "AllStreams.Day Of Week" 
  $row.FilterValue = "$DaysofWeekList" 
  $row.FilterCaption = "NA" 
  $row.FilterOperand = "0" 
  $row.FilterUnionGroup = "" 
  $Filters.Rows.Add($row)
  }


  if ($MonthYear -ne $null)
  {
  #Create a row
  $row = $Filters.NewRow()
  $row.FilterName = "AllStreams.Month Year" 
  $row.FilterValue = "$MonthYearParams" 
  $row.FilterCaption = "NA" 
  $row.FilterOperand = "0" 
  $row.FilterUnionGroup = "" 
  $Filters.Rows.Add($row)
  }



  if ($StartDate -ne $null -and $EndDate -ne $null)

{

$EndDateV = ProcessDateRange
#Create a row
$row = $Filters.NewRow()
$row.FilterName = "AllStreams.Date"
$row.FilterValue = "$EndDateV" 
$row.FilterCaption = "NA" 
$row.FilterOperand = "0" 
$row.FilterUnionGroup = "" 
$Filters.Rows.Add($row)  

}

if ($Week -ne $null) {
	$WeekV = (BuildWeekFilter $Week) | ConvertFrom-Json
	$row = $Filters.NewRow()
	$row.FilterName = "AllStreams.Week"
	$row.FilterValue = $WeekV.Value
	$row.FilterCaption = "NA" 
	$row.FilterOperand = $WeekV.Operand 
	$row.FilterUnionGroup = $WeekV.UnionGroup 
	$Filters.Rows.Add($row)  
}

if ($UACategory -ne $null) {
	$UACategoryV = (BuildUACategoryFilter $UACategory) | ConvertFrom-Json
	$row = $Filters.NewRow()
	$row.FilterName = "AllStreams.Second User Agent Category"
	$row.FilterValue = $UACategoryV.Value
	$row.FilterCaption = "NA" 
	$row.FilterOperand = $UACategoryV.Operand 
	$row.FilterUnionGroup = $UACategoryV.UnionGroup 
	$Filters.Rows.Add($row)  
}

if ($MediaType -ne $null) {
	$MediaTypeV = (BuildMediaTypeFilter $MediaType) | ConvertFrom-Json
	$row = $Filters.NewRow()
	$row.FilterName = "AllStreams.Media Type"
	$row.FilterValue = $MediaTypeV.Value
	$row.FilterCaption = "NA" 
	$row.FilterOperand = $MediaTypeV.Operand 
	$row.FilterUnionGroup = $MediaTypeV.UnionGroup 
	$Filters.Rows.Add($row)  
}

if ($IsServerPair -ne $null) {
	$IsServerPairV = (BuildIsServerPairFilter $IsServerPair) | ConvertFrom-Json
	$row = $Filters.NewRow()
	$row.FilterName = "AllStreams.Is Server Pair"
	$row.FilterValue = $IsServerPairV.Value
	$row.FilterCaption = "NA" 
	$row.FilterOperand = $IsServerPairV.Operand 
	$row.FilterUnionGroup = $IsServerPairV.UnionGroup 
	$Filters.Rows.Add($row)  
}

if ($Transport -ne $null) {
	$TransportV = (BuildTransportFilter $Transport) | ConvertFrom-Json
	$row = $Filters.NewRow()
	$row.FilterName = "AllStreams.Transport"
	$row.FilterValue = $TransportV.Value
	$row.FilterCaption = "NA" 
	$row.FilterOperand = $TransportV.Operand 
	$row.FilterUnionGroup = $TransportV.UnionGroup 
	$Filters.Rows.Add($row)  
}

if ($null -ne $CustomFilter)
{
foreach ($F in $CustomFilter)
	{
		$row = $Filters.NewRow()
		$row.FilterName = $F.FName
		$row.FilterValue = $F.FValue
		$row.FilterCaption = ""
		$row.FilterOperand = $F.Op
		$row.FilterUnionGroup = ""
		$Filters.Rows.Add($row)

	}
}

  return $Filters

}


function Get-CQDData {
  [CmdletBinding()]
  param(




    [Parameter(Mandatory = $false,Position = 3)]
    [array]$Transport = $null,

    [Parameter(Mandatory = $false,Position = 4)]
    [string]$OutPutFilePath = $null,

    [Parameter(Mandatory = $false,Position = 5)]
    [nullable[datetime]]$StartDate = $null,

    [Parameter(Mandatory = $false,Position = 6)]
    [nullable[datetime]]$EndDate = $null,

    [Parameter(Mandatory = $false,Position = 7)]
    [array]$MonthYear = $null,

    [Parameter(Mandatory = $false,Position = 8)]
    [array]$Weekdays = $null,

    [Parameter(Mandatory = $True,Position = 9)]
    [ValidateSet("CSV","DataTable")]
    [string]$OutPutType,

    [Parameter(Mandatory = $false,Position = 10)]
    [array]$Week = $null,

    [Parameter(Mandatory = $false,Position = 11)]
    [array]$UACategory = $null,

    [Parameter(Mandatory = $false,Position = 12)]
    [array]$MediaType = $null,

    [Parameter(Mandatory = $false,Position = 13)]
	[ValidateSet("Client : Client","Client : Server","Server : Server")]
    [array]$IsServerPair = $null,

    [Parameter(Mandatory = $false,Position = 14)]
    [bool]$ShowQuery = $false,
	
	[Parameter(Mandatory=$false,Position=15)]
  [switch] $OverWriteOutput,
  
  [Parameter(Mandatory=$false,Position=16)]
  [pscustomobject] $CustomFilter,
  
  [Parameter(Mandatory=$false,Position=17)]
  [switch] $LargeQuery,

  [Parameter(Mandatory=$false,Position=18)]
  [ValidateSet("V2","V3")]
  [string]$CQDVer = "V2",

  [Parameter(Mandatory = $True,Position = 19)]
  [string]$username,

  [Parameter(Mandatory = $True,Position = 20)]
  [string]$password,

  [Parameter(Mandatory = $True,Position = 21)]
  [string]$debugLog
  
  )


  dynamicparam {


    # Set the dynamic parameters' name for Dimensions and Measures
    $DimensionParameter = 'Dimensions'
    $MeasuresParameter = 'Measures'


    # Create the dictionary 
    $RuntimeParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

    # Create the collection of attributes
    $DimensionAttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
    $AttributeCollectionMeasures = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

    # Create and set the parameters' attributes
    $DimensionParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
    $DimensionParameterAttribute.Mandatory = $true
    $DimensionParameterAttribute.Position = 1

    $MeasuresParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
    $MeasuresParameterAttribute.Mandatory = $true
    $MeasuresParameterAttribute.Position = 2

    # Add the attributes to the attributes collection
    $DimensionAttributeCollection.Add($DimensionParameterAttribute)
    $AttributeCollectionMeasures.Add($MeasuresParameterAttribute)

    # Generate and set the ValidateSet 
    $DimensionarrSet = GetParams "Dimensions" -setDebug $debugLog
    $MeasuresarrSet = GetParams "Measures" -setDebug $debugLog
    $DimensionsValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute ($DimensionarrSet)
    $MeasuresValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute ($MeasuresarrSet)

    # Add the ValidateSet to the attributes collection
    $DimensionAttributeCollection.Add($DimensionsValidateSetAttribute)
    $AttributeCollectionMeasures.Add($MeasuresValidateSetAttribute)

    # Create and return the dynamic parameter
    $DimensionsRuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ($DimensionParameter,[array],$DimensionAttributeCollection)
    $MeasuresRuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter ($MeasuresParameter,[array],$AttributeCollectionMeasures)

    $RuntimeParamDictionary.Add($DimensionParameter,$DimensionsRuntimeParameter)
    $RuntimeParamDictionary.Add($MeasuresParameter,$MeasuresRuntimeParameter)
    return $RuntimeParamDictionary

  }

  begin {
    # Bind the parameter to a friendly variable
    $Dims = $PsBoundParameters[$DimensionParameter]
    $Measures = $PsBoundParameters[$MeasuresParameter]



  }

  process {

    try {
      Write-Host "Working...."

      #Check for Null CSV Path if CSV Chosen output
      if ($OutPutType -eq "CSV" -and $OutPutFilePath -eq "")
      {
        while ($OutPutFilePath -eq "" -or $OutPutFilePath -eq $null)
        {
          Write-Host "You must specify an output path when using CSV output type"
          Write-Host "Enter CSV Output Path:" -NoNewline
          $OutputFilePath = Read-Host
        }
        if ($OutputFilePath -eq $null) { Exit-PSSession }
      }

      $DimFixed = @()
      $DimSimple = @()
      $MeasureFixed = @()
      $MeasureSimple = @()
      ###Fix Dimension Brackets
      foreach ($DimShort in $Dims)
      {
        $Dim1 = $DimShort.Split(".")
        $DimSimple += $Dim1[1].ToString()
        $Dim1[0] = "[" + $Dim1[0] + "]"
        $Dim1[1] = "[" + $Dim1[1] + "]"
        $DimFixed += $Dim1[0] + "." + $Dim1[1]
      }

      foreach ($MeasureShort in $Measures)
      {
        $Measure1 = $MeasureShort.Split(".")
        $MeasureSimple += $Measure1[1].ToString()
        $Measure1[0] = "[" + $Measure1[0] + "]"
        $Measure1[1] = "[" + $Measure1[1] + "]"
        $MeasureFixed += $Measure1[0] + "." + $Measure1[1]
      }

      $Dims = $DimFixed
      $Measures = $MeasureFixed




      if ($Weekdays -ne $null)
      {
        $DaysofWeekList = GetDaysofWeek
      }
      else
      {
        $Weekdays = $null
      }







      $Filters = FilterCreation


      ##Dimension and Measure Arrays##
	 $DimensionsArray = $Dims 
	 $MeasurementsArray = $Measures 


      ##Create Results Table##
      $DataResults = New-Object system.Data.DataTable "DataResults"
      foreach ($DimensionVar in $DimSimple)
      {
        $Column = New-Object system.Data.DataColumn $DimensionVar,([string])
        $DataResults.columns.Add($Column)
      }

      foreach ($MeasureVar in $MeasureSimple)

      {
        $Column = New-Object system.Data.DataColumn $MeasureVar,([string])
        $DataResults.columns.Add($Column)
      }

      $FilterRowArray = @()
      $DimensionRowArray = @()
      $MeasureRowArray = @()
      $AllArray = $DimSimple + $MeasureSimple


      ##Build Query Structure##

      #Start/End Query 
      $StartQuery = "{"
      $EndQuery = "}"

      #Filter Sections
$StartFilters = @'
"Filters":[
'@

#Dimension Sections
$StartDimensions = @'
"Dimensions":[
'@

#Measure Sections
$StartMeasures = @'
"Measurements":[
'@


###############Loop Arrays of Each Type

foreach ($Filter in $Filters)
{
$FLN = $Filter.FilterName
$FCN = $Filter.FilterCaption
$FVE = $Filter.FilterValue
$FOP = $Filter.FilterOperand
$UNGP = $Filter.FilterUnionGroup
$FLNSplit = $FLN.Split(".")
$FLNName = "[" + $FLNSplit[0] + "].[" + $FLNSplit[1] + "]" 
$FilterRowArray += @"
{"DataModelName":"$FLNName","Caption":"$FCN","Value":"$FVE","Operand":$FOP,"UnionGroup":"$UNGP"},
"@
}
$DimCount = $DimensionsArray.Count - 1
foreach ($DimensionLoop in $DimensionsArray)
{

$DimensionRowArray += @"
{"DataModelName":"$DimensionLoop"},
"@

}
$MeaCount = $MeasurementsArray.Count - 1
foreach ($MeasureLoop in $MeasurementsArray)
{

$MeasureRowArray +=  @"
{"DataModelName":"$MeasureLoop"},
"@

}
########Build Dynamic Query
if ($LargeQuery) 
{

  $Query = @"
  {
  
  $StartFilters
  $FilterRowArray
  
  ]
  ,
  $StartDimensions
  $DimensionRowArray
  ]
  ,
  $StartMeasures
  $MeasureRowArray
  ]
  ,
  LimitResultRowsCount:200000
  }
"@

}
else {
  
  $Query = @"
  {
  
  $StartFilters
  $FilterRowArray
  
  ]
  ,
  $StartDimensions
  $DimensionRowArray
  ]
  ,
  $StartMeasures
  $MeasureRowArray
  ]
  }
"@

}


      #Check to see if ShowQuery is true. Used to show JSON query for debugging queries.
      if ($ShowQuery -eq $true)
      {
        Write-Host "JSON Query:"
        Write-Host $Query
      }

      CheckToken -setDebug $debugLog
      $verb = "POST"
      $content = $Query
      $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
      $IRM = Invoke-RestMethod -Uri ('{0}RunQuery' -f $DataServiceBaseUrl) -WebSession $WebSession -Method Post -Body $Query -ContentType 'application/json' -UserAgent "CQDPowerShell V2.0" -Headers $headers


      $IRM2 = ConvertTo-Json -InputObject $IRM
      $JsonGarb = ConvertFrom-Json -InputObject $IRM2

      $QueryExecutionTime = $JsonGarb.ExecutionTime
      $ErrorType = $JsonGarb.ErrorType
      $ResultList = $JsonGarb.DataResult

      if ($ErrorType -ne 0)
      {
        Write-Host "Error Querying CQD with Error " $ErrorType
        if ($ErrorType -eq 6) { Write-Host "Invalid Query. Check the query and try again." }
        if ($ErrorType -eq 5) { Write-Host "Query scope too large. Reduce Dimensions, Measures and Timespan" }
        if ($ErrorType -eq 3) { Write-Host "Cube currently unavailable. Try again later." }
      }



      foreach ($ResultItem in $ResultList)
      {
        if (!$ResultItem)
        #if(1 -eq 2)
        {
          Write-Host "Warning: Result is Null:" $ResultItem

        }
        else
        {
          $ItemCount = $ResultItem.Count
          $FieldCount = $DimensionsArray.Count + $MeasurementsArray.Count
          #Create a row
          $Resultrow = $DataResults.NewRow()


          $i = 0

          do
          {

            $Resultrow.($AllArray[$i]) = $ResultItem.Item($i)
            $i++
          }
          while ($i -lt $AllArray.Count)

          $DataResults.Rows.Add($Resultrow)

        }

      }

      if ($OutPutType -eq "CSV")
      {
        if ($OverWriteOutput -eq $true)
			{
			$DataResults | Export-Csv -Path $OutPutFilePath -notypeinformation 
			}
			else
			{
			$DataResults | Export-Csv -Path $OutPutFilePath -notypeinformation -Append
			}
      }
      elseif ($OutPutType -eq "DataTable")
      {
        return $DataResults
      }

      Write-Host "Execution Complete. Query took " $QueryExecutionTime
    }
    catch [System.Net.WebException]
    {
      "Unable to complete query. Please reduce query scope or try again later."
    }
    catch [System.Management.Automation.ParameterBindingException]{ "Error Binding" }

    catch { "Error: $Error" }

  }

}
Export-ModuleMember -Function Get-CQDData


# SIG # Begin signature block
# MIIlcAYJKoZIhvcNAQcCoIIlYTCCJV0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAT2jUEXXKz4TKL
# JMzlJOKsW75YsMx/Sjoa4bGXYm6zdKCCHnIwggUwMIIEGKADAgECAhAECRgbX9W7
# ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa
# Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD
# ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l
# qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT
# eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH
# CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+
# bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo
# LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB
# yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow
# eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA
# AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK
# BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j
# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s
# DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS
# dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6
# r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo
# +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz
# sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq
# aGxEMrJmoecYpJpkUe8wggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0G
# CSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0
# IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5
# NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
# BAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQg
# Um9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvk
# XUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdt
# HauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu
# 34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0
# QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2
# kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM
# 1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmI
# dph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZ
# K37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72
# gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqs
# X40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyh
# HsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8E
# BTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAW
# gBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUH
# AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI
# KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz
# c3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAE
# CjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX
# 979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offy
# ct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3
# J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0
# d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6ts
# ds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQw
# ggYzMIIFG6ADAgECAhAImnSeuC3D7CfqZerb+DUxMA0GCSqGSIb3DQEBCwUAMHIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJ
# RCBDb2RlIFNpZ25pbmcgQ0EwHhcNMjEwNTI1MDAwMDAwWhcNMjQwNTI5MjM1OTU5
# WjBxMQswCQYDVQQGEwJVUzETMBEGA1UECBMKTmV3IEplcnNleTEPMA0GA1UEBxMG
# SXNlbGluMR0wGwYDVQQKExRlRyBJbm5vdmF0aW9ucywgSW5jLjEdMBsGA1UEAxMU
# ZUcgSW5ub3ZhdGlvbnMsIEluYy4wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
# AoICAQDEZQnSmuNt/FyaBkaN3skPE6zuzJIe9pDfAZHYpsPdplqB5IT6QbS0qQSx
# Didrxuc1WJj/MXg9ujRmRi0zMkiTQBlcNMy9ev+YS+YrEJuIkVMKkqSZnixrkFl8
# 7uKAi9w1JrTMgjZrmOJAkXLzmP5/0jgSBOj05x1V4g1eGBDbFnpOMQr6luGqZVe+
# vwMHbolyU5p53QvlWzv4JsbNm4qJ+DEvaLx3kUPOSFlJl90K0a0S8Tmkt6xdJZ+n
# NR128I1GKwg8YHuaD5GWfZe9RnLJpFRG2XHXWlh8n8o1gCRQIvbeueHfz+ZrG1K9
# 16KhgGPjEvK3umhAEb5vS7MKOBEAN7f+TQ22JXAkijRkSxTIMA2nskgiMx4/Ow4x
# jN8w/KLxyUDB8FhzZeFK5RxffIp/iSlEWYKVh8rkuy0ZBE1s42OmwIFMSBm7AR+R
# 05TSuZozLBkVUscSmalhl4/PszdWE35pXnqRH95+jx4PzUTlUNCqYmIZYqr+z2OF
# uRgfuDa6EvYxdv+5GyFUNI6QVG8ggP+X7IMmiAMGlp7fHai/li+NjxmjxEXS1e/J
# LSt+wO8A1nDuam3MefQGWbDmW6DcXYJUmk1vNvUeYAfqTTIUeZZgKhQA1sEHRhH+
# Tgm+BjZvK20XtxA8nKXtQj0K7gpMCDtE6YaTB0uGZeg8IFyjQQIDAQABo4IBxDCC
# AcAwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYEFPnI
# RJrP2BNJLxoE1rWvfgwwDjZKMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggr
# BgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQuZGln
# aWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwSwYDVR0gBEQwQjA2Bglg
# hkgBhv1sAwEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20v
# Q1BTMAgGBmeBDAEEATCBhAYIKwYBBQUHAQEEeDB2MCQGCCsGAQUFBzABhhhodHRw
# Oi8vb2NzcC5kaWdpY2VydC5jb20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNlcnRz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURDb2RlU2lnbmluZ0NB
# LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCUCOdwU+8srLBP
# /gMQIbtsdu2JWX666Z9GPZisW4KGyCS/U+ZCmB/KnELeD4StX3JFXz5ddILA5oon
# e/pEud+mEuETBhzb7RCz82WJvk0DEMa4hzMAh5c5zmRFGm8WxDZdI20ztcvW0sOb
# qn9UbsGhQDKpFeww91ZtOQ3/2d0KYZKypzGMba53w7pWG170nktocFWTeUhkAy0f
# oAJ2jlBOszj2dshTxAa5Fg1dkpEsBS7oiAPpYkoxV9ldaXJ8Px1navd6PpDW4A78
# 1u13f3tP4FVCsNYw4bz4Z5GInCh6/FshaHRJ6dk5SsCr29MlpK8UxK9y2Qmb+jEt
# RxR0QZLfMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0B
# AQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk
# IFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIIC
# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKR
# N6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZz
# lm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1Oco
# LevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH
# 92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRA
# p8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+g
# GkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU
# 8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/
# FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwj
# jVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQ
# EgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUae
# tdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAw
# HQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LS
# cV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF
# BQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
# Z2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYy
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j
# cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB
# CwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftw
# ig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalW
# zxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQm
# h2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScb
# qyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLaf
# zYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbD
# Qc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0K
# XzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm
# 8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9
# gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8a
# pIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBsAwggSo
# oAMCAQICEAxNaXJLlPo8Kko9KQeAPVowDQYJKoZIhvcNAQELBQAwYzELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2Vy
# dCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0y
# MjA5MjEwMDAwMDBaFw0zMzExMjEyMzU5NTlaMEYxCzAJBgNVBAYTAlVTMREwDwYD
# VQQKEwhEaWdpQ2VydDEkMCIGA1UEAxMbRGlnaUNlcnQgVGltZXN0YW1wIDIwMjIg
# LSAyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAz+ylJjrGqfJru43B
# DZrboegUhXQzGias0BxVHh42bbySVQxh9J0Jdz0Vlggva2Sk/QaDFteRkjgcMQKW
# +3KxlzpVrzPsYYrppijbkGNcvYlT4DotjIdCriak5Lt4eLl6FuFWxsC6ZFO7Khbn
# UEi7iGkMiMbxvuAvfTuxylONQIMe58tySSgeTIAehVbnhe3yYbyqOgd99qtu5Wbd
# 4lz1L+2N1E2VhGjjgMtqedHSEJFGKes+JvK0jM1MuWbIu6pQOA3ljJRdGVq/9XtA
# bm8WqJqclUeGhXk+DF5mjBoKJL6cqtKctvdPbnjEKD+jHA9QBje6CNk1prUe2nhY
# HTno+EyREJZ+TeHdwq2lfvgtGx/sK0YYoxn2Off1wU9xLokDEaJLu5i/+k/kezbv
# BkTkVf826uV8MefzwlLE5hZ7Wn6lJXPbwGqZIS1j5Vn1TS+QHye30qsU5Thmh1EI
# a/tTQznQZPpWz+D0CuYUbWR4u5j9lMNzIfMvwi4g14Gs0/EH1OG92V1LbjGUKYvm
# QaRllMBY5eUuKZCmt2Fk+tkgbBhRYLqmgQ8JJVPxvzvpqwcOagc5YhnJ1oV/E9mN
# ec9ixezhe7nMZxMHmsF47caIyLBuMnnHC1mDjcbu9Sx8e47LZInxscS451NeX1XS
# fRkpWQNO+l3qRXMchH7XzuLUOncCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIH
# gDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZ
# MBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2
# mi91jGogj57IbzAdBgNVHQ4EFgQUYore0GH8jzEU7ZcLzT0qlBTfUpwwWgYDVR0f
# BFMwUTBPoE2gS4ZJaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUH
# AQEEgYMwgYAwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBY
# BggrBgEFBQcwAoZMaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG
# 9w0BAQsFAAOCAgEAVaoqGvNG83hXNzD8deNP1oUj8fz5lTmbJeb3coqYw3fUZPwV
# +zbCSVEseIhjVQlGOQD8adTKmyn7oz/AyQCbEx2wmIncePLNfIXNU52vYuJhZqMU
# KkWHSphCK1D8G7WeCDAJ+uQt1wmJefkJ5ojOfRu4aqKbwVNgCeijuJ3XrR8cuOyY
# QfD2DoD75P/fnRCn6wC6X0qPGjpStOq/CUkVNTZZmg9U0rIbf35eCa12VIp0bcrS
# BWcrduv/mLImlTgZiEQU5QpZomvnIj5EIdI/HMCb7XxIstiSDJFPPGaUr10CU+ue
# 4p7k0x+GAWScAMLpWnR1DT3heYi/HAGXyRkjgNc2Wl+WFrFjDMZGQDvOXTXUWT5D
# mhiuw8nLw/ubE19qtcfg8wXDWd8nYiveQclTuf80EGf2JjKYe/5cQpSBlIKdrAqL
# xksVStOYkEVgM4DgI974A6T2RUflzrgDQkfoQTZxd639ouiXdE4u2h4djFrIHprV
# wvDGIqhPm73YHJpRxC+a9l+nJ5e6li6FV8Bg53hWf2rvwpWaSxECyIKcyRoFfLpx
# tU56mWz06J7UWpjIn7+NuxhcQ/XQKujiYu54BNu90ftbCqhwfvCXhHjjCANdRyxj
# qCU4lwHSPzra5eX25pvcfizM/xdMTQCi2NYBDriL7ubgclWJLCcZYfZ3AYwxggZU
# MIIGUAIBATCBhjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT
# SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBAhAImnSeuC3D7CfqZerb+DUx
# MA0GCWCGSAFlAwQCAQUAoHwwEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkD
# MQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJ
# KoZIhvcNAQkEMSIEIEm+acqtrN5L4atPZ8NJyhHNOIBJSvip4DPw/UL3nZ0BMA0G
# CSqGSIb3DQEBAQUABIICAGo5tSKEx1JauqoQ+cLOdTamnG6Rifn3rfDx1vvQphhg
# odp1zpXLLSiCLKCqUqYSGDTEnkYWHMZlM0/o+klxuzZPfkQFYMGdqi79jh9fRLzk
# R+6hqnZM5a4pdWwX9uCcoIFXoOz06OBUwYhj+XNJiqy8i3VzWNn7PeqIGB+P7adv
# 6YHIF5kiR1cR3pw0HrBTwOgT3U5sLEZIuaKmALQxIujqz5pDUmdD7hNXBjEG7MbJ
# ZCtknUAIAPh2HFuigTDBvRYHMp3Kgcns7Zf9TZtf2qfIiTNeq9p9H1nDxSf1YVvG
# /s+4wkOOFkKD75SqRf7ui6nWsJ2EmIp6V3XvaSxpJzmxBrVR42dnbnjXPSsxkiy4
# 1A50UhFOG7E4dz3PLM6J29j79hKU7PIlTkVAx273ymGWyPkl1xVhbqCcwomj4NXH
# ignJDcsZn6yNh22cocEfOSYQbbRNiWivsQvr/xZLXIOlXJ/XFiXm98hVZ/owi+nl
# EWkZb4X2e2Sj/liXDMRHFPjaIIDtZHUWJ06uRvM9wp3gBYf2gIpaKATK9ZAHYivn
# eJPtUs4THBWvfS0Io/5UGQxMsB6RD6E39jIQknPYRJTJRvOcjkM/BxZmHb0OGuBZ
# 1Kmmium+WXZ1lc+kqPWMJ7eemYkXbPvYpTPyrN1pxj/0PTq7XHj+LEQqGq4+LVUK
# oYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVz
# dGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQDE1pckuU+jwq
# Sj0pB4A9WjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0B
# BwEwHAYJKoZIhvcNAQkFMQ8XDTIzMDgwMzA3MTYwMlowLwYJKoZIhvcNAQkEMSIE
# IKiwP0DqSb0hlXWJjv2r5ZF6BCD3mdZJAfHj2V5J7ggiMA0GCSqGSIb3DQEBAQUA
# BIICAIPQeCp+kbhNJNi7iiuZv8RlZyqCRCDLYbRg/e4xO4BHidpXMwCzCozGcOb5
# cuEdlbinbs9REC5k2mg/2r7qeTTb6lgq7ITHDGo8juMYyAfJNE48fLImPCa1eCYe
# enocr2/3guHOhp4vRsrhYI2gReNquwUk2STNtGSbygKSHxmbFGcGvXCTWBx6rfyO
# 4iE78CWHwmVjFZHBCPBQVzIkpGTJJKVfIuJ+H/9UzdCSXU3ooKf/tkmRCX5ZD4kn
# JirFH6GkQpqKg9nunbX6wMGpvaXapGUFp3m6VDIgP/Te4Q49kbXSk9Pw6debpgGo
# TjpyZvkzp5jWdCcYHcVcZhONKUH6CPrzlWqKim4UUuVeBFQi0eKRHsZMfqSh8DKH
# lhePFgoGVpGgUIEzVpBGAMlFo4tM4NYMMTtjWyMqHgR7usAuuL6FceF9eoztNH5H
# cLQ5OmgYQWPANpQs5hJ07Y3qwX10JKWfc5oWUYlpOKpfkrOJPEj9Ok1YMro7NQ4r
# 7jd5cMx/Sx6akdTJFMACS+mmGoGZ6ISGee5mH+wTZin8wdtOERnt4TC5dOoIDJGj
# Ird+Nn3MBtGMIaF+ZbIAoaSjAjC4g0KkZJYikCorhPdeRi9kPComn86pStdIAKL8
# UdBDKSn4ZOqxEfBcaAzK7d0tvWZVTG74yR3h9In1pAUrac7c
# SIG # End signature block
