package readline

import (
	"fmt"
	"strconv"
	"strings"
	"github.com/rivo/uniseg"
)	

// initGrid - Grid display details. Called each time we want to be sure to have
// a working completion group either immediately, or later on. Generally defered.
func (g *CompletionGroup) initGrid(rl *Instance) {

	// Compute size of each completion item box
	tcMaxLength := 1
	for i := range g.Suggestions {
		if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength {
			tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i])
		}
	}

	g.tcPosX = 0
	g.tcPosY = 1
	g.tcOffset = 0

	// Max number of columns
	g.tcMaxX = GetTermWidth() / (tcMaxLength + 2)
	if g.tcMaxX < 1 {
		g.tcMaxX = 1 // avoid a divide by zero error
	}

	// Maximum number of lines
	maxY := len(g.Suggestions) / g.tcMaxX
	rest := len(g.Suggestions) % g.tcMaxX
	if rest != 0 {
		// if rest != 0 && maxY != 1 {
		maxY++
	}
	if maxY > g.MaxLength {
		g.tcMaxY = g.MaxLength
	} else {
		g.tcMaxY = maxY
	}
}

// moveTabGridHighlight - Moves the highlighting for currently selected completion item (grid display)
func (g *CompletionGroup) moveTabGridHighlight(rl *Instance, x, y int) (done bool, next bool) {

	g.tcPosX += x
	g.tcPosY += y

	// Columns
	if g.tcPosX < 1 {
		if g.tcPosY == 1 && rl.tabCompletionReverse {
			g.tcPosX = 1
			g.tcPosY = 0
		} else {
			// This is when multiple ligns, not yet on first one.
			g.tcPosX = g.tcMaxX
			g.tcPosY--
		}
	}
	if g.tcPosY > g.tcMaxY {
		g.tcPosY = 1
		return true, true
	}

	// If we must move to next line in same group
	if g.tcPosX > g.tcMaxX {
		g.tcPosX = 1
		g.tcPosY++
	}

	// Real max number of suggestions.
	max := g.tcMaxX * g.tcMaxY
	if max > len(g.Suggestions) {
		max = len(g.Suggestions)
	}

	// We arrived at the end of suggestions. This condition can never be triggered
	// while going in the reverse order, only forward, so no further checks in it.
	if (g.tcMaxX*(g.tcPosY-1))+g.tcPosX > max {
		return true, true
	}

	// In case we are reverse cycling and currently selecting the first item,
	// we adjust the coordinates to point to the last item and return
	// We set g.tcPosY because the printer needs to get the a candidate nonetheless.
	if rl.tabCompletionReverse && g.tcPosX == 1 && g.tcPosY == 0 {
		g.tcPosY = 1
		return true, false
	}

	// By default, come back to this group for next item.
	return false, false
}

// writeGrid - A grid completion string
func (g *CompletionGroup) writeGrid(rl *Instance) (comp string) {

	// If group title, print it and adjust offset.
	if g.Name != "" {
		comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET)
		rl.tcUsedY++
	}

	cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4)
	x := 0
	y := 1

	for i := range g.Suggestions {
		x++
		if x > g.tcMaxX {
			x = 1
			y++
			if y > g.tcMaxY {
				y--
				break
			} else {
				comp += "\r\n"
			}
		}

		if (x == g.tcPosX && y == g.tcPosY) && (g.isCurrent) {
			comp += seqInvert
		}

		sugg := g.Suggestions[i]
		if len(sugg) > GetTermWidth() {
			sugg = sugg[:GetTermWidth() - 4] + "..."
		}
		formatStr := "%-"+cellWidth+"s%s "
		if g.tcMaxX == 1 {
			formatStr = "%s%s"
		}
		comp += fmt.Sprintf(formatStr, fmtEscape(sugg), seqReset)
	}

	// Always add a newline to the group if the end if not punctuated with one
	if !strings.HasSuffix(comp, "\n") {
		comp += "\n"
	}

	// Add the equivalent of this group's size to final screen clearing.
	// This is either the max allowed print size for this group, or its actual size if inferior.
	if g.MaxLength < y {
		rl.tcUsedY += g.MaxLength
	} else {
		rl.tcUsedY += y
	}

	return
}