// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package number

import (
	"reflect"
	"testing"
	"unsafe"
)

var testCases = []struct {
	pat  string
	want *Pattern
}{{
	"#",
	&Pattern{
		FormatWidth: 1,
		// TODO: Should MinIntegerDigits be 1?
	},
}, {
	"0",
	&Pattern{
		FormatWidth: 1,
		RoundingContext: RoundingContext{
			MinIntegerDigits: 1,
		},
	},
}, {
	"+0",
	&Pattern{
		Affix:       "\x01+\x00",
		FormatWidth: 2,
		RoundingContext: RoundingContext{
			MinIntegerDigits: 1,
		},
	},
}, {
	"0+",
	&Pattern{
		Affix:       "\x00\x01+",
		FormatWidth: 2,
		RoundingContext: RoundingContext{
			MinIntegerDigits: 1,
		},
	},
}, {
	"0000",
	&Pattern{
		FormatWidth: 4,
		RoundingContext: RoundingContext{
			MinIntegerDigits: 4,
		},
	},
}, {
	".#",
	&Pattern{
		FormatWidth: 2,
		RoundingContext: RoundingContext{
			MaxFractionDigits: 1,
		},
	},
}, {
	"#0.###",
	&Pattern{
		FormatWidth: 6,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxFractionDigits: 3,
		},
	},
}, {
	"#0.######",
	&Pattern{
		FormatWidth: 9,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxFractionDigits: 6,
		},
	},
}, {
	"#,0",
	&Pattern{
		FormatWidth:  3,
		GroupingSize: [2]uint8{1, 0},
		RoundingContext: RoundingContext{
			MinIntegerDigits: 1,
		},
	},
}, {
	"#,0.00",
	&Pattern{
		FormatWidth:  6,
		GroupingSize: [2]uint8{1, 0},
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MinFractionDigits: 2,
			MaxFractionDigits: 2,
		},
	},
}, {
	"#,##0.###",
	&Pattern{
		FormatWidth:  9,
		GroupingSize: [2]uint8{3, 0},
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxFractionDigits: 3,
		},
	},
}, {
	"#,##,##0.###",
	&Pattern{
		FormatWidth:  12,
		GroupingSize: [2]uint8{3, 2},
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxFractionDigits: 3,
		},
	},
}, {
	// Ignore additional separators.
	"#,####,##,##0.###",
	&Pattern{
		FormatWidth:  17,
		GroupingSize: [2]uint8{3, 2},
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxFractionDigits: 3,
		},
	},
}, {
	"#E0",
	&Pattern{
		FormatWidth: 3,
		RoundingContext: RoundingContext{
			MaxIntegerDigits:  1,
			MinExponentDigits: 1,
		},
	},
}, {
	// At least one exponent digit is required. As long as this is true, one can
	// determine that scientific rendering is needed if MinExponentDigits > 0.
	"#E#",
	nil,
}, {
	"0E0",
	&Pattern{
		FormatWidth: 3,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MinExponentDigits: 1,
		},
	},
}, {
	"##00.0#E0",
	&Pattern{
		FormatWidth: 9,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  2,
			MaxIntegerDigits:  4,
			MinFractionDigits: 1,
			MaxFractionDigits: 2,
			MinExponentDigits: 1,
		},
	},
}, {
	"#00.0E+0",
	&Pattern{
		FormatWidth: 8,
		Flags:       AlwaysExpSign,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  2,
			MaxIntegerDigits:  3,
			MinFractionDigits: 1,
			MaxFractionDigits: 1,
			MinExponentDigits: 1,
		},
	},
}, {
	"0.0E++0",
	nil,
}, {
	"#0E+",
	nil,
}, {
	// significant digits
	"@",
	&Pattern{
		FormatWidth: 1,
		RoundingContext: RoundingContext{
			MinSignificantDigits: 1,
			MaxSignificantDigits: 1,
		},
	},
}, {
	// significant digits
	"@@@@",
	&Pattern{
		FormatWidth: 4,
		RoundingContext: RoundingContext{
			MinSignificantDigits: 4,
			MaxSignificantDigits: 4,
		},
	},
}, {
	"@###",
	&Pattern{
		FormatWidth: 4,
		RoundingContext: RoundingContext{
			MinSignificantDigits: 1,
			MaxSignificantDigits: 4,
		},
	},
}, {
	// Exponents in significant digits mode gets normalized.
	"@@E0",
	&Pattern{
		FormatWidth: 4,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxIntegerDigits:  1,
			MinFractionDigits: 1,
			MaxFractionDigits: 1,
			MinExponentDigits: 1,
		},
	},
}, {
	"@###E00",
	&Pattern{
		FormatWidth: 7,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxIntegerDigits:  1,
			MinFractionDigits: 0,
			MaxFractionDigits: 3,
			MinExponentDigits: 2,
		},
	},
}, {
	// The significant digits mode does not allow fractions.
	"@###.#E0",
	nil,
}, {
	//alternative negative pattern
	"#0.###;(#0.###)",
	&Pattern{
		Affix:       "\x00\x00\x01(\x01)",
		NegOffset:   2,
		FormatWidth: 6,
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MaxFractionDigits: 3,
		},
	},
}, {
	// Rounding increment
	"1.05",
	&Pattern{
		FormatWidth: 4,
		RoundingContext: RoundingContext{
			Increment:         105,
			MinIntegerDigits:  1,
			MinFractionDigits: 2,
			MaxFractionDigits: 2,
		},
	},
}, {
	// Rounding increment with grouping
	"1,05",
	&Pattern{
		FormatWidth:  4,
		GroupingSize: [2]uint8{2, 0},
		RoundingContext: RoundingContext{
			Increment:         105,
			MinIntegerDigits:  3,
			MinFractionDigits: 0,
			MaxFractionDigits: 0,
		},
	},
}, {
	"0.0%",
	&Pattern{
		Affix:       "\x00\x01%",
		FormatWidth: 4,
		RoundingContext: RoundingContext{
			DigitShift:        2,
			MinIntegerDigits:  1,
			MinFractionDigits: 1,
			MaxFractionDigits: 1,
		},
	},
}, {
	"0.0‰",
	&Pattern{
		Affix:       "\x00\x03‰",
		FormatWidth: 4,
		RoundingContext: RoundingContext{
			DigitShift:        3,
			MinIntegerDigits:  1,
			MinFractionDigits: 1,
			MaxFractionDigits: 1,
		},
	},
}, {
	"#,##0.00¤",
	&Pattern{
		Affix:        "\x00\x02¤",
		FormatWidth:  9,
		GroupingSize: [2]uint8{3, 0},
		RoundingContext: RoundingContext{
			MinIntegerDigits:  1,
			MinFractionDigits: 2,
			MaxFractionDigits: 2,
		},
	},
}, {
	"#,##0.00 ¤;(#,##0.00 ¤)",
	&Pattern{Affix: "\x00\x04\u00a0¤\x01(\x05\u00a0¤)",
		NegOffset:    6,
		FormatWidth:  10,
		GroupingSize: [2]uint8{3, 0},
		RoundingContext: RoundingContext{
			DigitShift:        0,
			MinIntegerDigits:  1,
			MinFractionDigits: 2,
			MaxFractionDigits: 2,
		},
	},
}, {
	// padding
	"*x#",
	&Pattern{
		PadRune:     'x',
		FormatWidth: 1,
	},
}, {
	// padding
	"#*x",
	&Pattern{
		PadRune:     'x',
		FormatWidth: 1,
		Flags:       PadBeforeSuffix,
	},
}, {
	"*xpre#suf",
	&Pattern{
		Affix:       "\x03pre\x03suf",
		PadRune:     'x',
		FormatWidth: 7,
	},
}, {
	"pre*x#suf",
	&Pattern{
		Affix:       "\x03pre\x03suf",
		PadRune:     'x',
		FormatWidth: 7,
		Flags:       PadAfterPrefix,
	},
}, {
	"pre#*xsuf",
	&Pattern{
		Affix:       "\x03pre\x03suf",
		PadRune:     'x',
		FormatWidth: 7,
		Flags:       PadBeforeSuffix,
	},
}, {
	"pre#suf*x",
	&Pattern{
		Affix:       "\x03pre\x03suf",
		PadRune:     'x',
		FormatWidth: 7,
		Flags:       PadAfterSuffix,
	},
}, {
	`* #0 o''clock`,
	&Pattern{Affix: "\x00\x09 o\\'clock",
		FormatWidth: 10,
		PadRune:     32,
		RoundingContext: RoundingContext{
			MinIntegerDigits: 0x1,
		},
	},
}, {
	`'123'* #0'456'`,
	&Pattern{Affix: "\x05'123'\x05'456'",
		FormatWidth: 8,
		PadRune:     32,
		RoundingContext: RoundingContext{
			MinIntegerDigits: 0x1,
		},
		Flags: PadAfterPrefix},
}, {
	// no duplicate padding
	"*xpre#suf*x", nil,
}, {
	// no duplicate padding
	"*xpre#suf*x", nil,
}}

func TestParsePattern(t *testing.T) {
	for i, tc := range testCases {
		t.Run(tc.pat, func(t *testing.T) {
			f, err := ParsePattern(tc.pat)
			if !reflect.DeepEqual(f, tc.want) {
				t.Errorf("%d:%s:\ngot %#v;\nwant %#v", i, tc.pat, f, tc.want)
			}
			if got, want := err != nil, tc.want == nil; got != want {
				t.Errorf("%d:%s:error: got %v; want %v", i, tc.pat, err, want)
			}
		})
	}
}

func TestPatternSize(t *testing.T) {
	if sz := unsafe.Sizeof(Pattern{}); sz > 56 {
		t.Errorf("got %d; want <= 56", sz)
	}

}
