function Print-Msg {
  param ( [Parameter(Mandatory=$true, HelpMessage='String to output')][string]$msg, [string]$color = "Green" )
  Write-Host ('{0}' -f $msg) -ForegroundColor $color
}

function Create-Shortcut {
    param ( [Parameter(Mandatory=$true,HelpMessage='Target path')][string]$SourceExe, [Parameter(Mandatory=$true,HelpMessage='Arguments to the path/exe')][AllowEmptyString()]$ArgumentsToSourceExe, [Parameter(Mandatory=$true,HelpMessage='The destination of the desktop link')][string]$DestinationPath )
    $WshShell = New-Object -comObject WScript.Shell
    $Shortcut = $WshShell.CreateShortcut($DestinationPath)
    $Shortcut.TargetPath = $SourceExe
    if($ArgumentsToSourceExe) {
      $Shortcut.Arguments = $ArgumentsToSourceExe
    }
    $Shortcut.Save()
}

function Add-EnvPath {
    param(
        [Parameter(Mandatory=$true,HelpMessage='The Pathe to add to Users environment')]
        [string] $Path,

        [ValidateSet('Machine', 'User', 'Session')]
        [string] $Container = 'Session'
    )

    function Where-Something
    {
      param
      (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, HelpMessage='Data to filter')]
        $InputObject
      )
      process
      {
        if ($InputObject)
        {
          $InputObject
        }
      }
    }

  if ($Container -ne 'Session') {
        $containerMapping = @{
            Machine = [EnvironmentVariableTarget]::Machine
            User = [EnvironmentVariableTarget]::User
        }
        $containerType = $containerMapping[$Container]

        $persistedPaths = [Environment]::GetEnvironmentVariable('Path', $containerType) -split ';'
        if ($persistedPaths -notcontains $Path) {
            $persistedPaths = $persistedPaths + $Path | Where-Something
            [Environment]::SetEnvironmentVariable('Path', $persistedPaths -join ';', $containerType)
        }
    }

    $envPaths = $env:Path -split ';'
    if ($envPaths -notcontains $Path) {
        $envPaths = $envPaths + $Path | Where-Something
        $env:Path = $envPaths -join ';'
    }
}

filter Get-FileSize {
	'{0:N2} {1}' -f $(
	if ($_ -lt 1kb) { $_, 'Bytes' }
	elseif ($_ -lt 1mb) { ($_/1kb), 'KB' }
	elseif ($_ -lt 1gb) { ($_/1mb), 'MB' }
	elseif ($_ -lt 1tb) { ($_/1gb), 'GB' }
	elseif ($_ -lt 1pb) { ($_/1tb), 'TB' }
	else { ($_/1pb), 'PB' }
	)
}

function Get-FileWCSynchronous{
    param(
        [Parameter(Mandatory=$true)]
        [string]$url, 
        [string]$destinationFolder="$env:USERPROFILE\Downloads",
        [switch]$includeStats
    )
    $wc = New-Object -TypeName Net.WebClient
    $wc.UseDefaultCredentials = $true
    $destination = Join-Path -Path $destinationFolder -ChildPath ($url | Split-Path -Leaf)
    $start = Get-Date
    $wc.DownloadFile($url, $destination)
    $elapsed = ((Get-Date) - $start).ToString('hh\:mm\:ss')
    $totalSize = (Get-Item -Path $destination).Length | Get-FileSize
    if ($includeStats.IsPresent){
        [PSCustomObject]@{Name=$MyInvocation.MyCommand;TotalSize=$totalSize;Time=$elapsed}
    }
    Get-Item -Path $destination | Unblock-File
}

function Test-AbsolutePath {
  Param (
      [Parameter(Mandatory=$True)]
      [ValidateScript({[System.IO.Path]::IsPathRooted($_)})]
      [String]$Path
  )
}

$ErrorActionPreference = 'Stop'

$GhcupBasePrefix = [System.Environment]::GetEnvironmentVariable('GHCUP_INSTALL_BASE_PREFIX', 'user')

if ($GhcupBasePrefix) {
  $defaultGhcupBasePrefix = $GhcupBasePrefix
} else {
  $defaultGhcupBasePrefix = 'C:\'
}

while ($true) {
  Print-Msg -color Magenta -msg ('Where to install to (this should be a short Path, preferably a Drive like ''C:\''){1}Press enter to accept the default [{0}]:' -f $defaultGhcupBasePrefix, "`n")
  $basePrefixPrompt = Read-Host
  $GhcupBasePrefix = ($defaultGhcupBasePrefix,$basePrefixPrompt)[[bool]$basePrefixPrompt]
  if (!($GhcupBasePrefix.EndsWith('\'))) {
    $GhcupBasePrefix = ('{0}\' -f $GhcupBasePrefix)
  }

  if (!($GhcupBasePrefix)) {
    Print-Msg -color Red -msg "No directory specified!"
  } elseif (!(Test-Path -LiteralPath ('{0}' -f $GhcupBasePrefix))) {
    Print-Msg -color Red -msg "Directory does not exist, need to specify an existing Drive/Directory"
  } elseif (!(Split-Path -IsAbsolute -Path "$GhcupBasePrefix")) {
    Print-Msg -color Red -msg "Invalid/Non-absolute Path specified"
  } else {
    Break
  }
}
Print-Msg -msg ('Setting env variable GHCUP_INSTALL_BASE_PREFIX to ''{0}''' -f $GhcupBasePrefix)
$null = [Environment]::SetEnvironmentVariable("GHCUP_INSTALL_BASE_PREFIX", $GhcupBasePrefix, [System.EnvironmentVariableTarget]::User)


$GhcupDir = ('{0}\ghcup' -f $GhcupBasePrefix)
$MsysDir = ('{0}\msys64' -f $GhcupDir)
$Bash = ('{0}\usr\bin\bash' -f $MsysDir)
$BootstrapUrl = 'https://www.haskell.org/ghcup/sh/bootstrap-haskell-windows'
$GhcupMsys2 = [System.Environment]::GetEnvironmentVariable('GHCUP_MSYS2', 'user')

Print-Msg -msg 'Preparing for GHCup installation...'

if (Test-Path -LiteralPath ('{0}' -f $GhcupDir)) {
  $decision = $Host.UI.PromptForChoice('Install GHCup'
                                      , 'GHCup is already installed, what do you want to do?'
                                      , @('&Reinstall'
                                          '&Continue'
                                          '&Abort'), 1)
  if ($decision -eq 0) {
    $suffix = [IO.Path]::GetRandomFileName()
    Print-Msg -msg ('Backing up {0} to {0}-{1} ...' -f $GhcupDir, $suffix)
    Rename-Item -Path ('{0}' -f $GhcupDir) -NewName ('{0}-{1}' -f $GhcupDir, $suffix)
  } elseif ($decision -eq 1) {
    Print-Msg -msg 'Continuing installation...'
  } elseif ($decision -eq 2) {
    Break
  }
}


$null = New-Item -Path ('{0}' -f $GhcupDir) -ItemType 'directory' -ErrorAction SilentlyContinue
$null = New-Item -Path ('{0}' -f $GhcupDir) -Name 'bin' -ItemType 'directory' -ErrorAction SilentlyContinue

Print-Msg -msg 'First checking for Msys2...'

if (!(Test-Path -Path ('{0}' -f $MsysDir))) {
  $msys2Decision = $Host.UI.PromptForChoice('Install MSys2'
                                           , 'Do you want GHCup to install a default MSys2 toolchain (recommended)?'
                                           , @('&Yes'
                                               '&No'), 0)
  if ($msys2Decision -eq 0) {
    Print-Msg -msg ('...Msys2 doesn''t exist, installing into {0} ...this may take a while' -f $MsysDir)

    # Download the archive
    Print-Msg -msg 'Downloading Msys2 archive...'
    $archive = 'msys2-x86_64-latest.sfx.exe'
    
    if (Get-Command -Name 'curl.exe' -ErrorAction SilentlyContinue) {
      curl.exe -o ('{0}\{1}' -f $env:TEMP, $archive) ('https://repo.msys2.org/distrib/{0}' -f $archive)
    } else {
      Get-FileWCSynchronous -url ('https://repo.msys2.org/distrib/{0}' -f $archive) -destinationFolder "$env:TEMP" -includeStats
    }

    Print-Msg -msg 'Extracting Msys2 archive...'
    $null = & "$env:TEMP\$archive" '-y' ('-o{0}' -f $GhcupDir)  # Extract
    Remove-Item -Path ('{0}/{1}' -f $env:TEMP, $archive)

    Print-Msg -msg 'Processing MSYS2 bash for first time use...'
    & "$Bash" -lc 'exit'

    & "$env:windir\system32\taskkill.exe" /F /FI `"MODULES eq msys-2.0.dll`"

    Print-Msg -msg 'Upgrading full system...'
    & "$Bash" -lc 'pacman --noconfirm -Syuu'

    Print-Msg -msg 'Upgrading full system twice...'
    & "$Bash" -lc 'pacman --noconfirm -Syuu'

    Print-Msg -msg 'Installing GHC Build Dependencies...'
    & "$Bash" -lc 'pacman --noconfirm -S --needed git tar curl wget base-devel gettext binutils autoconf make libtool automake python p7zip patch unzip mingw-w64-x86_64-toolchain mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb mingw-w64-x86_64-python2 mingw-w64-x86_64-python3-sphinx'

    Print-Msg -msg 'Updating SSL root certificate authorities...'
    & "$Bash" -lc 'pacman --noconfirm -S ca-certificates'

    Print-Msg -msg 'Setting default home directory...'
    & "$Bash" -lc "sed -i -e 's/db_home:.*$/db_home: windows/' /etc/nsswitch.conf"
  } elseif ($msys2Decision -eq 1) {
    Print-Msg -color Yellow -msg 'Skipping MSys2 installation.'
    while ($true) {
      if ($GhcupMsys2) {
        $defaultMsys2Dir = $GhcupMsys2
        Print-Msg -color Magenta -msg ('Input existing MSys2 toolchain directory. Press enter to accept the default [{0}]:' -f $defaultMsys2Dir)
        $MsysDirPrompt = Read-Host
        $MsysDir = ($defaultMsys2Dir,$MsysDirPrompt)[[bool]$MsysDirPrompt]
      } else {
        Print-Msg -color Magenta -msg 'Input existing MSys2 toolchain directory:' 
        $MsysDir = Read-Host
      }
      if (!($MsysDir)) {
        Print-Msg -color Red -msg "No directory specified!"         
      } elseif (!(Test-Path -LiteralPath ('{0}' -f $MsysDir))) {
        Print-Msg -color Red -msg ('MSys2 installation at ''{0}'' could not be found!' -f $MsysDir)
      } elseif (!(Split-Path -IsAbsolute -Path "$MsysDir")) {
        Print-Msg -color Red -msg "Invalid/Non-absolute Path specified"
      } else {
        Break
      }
    }
    Print-Msg -msg ('Setting GHCUP_MSYS2 env var to ''{0}''' -f $MsysDir)
    $null = [Environment]::SetEnvironmentVariable("GHCUP_MSYS2", $MsysDir, [System.EnvironmentVariableTarget]::User)
    $Bash = ('{0}\usr\bin\bash' -f $MsysDir)
  }
} else {
    Print-Msg -msg ('...Msys2 found in {0} ...skipping Msys2 installation.' -f $MsysDir)
}

Print-Msg -msg 'Creating shortcuts...'
Create-Shortcut -SourceExe ('{0}\msys2_shell.cmd' -f $MsysDir) -ArgumentsToSourceExe '-mingw64' -DestinationPath ('{0}\Desktop\Mingw haskell shell.lnk' -f $HOME)
Create-Shortcut -SourceExe 'https://www.msys2.org/docs/package-management' -ArgumentsToSourceExe '' -DestinationPath ('{0}\Desktop\Mingw package management docs.url' -f $HOME)

Print-Msg -msg ('Adding {0}\bin to Users Path...' -f $GhcupDir)
Add-EnvPath -Path ('{0}\bin' -f $GhcupDir) -Container 'User'

Print-Msg -msg 'Starting GHCup installer...'

$Msys2Shell = ('{0}\msys2_shell.cmd' -f $MsysDir)

if ((Get-Process -ID $PID).ProcessName.StartsWith("bootstrap-haskell")) {
  & "$Bash" -lc ('[ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; curl --proto ''=https'' --tlsv1.2 -sSf {0} | bash' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir)
} else {
  & "$Msys2Shell" -mingw64 -mintty -c ('[ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; trap ''echo Press any key to exit && read -n 1 && exit'' 2 ; curl --proto =https --tlsv1.2 -sSf -k {0} | bash ; echo ''Press any key to exit'' && read -n 1' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir)
}


# SIG # Begin signature block
  # MIID4QYJKoZIhvcNAQcCoIID0jCCA84CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
  # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
  # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUVqKek181kF/Jx/P7z176herc
  # ZyCgggH/MIIB+zCCAWSgAwIBAgIQGOezhGS1A5tHh9VubW0liDANBgkqhkiG9w0B
  # AQUFADAYMRYwFAYDVQQDDA1KdWxpYW4gT3NwYWxkMB4XDTIxMDUzMDE4Mzk1OVoX
  # DTI1MDUzMDAwMDAwMFowGDEWMBQGA1UEAwwNSnVsaWFuIE9zcGFsZDCBnzANBgkq
  # hkiG9w0BAQEFAAOBjQAwgYkCgYEAs76XCXYPM14buR1RkVKhOB8pyM4Df6kPaz75
  # nkbA0nq1VmMhBfCYFWyYHd7jniqTH0LoAKGGquN1bniREaCP9j2pFWpMIgLpQH3H
  # +jpsfmxV2BTG8q+Jok88gTXS1FlAk72E85zO/Jhr6Fja1aFYAdibBRsRxcVMTVh7
  # 4AGLNGUCAwEAAaNGMEQwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHQYDVR0OBBYEFC+R
  # hdhPo0Ty5HnzHyo1pN35IfZQMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUF
  # AAOBgQAl3IdBVIwbJJDp7BksMYPeM4ivB3UyNvlw8aVxGwAzNgdSaezYIdMFtKXV
  # CSv5bd4VnFRAPDJW9dhW0h3SkeJUoklUxMjKXhR3qygQhSxPDjIatAuOCffGACba
  # ZZ7Om40b+pKXc6i/HnlApk9DGbXJ59bFcLGGcZ9QjoUae6Ex1DGCAUwwggFIAgEB
  # MCwwGDEWMBQGA1UEAwwNSnVsaWFuIE9zcGFsZAIQGOezhGS1A5tHh9VubW0liDAJ
  # BgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
  # CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAj
  # BgkqhkiG9w0BCQQxFgQUosm9nN1JgajqSBa1cUwxxhLrAsYwDQYJKoZIhvcNAQEB
  # BQAEgYCnKzfsH1aDjS6xkC/uymjaBowHSnh6nFu2AkjcKu8RgcBZzP5SLBXgU9wm
  # aED5Ujwyq3Qre+TGVRUqwkEauDhQiX2A008G00fRO6+di6yJRCRn5eaRAbdU3Xww
  # E5VhEwLBnwzWrvLKtdEclhgUCo5Tq87QMXVdgX4aRmunl4ZE+Q==
# SIG # End signature block