package card

import (
	"strings"
	"unicode"
	"unicode/utf8"
)

// breakable reports whether a line may break before r when it follows prev:
// between any two CJK characters, except before closing punctuation and
// small kana, which must not start a line.
func breakable(prev, r rune) bool {
	if strings.ContainsRune("、。，．・ー」』）)!！?？ぁぃぅぇぉっゃゅょゎァィゥェォッャュョヮ", r) {
		return false
	}
	return isWide(prev) || isWide(r)
}

func isWide(r rune) bool {
	return unicode.In(r, unicode.Han, unicode.Hiragana, unicode.Katakana) ||
		(r >= 0x3000 && r <= 0x303f) || (r >= 0xff00 && r <= 0xffef)
}

// wrap breaks s into lines no wider than max (logical px) at spaces and CJK
// boundaries, splitting overlong words anywhere.
func (l *layout) wrap(s string, size, max float64) []string {
	var lines []string
	for s != "" {
		n := l.fit(s, size, max)
		lines = append(lines, strings.TrimRight(s[:n], " "))
		s = strings.TrimLeft(s[n:], " ")
	}
	return lines
}

// fit returns the byte length of the longest prefix of s that fits in max and
// ends at a break opportunity; at least one rune.
func (l *layout) fit(s string, size, max float64) int {
	f := l.face(size)
	var w float64
	lastBreak := 0
	prev := rune(0)
	for i, r := range s {
		if i > 0 && (r == ' ' || prev == ' ' || prev == '/' || breakable(prev, r)) {
			lastBreak = i
		}
		w += l.dev(f.glyph(r, 0).adv)
		if w > max && i > 0 {
			if lastBreak > 0 {
				return lastBreak
			}
			return i
		}
		prev = r
	}
	return len(s)
}

// ellipsize trims s with "…" so it fits in max.
func (l *layout) ellipsize(s string, size, max float64) string {
	if l.width(s, size) <= max {
		return s
	}
	ell := l.width("…", size)
	n := l.fit(s, size, max-ell)
	for n > 0 && l.width(s[:n], size)+ell > max {
		_, sz := utf8.DecodeLastRuneInString(s[:n])
		n -= sz
	}
	return strings.TrimRight(s[:n], " ") + "…"
}
