// Copyright 2017 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 (
	"fmt"
	"log"
	"testing"

	"golang.org/x/text/language"
)

func TestAppendDecimal(t *testing.T) {
	type pairs map[string]string // alternates with decimal input and result

	testCases := []struct {
		pattern string
		// We want to be able to test some forms of patterns that cannot be
		// represented as a string.
		pat *Pattern

		test pairs
	}{{
		pattern: "0",
		test: pairs{
			"0":    "0",
			"1":    "1",
			"-1":   "-1",
			".00":  "0",
			"10.":  "10",
			"12":   "12",
			"1.2":  "1",
			"NaN":  "NaN",
			"-Inf": "-∞",
		},
	}, {
		pattern: "+0;+0",
		test: pairs{
			"0":    "+0",
			"1":    "+1",
			"-1":   "-1",
			".00":  "+0",
			"10.":  "+10",
			"12":   "+12",
			"1.2":  "+1",
			"NaN":  "NaN",
			"-Inf": "-∞",
			"Inf":  "+∞",
		},
	}, {
		pattern: "0 +;0 +",
		test: pairs{
			"0":   "0 +",
			"1":   "1 +",
			"-1":  "1 -",
			".00": "0 +",
		},
	}, {
		pattern: "0;0-",
		test: pairs{
			"-1":   "1-",
			"NaN":  "NaN",
			"-Inf": "∞-",
			"Inf":  "∞",
		},
	}, {
		pattern: "0000",
		test: pairs{
			"0":     "0000",
			"1":     "0001",
			"12":    "0012",
			"12345": "12345",
		},
	}, {
		pattern: ".0",
		test: pairs{
			"0":      ".0",
			"1":      "1.0",
			"1.2":    "1.2",
			"1.2345": "1.2",
		},
	}, {
		pattern: "#.0",
		test: pairs{
			"0": ".0",
		},
	}, {
		pattern: "#.0#",
		test: pairs{
			"0": ".0",
			"1": "1.0",
		},
	}, {
		pattern: "0.0#",
		test: pairs{
			"0": "0.0",
		},
	}, {
		pattern: "#0.###",
		test: pairs{
			"0":        "0",
			"1":        "1",
			"1.2":      "1.2",
			"1.2345":   "1.234", // rounding should have been done earlier
			"1234.5":   "1234.5",
			"1234.567": "1234.567",
		},
	}, {
		pattern: "#0.######",
		test: pairs{
			"0":           "0",
			"1234.5678":   "1234.5678",
			"0.123456789": "0.123457",
			"NaN":         "NaN",
			"Inf":         "∞",
		},

		// Test separators.
	}, {
		pattern: "#,#.00",
		test: pairs{
			"100": "1,0,0.00",
		},
	}, {
		pattern: "#,0.##",
		test: pairs{
			"10": "1,0",
		},
	}, {
		pattern: "#,0",
		test: pairs{
			"10": "1,0",
		},
	}, {
		pattern: "#,##,#.00",
		test: pairs{
			"1000": "1,00,0.00",
		},
	}, {
		pattern: "#,##0.###",
		test: pairs{
			"0":           "0",
			"1234.5678":   "1,234.568",
			"0.123456789": "0.123",
		},
	}, {
		pattern: "#,##,##0.###",
		test: pairs{
			"0":            "0",
			"123456789012": "1,23,45,67,89,012",
			"0.123456789":  "0.123",
		},
	}, {
		pattern: "0,00,000.###",
		test: pairs{
			"0":            "0,00,000",
			"123456789012": "1,23,45,67,89,012",
			"12.3456789":   "0,00,012.346",
			"0.123456789":  "0,00,000.123",
		},

		// Support for ill-formed patterns.
	}, {
		pattern: "#",
		test: pairs{
			".00": "", // This is the behavior of fmt.
			"0":   "", // This is the behavior of fmt.
			"1":   "1",
			"10.": "10",
		},
	}, {
		pattern: ".#",
		test: pairs{
			"0":      "", // This is the behavior of fmt.
			"1":      "1",
			"1.2":    "1.2",
			"1.2345": "1.2",
		},
	}, {
		pattern: "#,#.##",
		test: pairs{
			"10": "1,0",
		},
	}, {
		pattern: "#,#",
		test: pairs{
			"10": "1,0",
		},

		// Special patterns
	}, {
		pattern: "#,max_int=2",
		pat: &Pattern{
			RoundingContext: RoundingContext{
				MaxIntegerDigits: 2,
			},
		},
		test: pairs{
			"2017": "17",
		},
	}, {
		pattern: "0,max_int=2",
		pat: &Pattern{
			RoundingContext: RoundingContext{
				MaxIntegerDigits: 2,
				MinIntegerDigits: 1,
			},
		},
		test: pairs{
			"2000": "0",
			"2001": "1",
			"2017": "17",
		},
	}, {
		pattern: "00,max_int=2",
		pat: &Pattern{
			RoundingContext: RoundingContext{
				MaxIntegerDigits: 2,
				MinIntegerDigits: 2,
			},
		},
		test: pairs{
			"2000": "00",
			"2001": "01",
			"2017": "17",
		},
	}, {
		pattern: "@@@@,max_int=2",
		pat: &Pattern{
			RoundingContext: RoundingContext{
				MaxIntegerDigits:     2,
				MinSignificantDigits: 4,
			},
		},
		test: pairs{
			"2017": "17.00",
			"2000": "0.000",
			"2001": "1.000",
		},

		// Significant digits
	}, {
		pattern: "@@##",
		test: pairs{
			"1":     "1.0",
			"0.1":   "0.10", // leading zero does not count as significant digit
			"123":   "123",
			"1234":  "1234",
			"12345": "12340",
		},
	}, {
		pattern: "@@@@",
		test: pairs{
			"1":     "1.000",
			".1":    "0.1000",
			".001":  "0.001000",
			"123":   "123.0",
			"1234":  "1234",
			"12345": "12340", // rounding down
			"NaN":   "NaN",
			"-Inf":  "-∞",
		},

		// TODO: rounding
		// {"@@@@": "23456": "23460"}, // rounding up
		// TODO: padding

		// Scientific and Engineering notation
	}, {
		pattern: "#E0",
		test: pairs{
			"0":       "0×10⁰",
			"1":       "1×10⁰",
			"123.456": "1×10²",
		},
	}, {
		pattern: "#E+0",
		test: pairs{
			"0":      "0×10⁺⁰",
			"1000":   "1×10⁺³",
			"1E100":  "1×10⁺¹⁰⁰",
			"1E-100": "1×10⁻¹⁰⁰",
			"NaN":    "NaN",
			"-Inf":   "-∞",
		},
	}, {
		pattern: "##0E00",
		test: pairs{
			"100":     "100×10⁰⁰",
			"12345":   "12×10⁰³",
			"123.456": "123×10⁰⁰",
		},
	}, {
		pattern: "##0.###E00",
		test: pairs{
			"100":      "100×10⁰⁰",
			"12345":    "12.345×10⁰³",
			"123456":   "123.456×10⁰³",
			"123.456":  "123.456×10⁰⁰",
			"123.4567": "123.457×10⁰⁰",
		},
	}, {
		pattern: "##0.000E00",
		test: pairs{
			"100":     "100.000×10⁰⁰",
			"12345":   "12.345×10⁰³",
			"123.456": "123.456×10⁰⁰",
			"12.3456": "12.346×10⁰⁰",
		},
	}, {
		pattern: "@@E0",
		test: pairs{
			"0":    "0.0×10⁰",
			"99":   "9.9×10¹",
			"0.99": "9.9×10⁻¹",
		},
	}, {
		pattern: "@###E00",
		test: pairs{
			"0":     "0×10⁰⁰",
			"1":     "1×10⁰⁰",
			"11":    "1.1×10⁰¹",
			"111":   "1.11×10⁰²",
			"1111":  "1.111×10⁰³",
			"11111": "1.111×10⁰⁴",
			"0.1":   "1×10⁻⁰¹",
			"0.11":  "1.1×10⁻⁰¹",
			"0.001": "1×10⁻⁰³",
		},
	}, {
		pattern: "*x##0",
		test: pairs{
			"0":    "xx0",
			"10":   "x10",
			"100":  "100",
			"1000": "1000",
		},
	}, {
		pattern: "##0*x",
		test: pairs{
			"0":    "0xx",
			"10":   "10x",
			"100":  "100",
			"1000": "1000",
		},
	}, {
		pattern: "* ###0.000",
		test: pairs{
			"0":        "   0.000",
			"123":      " 123.000",
			"123.456":  " 123.456",
			"1234.567": "1234.567",
		},
	}, {
		pattern: "**0.0#####E00",
		test: pairs{
			"0":     "***0.0×10⁰⁰",
			"10":    "***1.0×10⁰¹",
			"11":    "***1.1×10⁰¹",
			"111":   "**1.11×10⁰²",
			"1111":  "*1.111×10⁰³",
			"11111": "1.1111×10⁰⁴",
			"11110": "*1.111×10⁰⁴",
			"11100": "**1.11×10⁰⁴",
			"11000": "***1.1×10⁰⁴",
			"10000": "***1.0×10⁰⁴",
		},
	}, {
		pattern: "*xpre0suf",
		test: pairs{
			"0":  "pre0suf",
			"10": "pre10suf",
		},
	}, {
		pattern: "*∞ pre ###0 suf",
		test: pairs{
			"0":    "∞∞∞ pre 0 suf",
			"10":   "∞∞ pre 10 suf",
			"100":  "∞ pre 100 suf",
			"1000": " pre 1000 suf",
		},
	}, {
		pattern: "pre *∞###0 suf",
		test: pairs{
			"0":    "pre ∞∞∞0 suf",
			"10":   "pre ∞∞10 suf",
			"100":  "pre ∞100 suf",
			"1000": "pre 1000 suf",
		},
	}, {
		pattern: "pre ###0*∞ suf",
		test: pairs{
			"0":    "pre 0∞∞∞ suf",
			"10":   "pre 10∞∞ suf",
			"100":  "pre 100∞ suf",
			"1000": "pre 1000 suf",
		},
	}, {
		pattern: "pre ###0 suf *∞",
		test: pairs{
			"0":    "pre 0 suf ∞∞∞",
			"10":   "pre 10 suf ∞∞",
			"100":  "pre 100 suf ∞",
			"1000": "pre 1000 suf ",
		},
	}, {
		// Take width of positive pattern.
		pattern: "**###0;**-#####0x",
		test: pairs{
			"0":  "***0",
			"-1": "*-1x",
		},
	}, {
		pattern: "0.00%",
		test: pairs{
			"0.1": "10.00%",
		},
	}, {
		pattern: "0.##%",
		test: pairs{
			"0.1":     "10%",
			"0.11":    "11%",
			"0.111":   "11.1%",
			"0.1111":  "11.11%",
			"0.11111": "11.11%",
		},
	}, {
		pattern: "‰ 0.0#",
		test: pairs{
			"0.1":      "‰ 100.0",
			"0.11":     "‰ 110.0",
			"0.111":    "‰ 111.0",
			"0.1111":   "‰ 111.1",
			"0.11111":  "‰ 111.11",
			"0.111111": "‰ 111.11",
		},
	}}

	// TODO:
	// 	"#,##0.00¤",
	// 	"#,##0.00 ¤;(#,##0.00 ¤)",

	for _, tc := range testCases {
		pat := tc.pat
		if pat == nil {
			var err error
			if pat, err = ParsePattern(tc.pattern); err != nil {
				log.Fatal(err)
			}
		}
		var f Formatter
		f.InitPattern(language.English, pat)
		for num, want := range tc.test {
			buf := make([]byte, 100)
			t.Run(tc.pattern+"/"+num, func(t *testing.T) {
				var d Decimal
				d.Convert(f.RoundingContext, dec(num))
				buf = f.Format(buf[:0], &d)
				if got := string(buf); got != want {
					t.Errorf("\n got %[1]q (%[1]s)\nwant %[2]q (%[2]s)", got, want)
				}
			})
		}
	}
}

func TestLocales(t *testing.T) {
	testCases := []struct {
		tag  language.Tag
		num  string
		want string
	}{
		{language.Make("en"), "123456.78", "123,456.78"},
		{language.Make("de"), "123456.78", "123.456,78"},
		{language.Make("de-CH"), "123456.78", "123’456.78"},
		{language.Make("fr"), "123456.78", "123 456,78"},
		{language.Make("bn"), "123456.78", "১,২৩,৪৫৬.৭৮"},
	}
	for _, tc := range testCases {
		t.Run(fmt.Sprint(tc.tag, "/", tc.num), func(t *testing.T) {
			var f Formatter
			f.InitDecimal(tc.tag)
			var d Decimal
			d.Convert(f.RoundingContext, dec(tc.num))
			b := f.Format(nil, &d)
			if got := string(b); got != tc.want {
				t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
			}
		})
	}
}

func TestFormatters(t *testing.T) {
	var f Formatter
	testCases := []struct {
		init func(t language.Tag)
		num  string
		want string
	}{
		{f.InitDecimal, "123456.78", "123,456.78"},
		{f.InitScientific, "123456.78", "1.23×10⁵"},
		{f.InitEngineering, "123456.78", "123.46×10³"},
		{f.InitEngineering, "1234", "1.23×10³"},

		{f.InitPercent, "0.1234", "12.34%"},
		{f.InitPerMille, "0.1234", "123.40‰"},
	}
	for i, tc := range testCases {
		t.Run(fmt.Sprint(i, "/", tc.num), func(t *testing.T) {
			tc.init(language.English)
			f.SetScale(2)
			var d Decimal
			d.Convert(f.RoundingContext, dec(tc.num))
			b := f.Format(nil, &d)
			if got := string(b); got != tc.want {
				t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
			}
		})
	}
}
