package poc.java.perf.write;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/// This app has a "bad" implementation since it recreates with each call of `convertNumberToWords` method
/// a map which is used to convert the input. It implies intensive use of GC : ZGC gives better application throughput than with G1
///
/// - reference execution which creates application stalls:
/// `java -Xmx4g -classpath target/classes poc.java.perf.write.TestPerf`
/// - faster zgc execution (also application stalls):
/// `java -Xmx4g -XX:+UseZGC -classpath target/classes poc.java.perf.write.TestPerf`
/// - Usage of jfr with zgc (strongly decrease throughput) :
/// `java -Xmx4g  -XX:+UseZGC  -XX:StartFlightRecording -classpath target/classes poc.java.perf.write.TestPerf`
public class TestPerf {

    private static final Map<Integer, String> ciffers = Map.of(
            0,"zerozeroceroNull",
            1, "unoneunoeins",
                2, "deuxtwodoszwei",
                3, "troisthreetresdrei",
                4, "quatrefourcuatrovier",
                5, "cinqfivecincofünf",
                6, "sixsixseissechs",
                7, "septsevensietesieben",
                8, "huiteightochoacht",
                9, "neufninenueveneun"
    );

    private String convertIndex;
    private final Random random = new Random();

    void main() {
        // Init app
        int size = 200;
        int[] randomNumbers = generateRandomNumbers(size);
        convertIndex = loadIndex();

        // Process conversions
        List<String> convertedNumbers = Arrays.stream(randomNumbers)
                .parallel()
                .mapToObj(this::convertNumberToWords)
                .toList();

        // consume result
        System.out.println(convertedNumbers.hashCode());
    }

    private int[] generateRandomNumbers(int size) {
        return IntStream.generate(() -> random.nextInt(199_999)).limit(size).toArray();
    }

    static String loadIndex() {
        return IntStream.rangeClosed(1, 200_000)
                .mapToObj(i -> i+":"+generateString(i))
                .collect(Collectors.joining("\n"));
    }

    private static String generateString(int i) {
        if (i < 10){
            return ciffers.get(i);
        }
        return generateString(i / 10) + ";" + ciffers.get(i%10);
    }

    public String convertNumberToWords(int number) {
        Map<Integer, String> index = Arrays.stream(convertIndex.split("\n"))
                .map(s -> s.split(":"))
                .collect(Collectors.toMap(tab -> Integer.parseInt(tab[0]), tab -> tab[1]));
        return index.get(number);
    }
}
