package org.unicode.cldr.util;

import java.io.File;
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.ChartDelta;
import org.unicode.cldr.tool.FormattedFileWriter;
import org.unicode.cldr.tool.Option;
import org.unicode.cldr.tool.Option.Options;
import org.unicode.cldr.tool.ShowData;
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.VERIFY_DIR + "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

    public static final Set<String> USES_GROUPS_OF_4 = new HashSet<>(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);
        new File(DIR).mkdirs();
        FileCopier.copy(ShowData.class, "verify-index.html", CLDRPaths.VERIFY_DIR, "index.html");
        FileCopier.copy(ChartDelta.class, "index.css", CLDRPaths.VERIFY_DIR, "index.css");
        FormattedFileWriter.copyIncludeHtmls(CLDRPaths.VERIFY_DIR);

        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<>(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<>();
            Set<String> errors = new LinkedHashSet<>();
            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);
            ICUServiceBuilder builder = new ICUServiceBuilder().setCldrFile(cldrFile);
            NumberFormat nf = builder.getNumberFormat(1);

            // nf.setMaximumFractionDigits(0);
            SupplementalDataInfo sdi = CLDR_CONFIG.getSupplementalDataInfo();
            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");

            Set<Double> allSamples = collectSamplesAndSetFormats(currencyCode, locale, sdi, cdf, cdfs, cdfCurr);

            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);
                    String compactCurrFormattedNumber = !showCurrency || cdfs == null ? "n/a" : cdfCurr.format(source);
                    // plainText.println(locale
                    // + "\t__" + source
                    // + "\t__" + compactFormattedNumber
                    // + "\t__" + compactLongFormattedNumber
                    // );
                    tablePrinter1.addRow()
                        .addCell(formattedNumber)
                        .addCell(compactFormattedNumber)
                        .addCell(compactLongFormattedNumber);
                    if (showCurrency) {
                        tablePrinter1
                            .addCell(compactCurrFormattedNumber)
//                            .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>Look over the Minimal Pairs to make sure they are ok. "
                + "Then review the examples in the cell to the left. "
                + "All of those you should be able to substitute for the numbers in the Minimal Pairs, "
                + "with an acceptable result. "
                + "If any would be incorrect, please "
                + "<a target='ticket' href='" + CLDRURLS.CLDR_NEWTICKET_URL + "'>file a ticket</a>.</p>"
                + "<p>For more details, see " +
                "<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);
        }
    }

    public static Set<Double> collectSamplesAndSetFormats(String currencyCode, String locale, SupplementalDataInfo sdi, CompactDecimalFormat cdf,
        CompactDecimalFormat cdfs, CompactDecimalFormat cdfCurr) {
        // 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
        PluralInfo pluralInfo = sdi.getPlurals(locale);
        Set<Double> samples = new TreeSet<>();
        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<>();
        for (long i = 1; i <= 100000000000000L; i *= factor) {
            for (Double sample : samples) {
                double source = i * sample;
                allSamples.add(source);
            }
        }
        return allSamples;
    }

    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<>();
        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();
        }
    }
}
