# stack.tcl

oo::class create stack
oo::define stack {
    variable Data 
    variable Debug_

    method push {args} {
        lappend Data {*}$args
    }

    method peek {{n 0} {retall {}}} {
        expr {$retall ne {}
                ? [lrange $Data end-$n end]
                : [lindex $Data end-$n] }
    } 

    method pkrange {nA nZ {reverse {}}} {
        set r [lrange $Data end-$nZ end-$nA]
        expr {$reverse ni {"" 0 no false off} ?
            [lreverse $r] : $r}
    }

    method pop {{n 0}} {
        set r [my peek $n all]
        set Data [lreplace $Data end-$n end]
        return $r
    }

    method size {} {
        llength $Data
    }

    method prune {keep} {
        set Data [lreplace $Data 0 [- [llength $Data] $keep 1]]
    }

    method set_debug { on_off } {
        set Debug_ [expr {$on_off ni {"" off 0 false no}}]
        puts "[self]: debug-print=[expr {$Debug_ ? {'on'} : {'off'}}]"
        return {}
    }

    constructor {} {
        set Data {}
        set Debug_ {}
    }

    destructor {
        if {$Debug_ ne {}} {
            puts "[self] destroyed"
        }
    }
}

oo::class create prefix {
    superclass stack

    method len {} {
        my variable Data
        return [string length [join $Data ""]]
    }

    method prn {} {
        my variable Data
        my variable Debug_
        set output [join $Data ""]
        # if {$Debug_} { puts $output}
    }

    method clear {} {
        my variable Data
        set Data {}
    }
}

oo::class create table 
oo::define table {
    variable Rows
    variable Rowcol
    variable Ncol
    variable Nrow
    variable Cols
    variable Title
    
    constructor {} {
        set Rows [dict create]
        set Nrow 0
        set Ncol 0
        set Width 0
        set Cols 0
        set Title ""
    }

    method title {title} {
        my variable Title
        set Title $title
    }

    method Addcol {row col item} {
        set itmlen [string length $item]
        dict set Rows $Nrow $Ncol [list _itm $item _len $itmlen _max 0]
        if {$Nrow > 0} {my ColMax $Ncol $itmlen}
        incr Ncol
    }

    method ColMax {col {max -1}} {
        set currmax [dict get $Rows 0 $col _max]
        expr {$max >= 0
            ? [dict set Rows 0 $col _max [max $max $currmax]]
            : $currmax}
    } 

    method addTD {td} {
        my Addcol $Nrow $Ncol $td
    }

    method endRow {} {
        incr Nrow
        set Cols $Ncol
        set Ncol 0
    }

    method PrintCell {row col} {
        set td [dict get $Rows $row $col _itm]
        set tdlen [dict get $Rows $row $col _len]
        set max [dict get $Rows 0 $col _max]
        return " $td[string repeat { } [- $max $tdlen]] |"
    }

    method printRow {row} {
        set out |
        foreach col [iota $Cols] {
            append out [my PrintCell $row $col]
        }
        return $out
    }

    method printRows {prfx {lnchar -}} {
        lappend out $prfx[my printRow 0 ]
        lappend out $prfx[my printHdrLine $lnchar]
        foreach row [iota $Nrow 1] {
            lappend out $prfx[my printRow $row]
        }
        join $out \n
    }

    method printHdrLine {{char -}} {
        set out |
        foreach col [iota $Cols] {
            append out [string repeat $char [+ 2 [dict get $Rows 0 $col _max]]] |
        }
        return $out
    }
}

proc iota {n {min 0} {ls {}}} {
    if {$n == $min} {
        return $ls
    }
    tailcall iota [incr n -1] $min [linsert $ls 0 $n]
}
