Layouts/LayoutEngine.ps1

$script:TerminalSlideLayouts = @(
    'TitleAndContent', 'Title', 'SectionHeader', 'TwoColumn', 'ThreeColumn',
    'CodeFocus', 'ImageFocus', 'Quote', 'Blank'
)
$script:TerminalElementRegionOrder = @('Content', 'Left', 'Center', 'Right', 'Image', 'Code', 'Quote')

function Assert-TerminalSlideLayout {
    param([Parameter(Mandatory)][string]$Layout)

    if ($Layout -notin $script:TerminalSlideLayouts) {
        throw "Unknown slide layout '$Layout'. Supported layouts: $($script:TerminalSlideLayouts -join ', ')."
    }
}

function Get-TerminalElementRegionNames {
    param([Parameter(Mandatory)][hashtable]$Regions)

    return @($script:TerminalElementRegionOrder | Where-Object { $Regions.ContainsKey($_) })
}

function Test-TerminalLayoutRegionOverlap {
    param(
        [Parameter(Mandatory)][hashtable]$First,
        [Parameter(Mandatory)][hashtable]$Second
    )

    return ($First.X -lt ($Second.X + $Second.Width) -and
        $Second.X -lt ($First.X + $First.Width) -and
        $First.Y -lt ($Second.Y + $Second.Height) -and
        $Second.Y -lt ($First.Y + $First.Height))
}

function Assert-TerminalElementRegionCombination {
    param(
        [Parameter(Mandatory)][string]$Layout,
        [Parameter(Mandatory)][hashtable]$Regions,
        [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$RegionNames
    )

    $usedRegions = @($RegionNames | Sort-Object -Unique)
    for ($firstIndex = 0; $firstIndex -lt $usedRegions.Count; $firstIndex++) {
        for ($secondIndex = $firstIndex + 1; $secondIndex -lt $usedRegions.Count; $secondIndex++) {
            $firstName = $usedRegions[$firstIndex]
            $secondName = $usedRegions[$secondIndex]
            if (Test-TerminalLayoutRegionOverlap -First $Regions[$firstName] -Second $Regions[$secondName]) {
                throw "Layout '$Layout' cannot combine overlapping element regions '$firstName' and '$secondName'. Choose Content alone or use only disjoint specialized regions."
            }
        }
    }
}

function Resolve-TerminalViewport {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][int]$Width,
        [Parameter(Mandatory)][int]$Height
    )

    return @{
        Width = [Math]::Max(20, $Width)
        Height = [Math]::Max(10, $Height)
    }
}

function Get-LayoutRegions {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$Layout,
        [Parameter(Mandatory)][int]$Width,
        [Parameter(Mandatory)][int]$Height
    )
    Assert-TerminalSlideLayout -Layout $Layout
    $viewport = Resolve-TerminalViewport -Width $Width -Height $Height
    $w = $viewport.Width
    $h = $viewport.Height
    switch ($Layout) {
        'Title' {
            $titleY = [Math]::Max(1, [Math]::Min([Math]::Floor($h / 3), $h - 9))
            $subtitleY = $titleY + 2
            $contentY = $subtitleY + 2
            return @{
                Title = @{ X = 2; Y = $titleY; Width = $w - 4; Height = 2 }
                Subtitle = @{ X = 2; Y = $subtitleY; Width = $w - 4; Height = 2 }
                Content = @{ X = 2; Y = $contentY; Width = $w - 4; Height = [Math]::Max(1, $h - $contentY - 3) }
            }
        }
        'SectionHeader' {
            $contentY = [Math]::Floor($h / 2) + 2
            return @{
                Title = @{ X = 2; Y = [Math]::Floor($h / 2) - 1; Width = $w - 4; Height = 3 }
                Content = @{ X = 2; Y = $contentY; Width = $w - 4; Height = [Math]::Max(1, ($h - 2) - $contentY) }
            }
        }
        'TwoColumn' {
            $contentY = 4
            $contentHeight = $h - 7
            $columnWidth = [Math]::Floor(($w - 6) / 2)
            return @{
                Title = @{ X = 2; Y = 1; Width = $w - 4; Height = 2 }
                Left = @{ X = 2; Y = $contentY; Width = $columnWidth; Height = $contentHeight }
                Right = @{ X = 4 + $columnWidth; Y = $contentY; Width = $columnWidth; Height = $contentHeight }
                Content = @{ X = 2; Y = $contentY; Width = $w - 4; Height = $contentHeight }
            }
        }
        'ThreeColumn' {
            $contentY = 4
            $contentHeight = $h - 7
            $columnWidth = [Math]::Floor(($w - 8) / 3)
            return @{
                Title = @{ X = 2; Y = 1; Width = $w - 4; Height = 2 }
                Left = @{ X = 2; Y = $contentY; Width = $columnWidth; Height = $contentHeight }
                Center = @{ X = 4 + $columnWidth; Y = $contentY; Width = $columnWidth; Height = $contentHeight }
                Right = @{ X = 6 + ($columnWidth * 2); Y = $contentY; Width = $columnWidth; Height = $contentHeight }
                Content = @{ X = 2; Y = $contentY; Width = $w - 4; Height = $contentHeight }
            }
        }
        'CodeFocus' {
            return @{
                Title = @{ X = 2; Y = 1; Width = $w - 4; Height = 2 }
                Code = @{ X = 2; Y = 4; Width = $w - 4; Height = $h - 7 }
                Content = @{ X = 2; Y = 4; Width = $w - 4; Height = $h - 7 }
            }
        }
        'ImageFocus' {
            return @{
                Title = @{ X = 2; Y = 1; Width = $w - 4; Height = 2 }
                Image = @{ X = 2; Y = 4; Width = $w - 4; Height = $h - 7 }
                Content = @{ X = 2; Y = 4; Width = $w - 4; Height = $h - 7 }
            }
        }
        'Quote' {
            return @{
                Title = @{ X = 2; Y = 1; Width = $w - 4; Height = 2 }
                Quote = @{ X = 6; Y = [Math]::Max(4, [Math]::Floor($h / 3)); Width = $w - 12; Height = [Math]::Max(4, [Math]::Floor($h / 3)) }
                Content = @{ X = 6; Y = [Math]::Max(4, [Math]::Floor($h / 3)); Width = $w - 12; Height = [Math]::Max(4, [Math]::Floor($h / 3)) }
            }
        }
        'Blank' {
            return @{ Content = @{ X = 1; Y = 1; Width = $w - 2; Height = $h - 3 } }
        }
        'TitleAndContent' {
            return @{
                Title = @{ X = 2; Y = 1; Width = $w - 4; Height = 2 }
                Content = @{ X = 2; Y = 4; Width = $w - 4; Height = $h - 7 }
            }
        }
    }
}