package d2util

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf16"
	"unicode/utf8"
)

// AsterToEmpty converts strings beginning with "*" to "", for use when handling columns where an asterix can be used to comment out entries
func AsterToEmpty(text string) string {
	if strings.HasPrefix(text, "*") {
		return ""
	}

	return text
}

// EmptyToZero converts empty strings to "0" and leaves non-empty strings as is,
// for use before converting numerical data which equates empty to zero
func EmptyToZero(text string) string {
	if text == "" || text == " " {
		return "0"
	}

	return text
}

// StringToInt converts a string to an integer
func StringToInt(text string) int {
	result, err := strconv.Atoi(text)
	if err != nil {
		panic(err)
	}

	return result
}

// SafeStringToInt converts a string to an integer, or returns -1 on falure

// StringToUint converts a string to a uint32
func StringToUint(text string) uint {
	result, err := strconv.ParseUint(text, 10, 32)
	if err != nil {
		panic(err)
	}

	return uint(result)
}

// StringToUint8 converts a string to an uint8
func StringToUint8(text string) uint8 {
	result, err := strconv.Atoi(text)
	if err != nil {
		panic(err)
	}

	if result < 0 || result > 255 {
		panic(fmt.Sprintf("value %d out of range of byte", result))
	}

	return uint8(result)
}

// StringToInt8 converts a string to an int8
func StringToInt8(text string) int8 {
	result, err := strconv.Atoi(text)
	if err != nil {
		panic(err)
	}

	if result < -128 || result > 122 {
		panic(fmt.Sprintf("value %d out of range of a signed byte", result))
	}

	return int8(result)
}

// Utf16BytesToString converts a utf16 byte array to string
func Utf16BytesToString(b []byte) (string, error) {
	if len(b)%2 != 0 {
		return "", fmt.Errorf("must have even length byte slice")
	}

	u16s := make([]uint16, 1)

	ret := &bytes.Buffer{}

	b8buf := make([]byte, 4)

	lb := len(b)
	for i := 0; i < lb; i += 2 {
		// nolint:gomnd // byte operation
		u16s[0] = uint16(b[i]) + (uint16(b[i+1]) << 8)
		r := utf16.Decode(u16s)
		n := utf8.EncodeRune(b8buf, r[0])
		ret.Write(b8buf[:n])
	}

	return ret.String(), nil
}

// SplitIntoLinesWithMaxWidth splits the given string into lines considering the given maxChars
func SplitIntoLinesWithMaxWidth(fullSentence string, maxChars int) []string {
	lines := make([]string, 0)
	line := ""
	totalLength := 0
	words := strings.Split(fullSentence, " ")

	if len(words[0]) > maxChars {
		// mostly happened within CJK characters (no whitespace)
		return splitCjkIntoChunks(fullSentence, maxChars)
	}

	for _, word := range words {
		totalLength += 1 + len(word)
		if totalLength > maxChars {
			totalLength = len(word)

			lines = append(lines, line)
			line = ""
		} else {
			line += " "
		}

		line += word
	}

	if len(line) > 0 {
		lines = append(lines, line)
	}

	return lines
}

func splitCjkIntoChunks(str string, chars int) []string {
	chunks := make([]string, chars/len(str))
	i, count := 0, 0

	for j, ch := range str {
		if ch < unicode.MaxLatin1 {
			count++
		} else {
			// assume we're truncating CJK characters
			count += 2
		}

		if count >= chars {
			chunks = append(chunks, str[i:j])
			i, count = j, 0
		}
	}

	return append(chunks, str[i:])
}
