PokePS.psm1

function Get-PokemonDamage {
    <#
    .SYNOPSIS
        Retrieve damage information of a Pokemon based on its type(s)
    .DESCRIPTION
        The `Get-PokemonDamage` function retrieves information about the damage a Pokemon takes from different types of attacks, based on its type(s).
        It can be invoked using either the name of the Pokemon or its type(s).
        By default, the function returns information about the most recent generation of the Pokemon, but it can also be configured to return information about past generations using the `Generation` parameter.
        An extra info switch can also be specified to include more details about the Pokemon.
    .PARAMETER Pokemon
        The name of a pokemon that you would like to receive damage relationship information for
    .PARAMETER ExtraInfo
        This switch used with the Pokemon parameter that pulls extra info from PokeAPI about the selected Pokemon.
        This information includes:
        * The generation they were introduced
        * The genus of the Pokemon
        * The BST of the Pokemon
        * The weight of the Pokemon in pounds
        * The damage this Pokemon receives from weight based attacks e.g. "low kick"
        * A list of alternate forms for the Pokemon
        * What the Pokemon evolves from
        * Any special types of the Pokemon e.g. Baby, Legendary, Mythical
        * A list of possible abilities that the Pokemon can have
    .PARAMETER StartingMoves
        Switch used with the Pokemon parameter that also outputs the starting move for the selected pokemon. If used with the Generation parameter the starting moves will be filtered.
    .PARAMETER Generation
        An integer used with the Pokemon or Types parameter that specifies the generation that you would like to receive damage information for. This is due to some damage relationships changing over different generations.
    .PARAMETER Types
        A list of types that you want to receive damage relationship information for
    .NOTES
        This function uses the PokeAPI (https://pokeapi.co) to retrieve information about Pokemon and their types.
    .LINK
        https://pokeapi.co/
    .EXAMPLE
        # Returns damage information about Pikachu in the most recent generation, along with extra details about the Pokemon.
        PS> Get-PokemonDamage Pikachu -ExtraInfo
         
        *** Pikachu Is Electric Type ***
        Serebii Link : https://www.serebii.net/pokemon/pikachu/
 
        --- Stats ---
        Introduced : Generation-I
        Genus : Mouse Pokémon
        BST : 320
        Weight (Lbs) : 13.23
        Weight Damage : 20
        Alt-Forms : Pikachu-Rock-Star, Pikachu-Belle, Pikachu-Pop-Star, Pikachu-Phd, Pikachu-Libre, Pikachu-Cosplay, Pikachu-Original-Cap, Pikachu-Hoenn-Cap, Pikachu-Sinnoh-Cap, Pikachu-Unova-Cap, Pikachu-Kalos-Cap, Pikachu-Alola-Cap, Pikachu-Partner-Cap, Pikachu-Starter, Pikachu-World-Cap, Pikachu-Gmax
        Evolves From : Pichu
 
        --- Abilities ---
        Static : Has a 30% chance of paralyzing attacking Pokémon on contact.
        Lightning Rod* : Redirects single-target electric moves to this Pokémon where possible. Absorbs Electric moves, raising Special Attack one stage.
        [* = Hidden]
 
        --- Electric Damage In ---
        Double : Ground
        Half : Flying, Electric, Steel
 
        --- Electric Damage Out ---
        Double : Flying, Water
        Half : Grass, Electric, Dragon
        None : Ground
    .EXAMPLE
        # Returns damage information about the Fire type in Generation III.
        PS> Get-PokemonDamage Fire -Generation 3
         
        --- Fire Damage In [III] ---
        Double : Rock, Water, Ground
        Half : Fairy, Grass, Ice, Steel, Bug, Fire
 
        --- Fire Damage Out [III] ---
        Double : Bug, Steel, Grass, Ice
        Half : Rock, Fire, Water, Dragon
    .EXAMPLE
        # Returns damage information about Charmander in Generation II.
        PS> Get-PokemonDamage Charmander -Generation 2
         
        *** Charmander Is Fire Type [II] ***
        Serebii Link : https://www.serebii.net/pokedex-gs/004.shtml
 
        --- Fire Damage In [II] ---
        Double : Rock, Water, Ground
        Half : Fairy, Grass, Ice, Steel, Bug, Fire
 
        --- Fire Damage Out [II] ---
        Double : Bug, Steel, Grass, Ice
        Half : Rock, Fire, Water, Dragon
    #>

    [CmdletBinding(DefaultParameterSetName = 'Pokemon')]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'Pokemon', Position = 0)]
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon") {
                    Build-PokeAPICache -Endpoint pokemon
                }
                $Global:PokeAPICache["pokemon"].name -like "$wordToComplete*"
            })]
        [string]$Pokemon,
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."type") {
                    Build-PokeAPICache -Endpoint type
                }
                $Global:PokeAPICache["type"].name -like "$wordToComplete*"
            })]
        [Parameter(Mandatory = $true, ParameterSetName = 'Type')]
        $Types,
        [Parameter(ParameterSetName = 'Pokemon')]
        [switch]$ExtraInfo,
        [Parameter(ParameterSetName = 'Pokemon')]
        [switch]$StartingMoves,
        [Parameter(ParameterSetName = 'Type')]
        [Parameter(ParameterSetName = 'Pokemon')]
        [int]$Generation
    )

    $StringOutput = @()

    $DamageInTypes = "quad_damage_from", "double_damage_from", "half_damage_from", "quarter_damage_from", "no_damage_from"
    $DamageOutTypes = "double_damage_to", "half_damage_to", "no_damage_to"

    if ($Pokemon) {
        $Types = $null
        $PokemonData = Get-Pokemon -Pokemon $Pokemon
        if ($Generation) {
            $PastGenTypes = $PokemonData.past_types
            if ($PastGenTypes) {
                $AvailableGenerations = $PastGenTypes.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -ge $Generation } | Sort-Object
                if ($AvailableGenerations) {
                    $Types = ($PastGenTypes | Where-Object { $_.generation.url -match "/$($AvailableGenerations[0])/$" }).types.type.name
                }
            }
        }
        if (!$Types) {
            $Types = $PokemonData.types.type.name
        }

        $Dex = switch ($Generation) {
            1 { "pokedex/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
            2 { "pokedex-gs/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
            3 { "pokedex-rs/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
            4 { "pokedex-dp/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
            5 { "pokedex-bw/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
            6 { "pokedex-xy/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
            7 { "pokedex-sm/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
            8 { "pokedex-swsh/$($PokemonData.species.name)/" }
            9 { "pokedex-sv/$($PokemonData.species.name)/" }
            default { "pokemon/$($PokemonData.species.name)/" }
        }

        $StringOutput += ""
        $StringOutput += "*** $((Get-Culture).TextInfo.ToTitleCase("$Pokemon is $($Types -join "/") type")) $(if($Generation){"[$(Convert-IntToRomanNumeral $Generation)] "})***"
        $StringOutput += ([PSCustomObject]@{"Serebii Link" = "https://www.serebii.net/$Dex" } | Format-List | Out-String).trim()
        $StringOutput += ""
        
        if ($ExtraInfo) {
            $PokemonInfoString = @()
            $PokemonInfoString += "--- Stats ---"
            $SpeciesInfo = Get-PokemonSpecies -PokemonSpecies $PokemonData.species.name

            $StatTable = [PSCustomObject]@{
                Introduced      = "Generation-$(($SpeciesInfo.generation.name -replace ".*-(.*)", '$1').ToUpper())"
                Genus           = ($SpeciesInfo.genera | Where-Object { $_.language.name -eq "en" }).genus
                BST             = $($PokemonData.stats.base_stat | Measure-Object -Sum).Sum
                "Weight (Lbs)"  = [math]::Round((($PokemonData.weight / 10) * 2.20462262), 2)
                "Weight Damage" = switch ($PokemonData.weight) {
                    { 0..100 -contains $_ } { 20 ; break }
                    { 101..250 -contains $_ } { 40 ; break }
                    { 251..500 -contains $_ } { 60 ; break }
                    { 501..1000 -contains $_ } { 80 ; break }
                    { 1001..2000 -contains $_ } { 100 ; break }
                    { $_ -gt 2000 } { 120 }
                }
            }
            
            switch ($SpeciesInfo) {
                { $_.is_baby } { 
                    Add-Member -InputObject $StatTable -NotePropertyName "Special Type" -NotePropertyValue "Baby"
                    break 
                }
                { $_.is_legendary } {
                    Add-Member -InputObject $StatTable -NotePropertyName "Special Type" -NotePropertyValue "Legendary"
                    break  
                }
                { $_.is_mythical } {
                    Add-Member -InputObject $StatTable -NotePropertyName "Special Type" -NotePropertyValue "Mythical"
                    break  
                }
            }

            if ($SpeciesInfo.varieties.count -gt 1) {
                Add-Member -InputObject $StatTable -NotePropertyName "Alt-Forms" -NotePropertyValue $((Get-Culture).TextInfo.ToTitleCase($($SpeciesInfo.varieties | Where-Object { -not $_.is_default }).pokemon.name -join ", "))
            }

            if ($SpeciesInfo.evolves_from_species) {
                Add-Member -InputObject $StatTable -NotePropertyName "Evolves From" -NotePropertyValue $((Get-Culture).TextInfo.ToTitleCase($SpeciesInfo.evolves_from_species.name))
            }

            $PokemonInfoString += ($StatTable | Format-List | Out-String).trim()
            $PokemonInfoString += ""
            
            $AbilityLine = "--- Abilities ---"
            $AbilityTable = [PSCustomObject]@{}
            foreach ($Ability in $PokemonData.abilities) {
                $AbilityData = Get-Ability -Ability $Ability.ability.name
                $AbilityName = $((Get-Culture).TextInfo.ToTitleCase(($Ability.ability.name -replace "-", " "))) + $(if ($Ability.is_hidden) { $HasHidden = $True; "*" })
                if ($null -like $($AbilityData.effect_entries)) {
                    $AbilityValue = $($AbilityData.flavor_text_entries | Where-Object { $_.language.name -eq "en" }).flavor_text -replace "`n+", " "
                }
                else {
                    $AbilityValue = $($AbilityData.effect_entries | Where-Object { $_.language.name -eq "en" }).short_effect -replace "`n+", " "
                }
                
                Add-Member -InputObject $AbilityTable -NotePropertyName $AbilityName -NotePropertyValue $AbilityValue
            } 
           
            $PokemonInfoString += $AbilityLine
            $PokemonInfoString += ($AbilityTable | Format-List | Out-String).trim()
            if ($HasHidden) {
                $PokemonInfoString += "[* = Hidden]"
            }

            $StringOutput += $PokemonInfoString
            $StringOutput += ""
        }
    }

    if ($StartingMoves) {
        $StartingMovesByGen = @{}
        Foreach ($Move in $PokemonData.moves) {
            :MoveVer foreach ($version_group_detail in $Move.version_group_details) {
                if (($version_group_detail.level_learned_at -le 5) -and ($version_group_detail.move_learn_method.name -eq "level-up")) {
                    $MoveGen = switch -regex ($version_group_detail.version_group.name) {
                        "(red-blue|yellow)" { 1; break }
                        "(gold-silver|crystal)" { 2 ; break }
                        "(ruby-sapphire|emerald|firered-leafgreen)" { 3; break }
                        "(diamond-pearl|platinum|heartgold-soulsilver)" { 4; break }
                        "(black-white|black-2-white-2)" { 5; break }
                        "(x-y|omega-ruby-alpha-sapphire)" { 6 ; break }
                        "(sun-moon|ultra-sun-ultra-moon|lets-go-pikachu-lets-go-eevee)" { 7; break }
                        "(sword-shield|ultra-sun-ultra-moon|lets-go-pikachu-lets-go-eevee)" { 8; break }
                        "(scarlet-violet)" { 9; break }
                        default { continue MoveVer }
                    }
                    if ($Generation) {
                        if ($MoveGen -ne $Generation) {
                            continue
                        }
                    }

                    try {
                        if (!$StartingMovesByGen[$MoveGen]) {
                            $StartingMovesByGen[$MoveGen] = @()
                        }
                    }
                    catch {
                        ""
                    }
                    
                    if ($StartingMovesByGen[$MoveGen] -contains $($Move.move.name)) {
                        continue
                    }
                    else {
                        $StartingMovesByGen[$MoveGen] += $Move.move.name
                    }
                }
            }
        }

        $MoveTable = [PSCustomObject]@{}
        $StringOutput += "--- Starting Moves $(if ($Generation) { "[$(Convert-IntToRomanNumeral $Generation)] " })---"
        Foreach ($Key in $($StartingMovesByGen.keys | Sort-Object)) {
            Add-Member -InputObject $MoveTable -NotePropertyName "Generation-$(Convert-IntToRomanNumeral $Key)" -NotePropertyValue $($StartingMovesByGen[$Key] -join ", ")
        }
        
        $StringOutput += ($MoveTable | Format-List | Out-String).trim()
        $StringOutput += ""
    }

    if ($Types) {
        $DamageFrom = @{}
        $DamageTo = @{}
        foreach ($Type in $Types) {
            $damage_relations = $null
            Try {
                $TypeData = Get-Type -Type $Type
            }
            catch {
                Write-Warning "Type not valid: $Type"
                Write-Verbose "Note: you can use tab-completion with the Pokemon and Type parameters to ensure valid values." -Verbose
                continue
            }
            if ($Generation) {
                $past_damage_relations = $TypeData.past_damage_relations
                if ($past_damage_relations) {
                    $TargetGeneration = $past_damage_relations.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -eq $Generation }
                    $EarlierGenerations = $past_damage_relations.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -le $Generation } | Sort-Object -Descending
                    $LaterGenerations = $past_damage_relations.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -gt $Generation } | Sort-Object -Descending
                    if ($TargetGeneration) {
                        $damage_relations = ($past_damage_relations | Where-Object { $_.generation.url -match "/$TargetGeneration/$" }).damage_relations
                    }
                    elseif ($EarlierGenerations -and $LaterGenerations) {
                        $damage_relations = ($past_damage_relations | Where-Object { $_.generation.url -match "/$($EarlierGenerations[0])/$" }).damage_relations
                    }
                }
            }
            if (!$damage_relations) {
                $damage_relations = $TypeData.damage_relations
            }
            
            foreach ($Property in $damage_relations.psobject.properties) {
                $damage_relations.$($Property.name) = $Property.value.name
                if ($Property.name -match "from$") {
                    $Mod = switch -Regex ($Property.name) {
                        "^double" { 2; break }
                        "^half" { .5; break }
                        "^no" { 0; break }
                    }
                    foreach ($DamageType in $damage_relations.$($Property.name)) {
                        if ($null -ne $DamageFrom[$DamageType]) {
                            $DamageFrom[$DamageType] = $DamageFrom[$DamageType] * $Mod
                        }
                        else {
                            $DamageFrom[$DamageType] = $Mod
                        }
                    }
                }
            }
            $DamageTo += @{
                $Type = $damage_relations | Select-Object "*_to"
            } 
        }

        $FromChart = [PSCustomObject]@{}
        Foreach ($DamageType in $DamageInTypes) {
            Add-Member -InputObject $FromChart -NotePropertyName $DamageType -NotePropertyValue @()
        }
        foreach ($Type in $DamageFrom.keys) {
            switch ($DamageFrom[$Type]) {
                1 { break }
                2 { $FromChart."double_damage_from" += $Type ; break }
                .5 { $FromChart."half_damage_from" += $Type ; break }
                4 { $FromChart."quad_damage_from" += $Type ; break }
                0 { $FromChart."no_damage_from" += $Type ; break }
                .25 { $FromChart."quarter_damage_from" += $Type ; break }
            }
        }

        $DamageIn = [PSCustomObject]@{}
        foreach ($DamageType in $DamageInTypes) {
            if ($FromChart.$DamageType -ne @()) {
                Add-Member -InputObject $DamageIn -NotePropertyName (Get-Culture).TextInfo.ToTitleCase(($DamageType -replace "damage_from", "" -replace "_", " " -replace "no", "none")) -NotePropertyValue (Get-Culture).TextInfo.ToTitleCase(($FromChart.$DamageType -join ", "))
            }
        }
        
        $StringOutput += (Get-Culture).TextInfo.ToTitleCase("--- $($Types -join "/") damage in $(if ($Generation) { "[$(Convert-IntToRomanNumeral $Generation)] " })---")
        $StringOutput += ($DamageIn | Format-List | Out-String).trim()
        $StringOutput += ""

        Foreach ($Type in $DamageTo.keys) {
            $StringOutput += (Get-Culture).TextInfo.ToTitleCase("--- $($Type) damage out $(if ($Generation) { "[$(Convert-IntToRomanNumeral $Generation)] " })---")
            $DamageOut = [PSCustomObject]@{}
            foreach ($DamageType in $DamageOutTypes) {
                if ($null -ne $DamageTo[$Type].$DamageType) {
                    Add-Member -InputObject $DamageOut -NotePropertyName (Get-Culture).TextInfo.ToTitleCase(($DamageType -replace "damage_to", "" -replace "_", " " -replace "no", "none")) -NotePropertyValue (Get-Culture).TextInfo.ToTitleCase(($DamageTo[$Type].$DamageType -join ", "))
                }
            }
            $StringOutput += ($DamageOut | Format-List | Out-String).trim()
            $StringOutput += ""
        }
    }
    return $StringOutput
}

function Invoke-PokeAPI {
    [cmdletbinding()]
    param(
        $Endpoint,
        $Base = "https://pokeapi.co/api/v2",
        $Limit = 100000,
        $Offset
    )
    
    $BodyParams = "limit", "Offset"
    $RecursiveParams = "Endpoint", "Base", "Limit", "Offset"

    $Results = @()

    $Body = @{}
    foreach ($Param in $BodyParams) {
        $Param = Get-Variable $Param
        if ($Param.value) {
            $Body[$Param.name.tolower()] = $Param.value
        }
    }

    $Splat = @{
        URI             = "$Base/$($Endpoint.ToLower())"
        Method          = "Get"
        Headers         = @{ Accept = "application/json" }
        ContentType     = "application/x-www-form-urlencoded"
        Body            = $Body
        UseBasicParsing = $true
    }

    $Response = Invoke-RestMethod @Splat
    if ($Response.results -and $Response.gettype().BaseType.name -ne "Array") {
        $Results += $Response.results
        if ($Response.next) {
            [int]$Limit = $Response.next -replace ".*limit=(\d+).*", '$1'
            [int]$Offset = $Response.next -replace ".*offset=(\d+).*", '$1'
            $RecursiveSplat = @{}
            foreach ($Param in $RecursiveParams) {
                $Param = Get-Variable $Param
                if ($Param.value) {
                    $RecursiveSplat[$Param.name] = $Param.value
                }
            }
            $Results += Invoke-PokeAPI @RecursiveSplat
        }
    }
    else { $Results = $Response }
    
    return $Results
}

function Build-PokeAPICache {
    [cmdletbinding()]
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                $Choices = Invoke-RestMethod "https://pokeapi.co/api/v2/"
                $Choices.psobject.properties.name -like "$wordToComplete*"
            })]
        $Endpoints
    )

    if (!$Global:PokeAPICache) {
        $Global:PokeAPICache = @{}
    }

    foreach ($Endpoint in $Endpoints) {
        $CacheData = Invoke-PokeAPI -Endpoint $Endpoint
        if ($CacheData) {
            $Global:PokeAPICache.$Endpoint = $CacheData
        }
    }
}

function Get-Berry {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."berry") {
                    Build-PokeAPICache -Endpoint berry
                }
                $Global:PokeAPICache["berry"].name -like "$wordToComplete*"
            })]
        $Berry
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "berry/$Berry"
    }
    catch {
        if ($Berry) {
            Write-Warning "Berry not valid: $Berry"
            Write-Verbose "Note: you can use tab-completion with the Berry parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-BerryFirmness {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."berry-firmness") {
                    Build-PokeAPICache -Endpoint berry-firmness
                }
                $Global:PokeAPICache["berry-firmness"].name -like "$wordToComplete*"
            })]
        $BerryFirmness
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "berry-firmness/$BerryFirmness"
    }
    catch {
        if ($BerryFirmness) {
            Write-Warning "Berry-firmness not valid: $BerryFirmness"
            Write-Verbose "Note: you can use tab-completion with the BerryFirmness parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-BerryFlavor {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."berry-flavor") {
                    Build-PokeAPICache -Endpoint berry-flavor
                }
                $Global:PokeAPICache["berry-flavor"].name -like "$wordToComplete*"
            })]
        $BerryFlavor
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "berry-flavor/$BerryFlavor"
    }
    catch {
        if ($BerryFlavor) {
            Write-Warning "Berry not valid: $BerryFlavor"
            Write-Verbose "Note: you can use tab-completion with the Berry parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-ContestType {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."contest-type") {
                    Build-PokeAPICache -Endpoint contest-type
                }
                $Global:PokeAPICache["contest-type"].name -like "$wordToComplete*"
            })]
        $ContestType
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "contest-type/$ContestType"
    }
    catch {
        if ($ContestType) {
            Write-Warning "ContestType not valid: $ContestType"
            Write-Verbose "Note: you can use tab-completion with the ContestType parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-ContestEffect {
    param(
        $ContestEffect
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "contest-effect/$ContestEffect"
    }
    catch {
        if ($ContestEffect) {
            Write-Warning "ContestEffect not valid: $ContestEffect"
            Write-Verbose "Note: you can use tab-completion with the ContestEffect parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-SuperContestEffect {
    param(
        $SuperContestEffect
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "super-contest-effect/$SuperContestEffect"
    }
    catch {
        if ($SuperContestEffect) {
            Write-Warning "SuperContestEffect not valid: $SuperContestEffect"
            Write-Verbose "Note: you can use tab-completion with the SuperContestEffect parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-EncounterMethod {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."encounter-method") {
                    Build-PokeAPICache -Endpoint encounter-method
                }
                $Global:PokeAPICache["encounter-method"].name -like "$wordToComplete*"
            })]
        $EncounterMethod
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "encounter-method/$EncounterMethod"
    }
    catch {
        if ($EncounterMethod) {
            Write-Warning "EncounterMethod not valid: $EncounterMethod"
            Write-Verbose "Note: you can use tab-completion with the EncounterMethod parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-EncounterCondition {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."encounter-condition") {
                    Build-PokeAPICache -Endpoint encounter-condition
                }
                $Global:PokeAPICache["encounter-condition"].name -like "$wordToComplete*"
            })]
        $EncounterCondition
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "encounter-condition/$EncounterCondition"
    }
    catch {
        if ($EncounterCondition) {
            Write-Warning "EncounterCondition not valid: $EncounterCondition"
            Write-Verbose "Note: you can use tab-completion with the EncounterCondition parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-EncounterConditionValue {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."encounter-condition-value") {
                    Build-PokeAPICache -Endpoint encounter-condition-value
                }
                $Global:PokeAPICache["encounter-condition-value"].name -like "$wordToComplete*"
            })]
        $EncounterConditionValue
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "encounter-condition-value/$EncounterConditionValue"
    }
    catch {
        if ($EncounterConditionValue) {
            Write-Warning "EncounterConditionValue not valid: $EncounterConditionValue"
            Write-Verbose "Note: you can use tab-completion with the EncounterConditionValue parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-EvolutionChain {
    param(
        $EvolutionChain
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "evolution-chain/$EvolutionChain"
    }
    catch {
        if ($EvolutionChain) {
            Write-Warning "EvolutionChain not valid: $EvolutionChain"
            Write-Verbose "Note: you can use tab-completion with the EvolutionChain parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-EvolutionTrigger {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."evolution-trigger") {
                    Build-PokeAPICache -Endpoint evolution-trigger
                }
                $Global:PokeAPICache["evolution-trigger"].name -like "$wordToComplete*"
            })]
        $EvolutionTrigger
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "evolution-trigger/$EvolutionTrigger"
    }
    catch {
        if ($EvolutionTrigger) {
            Write-Warning "EvolutionTrigger not valid: $EvolutionTrigger"
            Write-Verbose "Note: you can use tab-completion with the EvolutionTrigger parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Generation {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."generation") {
                    Build-PokeAPICache -Endpoint generation
                }
                $Global:PokeAPICache["generation"].name -like "$wordToComplete*"
            })]
        $Generation
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "generation/$Generation"
    }
    catch {
        if ($Generation) {
            Write-Warning "Generation not valid: $Generation"
            Write-Verbose "Note: you can use tab-completion with the Generation parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Pokedex {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokedex") {
                    Build-PokeAPICache -Endpoint pokedex
                }
                $Global:PokeAPICache["pokedex"].name -like "$wordToComplete*"
            })]
        $Pokedex
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokedex/$Pokedex"
    }
    catch {
        if ($Pokedex) {
            Write-Warning "Pokedex not valid: $Pokedex"
            Write-Verbose "Note: you can use tab-completion with the Pokedex parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Version {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."version") {
                    Build-PokeAPICache -Endpoint version
                }
                $Global:PokeAPICache["version"].name -like "$wordToComplete*"
            })]
        $Version
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "version/$Version"
    }
    catch {
        if ($Version) {
            Write-Warning "Version not valid: $Version"
            Write-Verbose "Note: you can use tab-completion with the Version parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-VersionGroup {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."version-group") {
                    Build-PokeAPICache -Endpoint version-group
                }
                $Global:PokeAPICache["version-group"].name -like "$wordToComplete*"
            })]
        $VersionGroup
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "version-group/$VersionGroup"
    }
    catch {
        if ($VersionGroup) {
            Write-Warning "VersionGroup not valid: $VersionGroup"
            Write-Verbose "Note: you can use tab-completion with the VersionGroup parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokeItem {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."item") {
                    Build-PokeAPICache -Endpoint item
                }
                $Global:PokeAPICache["item"].name -like "$wordToComplete*"
            })]
        $Item
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "item/$Item"
    }
    catch {
        if ($Item) {
            Write-Warning "Item not valid: $Item"
            Write-Verbose "Note: you can use tab-completion with the Item parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-ItemAttribute {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."item-attribute") {
                    Build-PokeAPICache -Endpoint item-attribute
                }
                $Global:PokeAPICache["item-attribute"].name -like "$wordToComplete*"
            })]
        $ItemAttribute
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "item-attribute/$ItemAttribute"
    }
    catch {
        if ($ItemAttribute) {
            Write-Warning "ItemAttribute not valid: $ItemAttribute"
            Write-Verbose "Note: you can use tab-completion with the ItemAttribute parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-ItemCategory {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."item-category") {
                    Build-PokeAPICache -Endpoint item-category
                }
                $Global:PokeAPICache["item-category"].name -like "$wordToComplete*"
            })]
        $ItemCategory
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "item-category/$ItemCategory"
    }
    catch {
        if ($ItemCategory) {
            Write-Warning "ItemCategory not valid: $ItemCategory"
            Write-Verbose "Note: you can use tab-completion with the ItemCategory parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-ItemFlingEffect {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."item-fling-effect") {
                    Build-PokeAPICache -Endpoint item-fling-effect
                }
                $Global:PokeAPICache["item-fling-effect"].name -like "$wordToComplete*"
            })]
        $ItemFlingEffect
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "item-fling-effect/$ItemFlingEffect"
    }
    catch {
        if ($ItemFlingEffect) {
            Write-Warning "ItemFlingEffect not valid: $ItemFlingEffect"
            Write-Verbose "Note: you can use tab-completion with the ItemFlingEffect parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-ItemPocket {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."item-pocket") {
                    Build-PokeAPICache -Endpoint item-pocket
                }
                $Global:PokeAPICache["item-pocket"].name -like "$wordToComplete*"
            })]
        $ItemPocket
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "item-pocket/$ItemPocket"
    }
    catch {
        if ($ItemPocket) {
            Write-Warning "ItemPocket not valid: $ItemPocket"
            Write-Verbose "Note: you can use tab-completion with the ItemPocket parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Locations {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."location") {
                    Build-PokeAPICache -Endpoint location
                }
                $Global:PokeAPICache["location"].name -like "$wordToComplete*"
            })]
        $Location
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "location/$Location"
    }
    catch {
        if ($Location) {
            Write-Warning "Location not valid: $Location"
            Write-Verbose "Note: you can use tab-completion with the Location parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-LocationArea {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."location-area") {
                    Build-PokeAPICache -Endpoint location-area
                }
                $Global:PokeAPICache["location-area"].name -like "$wordToComplete*"
            })]
        $LocationArea
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "location-area/$LocationArea"
    }
    catch {
        if ($LocationArea) {
            Write-Warning "LocationArea not valid: $LocationArea"
            Write-Verbose "Note: you can use tab-completion with the LocationArea parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PalParkArea {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pal-park-area") {
                    Build-PokeAPICache -Endpoint pal-park-area
                }
                $Global:PokeAPICache["pal-park-area"].name -like "$wordToComplete*"
            })]
        $PalParkArea
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pal-park-area/$PalParkArea"
    }
    catch {
        if ($PalParkArea) {
            Write-Warning "PalParkArea not valid: $PalParkArea"
            Write-Verbose "Note: you can use tab-completion with the PalParkArea parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Region {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."region") {
                    Build-PokeAPICache -Endpoint region
                }
                $Global:PokeAPICache["region"].name -like "$wordToComplete*"
            })]
        $Region
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "region/$Region"
    }
    catch {
        if ($Region) {
            Write-Warning "Region not valid: $Region"
            Write-Verbose "Note: you can use tab-completion with the Region parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Machine {
    param(
        $Machine
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "machine/$Machine"
    }
    catch {
        if ($Machine) {
            Write-Warning "Machine not valid: $Machine"
            Write-Verbose "Note: you can use tab-completion with the Machine parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Move {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move") {
                    Build-PokeAPICache -Endpoint move
                }
                $Global:PokeAPICache["move"].name -like "$wordToComplete*"
            })]
        $Move
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "move/$Move"
    }
    catch {
        if ($Move) {
            Write-Warning "Move not valid: $Move"
            Write-Verbose "Note: you can use tab-completion with the Move parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-MoveData {
    [cmdletbinding()]
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move") {
                    Build-PokeAPICache -Endpoint move
                }
                $Global:PokeAPICache["move"].name -like "$wordToComplete*"
            })]
        $Move,
        [switch]$LearnedBy
    )

    $Data = Get-Move -Move $Move

    $Speed = Switch ($Data.Priority) {
        { $_ -gt 0 } { "High"; $Plus = "+"; break }
        { $_ -lt 0 } { "Low"; break }
        default { "Normal" }
    }
   
    $ReturnData = [PSCustomObject]@{}

    if ($Move -is [int]) {
        $MemberSplat = @{
            InputObject       = $ReturnData 
            NotePropertyName  = "Name" 
            NotePropertyValue = (Get-Culture).TextInfo.ToTitleCase("$($Data.name)")
        }
        Add-Member @MemberSplat
    }

    $MemberSplat = @{
        InputObject       = $ReturnData 
        NotePropertyName  = "Type" 
        NotePropertyValue = (Get-Culture).TextInfo.ToTitleCase("$($Data.type.name)/$($Data.damage_class.name)")
    }
    Add-Member @MemberSplat

    if ($Data.power) {
        $MemberSplat = @{
            InputObject       = $ReturnData 
            NotePropertyName  = "Power" 
            NotePropertyValue = $Data.power
        }
        Add-Member @MemberSplat
    }

    if ($Data.accuracy) {
        $MemberSplat = @{
            InputObject       = $ReturnData 
            NotePropertyName  = "Accuracy" 
            NotePropertyValue = $Data.accuracy
        }
        Add-Member @MemberSplat
    }   

    $MemberSplat = @{
        InputObject       = $ReturnData 
        NotePropertyName  = "PP" 
        NotePropertyValue = $Data.pp
    }
    Add-Member @MemberSplat

    $MemberSplat = @{
        InputObject       = $ReturnData 
        NotePropertyName  = "Priority" 
        NotePropertyValue = "$Speed ($Plus$($Data.Priority))"
    }
    Add-Member @MemberSplat

    if ($Data.target) {
        $MemberSplat = @{
            InputObject       = $ReturnData 
            NotePropertyName  = "Target" 
            NotePropertyValue = (Get-Culture).TextInfo.ToTitleCase($Data.target.name)
        }
        Add-Member @MemberSplat
    }

    if (($Data.effect_entries) -or ($Data.flavor_text_entries)) {
        if ($Data.effect_entries) {
            $EffectData = ($Data.effect_entries | Where-Object { $_.language.name -eq "en" }).effect -replace '\$effect_chance', $Data.effect_chance -replace "\. ", ". "
        }
        else {
            $EffectData = ($Data.flavor_text_entries | Where-Object { $_.language.name -eq "en" }).flavor_text -replace "`n", " "
        }
        if ($EffectData -ne "Inflicts regular damage.") {
            $MemberSplat = @{
                InputObject       = $ReturnData 
                NotePropertyName  = "Effect" 
                NotePropertyValue = $EffectData -replace 'Inflicts regular damage\.\s*', ''
            }
            Add-Member @MemberSplat
        }
    }

    if ($LearnedBy -and $Data.learned_by_pokemon) {
        $MemberSplat = @{
            InputObject       = $ReturnData 
            NotePropertyName  = "Learned By" 
            NotePropertyValue = $Data.learned_by_pokemon.name -join ", "
        }
        Add-Member @MemberSplat
    } 

    return $ReturnData
}

function Get-MoveAilment {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move-ailment") {
                    Build-PokeAPICache -Endpoint move-ailment
                }
                $Global:PokeAPICache["move-ailment"].name -like "$wordToComplete*"
            })]
        $MoveAilment
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "move-ailment/$MoveAilment"
    }
    catch {
        if ($MoveAilment) {
            Write-Warning "MoveAilment not valid: $MoveAilment"
            Write-Verbose "Note: you can use tab-completion with the MoveAilment parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-MoveBattleStyle {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move-battle-style") {
                    Build-PokeAPICache -Endpoint move-battle-style
                }
                $Global:PokeAPICache["move-battle-style"].name -like "$wordToComplete*"
            })]
        $MoveBattleStyle
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "move-battle-style/$MoveBattleStyle"
    }
    catch {
        if ($MoveBattleStyle) {
            Write-Warning "MoveBattleStyle not valid: $MoveBattleStyle"
            Write-Verbose "Note: you can use tab-completion with the MoveBattleStyle parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-MoveCategory {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move-category") {
                    Build-PokeAPICache -Endpoint move-category
                }
                $Global:PokeAPICache["move-category"].name -like "$wordToComplete*"
            })]
        $MoveCategory
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "move-category/$MoveCategory"
    }
    catch {
        if ($MoveCategory) {
            Write-Warning "MoveCategory not valid: $MoveCategory"
            Write-Verbose "Note: you can use tab-completion with the MoveCategory parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-MoveDamageClass {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move-damage-class") {
                    Build-PokeAPICache -Endpoint move-damage-class
                }
                $Global:PokeAPICache["move-damage-class"].name -like "$wordToComplete*"
            })]
        $MoveDamageClass
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "move-damage-class/$MoveDamageClass"
    }
    catch {
        if ($MoveDamageClass) {
            Write-Warning "MoveDamageClass not valid: $MoveDamageClass"
            Write-Verbose "Note: you can use tab-completion with the MoveDamageClass parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-MoveLearnMethod {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move-learn-method") {
                    Build-PokeAPICache -Endpoint move-learn-method
                }
                $Global:PokeAPICache["move-learn-method"].name -like "$wordToComplete*"
            })]
        $MoveLearnMethod
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "move-learn-method/$MoveLearnMethod"
    }
    catch {
        if ($MoveLearnMethod) {
            Write-Warning "MoveLearnMethod not valid: $MoveLearnMethod"
            Write-Verbose "Note: you can use tab-completion with the MoveLearnMethod parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-MoveTarget {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."move-target") {
                    Build-PokeAPICache -Endpoint move-target
                }
                $Global:PokeAPICache["move-target"].name -like "$wordToComplete*"
            })]
        $MoveTarget
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "move-target/$MoveTarget"
    }
    catch {
        if ($MoveTarget) {
            Write-Warning "MoveTarget not valid: $MoveTarget"
            Write-Verbose "Note: you can use tab-completion with the MoveTarget parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Ability {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."ability") {
                    Build-PokeAPICache -Endpoint ability
                }
                $Global:PokeAPICache["ability"].name -like "$wordToComplete*"
            })]
        $Ability
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "ability/$Ability"
    }
    catch {
        if ($Ability) {
            Write-Warning "Ability not valid: $Ability"
            Write-Verbose "Note: you can use tab-completion with the Ability parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Characteristic {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."characteristic") {
                    Build-PokeAPICache -Endpoint characteristic
                }
                $Global:PokeAPICache["characteristic"].name -like "$wordToComplete*"
            })]
        $Characteristic
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "characteristic/$Characteristic"
    }
    catch {
        if ($Characteristic) {
            Write-Warning "Characteristic not valid: $Characteristic"
            Write-Verbose "Note: you can use tab-completion with the Characteristic parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-EggGroup {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."egg-group") {
                    Build-PokeAPICache -Endpoint egg-group
                }
                $Global:PokeAPICache["egg-group"].name -like "$wordToComplete*"
            })]
        $EggGroup
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "egg-group/$EggGroup"
    }
    catch {
        if ($EggGroup) {
            Write-Warning "EggGroup not valid: $EggGroup"
            Write-Verbose "Note: you can use tab-completion with the EggGroup parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Gender {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."gender") {
                    Build-PokeAPICache -Endpoint gender
                }
                $Global:PokeAPICache["gender"].name -like "$wordToComplete*"
            })]
        $Gender
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "gender/$Gender"
    }
    catch {
        if ($Gender) {
            Write-Warning "Gender not valid: $Gender"
            Write-Verbose "Note: you can use tab-completion with the Gender parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-GrowthRate {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."growth-rate") {
                    Build-PokeAPICache -Endpoint growth-rate
                }
                $Global:PokeAPICache["growth-rate"].name -like "$wordToComplete*"
            })]
        $GrowthRate
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "growth-rate/$GrowthRate"
    }
    catch {
        if ($GrowthRate) {
            Write-Warning "GrowthRate not valid: $GrowthRate"
            Write-Verbose "Note: you can use tab-completion with the GrowthRate parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Nature {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."nature") {
                    Build-PokeAPICache -Endpoint nature
                }
                $Global:PokeAPICache["nature"].name -like "$wordToComplete*"
            })]
        $Nature
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "nature/$Nature"
    }
    catch {
        if ($Nature) {
            Write-Warning "Nature not valid: $Nature"
            Write-Verbose "Note: you can use tab-completion with the Nature parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokeathlonStat {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokeathlon-stat") {
                    Build-PokeAPICache -Endpoint pokeathlon-stat
                }
                $Global:PokeAPICache["pokeathlon-stat"].name -like "$wordToComplete*"
            })]
        $PokeathlonStat
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokeathlon-stat/$PokeathlonStat"
    }
    catch {
        if ($PokeathlonStat) {
            Write-Warning "PokeathlonStat not valid: $PokeathlonStat"
            Write-Verbose "Note: you can use tab-completion with the PokeathlonStat parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Pokemon {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon") {
                    Build-PokeAPICache -Endpoint pokemon
                }
                $Global:PokeAPICache["pokemon"].name -like "$wordToComplete*"
            })]
        $Pokemon
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokemon/$Pokemon"
    }
    catch {
        if ($Pokemon) {
            Write-Warning "Pokemon not valid: $Pokemon"
            Write-Verbose "Note: you can use tab-completion with the Pokemon parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokemonEncounters {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon") {
                    Build-PokeAPICache -Endpoint pokemon
                }
                $Global:PokeAPICache["pokemon"].name -like "$wordToComplete*"
            })]
        $Pokemon
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokemon/$Pokemon/encounters"
    }
    catch {
        if ($Pokemon) {
            Write-Warning "Pokemon not valid: $Pokemon"
            Write-Verbose "Note: you can use tab-completion with the Pokemon parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokemonColor {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon-color") {
                    Build-PokeAPICache -Endpoint pokemon-color
                }
                $Global:PokeAPICache["pokemon-color"].name -like "$wordToComplete*"
            })]
        $PokemonColor
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokemon-color/$PokemonColor"
    }
    catch {
        if ($PokemonColor) {
            Write-Warning "PokemonColor not valid: $PokemonColor"
            Write-Verbose "Note: you can use tab-completion with the PokemonColor parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokemonForm {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon-form") {
                    Build-PokeAPICache -Endpoint pokemon-form
                }
                $Global:PokeAPICache["pokemon-form"].name -like "$wordToComplete*"
            })]
        $PokemonForm
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokemon-form/$PokemonForm"
    }
    catch {
        if ($PokemonForm) {
            Write-Warning "PokemonForm not valid: $PokemonForm"
            Write-Verbose "Note: you can use tab-completion with the PokemonForm parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokemonHabitat {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon-habitat") {
                    Build-PokeAPICache -Endpoint pokemon-habitat
                }
                $Global:PokeAPICache["pokemon-habitat"].name -like "$wordToComplete*"
            })]
        $PokemonHabitat
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokemon-habitat/$PokemonHabitat"
    }
    catch {
        if ($PokemonHabitat) {
            Write-Warning "PokemonHabitat not valid: $PokemonHabitat"
            Write-Verbose "Note: you can use tab-completion with the PokemonHabitat parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokemonShape {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon-shape") {
                    Build-PokeAPICache -Endpoint pokemon-shape
                }
                $Global:PokeAPICache["pokemon-shape"].name -like "$wordToComplete*"
            })]
        $PokemonShape
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokemon-shape/$PokemonShape"
    }
    catch {
        if ($PokemonShape) {
            Write-Warning "PokemonShape not valid: $PokemonShape"
            Write-Verbose "Note: you can use tab-completion with the PokemonShape parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-PokemonSpecies {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."pokemon-species") {
                    Build-PokeAPICache -Endpoint pokemon-species
                }
                $Global:PokeAPICache["pokemon-species"].name -like "$wordToComplete*"
            })]
        $PokemonSpecies
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "pokemon-species/$PokemonSpecies"
    }
    catch {
        if ($PokemonSpecies) {
            Write-Warning "PokemonSpecies not valid: $PokemonSpecies"
            Write-Verbose "Note: you can use tab-completion with the PokemonSpecies parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Stat {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."stat") {
                    Build-PokeAPICache -Endpoint stat
                }
                $Global:PokeAPICache["stat"].name -like "$wordToComplete*"
            })]
        $Stat
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "stat/$Stat"
    }
    catch {
        if ($Stat) {
            Write-Warning "Stat not valid: $Stat"
            Write-Verbose "Note: you can use tab-completion with the Stat parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Type {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."type") {
                    Build-PokeAPICache -Endpoint type
                }
                $Global:PokeAPICache["type"].name -like "$wordToComplete*"
            })]
        $Type
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "type/$Type"
    }
    catch {
        if ($Type) {
            Write-Warning "Type not valid: $Type"
            Write-Verbose "Note: you can use tab-completion with the Type parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Get-Languages {
    param(
        [ArgumentCompleter({
                param ($commandName, $parameterName, $wordToComplete)
                if ($null -eq $PokeAPICache."language") {
                    Build-PokeAPICache -Endpoint language
                }
                $Global:PokeAPICache["language"].name -like "$wordToComplete*"
            })]
        $Language
    )

    try {
        $Data = Invoke-PokeAPI -Endpoint "language/$Language"
    }
    catch {
        if ($Language) {
            Write-Warning "Language not valid: $Language"
            Write-Verbose "Note: you can use tab-completion with the Language parameter to ensure valid values." -Verbose
            continue
        }
        else {
            Write-Warning "Data could not be retrieved, server may be wrong or down."
        }
    }
    return $Data
}

function Convert-IntToRomanNumeral {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [int]$Number
    )
  
    $numerals = @{
        1000 = "M"
        900  = "CM"
        500  = "D"
        400  = "CD"
        100  = "C"
        90   = "XC"
        50   = "L"
        40   = "XL"
        10   = "X"
        9    = "IX"
        5    = "V"
        4    = "IV"
        1    = "I"
    }
  
    $result = ""
    foreach ($key in ($numerals.Keys | Sort-Object -Descending)) {
        while ($Number -ge $key) {
            $result += $numerals[$key]
            $Number -= $key
        }
    }
    return $result
}