package org.javacodegeeks.examples.v3.io;

import jdk.incubator.concurrent.StructuredTaskScope;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;

public class IoLoomExampleMain {

    public static void main(String[] args) throws Exception {

        final var thread = Thread.ofVirtual().start(IoLoomExampleMain::start);
        thread.join();
    }

    private static void start() {
        try (var batchScope = new BatchScope()) {
            int batchSize = 1000;
            try (final var lines = Files.lines(Paths.get("src/main/resources/names.txt"))) {
                final var iterator = lines.iterator();
                try (var fileReadingScope = new StructuredTaskScope.ShutdownOnFailure()) {
                    while (iterator.hasNext()) {
                        final var batchFuture = fileReadingScope.fork(() -> prepareBatch(batchSize, iterator));
                        fileReadingScope.join();
                        batchScope.fork(prepareBatchProcessing(batchFuture.resultNow()));
                    }
                }

            }
            batchScope.join();
            System.out.println("The most frequent name is " + batchScope.mostFrequentName());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static ArrayList<String> prepareBatch(int batchSize, Iterator<String> iterator) {
        System.out.printf("[%s][virtual=%s] Preparing batch... \n", LocalDateTime.now(), Thread.currentThread().isVirtual());
        ArrayList<String> batch = new ArrayList<>(batchSize);
        while (iterator.hasNext() && batch.size() < batchSize) {
            batch.add(iterator.next());
        }
        return batch;
    }

    private static Callable<Map<String, Long>> prepareBatchProcessing(List<String> batch) {
        return () -> {
            Map<String, Long> localCounts = new ConcurrentHashMap<>();
            System.out.printf("[%s][virtual=%s] Processing batch... \n", LocalDateTime.now(), Thread.currentThread().isVirtual());
            for (String name : batch) {
                localCounts.compute(name, (n, c) -> c == null ? 1L : c + 1);
            }
            return localCounts;
        };
    }

    private static class BatchScope extends StructuredTaskScope<Map<String, Long>> {

        private final ConcurrentHashMap<String, Long> result = new ConcurrentHashMap<>();

        @Override
        protected void handleComplete(Future<Map<String, Long>> future) {
            final var intermediateResult = future.resultNow();
            for (var stringLongEntry : intermediateResult.entrySet()) {
                result.compute(stringLongEntry.getKey(), (n, c) -> updateCount(stringLongEntry.getValue(), c));
            }
        }

        private long updateCount(Long newCount, Long existingCount) {
            return existingCount == null ? newCount : existingCount + newCount;
        }

        public String mostFrequentName() {
            return result.entrySet()
                .stream()
                .max(Map.Entry.comparingByValue())
                .get()
                .getKey();
        }
    }
}

