package tprint

import (
	"fmt"
	"strings"

	"git.tordarus.net/tordarus/gmath"
	"git.tordarus.net/tordarus/slices"
)

type Table struct {
	head [][]string

	rowSep     bool
	headheight int
	maxcols    int

	colwidth  []int
	rowheight []int

	data [][][]string
}

func NewTable(head ...string) *Table {
	thead := slices.Map(head, func(s string) []string { return strings.Split(s, "\n") })
	return &Table{
		head:       thead,
		colwidth:   slices.Map(head, func(s string) int { return strLen(s) }),
		headheight: len(Search(thead, maxLengthSlice[string])),
		rowheight:  []int{},
		maxcols:    len(thead),
	}
}

func (t *Table) AddRow(row ...interface{}) {
	irow := slices.Map(row, func(v interface{}) []string { return strings.Split(fmt.Sprint(v), "\n") })
	t.data = append(t.data, irow)

	rowheight := len(Search(irow, maxLengthSlice[string]))
	t.rowheight = append(t.rowheight, rowheight)
	if rowheight > 1 {
		t.rowSep = true
	}

	t.maxcols = gmath.Max(t.maxcols, len(irow))

	for i, cell := range irow {
		maxLineLength := strLen(Search(cell, maxLengthStr))
		if i < len(t.colwidth) {
			t.colwidth[i] = gmath.Max(t.colwidth[i], maxLineLength)
		} else {
			t.colwidth = append(t.colwidth, maxLineLength)
		}

		if i >= len(t.head) {
			headname := "unknown column"
			t.head = append(t.head, []string{headname})
			t.colwidth[i] = gmath.Max(t.colwidth[i], strLen(headname))
		}
	}
}

func (t *Table) StringNoHead() string {
	b := new(strings.Builder)

	b.WriteRune('┏')
	for i, colwidth := range t.colwidth {
		b.WriteString(strings.Repeat("━", colwidth))
		if i < len(t.colwidth)-1 {
			b.WriteRune('┯')
		}
	}
	b.WriteRune('┓')
	b.WriteRune('\n')

	for i, row := range t.data {
		t.printRow(b, row, t.rowheight[i])
		if t.rowSep && i < len(t.data)-1 {
			t.addNextCellLine(b)
		}
	}

	t.addLastCellLine(b)

	return b.String()
}

func (t *Table) String() string {
	b := new(strings.Builder)

	b.WriteRune('┏')
	for i, colwidth := range t.colwidth {
		b.WriteString(strings.Repeat("━", colwidth))
		if i < len(t.colwidth)-1 {
			b.WriteRune('┯')
		}
	}
	b.WriteRune('┓')
	b.WriteRune('\n')

	t.printRow(b, t.head, t.headheight)

	b.WriteRune('┣')
	for i, colwidth := range t.colwidth {
		b.WriteString(strings.Repeat("━", colwidth))
		if i < len(t.colwidth)-1 {
			b.WriteRune('┿')
		}
	}
	b.WriteRune('┫')
	b.WriteRune('\n')

	for i, row := range t.data {
		t.printRow(b, row, t.rowheight[i])
		if t.rowSep && i < len(t.data)-1 {
			t.addNextCellLine(b)
		}
	}

	t.addLastCellLine(b)

	return b.String()
}

func (t *Table) printRow(b *strings.Builder, rowData [][]string, rowHeight int) {
	for lineIndex := 0; lineIndex < rowHeight; lineIndex++ {
		b.WriteRune('┃')
		for colIndex := 0; colIndex < t.maxcols; colIndex++ {
			line := ""

			if colIndex < len(rowData) {
				cell := rowData[colIndex]
				if lineIndex < len(cell) {
					line = cell[lineIndex]
				}
			}

			b.WriteString(fmt.Sprint(padStringRight(line, ' ', t.colwidth[colIndex])))
			if colIndex < t.maxcols-1 {
				b.WriteRune('│')
			}
		}
		b.WriteString("┃\n")
	}
}

func (t *Table) addNextCellLine(b *strings.Builder) {
	b.WriteRune('┠')
	for i, colwidth := range t.colwidth {
		b.WriteString(strings.Repeat("─", colwidth))
		if i < len(t.colwidth)-1 {
			b.WriteRune('┼')
		}
	}
	b.WriteRune('┨')
	b.WriteRune('\n')
}

func (t *Table) addLastCellLine(b *strings.Builder) {
	b.WriteRune('┗')
	for i, colwidth := range t.colwidth {
		b.WriteString(strings.Repeat("━", colwidth))
		if i < len(t.colwidth)-1 {
			b.WriteRune('┷')
		}
	}
	b.WriteString("┛\n")
}