WoW.psm1

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains long links.')]
[CmdletBinding()]
param()


if ($PSVersionTable.PSVersion -lt '6.0') {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSAvoidAssignmentToAutomaticVariable', '', Justification = 'Compatibility with PowerShell 6.0 and newer.'
    )]
    $IsWindows = [System.Environment]::OSVersion.Platform -eq 'Win32NT'
}

$scriptName = 'WoW'
Write-Verbose "[$scriptName] - Importing module"

#region - From [classes]
Write-Verbose "[$scriptName] - [classes] - Processing folder"

#region - From [classes] - [Classes]
Write-Verbose "[$scriptName] - [classes] - [Classes] - Importing"

$script:ClassMap = @(
    [PSCustomObject]@{
        ClassID    = 1
        ClassName  = 'Warrior'
        ColorHex   = '#C79C6E'
        ColorFracR = 0.78
        ColorFracG = 0.61
        ColorFracB = 0.43
        ColorDecR  = 199
        ColorDecG  = 156
        ColorDecB  = 110
    }, # 1 - Warrior
    [PSCustomObject]@{
        ClassID    = 2
        ClassName  = 'Paladin'
        ColorHex   = '#F58CBA'
        ColorFracR = 0.96
        ColorFracG = 0.55
        ColorFracB = 0.73
        ColorDecR  = 245
        ColorDecG  = 140
        ColorDecB  = 186
    }, # 2 - Paladin
    [PSCustomObject]@{
        ClassID    = 3
        ClassName  = 'Hunter'
        ColorHex   = '#ABD473'
        ColorFracR = 0.67
        ColorFracG = 0.83
        ColorFracB = 0.45
        ColorDecR  = 171
        ColorDecG  = 212
        ColorDecB  = 115
    }, # 3 - Hunter
    [PSCustomObject]@{
        ClassID    = 4
        ClassName  = 'Rogue'
        ColorHex   = '#FFF569'
        ColorFracR = 1.00
        ColorFracG = 0.96
        ColorFracB = 0.41
        ColorDecR  = 255
        ColorDecG  = 245
        ColorDecB  = 105
    }, # 4 - Rogue
    [PSCustomObject]@{
        ClassID    = 5
        ClassName  = 'Priest'
        ColorHex   = '#FFFFFF'
        ColorFracR = 1.00
        ColorFracG = 1.00
        ColorFracB = 1.00
        ColorDecR  = 255
        ColorDecG  = 255
        ColorDecB  = 255
    }, # 5 - Priest
    [PSCustomObject]@{
        ClassID    = 6
        ClassName  = 'Death Knight'
        ColorHex   = '#C41F3B'
        ColorFracR = 0.77
        ColorFracG = 0.12
        ColorFracB = 0.23
        ColorDecR  = 196
        ColorDecG  = 31
        ColorDecB  = 59
    }, # 6 - Death Knight
    [PSCustomObject]@{
        ClassID    = 7
        ClassName  = 'Shaman'
        ColorHex   = '#0070DE'
        ColorFracR = 0.00
        ColorFracG = 0.44
        ColorFracB = 0.87
        ColorDecR  = 0
        ColorDecG  = 112
        ColorDecB  = 222
    }, # 7 - Shaman
    [PSCustomObject]@{
        ClassID    = 8
        ClassName  = 'Mage'
        ColorHex   = '#69CCF0'
        ColorFracR = 0.41
        ColorFracG = 0.80
        ColorFracB = 0.94
        ColorDecR  = 105
        ColorDecG  = 204
        ColorDecB  = 240
    }, # 8 - Mage
    [PSCustomObject]@{
        ClassID    = 9
        ClassName  = 'Warlock'
        ColorHex   = '#9482C9'
        ColorFracR = 0.58
        ColorFracG = 0.51
        ColorFracB = 0.79
        ColorDecR  = 148
        ColorDecG  = 130
        ColorDecB  = 201
    }, # 9 - Warlock
    [PSCustomObject]@{
        ClassID    = 10
        ClassName  = 'Monk'
        ColorHex   = '#00FF96'
        ColorFracR = 0.00
        ColorFracG = 1.00
        ColorFracB = 0.59
        ColorDecR  = 0
        ColorDecG  = 255
        ColorDecB  = 150
    }, # 10 - Monk
    [PSCustomObject]@{
        ClassID    = 11
        ClassName  = 'Druid'
        ColorHex   = '#FF7D0A'
        ColorFracR = 1.00
        ColorFracG = 0.49
        ColorFracB = 0.04
        ColorDecR  = 255
        ColorDecG  = 125
        ColorDecB  = 10
    }, # 11 - Druid
    [PSCustomObject]@{
        ClassID    = 12
        ClassName  = 'Demon Hunter'
        ColorHex   = '#A330C9'
        ColorFracR = 0.64
        ColorFracG = 0.19
        ColorFracB = 0.79
        ColorDecR  = 163
        ColorDecG  = 48
        ColorDecB  = 201
    }  # 12 - Demon Hunter
    # Missing Evoker
)

enum Role {
    Tank
    Healer
    Damage
}

enum Faction {
    Alliance = 0
    Horde = 1
    Neutral = 2
}

enum Gender {
    Male = 0
    Female = 1
    Unknown = $null
}

class Account {
    [string]$Name
    [bool]$IsMain
    [string]$FolderPath
    [Character[]]$Characters
    [string]$SettingsFolderPath
    [string]$ConfigCachePath
    [string]$BindingsCachePath
    [string]$MacrosCachePath

    Account (
        [string]$Name
    ) {
        $this.Name = $Name
    }

    [string]ToString() {
        return $this.Name
    }
}

class Addon {
    [string]$Name
    [string]$Interface
    [string]$SavedVariables
    [string]$AccountSettingsFile
    [string]$AccountSettingsFileBackup
    [string]$SavedVariablesPerCharacter
    [string]$CharacterSettingsFile
    [string]$CharacterSettingsFileBackup
    [string]$RequiredDeps
    [string]$OptionalDeps
    [string]$DefaultState
    [string]$AddonFilePath
    [string]$AddonFile

    Addon() {}

    [string]ToString() {
        return $this.Name
    }
}

class AzeriteEssence {
    [string]$Name
    [int]$ID
    [bool]$Valid
    [bool]$Unlocked
    [int]$Icon
    [int]$Rank

    AzeriteEssence(
        [string]$Name,
        [int]$ID,
        [bool]$Valid,
        [bool]$Unlocked,
        [int]$Icon,
        [int]$Rank
    ) {
        $this.Name = $Name
        $this.ID = $ID
        $this.Valid = $Valid
        $this.Unlocked = $Unlocked
        $this.Icon = $Icon
        $this.Rank = $Rank
    }

    [string]ToString() {
        return $this.Name
    }
}

class PlayableClass {
    [UInt16]$ID
    [string]$Name
    [PowerType]$PowerType

    PlayableClass() {}

    PlayableClass(
        [UInt16]$ID,
        [string]$Name,
        [PowerType]$PowerType
    ) {
        $this.ID = $ID
        $this.Name = $Name
        $this.PowerType = $PowerType
    }

    [string]ToString() {
        return $this.Name
    }
}

class Character {
    [uint]$ID
    [string]$Name
    [Account]$Account
    [Realm]$Realm
    [string]$Region
    [Gender]$Gender
    [Faction]$Faction
    [Race]$Race
    [PlayableClass]$Class
    [bool]$IsMainForClass
    [bool]$IsMain
    [Specialization]$Specialization
    [UInt16]$Level
    [string]$Guild
    [int]$AchievementPoints
    [datetime]$LastPlayed
    [UInt16]$averageItemLevel
    [UInt16]$averageItemLevelEquipped
    [string]$FolderPath
    [string]$SettingsFolderPath
    [string]$AddOnsFilePath
    [string]$BindingsCachePath
    [string]$ConfigCachePath
    [string]$MacrosCachePath
    [double]$Gold
    [Currency[]]$Currency
    [AzeriteEssence[]]$AzeriteEssences
    [timespan]$PlayedLevel
    [timespan]$PlayedTotal
    [bool]$DailyCurrent
    [datetime]$DailyReset
    [bool]$BiWeeklyCurrent
    [datetime]$BiWeeklyReset
    [bool]$WeeklyCurrent
    [datetime]$WeeklyReset
    [bool]$isResting
    [UInt16]$ArtifactLevel
    [double]$ArtifactProgress
    [string]$Zone
    [UInt16]$CloakLevel
    [UInt16]$CorruptionResistance
    [bool]$Warmode
    [bool]$VisionUnlocked
    [bool]$Vision10
    [bool]$Vision30
    [bool]$Vision50
    [bool]$Vision51
    [bool]$Vision52
    [bool]$Vision53
    [bool]$Vision54
    [bool]$Vision55
    [bool]$VisionCompleted
    [string]$Vision
    [UInt16]$KeyBest
    [string]$KeyBagName
    [UInt16]$KeyBagLevel
    [UInt16]$WarTornWeeklyMax
    [UInt16]$WarTornEarnedThisWeek
    [UInt16]$WarTornweeklyAvailable
    [UInt16]$WarTornTotalMax
    [UInt16]$WarTornTotal
    [double]$RIOScore
    [double]$RIOScoreTank
    [double]$RIOScoreHealer
    [double]$RIOScoreDPS
    [string]$AssultUld
    [string]$AssultVale
    [string]$EmiLeg1
    [string]$EmiLeg2
    [string]$EmiLeg3
    [string]$EmiBFA1
    [string]$EmiBFA2
    [string]$EmiBFA3
    [bool]$HasNewMail
    [int]$Durability

    Character() {}

    [string]ToString() {
        return $this.Name + ' - ' + $this.Realm.Name
    }
}

class Currency {
    [UInt16]$ID
    [string]$Name
    [int]$Amount

    Currency() {}

    Currency(
        [UInt16]$ID,
        [string]$Name,
        [int]$Amount
    ) {
        $this.ID = $ID
        $this.Name = $Name
        $this.Amount = $Amount
    }

    [string]ToString() {
        return $this.Amount + 'x ' + $this.Name
    }
}

class PowerType {
    [UInt16]$ID
    [string]$Name

    PowerType() {}

    PowerType(
        [UInt16]$ID,
        [string]$Name
    ) {
        $this.ID = $ID
        $this.Name = $Name
    }

    [string]ToString() {
        return $this.Name
    }
}

class Race {
    [UInt16]$ID
    [string]$Name
    [Faction]$Faction
    [bool]$IsSelectable
    [bool]$IsAlliedRace

    Race() {}

    Race (
        [UInt16]$ID,
        [string]$Name,
        [Faction]$Faction,
        [bool]$IsSelectable,
        [bool]$IsAlliedRace
    ) {
        $this.ID = $ID
        $this.Name = $Name
        $this.Faction = $Faction
        $this.IsSelectable = $IsSelectable
        $this.IsAlliedRace = $IsAlliedRace
    }

    [string]ToString() {
        return $this.Name
    }
}

class Realm {
    [UInt16]$ID
    [string]$Name
    [string]$Region
    [string]$Category
    [string]$Locale
    [string]$Timezone
    [string]$Type
    [bool]$IsTournament
    [string]$Slug

    Realm() {}

    Realm (
        [UInt16]$ID,
        [string]$Name,
        [string]$Region,
        [string]$Category,
        [string]$Locale,
        [string]$Timezone,
        [string]$Type,
        [bool]$IsTournament,
        [string]$Slug
    ) {
        $this.ID = $ID
        $this.Name = $Name
        $this.Region = $Region
        $this.Category = $Category
        $this.Locale = $Locale
        $this.Timezone = $Timezone
        $this.Type = $Type
        $this.IsTournament = $IsTournament
        $this.Slug = $Slug
    }

    Realm (
        [string]$Name
    ) {
        $this.Name = $Name
    }

    [string]ToString() {
        return $this.Name
    }
}

class Specialization {
    [UInt16]$ID
    [string]$Name
    [string]$Description
    [PowerType]$PowerType
    [Role]$Role

    Specialization() {}

    Specialization(
        [UInt16]$ID,
        [string]$Name,
        [string]$Description,
        [Role]$Role
    ) {
        $this.ID = $ID
        $this.Name = $Name
        $this.Description = $Description
        $this.Role = $Role
    }

    [string]ToString() {
        return $this.Name
    }
}

Write-Verbose "[$scriptName] - [classes] - [Classes] - Done"
#endregion - From [classes] - [Classes]

Write-Verbose "[$scriptName] - [classes] - Done"
#endregion - From [classes]

#region - From [private]
Write-Verbose "[$scriptName] - [private] - Processing folder"

#region - From [private] - [Account]
Write-Verbose "[$scriptName] - [private] - [Account] - Processing folder"

#region - From [private] - [Account] - [Export-WoWAccount]
Write-Verbose "[$scriptName] - [private] - [Account] - [Export-WoWAccount] - Importing"

function Export-WoWAccount {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    Get-WoWAccount | ConvertTo-Json -EnumsAsStrings | Out-File -FilePath "$Script:WoW_Folder_Cache\Accounts.json" -Force
}

Write-Verbose "[$scriptName] - [private] - [Account] - [Export-WoWAccount] - Done"
#endregion - From [private] - [Account] - [Export-WoWAccount]
#region - From [private] - [Account] - [Import-WoWAccount]
Write-Verbose "[$scriptName] - [private] - [Account] - [Import-WoWAccount] - Importing"

function Import-WoWAccount {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    $CacheFilePath = "$Script:WoW_Folder_Cache\Accounts.json"
    if (Test-Path $CacheFilePath) {
        $ImportedAccounts = Get-Content $CacheFilePath | ConvertFrom-Json
        foreach ($ImportedAccount in $ImportedAccounts) {
            $Account = [Account]($Script:WoW_Accounts | Where-Object Name -Match $ImportedAccount.Name | Select-Object -First 1)
            $Account.IsMain = $ImportedAccount.IsMain
        }
    } else {
        Write-WoWVerbose 'Import-WoWAccount: Nothing to import'
    }
}

Write-Verbose "[$scriptName] - [private] - [Account] - [Import-WoWAccount] - Done"
#endregion - From [private] - [Account] - [Import-WoWAccount]
#region - From [private] - [Account] - [Initialize-WoWAccount]
Write-Verbose "[$scriptName] - [private] - [Account] - [Initialize-WoWAccount] - Importing"

function Initialize-WoWAccount {
    <#
        .SYNOPSIS
        Short description

        .DESCRIPTION
        Long description

        .PARAMETER WoWAccountsFolderPath
        Parameter description

        .EXAMPLE
        An example

        .NOTES
        General notes
    #>

    [CmdletBinding()]
    [OutputType([Account[]])]
    param(
        $WoWAccountsFolderPath = $Script:WoW_Folder_Accounts
    )
    [Account[]]$Accounts = $null

    Write-WoWVerbose 'Accounts: Finding'
    $AccountFolders = $WoWAccountsFolderPath | Get-ChildItem -Directory | Where-Object Name -NE SavedVariables |
        Select-Object Name, FullName | Sort-Object name
    Write-WoWVerbose "Accounts: Found $($AccountFolders.count)"

    Write-WoWVerbose 'Accounts: Processing'
    $i = 0
    foreach ($AccountFolder in $AccountFolders) {
        $i++
        $Status = "$i/$($AccountFolders.count)"
        Write-WoWVerbose "Accounts: Processing: $Status $($AccountFolder.Name)"

        $Account = [Account]::new($AccountFolder.Name)
        $Account.FolderPath = $AccountFolder.FullName
        $Account.SettingsFolderPath = "$($Account.FolderPath)\SavedVariables"
        $Account.ConfigCachePath = "$($Account.FolderPath)\config-cache.wtf"
        $Account.BindingsCachePath = "$($Account.FolderPath)\bindings-cache.wtf"
        $Account.MacrosCachePath = "$($Account.FolderPath)\macros-cache.txt"
        $Account.IsMain = $false
        $Accounts += $Account
        Write-WoWVerbose "Accounts: Processing: $Status $($AccountFolder.Name): Done"
    }
    Write-WoWVerbose 'Accounts: Processing: Done'
    return $Accounts | Sort-Object name
}

Write-Verbose "[$scriptName] - [private] - [Account] - [Initialize-WoWAccount] - Done"
#endregion - From [private] - [Account] - [Initialize-WoWAccount]

Write-Verbose "[$scriptName] - [private] - [Account] - Done"
#endregion - From [private] - [Account]

#region - From [private] - [Addon]
Write-Verbose "[$scriptName] - [private] - [Addon] - Processing folder"

#region - From [private] - [Addon] - [Initialize-WoWAddon]
Write-Verbose "[$scriptName] - [private] - [Addon] - [Initialize-WoWAddon] - Importing"

function Initialize-WoWAddon {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER WoWAddonsFolderPath
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([Addon[]])]
    param(
        [Parameter(Mandatory)]
        [string]
        $WoWAddonsFolderPath
    )
    [Addon[]]$Addons = $null

    Write-WoWVerbose 'Addons: Finding'
    $WoWAddonsFolders = $WoWAddonsFolderPath | Get-ChildItem -Directory
    $AddonTOCFiles = $WoWAddonsFolders | Get-ChildItem | Where-Object name -Like *.toc | Sort-Object name
    Write-WoWVerbose "Addons: Found $($AddonTOCFiles.count)"

    Write-WoWVerbose 'Addons: Processing'
    $i = 0
    foreach ($AddonTOCFile in $AddonTOCFiles) {
        $i++
        $Status = "$i/$($AddonTOCFiles.count)"
        Write-WoWVerbose "Addons: Processing: $Status $($AddonTOCFile.Name)"

        $Content = Get-Content -Path $AddonTOCFile.FullName
        $Addon = [Addon]::new()
        $Addon.AddonFilePath = $AddonTOCFile.FullName
        $Addon.AddonFile = $AddonTOCFile.Name
        $SettingsFileName = "$($AddonTOCFile.BaseName).lua"

        # $Line = $Content[0]
        foreach ($Line in $Content) {
            if ($Line -like '*##*Title:*') {
                $Text = ''
                $Text = ($Line -split 'Title:').trim()[-1]
                $Text = $Text.Replace('|r', '')
                $Text = $Text.Replace('|c', '#|')
                $Text = $Text.Split('#')
                $Text = $Text.trim()

                $Title = $null
                # $Part = $Text[0]
                foreach ($Part in $Text) {
                    if ($Part -like '|*') {
                        $Part = $Part.Substring(9)
                    }
                    $Title += $Part
                }
                $Addon.Name = $Title
            }
            if ($Line -like '*##*Interface:*') {
                $Interface = ($Line -split 'Interface:').trim()[-1]
                $Addon.Interface = $Interface
            }
            if ($Line -like '*##*SavedVariables:*') {
                [string[]]$SavedVariables = ($Line -split 'SavedVariables:').trim()
                $SavedVariables = $SavedVariables[1..($SavedVariables.Length - 1)]
                $Addon.SavedVariables = $SavedVariables.split(',').Trim()
                $Addon.AccountSettingsFile = "$SettingsFileName"
                $Addon.AccountSettingsFileBackup = "$SettingsFileName.bak"
            }
            if ($Line -like '*##*SavedVariablesPerCharacter:*') {
                [string[]]$SavedVariablesPerCharacter = ($Line -split 'SavedVariablesPerCharacter:').trim()
                $SavedVariablesPerCharacter = [string[]]$SavedVariablesPerCharacter[1..($SavedVariablesPerCharacter.Length - 1)]
                $Addon.SavedVariablesPerCharacter = $SavedVariablesPerCharacter.split(',').Trim()
                $Addon.CharacterSettingsFile = "$SettingsFileName"
                $Addon.CharacterSettingsFileBackup = "$SettingsFileName.bak"
            }
            if ($Line -like '*##*RequiredDeps:*') {
                [string[]]$RequiredDeps = ($Line -split 'RequiredDeps:').trim()
                $RequiredDeps = [string[]]$RequiredDeps[1..($RequiredDeps.Length - 1)]
                $RequiredDeps = $RequiredDeps.split(',').Trim()
                $Addon.RequiredDeps = $RequiredDeps
            }
            if ($Line -like '*##*OptionalDeps:*') {
                [string[]]$OptionalDeps = ($Line -split 'OptionalDeps:').trim()
                $OptionalDeps = [string[]]$OptionalDeps[1..($OptionalDeps.Length - 1)]
                $OptionalDeps = $OptionalDeps.split(',').Trim()
                $Addon.OptionalDeps = $OptionalDeps
            }
            if ($Line -like '*##*DefaultState:*') {
                $DefaultState = ($Line -split 'DefaultState:').trim()[-1]
                $Addon.DefaultState = $DefaultState
            }
        }

        $Addons += $Addon
        Write-WoWVerbose "Addons: Processing: $Status $($AddonTOCFile.Name): Done"
    }
    Write-WoWVerbose 'Addons: Processing: Done'
    return $Addons | Sort-Object name
}

Write-Verbose "[$scriptName] - [private] - [Addon] - [Initialize-WoWAddon] - Done"
#endregion - From [private] - [Addon] - [Initialize-WoWAddon]

Write-Verbose "[$scriptName] - [private] - [Addon] - Done"
#endregion - From [private] - [Addon]

#region - From [private] - [BNetAPI]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - Processing folder"

#region - From [private] - [BNetAPI] - [Export-BNetAPISetting]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Export-BNetAPISetting] - Importing"

function Export-BNetAPISetting {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    Write-WoWVerbose 'Export-BNetAPISetting: Export: Start'
    Get-BNetAPIRegion | Select-Object Region | ConvertTo-Json -EnumsAsStrings |
        Out-File -FilePath "$Script:WoW_Folder_Cache\BNetAPISettings.json" -Force
    Write-WoWVerbose 'Export-BNetAPISetting: Export: Done'
}

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Export-BNetAPISetting] - Done"
#endregion - From [private] - [BNetAPI] - [Export-BNetAPISetting]
#region - From [private] - [BNetAPI] - [Get-BNetAPIUserAccessToken]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Get-BNetAPIUserAccessToken] - Importing"


function Get-BNetAPIUserAccessToken {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Token
    Parameter description

    .PARAMETER Region
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        $Token = $Script:BNetAPI_AccessToken,
        $Region = $Script:BNetAPI_Settings.Region
    )
    $Body = @{
        ':region' = $Region
        token     = $Token
    }

    try {
        $params = @{
            Method      = 'Post'
            Uri         = "$($Script:BNetAPI_Settings.BNetAPIPath)oauth/check_token"
            Body        = $Body
            ContentType = 'application/x-www-form-urlencoded'
            ErrorAction = 'Stop'
        }

        $Response = Invoke-RestMethod @params
    } catch {
        throw $_
    }
    return $Response
}

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Get-BNetAPIUserAccessToken] - Done"
#endregion - From [private] - [BNetAPI] - [Get-BNetAPIUserAccessToken]
#region - From [private] - [BNetAPI] - [Get-BNetAPIUserInfo]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Get-BNetAPIUserInfo] - Importing"

function Get-BNetAPIUserInfo {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Token
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param(
        $Token = $Script:BNetAPI_AccessToken
    )
    $Headers = @{
        Authorization = "Bearer $Token"
    }

    try {
        $params = @{
            Method      = 'Get'
            Uri         = "$($Script:BNetAPI_Settings.BNetAPIPath)oauth/userinfo"
            Headers     = $Headers
            ContentType = 'application/x-www-form-urlencoded'
            ErrorAction = 'Stop'
        }
        $Response = Invoke-RestMethod @params
    } catch {
        throw $_
    }
    return $Response
}

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Get-BNetAPIUserInfo] - Done"
#endregion - From [private] - [BNetAPI] - [Get-BNetAPIUserInfo]
#region - From [private] - [BNetAPI] - [Get-BNetAPIWoWRealm]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Get-BNetAPIWoWRealm] - Importing"

function Get-BNetAPIWoWRealm {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    Write-WoWVerbose 'Get-BNetAPIWoWRealm: Finding'
    [Realm[]]$Realms = $null

    $Headers = @{
        'Battlenet-Namespace' = $Script:BNetAPI_Settings.WoWNameSpace.Dynamic
        Authorization         = "Bearer $Script:BNetAPI_AccessToken"
    }
    $APIURI = $Script:BNetAPI_Settings.APIURI
    $Locale = $Script:BNetAPI_Settings.Locale

    $RawRealmIndex = Invoke-RestMethod -Method Get -Uri "$($APIURI)data/wow/realm/index?locale=$($Locale)" -Headers $Headers
    $RawRealmIndex.realms | ForEach-Object -ThrottleLimit 50 -Parallel {
        $params = @{
            Method  = 'Get'
            Uri     = "$using:APIURI/data/wow/realm/$($_.id)?namespace=dynamic-eu&locale=$using:Locale"
            Headers = $using:Headers
        }
        Invoke-RestMethod @params
    } | ForEach-Object {
        $Realms += [Realm]::new(
            $_.id,
            $_.name,
            $_.region.name,
            $_.category,
            $_.locale,
            $_.timezone,
            $_.type.name,
            $_.is_tournament,
            $_.slug)
    }
    return $Realms
}

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Get-BNetAPIWoWRealm] - Done"
#endregion - From [private] - [BNetAPI] - [Get-BNetAPIWoWRealm]
#region - From [private] - [BNetAPI] - [Import-BNetAPISetting]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Import-BNetAPISetting] - Importing"

function Import-BNetAPISetting {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    Write-WoWVerbose 'Import-BNetAPISetting: Import: Start'
    $CacheFilePath = "$Script:WoW_Folder_Cache\BNetAPISettings.json"
    if (Test-Path $CacheFilePath) {
        $ImportedBNetAPISettings = Get-Content $CacheFilePath | ConvertFrom-Json
        Write-WoWVerbose "Import-BNetAPISetting: Import: Setting region to $($ImportedBNetAPISettings.Region)"
        Set-BNetAPIRegion -Region $ImportedBNetAPISettings.Region
        Write-WoWVerbose 'Import-BNetAPISetting: Import: Done'
    } else {
        Write-WoWVerbose 'Import-BNetAPISetting: Import: Nothing to import'
    }
}

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Import-BNetAPISetting] - Done"
#endregion - From [private] - [BNetAPI] - [Import-BNetAPISetting]
#region - From [private] - [BNetAPI] - [Initialize-BNetAPI]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Initialize-BNetAPI] - Importing"

#Force the Invoke-RestMethod PowerShell cmdlet to use TLS 1.2
#[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls13

enum BNetAPIRegion {
    CN = 0
    US = 1
    EU = 2
    KR = 3
    TW = 4
}

$Script:BNetAPI_RegionSettings = @(
    @{
        APIURI       = 'https://gateway.battlenet.com.cn/'
        BNetAPIPath  = 'https://www.battlenet.com.cn/'
        LocaleList   = @('zh_CN')
        Locale       = 'zh_CN'
        Region       = 'cn'
        WoWNameSpace = @{
            Static  = 'static-cn'
            Dynamic = 'dynamic-cn'
            Profile = 'profile-cn'
        }
    }
    @{
        APIURI       = 'https://us.api.blizzard.com/'
        BNetAPIPath  = 'https://us.battle.net/'
        LocaleList   = @('en_US', 'es_MX', 'pt_BR')
        Locale       = 'en_US'
        Region       = 'us'
        WoWNameSpace = @{
            Static  = 'static-us'
            Dynamic = 'dynamic-us'
            Profile = 'profile-us'
        }
    }
    @{
        APIURI       = 'https://eu.api.blizzard.com/'
        BNetAPIPath  = 'https://eu.battle.net/'
        LocaleList   = @('en_GB', 'es_ES', 'fr_FR', 'ru_RU', 'de_DE', 'pt_PT', 'it_IT')
        Locale       = 'en_US'
        Region       = 'eu'
        WoWNameSpace = @{
            Static  = 'static-eu'
            Dynamic = 'dynamic-eu'
            Profile = 'profile-eu'
        }
    }
    @{
        APIURI       = 'https://kr.api.blizzard.com/'
        BNetAPIPath  = 'https://kr.battle.net/'
        LocaleList   = @('ko_KR')
        Locale       = @('ko_KR')
        Region       = 'kr'
        WoWNameSpace = @{
            Static  = 'static-kr'
            Dynamic = 'dynamic-kr'
            Profile = 'profile-kr'
        }
    }
    @{
        APIURI       = 'https://tw.api.blizzard.com/'
        BNetAPIPath  = 'https://tw.battle.net/'
        LocaleList   = @('zh_TW')
        Locale       = @('zh_TW')
        Region       = 'tw'
        WoWNameSpace = @{
            Static  = 'static-tw'
            Dynamic = 'dynamic-tw'
            Profile = 'profile-tw'
        }
    }
)

$Script:BNetAPI_ClientID = '<get from secret vault>'
$Script:BNetAPI_ClientSecret = '<get from secret vault>'
$Script:BNetAPI_Settings = $Script:BNetAPI_RegionSettings[2]

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [Initialize-BNetAPI] - Done"
#endregion - From [private] - [BNetAPI] - [Initialize-BNetAPI]
#region - From [private] - [BNetAPI] - [New-BNetAPIUserAccessToken]
Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [New-BNetAPIUserAccessToken] - Importing"


#https://develop.battle.net/documentation/guides/using-oauth/client-credentials-flow
function New-BNetAPIUserAccessToken {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER ClientID
    Parameter description

    .PARAMETER ClientSecret
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
        Justification = 'Not changing state, just an object in memory.'
    )]
    [CmdletBinding()]
    param(
        $ClientID = $Script:BNetAPI_ClientID,
        $ClientSecret = $Script:BNetAPI_ClientSecret
    )
    $Body = @{
        client_id     = $ClientID
        client_secret = $ClientSecret
        grant_type    = 'client_credentials'
        scope         = ''
    }

    try {
        $params = @{
            Method      = 'Post'
            Uri         = "$($Script:BNetAPI_Settings.BNetAPIPath)oauth/token"
            Body        = $Body
            ContentType = 'application/x-www-form-urlencoded'
            ErrorAction = 'Stop'
        }
        $Response = Invoke-RestMethod @params
    } catch {
        throw $_
    }
    return $Response.access_token
}

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - [New-BNetAPIUserAccessToken] - Done"
#endregion - From [private] - [BNetAPI] - [New-BNetAPIUserAccessToken]

Write-Verbose "[$scriptName] - [private] - [BNetAPI] - Done"
#endregion - From [private] - [BNetAPI]

#region - From [private] - [Character]
Write-Verbose "[$scriptName] - [private] - [Character] - Processing folder"

#region - From [private] - [Character] - [PowerType]
Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - Processing folder"

#region - From [private] - [Character] - [PowerType] - [Export-WoWPowerType]
Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - [Export-WoWPowerType] - Importing"

function Export-WoWPowerType {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param ()

    Get-WoWPowerType | ConvertTo-Json -EnumsAsStrings | Out-File -FilePath "$Script:WoW_Folder_Cache\PowerType.json" -Force
}

Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - [Export-WoWPowerType] - Done"
#endregion - From [private] - [Character] - [PowerType] - [Export-WoWPowerType]
#region - From [private] - [Character] - [PowerType] - [Import-WoWPowerType]
Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - [Import-WoWPowerType] - Importing"

function Import-WoWPowerType {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    $CacheFilePath = "$Script:WoW_Folder_Cache\PowerType.json"
    if (Test-Path $CacheFilePath) {
        $ImportedPowerTypes = Get-Content $CacheFilePath | ConvertFrom-Json
        foreach ($ImportedPowerType in $ImportedPowerTypes) {
            [PowerType]::new()
        }
    } else {
        Write-WoWVerbose 'Import-WoWCharacter: Nothing to import'
    }
}

Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - [Import-WoWPowerType] - Done"
#endregion - From [private] - [Character] - [PowerType] - [Import-WoWPowerType]
#region - From [private] - [Character] - [PowerType] - [Update-WoWPowerType]
Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - [Update-WoWPowerType] - Importing"

function Update-WoWPowerType {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
        Justification = 'Not changing state, just an object in memory.'
    )]
    [CmdletBinding()]
    param()

    [PowerType[]]$PowerTypes = $null
    $Headers = @{
        'Battlenet-Namespace' = $Script:BNetAPI_Settings.WoWNameSpace.Static
        Authorization         = "Bearer $Script:BNetAPI_AccessToken"
    }
    $APIURI = $Script:BNetAPI_Settings.APIURI
    $Locale = $Script:BNetAPI_Settings.Locale

    Write-WoWVerbose 'Update-WoWPowerTypes: Finding PowerTypes online'
    $PowerTypeIndex = Invoke-RestMethod -Method Get -Uri "$($APIURI)data/wow/power-type/index?locale=$Locale" -Headers $Headers
    $PowerTypeIndex.power_types | ForEach-Object -Parallel {
        Invoke-RestMethod -Method Get -Uri "$($using:APIURI)data/wow/power-type/$($_.id)?&locale=$using:Locale" -Headers $using:Headers
    } | ForEach-Object {
        $PowerTypes += [PowerType]::new($_.id, $_.name)
    }
    return $PowerTypes
}

Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - [Update-WoWPowerType] - Done"
#endregion - From [private] - [Character] - [PowerType] - [Update-WoWPowerType]

Write-Verbose "[$scriptName] - [private] - [Character] - [PowerType] - Done"
#endregion - From [private] - [Character] - [PowerType]

#region - From [private] - [Character] - [Export-WoWCharacter]
Write-Verbose "[$scriptName] - [private] - [Character] - [Export-WoWCharacter] - Importing"

function Export-WoWCharacter {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    Get-WoWCharacter | ConvertTo-Json -EnumsAsStrings | Out-File -FilePath "$Script:WoW_Folder_Cache\Characters.json" -Force
}

Write-Verbose "[$scriptName] - [private] - [Character] - [Export-WoWCharacter] - Done"
#endregion - From [private] - [Character] - [Export-WoWCharacter]
#region - From [private] - [Character] - [Import-WoWCharacter]
Write-Verbose "[$scriptName] - [private] - [Character] - [Import-WoWCharacter] - Importing"

function Import-WoWCharacter {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    $CacheFilePath = "$Script:WoW_Folder_Cache\Characters.json"
    if (Test-Path $CacheFilePath) {
        $ImportedCharacters = Get-Content $CacheFilePath | ConvertFrom-Json
        foreach ($ImportedCharacter in $ImportedCharacters) {
            $Character = [Character](
                $Script:WoW_Characters |
                    Where-Object { ($_.Realm.Name -match $ImportedCharacter.Realm.Name) -and ($_.Name -match $ImportedCharacter.Name) } |
                    Select-Object -First 1)
            $Character.ID = $ImportedCharacter.ID
            $Character.Gender = $ImportedCharacter.Gender
            $Character.Faction = $ImportedCharacter.Faction
            $Character.Race = $ImportedCharacter.Race
            $Character.IsMainForClass = $ImportedCharacter.IsMainForClass
            $Character.IsMain = $ImportedCharacter.IsMain
            $Character.Level = $ImportedCharacter.Level
            $Character.Guild = $ImportedCharacter.Guild
            $Character.AchievementPoints = $ImportedCharacter.AchievementPoints
            $Character.LastPlayed = [datetime]$ImportedCharacter.LastPlayed
            $Character.averageItemLevel = $ImportedCharacter.averageItemLevel
            $Character.averageItemLevelEquipped = $ImportedCharacter.averageItemLevelEquipped
            $Character.Gold = $ImportedCharacter.Gold
            $Character.PlayedLevel = (New-TimeSpan).Add($ImportedCharacter.PlayedLevel.Ticks)
            $Character.PlayedTotal = (New-TimeSpan).Add($ImportedCharacter.PlayedTotal.Ticks)
            $Character.isResting = $ImportedCharacter.isResting
            $Character.ArtifactLevel = $ImportedCharacter.ArtifactLevel
            $Character.ArtifactProgress = $ImportedCharacter.ArtifactProgress
            $Character.Zone = $ImportedCharacter.Zone
            $Character.Warmode = $ImportedCharacter.Warmode
            $Character.HasNewMail = $ImportedCharacter.HasNewMail
            $Character.Durability = $ImportedCharacter.Durability
        }
    } else {
        Write-WoWVerbose 'Import-WoWCharacter: Nothing to import'
    }
}

Write-Verbose "[$scriptName] - [private] - [Character] - [Import-WoWCharacter] - Done"
#endregion - From [private] - [Character] - [Import-WoWCharacter]
#region - From [private] - [Character] - [Initialize-WoWCharacter]
Write-Verbose "[$scriptName] - [private] - [Character] - [Initialize-WoWCharacter] - Importing"

function Initialize-WoWCharacter {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER WoWAccountsFolderPath
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Cmdletbinding()]
    param(
        $WoWAccountsFolderPath = $Script:WoW_Folder_Accounts
    )
    [Character[]]$Characters = $null

    Write-WoWVerbose 'Characters: Finding'
    $AccountFolders = $WoWAccountsFolderPath | Get-ChildItem -Directory | Where-Object Name -NE SavedVariables
    $RealmFolders = $AccountFolders | Get-ChildItem -Directory | Where-Object Name -NE SavedVariables
    $CharFolders = $RealmFolders | Get-ChildItem -Directory | Where-Object Name -NE SavedVariables | Sort-Object name
    Write-WoWVerbose "Characters: Found $($CharFolders.count)"

    Write-WoWVerbose 'Characters: Processing'
    $i = 0
    foreach ($CharFolder in $CharFolders) {
        $i++
        $Status = "$i/$($CharFolders.count)"
        Write-WoWVerbose "Characters: Processing: $Status $($CharFolder.Name)"

        $Char = [Character]::new()
        $Char.FolderPath = $CharFolder
        $CharFolderCN = ($CharFolder -split ('\\'))
        $CharName = $CharFolderCN[-1]
        $CharRealmName = $CharFolderCN[-2]
        $CharAccountName = $CharFolderCN[-3]
        $Char.Name = $CharName
        $Char.Realm = $Script:WoW_Realms | Where-Object Name -EQ $CharRealmName
        $Char.Account = $Script:WoW_Accounts | Where-Object Name -EQ $CharAccountName
        $Char.Account.Characters += $Char
        $Char.IsMainForClass = $false
        $Char.IsMain = $false
        $Char.SettingsFolderPath = "$($Char.FolderPath)\SavedVariables"
        $Char.AddOnsFilePath = "$($Char.FolderPath)\AddOns.txt"
        $Char.BindingsCachePath = "$($Char.FolderPath)\bindings-cache.wtf"
        $Char.ConfigCachePath = "$($Char.FolderPath)\config-cache.wtf"
        $Char.MacrosCachePath = "$($Char.FolderPath)\macros-cache.txt"
        $Characters += $Char

        Write-WoWVerbose "Characters: Processing: $Status $($CharFolder.Name): Done"
    }

    Write-WoWVerbose 'Characters: Processing: Done'
    return $Characters | Sort-Object name
}

Write-Verbose "[$scriptName] - [private] - [Character] - [Initialize-WoWCharacter] - Done"
#endregion - From [private] - [Character] - [Initialize-WoWCharacter]

Write-Verbose "[$scriptName] - [private] - [Character] - Done"
#endregion - From [private] - [Character]

#region - From [private] - [Core]
Write-Verbose "[$scriptName] - [private] - [Core] - Processing folder"

#region - From [private] - [Core] - [Find-WoWFolder]
Write-Verbose "[$scriptName] - [private] - [Core] - [Find-WoWFolder] - Importing"

function Find-WoWFolder {
    <#
    .SYNOPSIS
    Find WoW (retail) folder

    .DESCRIPTION
    Finds WoW (retail) folder, by first checking for the default location, then looking for all folders with a WoW.exe file.
    User is prompted for possible location where WoW.exe is found.

    .EXAMPLE
    Get-WoWFolder

    #>

    [Cmdletbinding()]
    [OutputType([System.IO.DirectoryInfo])]
    param(
        $Path = 'C:\Program Files (x86)\World of Warcraft\_retail_'
    )
    $DefaultPath = 'C:\Program Files (x86)\World of Warcraft\_retail_'

    Write-WoWVerbose 'Looking for WoW folders'
    if (Test-Path -Path $Path) {
        Write-WoWVerbose "Found $Path"
        Write-WoWVerbose "Looking for WoW.exe in $Path"
        $WoWFile = Get-ChildItem -Path $Path -Filter Wow.exe -File -Recurse -ErrorAction SilentlyContinue
        if ($WoWFile) {
            Write-WoWVerbose "Found WoW.exe in $($WoWFile.Directory)"
            return $WoWFile.Directory
        }
    } elseif (Test-Path -Path $DefaultPath) {
        Write-WoWVerbose "Found $DefaultPath"
        Write-WoWVerbose "Looking for WoW.exe in $DefaultPath"
        $WoWFile = Get-ChildItem -Path $DefaultPath -Filter Wow.exe -File -Recurse -ErrorAction SilentlyContinue
        if ($WoWFile) {
            Write-WoWVerbose "Found WoW.exe in $($WoWFile.Directory)"
            return $WoWFile.Directory
        }
    } else {
        $Disks = Get-Volume | Where-Object { ($null -ne $_.DriveLetter) -and ($_.DriveType -eq 'Fixed') } |
            Select-Object -ExpandProperty DriveLetter | ForEach-Object { Get-Item -Path "$_`:\" }
        $WoWFolders = $Disks | Get-ChildItem -Recurse -ErrorAction SilentlyContinue -Include 'wow.exe' |
            Select-Object -ExpandProperty DirectoryName

        if ($WoWFolders.count -gt 1) {
            Write-WoWVerbose 'Multiple instances of WoW detected:'
            $i = 0
            # $WoWFolder = $WoWFolders[0]
            foreach ($WoWFolder in $WoWFolders) {
                Write-WoWVerbose "$i - $WoWFolder"
                $i++
            }
            $Selection = Read-Host -Prompt 'Select WoW instance: '
        } else {
            return $WoWFolders
        }
        return $WoWFolders[$Selection]
    }
}

Write-Verbose "[$scriptName] - [private] - [Core] - [Find-WoWFolder] - Done"
#endregion - From [private] - [Core] - [Find-WoWFolder]
#region - From [private] - [Core] - [Get-WoWCacheFolder]
Write-Verbose "[$scriptName] - [private] - [Core] - [Get-WoWCacheFolder] - Importing"

function Get-WoWCacheFolder {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER FolderPath
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Cmdletbinding()]
    param(
        [string]
        [Parameter(Mandatory)]
        $FolderPath
    )
    if (Test-Path $FolderPath) {
        Write-WoWVerbose 'Folder exists, returning the object'
        $FolderObj = Get-Item -Path $FolderPath
    } else {
        Write-WoWVerbose "Folder doen't exists, creating folder"
        $FolderObj = New-Item -Path $FolderPath -ItemType Directory -Force
        Write-WoWVerbose 'Folder created'
    }
    return $FolderObj
}

Write-Verbose "[$scriptName] - [private] - [Core] - [Get-WoWCacheFolder] - Done"
#endregion - From [private] - [Core] - [Get-WoWCacheFolder]
#region - From [private] - [Core] - [Write-WoWVerbose]
Write-Verbose "[$scriptName] - [private] - [Core] - [Write-WoWVerbose] - Importing"


function Write-WoWVerbose {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Message
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Cmdletbinding()]
    param(
        [string]
        $Message
    )
    <#
    $CallStack = (Get-PSCallStack).Command | Select-Object -SkipLast 1
    if ($CallStack.count -gt 1) {
        $CallStack = $CallStack | Select-Object -Skip 1
        $CallStack = $CallStack | Select-Object -Skip 1 | Select-Object -First 1
        [array]::Reverse($CallStack)
        $CallStack = $CallStack -join " > "
    }
    Write-Verbose "[$(Get-Date -Format $Script:WoW_DateFormat)]: $CallStack`: $Message"
    #>

    Write-Verbose "[$(Get-Date -Format $Script:WoW_DateFormat)]: $Message"
}

Write-Verbose "[$scriptName] - [private] - [Core] - [Write-WoWVerbose] - Done"
#endregion - From [private] - [Core] - [Write-WoWVerbose]
#region - From [private] - [Core] - [Write-WoWWarning]
Write-Verbose "[$scriptName] - [private] - [Core] - [Write-WoWWarning] - Importing"

function Write-WoWWarning {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Message
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Cmdletbinding()]
    param(
        [Parameter(Mandatory)]
        [string]
        $Message
    )
    $CallStack = (Get-PSCallStack).Command | Select-Object -SkipLast 1
    if ($CallStack.count -gt 1) {
        $CallStack = $CallStack | Select-Object -Skip 1
        [array]::Reverse($CallStack)
    }
    Write-Warning "[$(Get-Date -Format $Script:DateFormat)]: $CallStack`: $Message"
}

Write-Verbose "[$scriptName] - [private] - [Core] - [Write-WoWWarning] - Done"
#endregion - From [private] - [Core] - [Write-WoWWarning]

Write-Verbose "[$scriptName] - [private] - [Core] - Done"
#endregion - From [private] - [Core]

#region - From [private] - [Realm]
Write-Verbose "[$scriptName] - [private] - [Realm] - Processing folder"

#region - From [private] - [Realm] - [Export-WoWRealm]
Write-Verbose "[$scriptName] - [private] - [Realm] - [Export-WoWRealm] - Importing"

function Export-WoWRealm {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    Get-WoWRealm | ConvertTo-Json -EnumsAsStrings | Out-File -FilePath "$Script:WoW_Folder_Cache\Realms.json" -Force
}

Write-Verbose "[$scriptName] - [private] - [Realm] - [Export-WoWRealm] - Done"
#endregion - From [private] - [Realm] - [Export-WoWRealm]
#region - From [private] - [Realm] - [Import-WoWRealm]
Write-Verbose "[$scriptName] - [private] - [Realm] - [Import-WoWRealm] - Importing"

function Import-WoWRealm {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Realm[]]$Realms = $null
    $CacheFilePath = "$Script:WoW_Folder_Cache\Realms.json"
    if (Test-Path $CacheFilePath) {
        $ImportedRealms = Get-Content $CacheFilePath | ConvertFrom-Json
        #$ImportedRealm = $ImportedRealms[0]
        foreach ($ImportedRealm in $ImportedRealms) {
            $Realm = [Realm]::new()
            $Realm.ID = $ImportedRealm.ID
            $Realm.Region = $ImportedRealm.Region
            $Realm.Category = $ImportedRealm.Category
            $Realm.Locale = $ImportedRealm.Locale
            $Realm.Timezone = $ImportedRealm.Timezone
            $Realm.Type = $ImportedRealm.Type
            $Realm.IsTournament = $ImportedRealm.IsTournament
            $Realm.Slug = $ImportedRealm.Slug
            $Realms += $Realm
        }
        return $Realms
    } else {
        Write-WoWVerbose 'Import-WoWRealm: Nothing to import'
        return
    }
}

Write-Verbose "[$scriptName] - [private] - [Realm] - [Import-WoWRealm] - Done"
#endregion - From [private] - [Realm] - [Import-WoWRealm]
#region - From [private] - [Realm] - [Initialize-WoWRealm]
Write-Verbose "[$scriptName] - [private] - [Realm] - [Initialize-WoWRealm] - Importing"

function Initialize-WoWRealm {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER WoWAccountsFolderPath
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([Realm[]])]
    param(
        $WoWAccountsFolderPath = $Script:WoW_Folder_Accounts
    )
    [Realm[]]$Realms = $null

    Write-WoWVerbose 'Realms: Finding'
    $AccountFolders = $WoWAccountsFolderPath | Get-ChildItem -Directory | Where-Object Name -NE SavedVariables
    $RealmNames = $AccountFolders | Get-ChildItem -Directory | Where-Object Name -NE SavedVariables | Select-Object -ExpandProperty Name -Unique
    Write-WoWVerbose "Realms: Found $($RealmNames.count)"

    Write-WoWVerbose 'Realms: Processing'
    $i = 0
    # $AccountFolder = $AccountFolders | Where-Object name -match "Catchius"
    foreach ($RealmName in $RealmNames) {
        $i++
        $Status = "$i/$($RealmNames.count)"
        Write-WoWVerbose "Realms: Processing: $Status $RealmName"

        $Realm = [Realm]::new($RealmName)
        $Realms += $Realm

        Write-WoWVerbose "Realms: Processing: $Status $($RealmName): Done"
    }

    Write-WoWVerbose 'Realms: Processing: Done'
    return $Realms | Sort-Object name
}

Write-Verbose "[$scriptName] - [private] - [Realm] - [Initialize-WoWRealm] - Done"
#endregion - From [private] - [Realm] - [Initialize-WoWRealm]
#region - From [private] - [Realm] - [Update-WoWRealm]
Write-Verbose "[$scriptName] - [private] - [Realm] - [Update-WoWRealm] - Importing"

function Update-WoWRealm {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
        Justification = 'Not changing state, just an object in memory.'
    )]
    [CmdletBinding()]
    param()
    [Realm[]]$Realms = $null
    $Headers = @{
        'Battlenet-Namespace' = $Script:BNetAPI_Settings.WoWNameSpace.Dynamic
        Authorization         = "Bearer $Script:BNetAPI_AccessToken"
    }
    $APIURI = $Script:BNetAPI_Settings.APIURI
    $Locale = $Script:BNetAPI_Settings.Locale
    #$ApplicableRealms = $Script:WoW_Realms.Name

    Write-WoWVerbose 'Update-WoWRealm: Finding realms online'
    $RealmIndex = Invoke-RestMethod -Method Get -Uri "$($APIURI)data/wow/realm/index?locale=$Locale" -Headers $Headers
    $RealmsToUpdate = $RealmIndex.realms # | Where-Object Name -In -Value $ApplicableRealms
    $RealmsToUpdate | ForEach-Object -ThrottleLimit 50 -Parallel {
        Invoke-RestMethod -Method Get -Uri "$($using:APIURI)data/wow/realm/$($_.id)?locale=$using:Locale" -Headers $using:Headers
    } | ForEach-Object {
        $Realms += [Realm]::new(
            $_.id,
            $_.name,
            $_.region.name,
            $_.category,
            $_.locale,
            $_.timezone,
            $_.type.name,
            $_.is_tournament,
            $_.slug)
    }
    return $Realms
}

Write-Verbose "[$scriptName] - [private] - [Realm] - [Update-WoWRealm] - Done"
#endregion - From [private] - [Realm] - [Update-WoWRealm]

Write-Verbose "[$scriptName] - [private] - [Realm] - Done"
#endregion - From [private] - [Realm]


Write-Verbose "[$scriptName] - [private] - Done"
#endregion - From [private]

#region - From [public]
Write-Verbose "[$scriptName] - [public] - Processing folder"

#region - From [public] - [Account]
Write-Verbose "[$scriptName] - [public] - [Account] - Processing folder"

#region - From [public] - [Account] - [Get-WoWAccount]
Write-Verbose "[$scriptName] - [public] - [Account] - [Get-WoWAccount] - Importing"

function Get-WoWAccount {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Name
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [String]
        $Name
    )
    return $Script:WoW_Accounts | Where-Object Name -Match $Name
}

Write-Verbose "[$scriptName] - [public] - [Account] - [Get-WoWAccount] - Done"
#endregion - From [public] - [Account] - [Get-WoWAccount]
#region - From [public] - [Account] - [Set-WoWAccount]
Write-Verbose "[$scriptName] - [public] - [Account] - [Set-WoWAccount] - Importing"

function Set-WoWAccount {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Name
    Parameter description

    .PARAMETER IsMain
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
        Justification = 'Not changing state, just an object in memory.'
    )]
    [cmdletbinding()]
    param(
        [parameter(mandatory)]
        [String]
        $Name,
        [bool]
        $IsMain
    )

    [Account]$Account = Get-WoWAccount | Where-Object Name -Match $Name
    $Account.IsMain = $IsMain
    Export-WoWAccount
}

Write-Verbose "[$scriptName] - [public] - [Account] - [Set-WoWAccount] - Done"
#endregion - From [public] - [Account] - [Set-WoWAccount]
#region - From [public] - [Account] - [Show-WoWAccount]
Write-Verbose "[$scriptName] - [public] - [Account] - [Show-WoWAccount] - Importing"

function Show-WoWAccount {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([void])]
    param()
    $Script:WoW_Account | Out-GridView -Title 'WoWManager - List WoW Accounts'
}

Write-Verbose "[$scriptName] - [public] - [Account] - [Show-WoWAccount] - Done"
#endregion - From [public] - [Account] - [Show-WoWAccount]

Write-Verbose "[$scriptName] - [public] - [Account] - Done"
#endregion - From [public] - [Account]

#region - From [public] - [Addon]
Write-Verbose "[$scriptName] - [public] - [Addon] - Processing folder"

#region - From [public] - [Addon] - [Get-WoWAddon]
Write-Verbose "[$scriptName] - [public] - [Addon] - [Get-WoWAddon] - Importing"

function Get-WoWAddon {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Name
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([Addon[]])]
    param(
        [String]
        $Name
    )
    return $Script:WoW_Addons | Where-Object Name -Match $Name
}

Write-Verbose "[$scriptName] - [public] - [Addon] - [Get-WoWAddon] - Done"
#endregion - From [public] - [Addon] - [Get-WoWAddon]
#region - From [public] - [Addon] - [Select-WoWAddon]
Write-Verbose "[$scriptName] - [public] - [Addon] - [Select-WoWAddon] - Importing"

function Select-WoWAddon {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([Addon[]])]
    param()
    return $Script:WoW_Addons | Out-GridView -Title 'WoWManager - Installed WoW Addons' -OutputMode Multiple
}

Write-Verbose "[$scriptName] - [public] - [Addon] - [Select-WoWAddon] - Done"
#endregion - From [public] - [Addon] - [Select-WoWAddon]
#region - From [public] - [Addon] - [Show-WoWAddon]
Write-Verbose "[$scriptName] - [public] - [Addon] - [Show-WoWAddon] - Importing"

function Show-WoWAddon {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([void])]
    param()
    $Script:WoW_Addons | Out-GridView -Title 'WoWManager - Installed WoW Addons'
}

Write-Verbose "[$scriptName] - [public] - [Addon] - [Show-WoWAddon] - Done"
#endregion - From [public] - [Addon] - [Show-WoWAddon]

Write-Verbose "[$scriptName] - [public] - [Addon] - Done"
#endregion - From [public] - [Addon]

#region - From [public] - [BNetAPI]
Write-Verbose "[$scriptName] - [public] - [BNetAPI] - Processing folder"

#region - From [public] - [BNetAPI] - [Get-BNetAPIRegion]
Write-Verbose "[$scriptName] - [public] - [BNetAPI] - [Get-BNetAPIRegion] - Importing"

function Get-BNetAPIRegion {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    return $Script:BNetAPI_Settings
}

Write-Verbose "[$scriptName] - [public] - [BNetAPI] - [Get-BNetAPIRegion] - Done"
#endregion - From [public] - [BNetAPI] - [Get-BNetAPIRegion]
#region - From [public] - [BNetAPI] - [Set-BNetAPIRegion]
Write-Verbose "[$scriptName] - [public] - [BNetAPI] - [Set-BNetAPIRegion] - Importing"


function Set-BNetAPIRegion {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Region
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
        Justification = 'Not changing state, just an object in memory.'
    )]
    [CmdletBinding()]
    param(
        [BNetAPIRegion]
        $Region
    )
    $Script:BNetAPI_Settings = $Script:BNetAPI_RegionSettings[$Region]
    Export-BNetAPISetting
}

Write-Verbose "[$scriptName] - [public] - [BNetAPI] - [Set-BNetAPIRegion] - Done"
#endregion - From [public] - [BNetAPI] - [Set-BNetAPIRegion]

Write-Verbose "[$scriptName] - [public] - [BNetAPI] - Done"
#endregion - From [public] - [BNetAPI]

#region - From [public] - [Character]
Write-Verbose "[$scriptName] - [public] - [Character] - Processing folder"

#region - From [public] - [Character] - [PowerType]
Write-Verbose "[$scriptName] - [public] - [Character] - [PowerType] - Processing folder"

#region - From [public] - [Character] - [PowerType] - [Get-WoWPowerType]
Write-Verbose "[$scriptName] - [public] - [Character] - [PowerType] - [Get-WoWPowerType] - Importing"

function Get-WoWPowerType {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Name
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [String]
        $Name
    )
    return $Script:WoW_PowerTypes | Where-Object Name -Match $Name
}

Write-Verbose "[$scriptName] - [public] - [Character] - [PowerType] - [Get-WoWPowerType] - Done"
#endregion - From [public] - [Character] - [PowerType] - [Get-WoWPowerType]

Write-Verbose "[$scriptName] - [public] - [Character] - [PowerType] - Done"
#endregion - From [public] - [Character] - [PowerType]

#region - From [public] - [Character] - [Get-WoWCharacter]
Write-Verbose "[$scriptName] - [public] - [Character] - [Get-WoWCharacter] - Importing"

function Get-WoWCharacter {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Name
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [String]
        $Name
    )
    return $Script:WoW_Characters | Where-Object Name -Match $Name
}

Write-Verbose "[$scriptName] - [public] - [Character] - [Get-WoWCharacter] - Done"
#endregion - From [public] - [Character] - [Get-WoWCharacter]
#region - From [public] - [Character] - [Set-WoWCharacter]
Write-Verbose "[$scriptName] - [public] - [Character] - [Set-WoWCharacter] - Importing"

function Set-WoWCharacter {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Name
    Parameter description

    .PARAMETER Realm
    Parameter description

    .PARAMETER IsMain
    Parameter description

    .PARAMETER IsMainForClass
    Parameter description

    .PARAMETER ClassName
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
        Justification = 'Not changing state, just an object in memory.'
    )]
    [Cmdletbinding()]
    param(
        [parameter(mandatory)]
        [String]
        $Name,
        [parameter(mandatory)]
        [String]
        $Realm,
        [bool]
        $IsMain,
        [bool]
        $IsMainForClass,
        [string]
        $ClassName
    )

    $Character = [Character]($Script:WoW_Characters |
            Where-Object { ($_.Realm.Name -match $Realm) -and ($_.Name -match $Name) } | Select-Object -First 1)
    if ($Character.count -eq 1) {
        if ($PSBoundParameters.ContainsKey('IsMain')) {
            $Character.IsMain = $IsMain
        }
        if ($PSBoundParameters.ContainsKey('IsMainForClass')) {
            $Character.IsMainForClass = $IsMainForClass
        }
        if ($PSBoundParameters.ContainsKey('ClassName')) {
            $Character.Class = $Script:WoW_Classes | Where-Object Name -Match $ClassName
        }

        Export-WoWCharacter
    }
}

Write-Verbose "[$scriptName] - [public] - [Character] - [Set-WoWCharacter] - Done"
#endregion - From [public] - [Character] - [Set-WoWCharacter]

Write-Verbose "[$scriptName] - [public] - [Character] - Done"
#endregion - From [public] - [Character]

#region - From [public] - [Core]
Write-Verbose "[$scriptName] - [public] - [Core] - Processing folder"

#region - From [public] - [Core] - [Get-WoWFolder]
Write-Verbose "[$scriptName] - [public] - [Core] - [Get-WoWFolder] - Importing"

function Get-WoWFolder {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Folder
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    param(
        [ValidateSet('All', 'Root', 'Account', 'WTF', 'Cache')]
        [String]
        $Folder = 'Root'
    )
    switch ($Folder) {
        'All' {
            return Get-Variable -Scope Script -Name WoW_Folder_* | Sort-Object Value
        }
        'Root' {
            return $Script:WoW_Folder_Root
        }
        'Account' {
            return $Script:WoW_Folder_Account
        }
        'WTF' {
            return $Script:WoW_Folder_WTF
        }
        'Cache' {
            return $Script:WoW_Folder_Cache
        }
        default {}
    }
}

Write-Verbose "[$scriptName] - [public] - [Core] - [Get-WoWFolder] - Done"
#endregion - From [public] - [Core] - [Get-WoWFolder]
#region - From [public] - [Core] - [Invoke-WoWManager]
Write-Verbose "[$scriptName] - [public] - [Core] - [Invoke-WoWManager] - Importing"

function Invoke-WoWManager {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param()
    #Common date format
    $Script:WoW_DateFormat = 'yyyyMMdd-HHmmss'

    $Script:WoW_Folder_Root = Find-WoWFolder
    $Script:WoW_Folder_WTF = Get-Item "$Script:WoW_Folder_Root\WTF"
    $Script:WoW_Folder_Accounts = Get-Item "$Script:WoW_Folder_WTF\Account"
    $Script:WoW_Folder_Addons = Get-Item "$Script:WoW_Folder_Root\Interface\addons"

    #Location for saving data
    $Script:WoW_Folder_Cache = Get-WoWCacheFolder -FolderPath "$env:appdata\WoWManager"

    #Preparing connection with BNet API
    $Script:BNetAPI_AccessToken = New-BNetAPIUserAccessToken
    Import-BNetAPISetting
    Export-BNetAPISetting

    #Collecting info from the game folder
    [Addon[]]$Script:WoW_Addons = Initialize-WoWAddon -WoWAddonsFolderPath $Script:WoW_Folder_Addons
    [Account[]]$Script:WoW_Accounts = Initialize-WoWAccount -WoWAccountsFolderPath $Script:WoW_Folder_Accounts
    Import-WoWAccount
    Export-WoWAccount

    #[Realm[]]$Script:WoW_Realms = Initialize-WoWRealm -WoWAccountsFolderPath $Script:WoW_Folder_Accounts
    [Realm[]]$Script:WoW_Realms = Import-WoWRealm
    Export-WoWRealm

    [PowerType[]]$Script:WoW_PowerTypes = Update-WoWPowerTypes

    [Character[]]$Script:WoW_Characters = Initialize-WoWCharacter -WoWAccountsFolderPath $Script:WoW_Folder_Accounts
    Import-WoWCharacter
    Export-WoWCharacter
}

Write-Verbose "[$scriptName] - [public] - [Core] - [Invoke-WoWManager] - Done"
#endregion - From [public] - [Core] - [Invoke-WoWManager]

Write-Verbose "[$scriptName] - [public] - [Core] - Done"
#endregion - From [public] - [Core]

#region - From [public] - [Realm]
Write-Verbose "[$scriptName] - [public] - [Realm] - Processing folder"

#region - From [public] - [Realm] - [Get-WoWRealm]
Write-Verbose "[$scriptName] - [public] - [Realm] - [Get-WoWRealm] - Importing"

function Get-WoWRealm {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .PARAMETER Name
    Parameter description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([Addon[]])]
    param(
        [String]
        $Name
    )
    return $Script:WoW_Realms | Where-Object Name -Match $Name
}

Write-Verbose "[$scriptName] - [public] - [Realm] - [Get-WoWRealm] - Done"
#endregion - From [public] - [Realm] - [Get-WoWRealm]
#region - From [public] - [Realm] - [Update-WoWRealm]
Write-Verbose "[$scriptName] - [public] - [Realm] - [Update-WoWRealm] - Importing"

function Update-WoWRealm {
    <#
    .SYNOPSIS
    Short description

    .DESCRIPTION
    Long description

    .EXAMPLE
    An example

    .NOTES
    General notes
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
        'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function',
        Justification = 'Not changing state, just an object in memory.'
    )]
    [CmdletBinding()]
    param()
    [Realm[]]$Realms = $null
    $Headers = @{
        'Battlenet-Namespace' = $Script:BNetAPI_Settings.WoWNameSpace.Dynamic
        Authorization         = "Bearer $Script:BNetAPI_AccessToken"
    }
    $APIURI = $Script:BNetAPI_Settings.APIURI
    $Locale = $Script:BNetAPI_Settings.Locale
    #$ApplicableRealms = $Script:WoW_Realms.Name

    Write-WoWVerbose 'Update-WoWRealm: Finding realms online'
    $RealmIndex = Invoke-RestMethod -Method Get -Uri "$($APIURI)data/wow/realm/index?locale=$Locale" -Headers $Headers
    $RealmsToUpdate = $RealmIndex.realms # | Where-Object Name -In -Value $ApplicableRealms
    $RealmsToUpdate | ForEach-Object -ThrottleLimit 50 -Parallel {
        Invoke-RestMethod -Method Get -Uri "$($using:APIURI)data/wow/realm/$($_.id)?locale=$using:Locale" -Headers $using:Headers
    } | ForEach-Object {
        $Realms += [Realm]::new(
            $_.id,
            $_.name,
            $_.region.name,
            $_.category,
            $_.locale,
            $_.timezone,
            $_.type.name,
            $_.is_tournament,
            $_.slug)
    }
    return $Realms
}

Write-Verbose "[$scriptName] - [public] - [Realm] - [Update-WoWRealm] - Done"
#endregion - From [public] - [Realm] - [Update-WoWRealm]

Write-Verbose "[$scriptName] - [public] - [Realm] - Done"
#endregion - From [public] - [Realm]


Write-Verbose "[$scriptName] - [public] - Done"
#endregion - From [public]

# Get the internal TypeAccelerators class to use its static methods.
$TypeAcceleratorsClass = [psobject].Assembly.GetType(
    'System.Management.Automation.TypeAccelerators'
)
# Ensure none of the types would clobber an existing type accelerator.
# If a type accelerator with the same name exists, throw an exception.
$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get
# Define the types to export with type accelerators.
$ExportableEnums = @(
    [Role]
    [Faction]
    [Gender]
    [BNetAPIRegion]
)
$ExportableEnums | Foreach-Object { Write-Verbose "Exporting enum '$($_.FullName)'." }
foreach ($Type in $ExportableEnums) {
    if ($Type.FullName -in $ExistingTypeAccelerators.Keys) {
        Write-Warning "Enum already exists [$($Type.FullName)]. Skipping."
    } else {
        Write-Verbose "Importing enum '$Type'."
        $TypeAcceleratorsClass::Add($Type.FullName, $Type)
    }
}
$ExportableClasses = @(
    [Account]
    [Addon]
    [AzeriteEssence]
    [PlayableClass]
    [Character]
    [Currency]
    [PowerType]
    [Race]
    [Realm]
    [Specialization]
)
$ExportableClasses | Foreach-Object { Write-Verbose "Exporting class '$($_.FullName)'." }
foreach ($Type in $ExportableClasses) {
    if ($Type.FullName -in $ExistingTypeAccelerators.Keys) {
        Write-Warning "Class already exists [$($Type.FullName)]. Skipping."
    } else {
        Write-Verbose "Importing class '$Type'."
        $TypeAcceleratorsClass::Add($Type.FullName, $Type)
    }
}

# Remove type accelerators when the module is removed.
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
    foreach ($Type in ($ExportableEnums + $ExportableClasses)) {
        $TypeAcceleratorsClass::Remove($Type.FullName)
    }
}.GetNewClosure()
$exports = @{
    Alias    = '*'
    Cmdlet   = ''
    Function = @(
        'Get-WoWAccount'
        'Set-WoWAccount'
        'Show-WoWAccount'
        'Get-WoWAddon'
        'Select-WoWAddon'
        'Show-WoWAddon'
        'Get-BNetAPIRegion'
        'Set-BNetAPIRegion'
        'Get-WoWPowerType'
        'Get-WoWCharacter'
        'Set-WoWCharacter'
        'Get-WoWFolder'
        'Invoke-WoWManager'
        'Get-WoWRealm'
        'Update-WoWRealm'
    )
    Variable = ''
}
Export-ModuleMember @exports