func justRight(s string, width int, pad int, sty *Style) string {
	contentLen := len(s)
	onLeft := width - contentLen
	if onLeft < 0 {
		onLeft = 0
	}
	switch {
	case sty == nil:
		return fmt.Sprintf("%s%s%s", spaces(onLeft+pad), s, spaces(pad))
	default:
		return fmt.Sprintf("%s%s%s", spaces(onLeft+pad), sty.ApplyTo(s), spaces(pad))
	}
}
justRight is right-justified text with padding and style
package clt
import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
)
type Justification int
const (
	Left Justification = iota
	Center
	Right
)
type Cell struct {
	value string
	width int
	style *Style
}
type Title struct {
	value string
	width int
	style *Style
}
type Row struct {
	cells []Cell
}
type Col struct {
	index         int
	naturalWidth  int
	computedWidth int
	wrap          bool
	style         *Style
	justify       Justification
}
type Table struct {
	title     Title
	columns   []Col
	headers   []Cell
	rows      []Row
	pad       int
	maxWidth  int
	maxHeight int
	spacing   int
	writer io.Writer
}
type TableOption func(t *Table) error
func MaxHeight(h int) TableOption {
	return func(t *Table) error {
		t.maxHeight = h
		return nil
	}
}
func MaxWidth(w int) TableOption {
	return func(t *Table) error {
		if t.maxWidth > w {
			t.maxWidth = w
		}
		return nil
	}
}
func Spacing(n int) TableOption {
	return func(t *Table) error {
		switch {
		case n > 1:
			t.spacing = n
		default:
			t.spacing = 1
		}
		return nil
	}
}
func (r *Row) addCell(c Cell) {
	r.cells = append(r.cells, c)
}
func (t *Table) AddRow(rowStrings ...string) *Table {
	newRow := Row{}
	for i, rValue := range rowStrings {
		if i >= len(t.columns) {
			break
		}
		newRow.addCell(Cell{value: rValue, width: len(rValue), style: t.columns[i].style})
	}
	for len(newRow.cells) < len(t.columns) {
		newRow.addCell(Cell{value: "", width: 0, style: Styled(Default)})
	}
	t.rows = append(t.rows, newRow)
	return t
}
func (t *Table) AddStyledRow(cells ...Cell) *Table {
	newRow := Row{}
	for i, cell1 := range cells {
		if i >= len(t.columns) {
			break
		}
		newRow.addCell(cell1)
	}
	for len(newRow.cells) < len(t.columns) {
		newRow.addCell(Cell{value: "", width: 0, style: Styled(Default)})
	}
	t.rows = append(t.rows, newRow)
	return t
}
func StyledCell(v string, sty *Style) Cell {
	return Cell{value: v, width: len(v), style: sty}
}
func (t *Table) ColumnStyles(styles ...*Style) *Table {
	for i, sty := range styles {
		if i >= len(t.columns) {
			return t
		}
		t.columns[i].style = sty
	}
	return t
}
func (t *Table) Title(s string, styles ...Styler) *Table {
	var sty *Style
	switch {
	case len(styles) > 0:
		sty = Styled(styles...)
	default:
		sty = Styled(Bold)
	}
	t.title = Title{value: s, width: len(s), style: sty}
	return t
}
func (t *Table) ColumnHeaders(headers ...string) *Table {
	for i, header := range headers {
		if i >= len(t.columns) {
			return t
		}
		t.headers[i].value = header
		t.headers[i].style = Styled(Bold, Underline)
		t.headers[i].width = len(header)
	}
	return t
}
func (t *Table) ColumnHeaderStyles(styles ...*Style) *Table {
	for i, style := range styles {
		if i > len(t.columns) {
			return t
		}
		t.headers[i].style = style
	}
	return t
}
func (t *Table) Justification(cellJustifications ...Justification) *Table {
	for i, just := range cellJustifications {
		if i > len(t.columns) {
			return t
		}
		t.columns[i].justify = just
	}
	return t
}
func NewTable(numColumns int, options ...TableOption) *Table {
	w, h, err := getTerminalSize()
	if err != nil || w == 0 || h == 0 {
		w = 80
		h = 25
	}
	defaultColumns := make([]Col, numColumns)
	emptyHeaders := make([]Cell, numColumns)
	for i := 0; i < numColumns; i++ {
		defaultColumns[i].index = i
		defaultColumns[i].style = Styled(Default)
		defaultColumns[i].justify = Left
		defaultColumns[i].wrap = false
	}
	t := &Table{
		columns:   defaultColumns,
		maxWidth:  w,
		maxHeight: h,
		headers:   emptyHeaders,
		pad:       1,
		title:     Title{value: "", width: 0, style: Styled(Default)},
		writer:    os.Stdout,
	}
	for _, opt := range options {
		opt(t)
	}
	return t
}
func (t *Table) Show() {
	tableAsString := t.AsString()
	fmt.Fprintf(t.writer, tableAsString)
}
func (t *Table) ShowPage(n int) {
	if n == 0 {
		n = t.maxHeight - 3
	}
	tableAsString := t.AsString()
	lines := strings.SplitAfter(tableAsString, "\n")
	sess := NewInteractiveSession()
	start := 1
	for i := range lines {
		switch {
		case i > 0 && i%n == 0:
			fmt.Fprintf(t.writer, lines[i])
			sess.PauseWithPrompt("\nResults %d-%d of %d. Press [Enter] to continue.\n", start, i+1, len(lines))
			start = i + 2
		default:
			fmt.Fprintf(t.writer, lines[i])
		}
	}
}
func (t *Table) SetWriter(w io.Writer) {
	t.writer = w
}
func (t *Table) AsString() string {
	err := t.computeColWidths()
	if err != nil {
		log.Fatal(err)
	}
	var renderedT bytes.Buffer
	renderedT.WriteString(renderTitle(t) + "\n\n")
	renderedT.WriteString(renderHeaders(t.headers, t.columns, t.pad))
	for _, row := range t.rows {
		renderedT.WriteString(renderRow(row.cells, t.columns, t.pad, t.spacing))
	}
	return renderedT.String()
}
func renderTitle(t *Table) string {
	return justCenter(t.title.value, t.width(), 0, t.title.style)
}
func renderHeaders(cells []Cell, cols []Col, pad int) string {
	wrappedLinesCount := make([]int, len(cells))
	for i, cell1 := range cells {
		wrappedL := wrap(cell1.value, cols[i].computedWidth)
		wrappedLinesCount[i] = len(wrappedL)
	}
	_, totalLines := max(wrappedLinesCount)
	lines := make([]bytes.Buffer, totalLines)
	for cellN, cellV := range cells {
		wL := wrap(cellV.value, cols[cellN].computedWidth)
		for i := 0; i < totalLines; i++ {
			switch {
			case i < len(wL):
				lines[i].WriteString(renderCell(wL[i], cols[cellN].computedWidth, pad, cellV.style, cols[cellN].justify))
			default:
				lines[i].WriteString(renderCell("", cols[cellN].computedWidth, pad, cellV.style, cols[cellN].justify))
			}
		}
	}
	var out bytes.Buffer
	for _, line := range lines {
		out.Write(line.Bytes())
		out.WriteString("\n")
	}
	return out.String()
}
func renderRow(cells []Cell, cols []Col, pad int, spacing int) string {
	wrappedLinesCount := make([]int, len(cells))
	for i, cell1 := range cells {
		wrappedL := wrap(cell1.value, cols[i].computedWidth)
		wrappedLinesCount[i] = len(wrappedL)
	}
	_, totalLines := max(wrappedLinesCount)
	lines := make([]bytes.Buffer, totalLines)
	for cellN, cellV := range cells {
		var sty *Style
		switch {
		case cellV.style != cols[cellN].style:
			sty = cellV.style
		default:
			sty = cols[cellN].style
		}
		wL := wrap(cellV.value, cols[cellN].computedWidth)
		for i := 0; i < totalLines; i++ {
			switch {
			case i < len(wL):
				lines[i].WriteString(renderCell(wL[i], cols[cellN].computedWidth, pad, sty, cols[cellN].justify))
			default:
				lines[i].WriteString(renderCell("", cols[cellN].computedWidth, pad, sty, cols[cellN].justify))
			}
		}
	}
	var out bytes.Buffer
	for _, line := range lines {
		out.Write(line.Bytes())
		out.WriteString("\n")
	}
	if spacing > 1 {
		out.WriteString(strings.Repeat("\n", spacing-1))
	}
	return out.String()
}
func renderCell(s string, width int, pad int, sty *Style, justify Justification) string {
	switch justify {
	case Left:
		return justLeft(s, width, pad, sty)
	case Center:
		return justCenter(s, width, pad, sty)
	case Right:
		return justRight(s, width, pad, sty)
	}
	return ""
}
func justCenter(s string, width int, pad int, sty *Style) string {
	contentLen := len(s)
	onLeft := (width - contentLen) / 2
	if onLeft < 0 {
		onLeft = 0
	}
	onRight := width - contentLen - onLeft
	if onRight < 0 {
		onRight = 0
	}
	switch {
	case sty == nil:
		return fmt.Sprintf("%s%s%s", spaces(onLeft+pad), s, spaces(onRight+pad))
	default:
		return fmt.Sprintf("%s%s%s", spaces(onLeft+pad), sty.ApplyTo(s), spaces(onRight+pad))
	}
}
func justLeft(s string, width int, pad int, sty *Style) string {
	contentLen := len(s)
	onRight := width - contentLen
	if onRight < 0 {
		onRight = 0
	}
	switch {
	case sty == nil:
		return fmt.Sprintf("%s%s%s", spaces(pad), s, spaces(onRight+pad))
	default:
		return fmt.Sprintf("%s%s%s", spaces(pad), sty.ApplyTo(s), spaces(onRight+pad))
	}
}