// © 2019 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.dev.test.format;

import static org.junit.Assert.assertEquals;

import java.text.ParsePosition;
import java.util.Locale;
import java.util.Objects;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import com.ibm.icu.dev.test.TestUtil;
import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.util.ULocale;

/**
 * Test for {@link DecimalFormat} in {@link ParseMode#JAVA_COMPATIBILITY} mode.
 */
@RunWith(JUnit4.class)
public class NumberFormatJavaCompatilityTest {

    @Test
    public void testIgnoreables() {
        // Test bidi characters
        assertParseError("0", "\u200e1");
        assertParsed("0", "1\u200e", 1);
        assertParseError("0%", "\u200e1%");
    }

    @Test
    public void testParseGroupingSeparator() {
        // Test that grouping separator is optional when the group separator is specified
        assertParsed("#,##0", "9,999", 9999);
        assertParsed("#,##0", "9999", 9999);
        assertParsed("#,###0", "9,9999", 99999);

        // Test that grouping size doesn't affect parsing at all
        assertParsed("#,##0", "9,9999", 99999);
        assertParsed("#,###0", "99,999", 99999);

        assertParsed("###0", "9999", 9999);
        assertParsed("###0", "99999", 99999);

        // Test that grouping separator must not be present when the group separator is NOT specified
        // Only the 1st character in front of separator , should be consumed.
        assertParsed("###0", "9,9999", 9);
        assertParsed("###0", "9,999", 9);
    }

    @Test
    public void testParseScienificNotation() {
        assertParsed("0.###E0", "1E-3", 0.001);
        assertParsed("0.###E0", "1E0", 1);
        assertParsed("0.###E0", "1E3", 1000);
        assertParsed("0.###E0", "1.111E3", 1111);
        assertParsed("0.###E0", "1.1E3", 1100);

        // "0.###E0" is engineering notation, i.e. the exponent should be a multiple of 3
        // for formatting. But it shouldn't affect parsing.
        assertParsed("0.###E0", "1E1", 10);

        // Test that exponent is not required for parsing
        assertParsed("0.###E0", "1.1", 1.1);
        assertParsed("0.###E0", "1100", 1100);

        // Test that the max of fraction, integer or signficant digits don't affect parsing
        // Note that the max of signficant digits is 4 = min integer digits (1)
        //   + max fraction digits (3)
        assertParsed("0.###E0", "1111.4E3", 1111400);
        assertParsed("0.###E0", "1111.9999E3", 1111999.9);
    }

    private void assertParseError(String pattern, String input) {
        assertParsed(pattern, input, null);
    }

    private void assertParsed(String pattern, String input, Number expected) {
        assertParsedICU4J(pattern, input, expected);

        // Skip the OpenJDK test if the runtime is not OpenJDK
        if (TestUtil.getJavaRuntimeName() != TestUtil.JavaRuntimeName.OpenJDK) {
            return;
        }

        assertParsedOpenJDK(pattern, input, expected);
    }

    private void assertParsedICU4J(String pattern, String input, Number expected) {
        DecimalFormat df = new DecimalFormat(pattern, new DecimalFormatSymbols(ULocale.US));
        df.setParseStrictMode(ParseMode.JAVA_COMPATIBILITY);
        ParsePosition ppos = new ParsePosition(0);
        Number actual = df.parse(input, ppos);
        assertEquals(String.format("pattern: %s input: %s", pattern, input),
                Objects.toString(expected), Objects.toString(actual));
    }

    private void assertParsedOpenJDK(String pattern, String input, Number expected) {
        java.text.DecimalFormat df = new java.text.DecimalFormat(pattern,
                new java.text.DecimalFormatSymbols(Locale.US));
        ParsePosition ppos = new ParsePosition(0);
        Number actual = df.parse(input, ppos);
        assertEquals(String.format("pattern: %s input: %s", pattern, input),
                Objects.toString(expected), Objects.toString(actual));
    }

}
