// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

// Package stringsx provides additional string manipulation functions
// that aren't in the standard library's strings package or go4.org/mem.
package stringsx

import (
	"unicode"
	"unicode/utf8"
)

// CompareFold returns -1, 0, or 1 depending on whether a < b, a == b, or a > b,
// like cmp.Compare, but case insensitively.
func CompareFold(a, b string) int {
	// Track our position in both strings
	ia, ib := 0, 0
	for ia < len(a) && ib < len(b) {
		ra, wa := nextRuneLower(a[ia:])
		rb, wb := nextRuneLower(b[ib:])
		if ra < rb {
			return -1
		}
		if ra > rb {
			return 1
		}
		ia += wa
		ib += wb
		if wa == 0 || wb == 0 {
			break
		}
	}

	// If we've reached here, one or both strings are exhausted
	// The shorter string is "less than" if they match up to this point
	switch {
	case ia == len(a) && ib == len(b):
		return 0
	case ia == len(a):
		return -1
	default:
		return 1
	}
}

// nextRuneLower returns the next rune in the string, lowercased, along with its
// original (consumed) width in bytes. If the string is empty, it returns
// (utf8.RuneError, 0)
func nextRuneLower(s string) (r rune, width int) {
	r, width = utf8.DecodeRuneInString(s)
	return unicode.ToLower(r), width
}
