package org.unicode.cldr.util;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;

import org.unicode.cldr.draft.FileUtilities;
import org.unicode.cldr.test.BuildIcuCompactDecimalFormat;
import org.unicode.cldr.test.BuildIcuCompactDecimalFormat.CurrencyStyle;
import org.unicode.cldr.tool.Option;
import org.unicode.cldr.tool.Option.Options;
import org.unicode.cldr.tool.ShowPlurals;
import org.unicode.cldr.tool.TablePrinter;
import org.unicode.cldr.util.CLDRFile.DraftStatus;
import org.unicode.cldr.util.PathHeader.PageId;
import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo;

import com.ibm.icu.text.CompactDecimalFormat;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.ICUUncheckedIOException;
import com.ibm.icu.util.ULocale;

public class VerifyCompactNumbers {

    private static final CLDRConfig CLDR_CONFIG = CLDRConfig.getInstance();
    private static final String DIR = CLDRPaths.CHART_DIRECTORY + "verify/numbers/";
    final static Options myOptions = new Options();

    enum MyOptions {
        organization(".*", "CLDR", "organization"),
        filter(".*", ".*", "locale filter (regex)"),
        currency(".*", "EUR", "show currency"), ;
        // boilerplate
        final Option option;

        MyOptions(String argumentPattern, String defaultArgument, String helpText) {
            option = myOptions.add(this, argumentPattern, defaultArgument, helpText);
        }
    }

    // later, look at DateTimeFormats to set up as an HTML table

    private static final Set<String> USES_GROUPS_OF_4 = new HashSet<String>(Arrays.asList("ko", "ja", "zh", "zh_Hant"));

    /**
     * Produce a set of static tables from the vxml data. Only a stopgap until the above is integrated into ST.
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        myOptions.parse(MyOptions.organization, args, true);

        String organization = MyOptions.organization.option.getValue();
        String filter = MyOptions.filter.option.getValue();
        boolean showCurrency = true; // MyOptions.currency.option.doesOccur();
        String currencyCode = MyOptions.currency.option.getValue();

        Factory factory2 = Factory.make(CLDRPaths.MAIN_DIRECTORY, filter);
        CLDRFile englishCldrFile = factory2.make("en", true);

        SupplementalDataInfo sdi = CLDR_CONFIG.getSupplementalDataInfo();
        Set<String> defaultContentLocales = sdi.getDefaultContentLocales();
        NumberFormat enf = NumberFormat.getIntegerInstance(ULocale.ENGLISH);
        enf.setGroupingUsed(false);

        Set<String> availableLanguages = new TreeSet<String>(factory2.getAvailableLanguages());
        if (Pattern.matches(filter, "pt_PT")) {
            availableLanguages.add("pt_PT");
        }

        PrintWriter plainText = FileUtilities.openUTF8Writer(DIR, "compactTestFile.txt");
        DateTimeFormats.writeCss(DIR);
        final CLDRFile english = CLDR_CONFIG.getEnglish();

        Map<String, String> indexMap = new TreeMap<>(CLDR_CONFIG.getCollator());

        for (String locale : availableLanguages) {
            if (defaultContentLocales.contains(locale)) {
                continue;
            }
            Level level = StandardCodes.make().getLocaleCoverageLevel(organization, locale);
            if (Level.MODERN.compareTo(level) > 0) {
                continue;
            }
            // TODO: fix to ignore locales with no data.
            if (locale.equals("ne") || locale.equals("cy")) {
                continue;
            }

            PrintWriter out = FileUtilities.openUTF8Writer(DIR, locale + ".html");
            String title = "Verify Number Formats: " + englishCldrFile.getName(locale);
            out.println("<!doctype HTML PUBLIC '-//W3C//DTD HTML 4.0 Transitional//EN'><html><head>\n" +
                "<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\n" +
                "<title>" + title + "</title>\n" +
                "<link rel='stylesheet' type='text/css' href='index.css'>\n" +
                "</head><body><h1>" + title + "</h1>\n"
                + "<p><a href='index.html'>Index</a></p>\n");

            CLDRFile cldrFile = factory2.make(locale, true, DraftStatus.contributed);

            showNumbers(cldrFile, showCurrency, currencyCode, out, factory2);

            out.println("</body></html>");
            out.close();
            indexMap.put(english.getName(locale), locale + ".html");

        }
        try (PrintWriter index = DateTimeFormats.openIndex(DIR, "Numbers")) {
            DateTimeFormats.writeIndexMap(indexMap, index);
        }

        plainText.close();

    }

    public static void showNumbers(CLDRFile cldrFile, boolean showCurrency,
        String currencyCode, Appendable out, Factory factory) {
        try {
            Set<String> debugCreationErrors = new LinkedHashSet<String>();
            Set<String> errors = new LinkedHashSet<String>();
            String locale = cldrFile.getLocaleID();

            TablePrinter tablePrinter1 = new TablePrinter()
            // .setCaption("Timezone Formats")
            .setTableAttributes("class='dtf-table'")
            .addColumn("Numeric Format").setHeaderCell(true).setHeaderAttributes("class='dtf-th'")
                .setCellAttributes("class='dtf-s'")
            .addColumn("Compact-Short").setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'")
            .addColumn("Compact-Long").setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'");
            if (showCurrency) {
                tablePrinter1
                .addColumn("Compact-Short<br>+Currency")
                    .setHeaderAttributes("class='dtf-th'")
                    .setCellAttributes("class='dtf-s'")
//                    .addColumn("Compact-Short<br>+Unit")
//                    .setHeaderAttributes("class='dtf-th'")
//                    .setCellAttributes("class='dtf-s'")
                // .addColumn("Compact-Long<br>+Currency")
                // .addColumn("Compact-Long<br>+Currency-Long")
//                    .addColumn("Numeric Format").setHeaderCell(true).setHeaderAttributes("class='dtf-th'")
//                      .setCellAttributes("class='dtf-s'")
                ;
            }
            //            tablePrinter1.addColumn("View").setHeaderCell(true).setHeaderAttributes("class='dtf-th'").setCellAttributes("class='dtf-s'");

            ;

            ULocale locale2 = new ULocale(locale);
            NumberFormat nf = NumberFormat.getInstance(locale2);
            // nf.setMaximumFractionDigits(0);
            SupplementalDataInfo sdi = CLDR_CONFIG.getSupplementalDataInfo();
            PluralInfo pluralInfo = sdi.getPlurals(locale);
            String[] debugOriginals = null;
            CompactDecimalFormat cdf = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors,
                debugOriginals, CompactStyle.SHORT, locale2, CurrencyStyle.PLAIN, currencyCode);
            captureErrors(debugCreationErrors, errors, locale, "short");
            CompactDecimalFormat cdfs = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors,
                debugOriginals, CompactStyle.LONG, locale2, CurrencyStyle.PLAIN, currencyCode);
            captureErrors(debugCreationErrors, errors, locale, "long");

            CompactDecimalFormat cdfCurr = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors,
                debugOriginals, CompactStyle.SHORT, locale2, CurrencyStyle.CURRENCY, currencyCode);
            captureErrors(debugCreationErrors, errors, locale, "short-curr");
//            CompactDecimalFormat cdfU = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors,
//                debugOriginals, CompactStyle.SHORT, locale2, CurrencyStyle.UNIT, "EUR");
//            captureErrors(debugCreationErrors, errors, locale, "short-kg");
//             CompactDecimalFormat cdfsCurr = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors,
//             debugOriginals, CompactStyle.SHORT, locale2, CurrencyStyle.CURRENCY, currencyCode);
//             CompactDecimalFormat cdfsCurrISO = BuildIcuCompactDecimalFormat.build(cldrFile, debugCreationErrors,
//             debugOriginals, CompactStyle.LONG, locale2, CurrencyStyle.ISO_CURRENCY, "EUR");

            // Collect samples for display
            // one path for group-3, one for group-4
            // TODO, fix for indic.
            int factor = USES_GROUPS_OF_4.contains(locale) ? 10000 : 1000;

            // we want to collect a sample of at least one sample for each plural category for each
            // power of ten
            Set<Double> samples = new TreeSet<Double>();
            samples.add(1.1d);
            samples.add(1.5d);
            samples.add(1100d);
            collectItems(pluralInfo, 1, 10, samples);
            collectItems(pluralInfo, 10, 100, samples);
            collectItems(pluralInfo, 100, 1000, samples);
            int sigDigits = 3;
            if (factor > 1000) {
                collectItems(pluralInfo, 1000, 10000, samples);
                sigDigits = 4;
            }
            if (cdf != null) {
                cdf.setMaximumSignificantDigits(sigDigits);
            }
            if (cdfs != null) {
                cdfs.setMaximumSignificantDigits(sigDigits);
            }
            if (cdfCurr != null) {
                cdfCurr.setCurrency(Currency.getInstance(currencyCode));
                cdfCurr.setMaximumSignificantDigits(sigDigits);
            }
//            cdfU.setMaximumSignificantDigits(sigDigits);

            // for (Entry<Count, List<Double>> entry : pluralInfo.getCountToExamplesMap().entrySet()) {
            // samples.add(entry.getValue().get(0));
            // }
            //
            // Set<Double> samples2 = new TreeSet<Double>();
            // for (int i = 10; i < factor; i *= 10) {
            // for (Double sample : samples) {
            // samples2.add(sample*i);
            // }
            // }
            // samples.addAll(samples2);

            Set<Double> allSamples = new TreeSet<Double>();
            for (long i = 1; i <= 100000000000000L; i *= factor) {
                for (Double sample : samples) {
                    double source = i * sample;
                    allSamples.add(source);
                }
            }

            try {
                for (double source : allSamples) {
                    if (false && source == 22000000 && locale.equals("cs")) {
                        System.out.println("**");
                    }

                    String formattedNumber = nf.format(source);
                    String compactFormattedNumber = cdf == null ? "n/a" : cdf.format(source);
                    String compactLongFormattedNumber = cdfs == null ? "n/a" : cdfs.format(source);
                    // plainText.println(locale
                    // + "\t__" + source
                    // + "\t__" + compactFormattedNumber
                    // + "\t__" + compactLongFormattedNumber
                    // );
                    tablePrinter1.addRow()
                    .addCell(formattedNumber)
                    .addCell(compactFormattedNumber)
                    .addCell(compactLongFormattedNumber);
                    if (showCurrency) {
                        tablePrinter1
                        .addCell(cdfCurr == null ? "n/a" : cdfCurr.format(source))
//                            .addCell(cdfU.format(source))
//                             .addCell(cdfsCurr.format(source))
                        // .addCell(cdfsCurrLong.format(source))
                        // .addCell(cdfsCurrLong.format(source))
                        //.addCell(formattedNumber)
                        ;
                    }
                    //                    String view = PathHeader.getLinkedView(surveyUrl, cldrFile, METAZONE_PREFIX + metazone + METAZONE_SUFFIX);
                    //                    tablePrinter1.addCell(view == null
                    //                            ? ""
                    //                                    : view);
                    tablePrinter1
                    .finishRow();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            out.append("<p>To correct problems in compact numbers below, please go to "
                + PathHeader.SECTION_LINK
                + CLDR_CONFIG.urls().forPage(cldrFile.getLocaleID(), PageId.Compact_Decimal_Formatting)
                + "'><em>" + PageId.Compact_Decimal_Formatting
                + "</em></a>.</p>");
            out.append(tablePrinter1.toString() + "\n");
            out.append("<h3>Plural Rules</h3>");
            out.append("<p>To correct problems in plural rules below, please go to " +
                "<a target='CLDR-ST-DOCS' href='http://cldr.unicode.org/index/cldr-spec/plural-rules'>Plural Rules</a>.</p>");
            ShowPlurals showPlurals = new ShowPlurals(CLDR_CONFIG.getSupplementalDataInfo());
            showPlurals.printPluralTable(cldrFile, locale, out, factory);
            showPlurals.appendBlanksForScrolling(out);
            showErrors(errors, out);
            showErrors(debugCreationErrors, out);
        } catch (IOException e) {
            throw new ICUUncheckedIOException(e);
        }
    }

    private static String surveyUrl = CLDR_CONFIG.getProperty("CLDR_SURVEY_URL",
        "http://st.unicode.org/cldr-apps/survey");

    private static void showErrors(Set<String> errors, Appendable out) throws IOException {
        if (errors.size() != 0) {
            out.append("<h2>" + "Errors" + "</h2>\n");
            for (String s : errors) {
                out.append("<p>" + s + "</p>\n");
            }
            errors.clear();
        }
    }

    private static Set<Double> collectItems(PluralInfo pluralInfo, double start, double limit,
        Set<Double> samples) {
        // TODO optimize once we have all the keywords
        Map<String, Double> ones = new TreeMap<String, Double>();
        for (double i = start; i < limit; ++i) {
            String cat = pluralInfo.getPluralRules().select(i);
            if (ones.containsKey(cat)) {
                continue;
            }
            ones.put(cat, i);
        }
        samples.addAll(ones.values());
        return samples;
    }

    private static void captureErrors(Set<String> debugCreationErrors, Set<String> errors, String locale, String length) {
        if (debugCreationErrors.size() != 0) {
            for (String s : debugCreationErrors) {
                errors.add(locale + "\t" + length + "\t" + s);
            }
            debugCreationErrors.clear();
        }
    }
}
