DirwTools.psm1

function Format-FileSize {
    param ([long]$bytes)
    if ($bytes -ge 1GB) { "{0:N1} GB" -f ($bytes / 1GB) }
    elseif ($bytes -ge 1MB) { "{0:N1} MB" -f ($bytes / 1MB) }
    elseif ($bytes -ge 1KB) { "{0:N1} KB" -f ($bytes / 1KB) }
    else { "$bytes B" }
}

function Get-FolderSize {
    param ($folderPath)
    try {
        (Get-ChildItem -LiteralPath $folderPath -Recurse -Force -File -ErrorAction SilentlyContinue |
         Measure-Object -Property Length -Sum).Sum
    } catch {
        0
    }
}

function dirw {
<#
.SYNOPSIS
  Multi-column advanced DIR-like file listing for PowerShell.
.DESCRIPTION
  Shows files and folders in multi-column format, like DIR /W, with color and (optional) file info. Supports sorting.
.PARAMETER Target
  Folder or wildcard pattern (e.g. C:\Temp\*.txt). Default: current directory.
.PARAMETER l
  Show sizes/dates (long format).
.PARAMETER s
  Calculate folder sizes (recursive, slow on big dirs; implies -l).
.PARAMETER a
  Show hidden files/directories.
.PARAMETER sn
  Sort by name (default).
.PARAMETER ss
  Sort by size.
.PARAMETER sd
  Sort by date.
.PARAMETER sortName
  Sort by name (long key).
.PARAMETER sortSize
  Sort by size (long key).
.PARAMETER sortDate
  Sort by date (long key).
.PARAMETER oa
  Order ascending (default).
.PARAMETER od
  Order descending.
.PARAMETER orderAscending
  Order ascending (long key).
.PARAMETER orderDescending
  Order descending (long key).
.EXAMPLE
  dirw -l -a -ss -od
  dirw c:\temp\*.txt -ss
.NOTES
  Author: Michael.Voitovich@gmail.com
#>

    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(Position=0)]
        [string]$Target = ".",

        [switch]$l,
        [switch]$s,
        [switch]$a,
        [switch]$h,

        [switch]$long,
        [switch]$size,
        [switch]$all,
        [switch]$help,

        [switch]$sn,
        [switch]$ss,
        [switch]$sd,
        [switch]$sortName,
        [switch]$sortSize,
        [switch]$sortDate,

        [switch]$oa,
        [switch]$od,
        [switch]$orderAscending,
        [switch]$orderDescending
    )

if ($h -or $help) {
    Write-Host "Usage: dirw [path or pattern] [-l|-long] [-s|-size] [-a|-all] [-sn|-ss|-sd] [-oa|-od] [-h|-help]" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "Options:"
    Write-Host " path/pattern Folder or wildcard pattern (default: . )"
    Write-Host " -l, -long Show sizes and dates (long format)"
    Write-Host " -s, -size Calculate size of folders (implies -l; may be slow on large folders)"
    Write-Host " -a, -all Show hidden files and folders"
    Write-Host " -sn, -sortName Sort by name (default)"
    Write-Host " -ss, -sortSize Sort by size"
    Write-Host " -sd, -sortDate Sort by date"
    Write-Host " -oa, -orderAscending Order ascending (default)"
    Write-Host " -od, -orderDescending Order descending"
    Write-Host " -h, -help Show this help"
    Write-Host ""
    Write-Host "Examples:"
    Write-Host " dirw"
    Write-Host " Show all files and folders in the current directory"
    Write-Host " dirw *.ps1 -sd -od"
    Write-Host " Show .ps1 files sorted by date, newest first"
    Write-Host " dirw C:\temp -l -ss"
    Write-Host " Show all files in C:\temp\ in long format, sorted by size (smallest to largest)"
    Write-Host " dirw C:\windows\*.exe -long -sortSize -a"
    Write-Host " Show all .exe files in C:\windows\ in long format, including hidden files"
    Write-Host " dirw C:\windows\ -s -a -od"
    Write-Host " Show all items in C:\windows\ (long format, folder sizes calculated, sorted largest to smallest, including hidden)"


    Write-Host ""
    return
}


    $Long = $l -or $long
    $ShowHidden = $a -or $all
    $CalculateFolderSize = $s -or $size

    # Если -s или -size, обязательно включаем Long
    if ($CalculateFolderSize) { $Long = $true }

    # Определяем тип сортировки
    $SortBy = "Name"
    if ($ss -or $sortSize) { $SortBy = "Size" }
    elseif ($sd -or $sortDate) { $SortBy = "Date" }
    elseif ($sn -or $sortName) { $SortBy = "Name" }

    # Определяем направление сортировки
    $SortDescending = $false
    if ($od -or $orderDescending) { $SortDescending = $true }
    elseif ($oa -or $orderAscending) { $SortDescending = $false }

    if (-not $Target) { $Target = "." }
    $parentPath = (Split-Path $Target -Parent)
    if ($parentPath -and -not (Test-Path $parentPath)) {
        Write-Host "Path not found: $parentPath" -ForegroundColor Red
        return
    }

    $items = if ($ShowHidden) {
        Get-ChildItem -Path $Target -Force
    } else {
        Get-ChildItem -Path $Target | Where-Object { -not $_.Attributes.ToString().Contains("Hidden") }
    }

    if (-not $items) {
        Write-Host "(nothing found)"
        return
    }

    $execExtensions = @('.exe', '.bat', '.cmd', '.ps1', '.msi')

    # Сортировка
    if ($SortBy -eq "Size" -and $CalculateFolderSize) {
        foreach ($item in $items) {
            if ($item.PSIsContainer) {
                $item | Add-Member -NotePropertyName RealLength -NotePropertyValue (Get-FolderSize $item.FullName)
            } else {
                $item | Add-Member -NotePropertyName RealLength -NotePropertyValue $item.Length
            }
        }
        $items = if ($SortDescending) {
            $items | Sort-Object -Property RealLength -Descending
        } else {
            $items | Sort-Object -Property RealLength
        }
    }
    elseif ($SortBy -eq "Size") {
        $items = if ($SortDescending) {
            $items | Sort-Object -Property Length -Descending
        } else {
            $items | Sort-Object -Property Length
        }
    }
    elseif ($SortBy -eq "Date") {
        $items = if ($SortDescending) {
            $items | Sort-Object -Property LastWriteTime -Descending
        } else {
            $items | Sort-Object -Property LastWriteTime
        }
    }
    else {
        $items = if ($SortDescending) {
            $items | Sort-Object -Property Name -Descending
        } else {
            $items | Sort-Object -Property Name
        }
    }

    if ($Long) {
        $maxNameLength = ($items | ForEach-Object { $_.Name.Length } | Measure-Object -Maximum).Maximum
        $maxSizeLength = 10
        $maxDateLength = 19

        $rows = @()
        foreach ($item in $items) {
            $name = $item.Name.PadRight($maxNameLength)
            $color = "White"
            if ($item.PSIsContainer) {
            if ($CalculateFolderSize) {
                if ($item.PSObject.Properties["RealLength"]) {
                    $folderBytes = $item.RealLength
                } else {
                    $folderBytes = Get-FolderSize $item.FullName
                }
                $sizeVal = Format-FileSize($folderBytes)
            } else {
                $sizeVal = "<DIR>"
            }
            $color = "Yellow"
        } else {
        $sizeVal = Format-FileSize($item.Length)
        if ($execExtensions -contains $item.Extension.ToLower()) {
                $color = "Green"
            }
         }

            $sizeText = $sizeVal.PadLeft($maxSizeLength)
            $date = $item.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")
            $rowText = "$name $sizeText $date"
            $rows += [PSCustomObject]@{ Text = $rowText; Color = $color }
        }

        $header = ("Name".PadRight($maxNameLength) + " " + "Size".PadLeft($maxSizeLength) + " " + "Modified")
        $maxRowLength = ($rows | ForEach-Object { $_.Text.Length } | Measure-Object -Maximum).Maximum
        $consoleWidth = [console]::WindowWidth
        $spacing = 6
        $columnWidth = $maxRowLength + $spacing
        $columns = [Math]::Max(1, [Math]::Floor($consoleWidth / $columnWidth))

        # Печатаем заголовок над каждой колонкой
        $headerLine = ""
        for ($c = 0; $c -lt $columns; $c++) {
            $headerLine += $header.PadRight($columnWidth)
        }
        Write-Host $headerLine -ForegroundColor Cyan

        # Выводим в несколько колонок
        for ($i = 0; $i -lt $rows.Count; $i += $columns) {
            for ($j = 0; $j -lt $columns; $j++) {
                $index = $i + $j
                if ($index -lt $rows.Count) {
                    $row = $rows[$index]
                    Write-Host -NoNewline $row.Text.PadRight($columnWidth) -ForegroundColor $row.Color
                }
            }
            Write-Host ""
        }
    }
    else {
        # краткий вывод в колонках
        $maxNameLength = ($items | ForEach-Object { $_.Name.Length } | Measure-Object -Maximum).Maximum
        $consoleWidth = [console]::WindowWidth
        $spacing = 2
        $columnWidth = $maxNameLength + $spacing
        $columns = [Math]::Max(1, [Math]::Floor($consoleWidth / $columnWidth))

        $i = 0
        foreach ($item in $items) {
            $name = $item.Name
            $padded = $name.PadRight($columnWidth)

            if ($item.PSIsContainer) {
                $color = 'Yellow'
            }
            elseif ($execExtensions -contains $item.Extension.ToLower()) {
                $color = 'Green'
            }
            else {
                $color = 'White'
            }

            Write-Host -NoNewline $padded -ForegroundColor $color
            $i++
            if ($i -ge $columns) {
                $i = 0
                Write-Host ""
            }
        }

        if ($i -ne 0) { Write-Host "" }
    }
    Write-Host ""
}
Export-ModuleMember -Function dirw