[Console]::OutputEncoding = New-Object -typename System.Text.ASCIIEncoding
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls,Tls11,Tls12'

function Get-PlatformVersion {
  switch -regex ((Get-Win32OS).version) {
    '10\.0\.\d+'   {$platform_version = '2016'}
    '10\.0\.17\d+' {$platform_version = '2019'}
    '6\.3\.\d+'    {$platform_version = '2012r2'}
    '6\.2\.\d+'    {$platform_version = '2012'}
    '6\.1\.\d+'    {$platform_version = '2008r2'}
    '6\.0\.\d+'    {$platform_version = '2008'}
  }

  if(Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Server\ServerLevels') {
    $levels = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Server\ServerLevels'
    if($levels.NanoServer -eq 1) { $platform_version += 'nano' }
  }

  return $platform_version
}

function Get-PlatformArchitecture {
  if ((Get-Win32OS).osarchitecture -like '64-bit') {
    $architecture = 'x86_64'
  } else {
    $architecture = 'i386'
  }
  return $architecture
}

function Get-Win32OS {
  if(!$global:win32OS)
  {
    $global:win32OS = Get-WMIQuery win32_operatingsystem
  }
  $global:win32OS
}

function New-Uri {
  param ($baseuri, $newuri)

  try {
    $base = new-object System.Uri $baseuri
    new-object System.Uri $base, $newuri
  }
  catch [System.Management.Automation.MethodInvocationException]{
    Write-Error "$($_.exception.message)"
    throw $_.exception
  }
}

function Get-WebContent {
  param ($uri, $filepath)

  try {
    if($PSVersionTable.PSEdition -eq 'Core') {
      Get-WebContentOnCore $uri $filepath
    }
    else {
      Get-WebContentOnFullNet $uri $filepath
    }
  }
  catch {
    $exception = $_.Exception
    Write-Host "There was an error: "
    do {
      Write-Host "`t$($exception.message)"
      $exception = $exception.innerexception
    } while ($exception)
    throw "Failed to download from $uri."
  }
}

function Get-WebContentOnFullNet {
  param ($uri, $filepath)

  $proxy = New-Object -TypeName System.Net.WebProxy
  $wc = new-object System.Net.WebClient
  $wc.Headers.Add("user-agent", "<%= user_agent_string %>")
  $proxy.Address = $env:http_proxy
  $bypassList = $env:no_proxy


  if($bypassList -ne $null){

	 $bypassList = $bypassList.split(",")
	 $proxy.BypassList = $byPassList
  }

  $wc.Proxy = $proxy

  if ([string]::IsNullOrEmpty($filepath)) {
    $wc.downloadstring($uri)
  }
  else {
    $wc.downloadfile($uri, $filepath)
  }
}

function Get-WebContentOnCore {
  param ($uri, $filepath)

  $handler = New-Object System.Net.Http.HttpClientHandler
  $client = New-Object System.Net.Http.HttpClient($handler)
  $client.DefaultRequestHeaders.UserAgent.ParseAdd("<%= user_agent_string %>")
  $client.Timeout = New-Object System.TimeSpan(0, 30, 0)
  $cancelTokenSource = [System.Threading.CancellationTokenSource]::new()
  $responseMsg = $client.GetAsync([System.Uri]::new($uri), $cancelTokenSource.Token)
  $responseMsg.Wait()
  if (!$responseMsg.IsCanceled) {
    $response = $responseMsg.Result
    if ($response.IsSuccessStatusCode) {
      if ([string]::IsNullOrEmpty($filepath)) {
        $response.Content.ReadAsStringAsync().Result
      }
      else {
        $downloadedFileStream = [System.IO.FileStream]::new($filepath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
        $copyStreamOp = $response.Content.CopyToAsync($downloadedFileStream)
        $copyStreamOp.Wait()
        $downloadedFileStream.Close()
        if ($copyStreamOp.Exception -ne $null) {
          throw $copyStreamOp.Exception
        }
      }
    }
  }
}

function Test-ProjectPackage {
  [cmdletbinding()]
  param ($Path, $Algorithm = 'SHA256', $Hash)
  if (!$env:Valid_ProjectPackage){
    Write-Verbose "Testing the $Algorithm hash for $path."
    $ActualHash = (Custom-GetFileHash -Algorithm $Algorithm -Path $Path).Hash.ToLower()

    Write-Verbose "`tDesired Hash - '$Hash'"
    Write-Verbose "`tActual Hash  - '$ActualHash'"
    $env:Valid_ProjectPackage = $ActualHash -eq $Hash
    if (-not $env:Valid_ProjectPackage) {
      Write-Error "Failed to validate the downloaded installer.  The expected $Algorithm hash was '$Hash' and the actual hash was '$ActualHash' for $path"
    }
  }
  return $env:Valid_ProjectPackage
}

function Custom-GetFileHash ($Path, $Algorithm) {
  function disposable($o){($o -is [IDisposable]) -and (($o | get-member | foreach-object {$_.name}) -contains 'Dispose')}
  function use($obj, [scriptblock]$sb){try {& $sb} catch [exception]{throw $_} finally {if (disposable $obj) {$obj.Dispose()}} }
  $Path = (resolve-path $Path).providerpath
  $hash = @{Algorithm = $Algorithm; Path = $Path}
  use ($c = Get-SHA256Converter) {
    use ($in = (gi $Path).OpenRead()) {
      $hash.Hash = ([BitConverter]::ToString($c.ComputeHash($in))).Replace("-", "").ToUpper()
    }
  }
  return $hash
}

function Get-SHA256Converter {
  if ($(Is-FIPS) -ge 1) {
    New-Object -TypeName Security.Cryptography.SHA256Cng
  } else {
    if($PSVersionTable.PSEdition -eq 'Core') {
      [System.Security.Cryptography.SHA256]::Create()
    }
    else {
      New-Object -TypeName Security.Cryptography.SHA256Managed
    }
  }
}

function Is-FIPS {
  if (!$env:fips){
    $env:fips = (Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy).Enabled
  }
  return $env:fips
}

function Get-WMIQuery {
  param ($class)

  if(Get-Command -Name Get-CimInstance -ErrorAction SilentlyContinue) {
    try{
      $classObject = Get-CimInstance $class
      # If the Get-CimInstance command exists but fails due to security settings, try Get-WmiObject.
    }
    catch {
      $classObject = Get-WmiObject $class
    }
  }
  else {
    $classObject = Get-WmiObject $class
  }
  return $classObject
}
