Format/src/Format.psm1

Import-Module $PSScriptRoot\..\..\TypeClass\src\TypeClass.psm1

function Format-Collection ($Value, [switch]$Pretty) { 
    $separator = ', '
    if ($Pretty){
        $separator = ",`n"
    }
    ($Value | % { Format-Custom -Value $_ -Pretty:$Pretty }) -join $separator
}

function Format-Object ($Value, $Property, [switch]$Pretty) {
    if ($null -eq $Property)
    {
        $Property = $Value.PSObject.Properties | Select-Object -ExpandProperty Name
    }
    $orderedProperty = $Property | Sort-Object
    $valueType = Get-ShortType $Value
    $valueFormatted = ([string]([PSObject]$Value | Select-Object -Property $orderedProperty))

    if ($Pretty) {
        $margin = " "
        $valueFormatted = $valueFormatted `
            -replace '^@{',"@{`n$margin" `
            -replace '; ',";`n$margin" `
            -replace '}$',"`n}" `
    }

    $valueFormatted -replace "^@", $valueType
}

function Format-Null {
    '$null'
}

function Format-Boolean ($Value) {
    '$' + $Value.ToString().ToLower()
}

function Format-ScriptBlock ($Value) {
    '{' + $Value + '}'
}

function Format-Number ($Value) { 
    [string]$Value
}

function Format-Hashtable ($Value) {
    $head = '@{'
    $tail = '}'

    $entries = $Value.Keys | sort | foreach { 
        $formattedValue = Format-Custom $Value.$_
        "$_=$formattedValue" }
    
    $head + ( $entries -join '; ') + $tail
}

function Format-Dictionary ($Value) {
    $head = 'Dictionary{'
    $tail = '}'

    $entries = $Value.Keys | sort | foreach { 
        $formattedValue = Format-Custom $Value.$_
        "$_=$formattedValue" }
    
    $head + ( $entries -join '; ') + $tail
}

function Format-Custom ($Value, [switch]$Pretty) { 
    if ($null -eq $Value) 
    { 
        return Format-Null -Value $Value
    }

    if ($Value -is [bool])
    {
        return Format-Boolean -Value $Value
    }

    if (Test-DecimalNumber -Value $Value) 
    {
        return Format-Number -Value $Value
    }

    if (Test-ScriptBlock -Value $Value)
    {
        return Format-ScriptBlock -Value $Value
    }

    if (Test-Value -Value $Value) 
    { 
        return $Value
    }

    if (Test-Hashtable -Value $Value)
    {
        return Format-Hashtable -Value $Value
    }
    
    if (Test-Dictionary -Value $Value)
    {
        return Format-Dictionary -Value $Value
    }

    if (Test-Collection -Value $Value) 
    { 
        return Format-Collection -Value $Value -Pretty:$Pretty
    }

    Format-Object -Value $Value -Property (Get-IdentityProperty ($Value.GetType())) -Pretty:$Pretty
}

function Get-IdentityProperty ([Type]$Type) {
    # rename to Get-DisplayProperty?

    <# some objects are simply too big to show all of their properties,
    so we can create a list of properties to show from an object
    maybe the default info from Get-FormatData could be utilized here somehow
    so we show only stuff that would normally show in format-table view
    leveraging the work PS team already did #>


    # this will become more advanced, basically something along the lines of:
    # foreach type, try constructing the type, and if it exists then check if the
    # incoming type is assingable to the current type, if so then return the properties,
    # this way I can specify the map from the most concrete type to the least concrete type
    # and for types that do not exist
 
    $propertyMap = @{
        'System.Diagnostics.Process' = 'Id', 'Name'
    }
    
    $propertyMap[$Type.FullName]
}

function Get-ShortType ($Value) {
    $type = '<null>'
    if ($null -ne $value)
    {
        $type = ([string]$Value.GetType()) 
    }
    $type `
        -replace "^System\." `
        -replace "^Management\.Automation\.PSCustomObject$","PSObject" `
        -replace "^Object\[\]$","collection" `
}

Export-ModuleMember -Function @(
    'Format-Collection'
    'Format-Object'
    'Format-Null'
    'Format-Boolean'
    'Format-ScriptBlock'
    'Format-Number'
    'Format-Hashtable'
    'Format-Dictionary'
    'Format-Custom'
    'Get-IdentityProperty'
    'Get-ShortType'
)