functions/private.ps1

#private functions for this module

function Invoke-QuizQuestion {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String]$Question,
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [String]$Answer,
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Distractors,
        [Parameter(ValueFromPipelineByPropertyName)]
        [String]$Note,
        [String]$Title = 'PowerShell Quiz',
        [string]$Version,
        [switch]$Protected
    )

    begin {
        Write-Verbose "Starting $($MyInvocation.MyCommand)"
    }

    process {
        Write-Verbose $Question

        #Modified 8/15/2023 to allow for a masked answer. Issue #3
        # 29 June 2026 get protection settings from metadata
        if ($Protected -And ($answer -match '^[\s\d+]+$')) {
                Write-Verbose 'Unmasking Answer'
                $answer = _showAnswer -ProtectedAnswer $answer
        }
        If ($Protected -AND (($note -match '^[\s\d+]+$') )) {
            #19 June 2026 Unmask Notes
                Write-Verbose 'Unmasking note'
                $note = _showAnswer -ProtectedAnswer $note
            #}
        } #unmask

        Write-Verbose "Detected $($distractors.count) distractors"
        #17 Oct 2024 unmask distractors if protected
        $Distractors = foreach ($distractor in $Distractors) {
            If ($protected -AND ($distractor -match '^[\s\d+]+$')) {
                Write-Verbose "Unmasking distractor: $distractor"
                _showAnswer -ProtectedAnswer $distractor
            }
            else {
                $distractor
            }
        }
        $possible = @($Answer, $Distractors) | Get-Random -Count ($Distractors.count + 1)

        $cue = @"
 
$Question
 
$(Write-SpectreRule -Width 75 -LineColor $psQuizTheme.BorderColor)
"@

#$('-'*75)

        for ($i = 1; $i -lt $possible.count + 1; $i++) {
            $cue += "[[$i]] $($possible[$i-1])`n"
        }

        $cue += "[[$i]] Quit`n"
     # $cue += $(Write-SpectreRule -Width 75 -LineColor PaleGreen1)
        #$('-' * 75)
      # Write-SpectreHost ("[{0}]$Title[/]" -f $psQuizTheme.Title)
        Write-SpectreHost ('[{0}]Question {1}/{2}[/]' -f $psQuizTheme.Question,$QuestionCount, $AllCount)
        Write-SpectreHost $cue
        Write-SpectreRule -Width 75 -LineColor $psQuizTheme.BorderColor

        $count = $Distractors.count + 1
        Write-Verbose "$count answers"
        do {
            try {
                #[ValidateScript( { $_ -ge 1 -and $_ -le $count + 1 })][int32]$r = Read-Host -Prompt "Select an answer [1-$($count+1)]" -ErrorAction stop
                [ValidateScript( { $_ -ge 1 -and $_ -le $count + 1 })][int32]$r = Read-SpectreText -Message "[italic]Select an answer[/] [[1-$($count+1)]]" -ErrorAction stop
                Write-Verbose "You entered $r"
            }
            catch {
                #ignore the error
                #Write-Warning $_.exception.message
                Write-Warning "Please select a value between 1 and $($count+1)"
                $r = 0
            }
        } until ($r -gt 0)

        if ($possible[$r - 1] -eq $answer) {
            Write-SpectreHost ("[{0}]Correct![/]`n" -f $psQuizTheme.correct)
            $True
        }
        elseif ($r -eq $count + 1) {
            Write-Verbose 'You selected Quit'
            return -1
        }
        else {
            Write-SpectreHost ("[{0}]`nThe correct answer is:[/] $answer`n" -f $psQuizTheme.Incorrect)
            $false
        }

        if ($Note) {
            Write-SpectreHost ("`n[{0}]Additional Notes[/]" -f $psQuizTheme.Note)
            #Write-Host '----------------' -ForegroundColor yellow
            #Write-Host $Note -ForegroundColor Yellow
            Write-SpectreHost $Note
            Write-SpectreHost "`n"
        }
    } #process
    end {
        Write-Verbose "Ending $($MyInvocation.MyCommand)"
    }

} #close function

function Get-GPA {
    [CmdletBinding()]
    param([int32]$Correct, [int32]$Possible)

    $grades = [ordered]@{
        'A'  = 4
        'A-' = 3.7
        'B+' = 3.3
        'B'  = 3
        'B-' = 2.7
        'C+' = 2.3
        'C'  = 2.0
        'C-' = 1.7
        'D+' = 1.3
        'D'  = 1
        'D-' = .7
        'F'  = 0
    }

    #2 July 2026 Allow for the user to quit before answering any questions
    if ($Possible -gt 0) {
        $pct = ($Correct / $Possible) * 100
        $gpa = [math]::round(($pct / 20), 1)
        $grade = $grades.GetEnumerator() | Where-Object { $_.value -le $gpa } | Select-Object -First 1

        [PSCustomObject]@{
            Grade   = $grade.name
            Minimum = $grade.Value
            GPA     = $GPA
        }
    }
} #end Get-GPA


#functions to obfuscate the answer

<#
[regex]$word = "\b\S+\b"
$message = "I am foo"
$test = foreach ($part in $message) {
 $word.Matches($part).Value.ForEach({
    $_.toCharArray().Foreach({ "{0:d3}" -f ($_ -as [int])}) -join ''
 }) -join ' '
}
 
 
[regex]$number = "\d{3}"
$out = foreach ($part in $test.split()) {
#write-host $part -fore cyan
  $number.Matches($part).Value.ForEach({
   ([int]$_ -as [string][char])
  }) -join ''
}
$out -join ' '
 
#>


function _hideAnswer {
    param([string]$Answer)
    [regex]$word = '\S+'
    foreach ($part in $Answer) {
        $word.Matches($part).Value.ForEach({
                $_.toCharArray().Foreach({ '{0:d3}' -f ($_ -as [int]) }) -join ''
            }) -join ' '
    }
}

function _showAnswer {
    param([string]$ProtectedAnswer)
    [regex]$number = '\d{3}'
    $out = foreach ($part in $ProtectedAnswer.split()) {
        $number.Matches($part).Value.ForEach({
                ([int]$_ -as [string][char])
            }) -join ''
    }
    $out -join ' '
}

function _GetSelected {
    $Context = $psEditor.GetEditorContext()
    $selected = [Microsoft.PowerShell.EditorServices.Extensions.FileRange, Microsoft.PowerShell.EditorServices]::new($Context.SelectedRange.Start, $Context.SelectedRange.end)
    [string]$text = $Context.CurrentFile.GetText($selected)
    [PSCustomObject]@{
        File         = $Context.CurrentFile.Path
        SelectedText = $text
        Start        = $Selected.Start
        End          = $selected.end
        Context      = $Context
    }
}