// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// formatter.go
package kern

import (
	"fmt"
	"strings"
)

type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number

const (
	TTY FmtOpt = 1 << iota
	MultiLine
	Truncate
	Base2
	Base8
	Base10
	Base16
)

type Formatter interface {
	ToString(options FmtOpt) string
}

const (
	TruncateEllipsis = "(...)"
	MinTruncateSize  = 10
	TruncateSize     = MinTruncateSize + 15
)

func TruncateString(s string) (trunc string) {
	finalPart := len(s) - (MinTruncateSize - len(TruncateEllipsis))
	trunc = s[0:len(s)-MinTruncateSize] + TruncateEllipsis + s[finalPart:]
	return
}

func MakeFormatOptions(flags FmtOpt, indent int) FmtOpt {
	return FmtOpt(indent<<16) | flags
}

func GetFormatFlags(opt FmtOpt) FmtOpt {
	return opt & 0xFFFF
}

func GetFormatIndent(opt FmtOpt) int {
	return int(opt >> 16)
}

func Format(sb *strings.Builder, item any, opt FmtOpt) {
	if s, ok := item.(string); ok {
		sb.WriteByte('"')
		sb.WriteString(s)
		sb.WriteByte('"')
	} else if formatter, ok := item.(Formatter); ok {
		sb.WriteString(formatter.ToString(opt))
	} else {
		fmt.Fprintf(sb, "%v", item)
	}
}

func GetFormatted(v any, opt FmtOpt) (text string) {
	if v == nil {
		text = "(nil)"
	} else if s, ok := v.(string); ok {
		text = s
	} else if formatter, ok := v.(Formatter); ok {
		text = formatter.ToString(opt)
	} else {
		text = fmt.Sprintf("%v", v)
	}
	return
}
