//go:build windows

package winapi

import (
	"strings"
	"testing"
	"unicode/utf16"
)

func wideStringsEqual(target, actual []uint16) bool {
	if len(target) != len(actual) {
		return false
	}

	for i := range target {
		if target[i] != actual[i] {
			return false
		}
	}
	return true
}

func TestNewUnicodeString(t *testing.T) {
	// include UTF8 chars which take more than 8 bits to encode
	targetStrings := []string{"abcde", "abcd\n", "C:\\Test", "\\&_Test", "Äb", "\u8483\u119A2\u0041"}
	for _, target := range targetStrings {
		targetWideString := utf16.Encode(([]rune)(target))
		targetLength := uint16(len(targetWideString) * 2)

		uni, err := NewUnicodeString(target)
		if err != nil {
			t.Fatalf("failed to convert target string %s to Unicode String with %v", target, err)
		}

		if uni.Length != targetLength {
			t.Fatalf("Expected new Unicode String length to be %d for target string %s, got %d instead", targetLength, target, uni.Length)
		}
		if uni.MaximumLength != targetLength {
			t.Fatalf("Expected new Unicode String maximum length to be %d for target string %s, got %d instead", targetLength, target, uni.MaximumLength)
		}

		uniBufferStringAsSlice := Uint16BufferToSlice(uni.Buffer, int(targetLength/2))

		if !wideStringsEqual(targetWideString, uniBufferStringAsSlice) {
			t.Fatalf("Expected wide string %v, got %v instead", targetWideString, uniBufferStringAsSlice)
		}
	}
}

func TestUnicodeToString(t *testing.T) {
	targetStrings := []string{"abcde", "abcd\n", "C:\\Test", "\\&_Test", "Äb", "\u8483\u119A2\u0041"}
	for _, target := range targetStrings {
		uni, err := NewUnicodeString(target)
		if err != nil {
			t.Fatalf("failed to convert target string %s to Unicode String with %v", target, err)
		}

		actualString := uni.String()
		if actualString != target {
			t.Fatalf("Expected unicode string function to return %s, instead got %s", target, actualString)
		}
	}
}

func TestUnicodeStringLimit(t *testing.T) {
	var sb strings.Builder

	// limit in bytes of how long the input string can be
	// -1 to account for null character.
	limit := NTSTRSAFE_UNICODE_STRING_MAX_CCH - 1

	lengths := []int{limit - 1, limit, limit + 1}
	testStrings := []string{}
	for _, len := range lengths {
		sb.Reset()
		for i := 0; i < len; i++ {
			// We are deliberately writing byte 41 here as it takes only 8
			// bits in UTF-8 encoding.  If we use non-ASCII chars the limit
			// calculations used above won't work.
			if err := sb.WriteByte(41); err != nil {
				t.Fatalf("string creation failed: %s", err)
			}
		}
		testStrings = append(testStrings, sb.String())
	}

	for i, testStr := range testStrings {
		_, err := NewUnicodeString(testStr)
		if lengths[i] > limit && err == nil {
			t.Fatalf("input string of length %d should throw ENAMETOOLONG error", lengths[i])
		} else if lengths[i] <= limit && err != nil {
			t.Fatalf("unexpected error for length %d: %s", lengths[i], err)
		}
	}
}
