<commit_before>package org.helioviewer.jhv.timelines.band;

import java.awt.EventQueue;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;

import javax.annotation.Nonnull;

import org.helioviewer.jhv.base.interval.Interval;
import org.helioviewer.jhv.io.JSONUtils;
import org.helioviewer.jhv.log.Log;
import org.helioviewer.jhv.threads.EventQueueCallbackExecutor;
import org.helioviewer.jhv.time.TimeUtils;
import org.helioviewer.jhv.timelines.Timelines;
import org.helioviewer.jhv.timelines.TimelineSettings;
import org.helioviewer.jhv.timelines.draw.DrawController;
import org.json.JSONArray;
import org.json.JSONObject;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.util.concurrent.FutureCallback;

public class BandDataProvider {

    private static final ArrayListMultimap<Band, Future<BandResponse>> workerMap = ArrayListMultimap.create();

    public static void loadBandTypes() {
        EventQueueCallbackExecutor.pool.submit(new BandTypeDownload(), new BandTypeDownloadCallback());
    }

    public static void loadBand(JSONObject jo) {
        EventQueueCallbackExecutor.pool.submit(new BandLoad(jo), new BandLoadCallback());
    }

    public static void loadBandResponse(JSONObject jo) {
        BandResponse response = new BandResponse(jo); // outside EDT
        EventQueue.invokeLater(() -> {
            Band band = new Band(response.bandType);
            band.addToCache(response.values, response.dates);
            Timelines.getLayers().add(band);

            DrawController.setSelectedInterval(response.dates[0], response.dates[response.dates.length - 1]);
        });
    }

    static void addDownloads(Band band, List<Interval> intervals) {
        if ("".equals(band.getBandType().getBaseURL()))
            return;
        for (Interval interval : intervals) {
            Future<BandResponse> worker = EventQueueCallbackExecutor.pool.submit(
                    new BandDownload(band, interval.start, interval.end), new BandDownloadCallback(band));
            workerMap.put(band, worker);
        }
    }

    static void stopDownloads(Band band) {
        workerMap.get(band).forEach(worker -> worker.cancel(true));
        workerMap.removeAll(band);
    }

    static boolean isDownloadActive(Band band) {
        for (Future<BandResponse> worker : workerMap.get(band)) {
            if (!worker.isDone())
                return true;
        }
        return false;
    }

    private record BandDownload(Band band, long startTime, long endTime) implements Callable<BandResponse> {<commit_msg>Allow loading several external bands<commit_after>package org.helioviewer.jhv.timelines.band;

import java.awt.EventQueue;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;

import javax.annotation.Nonnull;

import org.helioviewer.jhv.base.interval.Interval;
import org.helioviewer.jhv.io.JSONUtils;
import org.helioviewer.jhv.log.Log;
import org.helioviewer.jhv.threads.EventQueueCallbackExecutor;
import org.helioviewer.jhv.time.TimeUtils;
import org.helioviewer.jhv.timelines.Timelines;
import org.helioviewer.jhv.timelines.TimelineSettings;
import org.helioviewer.jhv.timelines.draw.DrawController;
import org.json.JSONArray;
import org.json.JSONObject;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.util.concurrent.FutureCallback;

public class BandDataProvider {

    private static final ArrayListMultimap<Band, Future<BandResponse>> workerMap = ArrayListMultimap.create();
    private static final HashMap<BandType, Band> externalLoad = new HashMap<>();

    public static void loadBandTypes() {
        EventQueueCallbackExecutor.pool.submit(new BandTypeDownload(), new BandTypeDownloadCallback());
    }

    public static void loadBand(JSONObject jo) {
        EventQueueCallbackExecutor.pool.submit(new BandLoad(jo), new BandLoadCallback());
    }

    public static void loadBandResponse(JSONObject jo) {
        BandResponse response = new BandResponse(jo); // outside EDT
        EventQueue.invokeLater(() -> {
            Band band = externalLoad.computeIfAbsent(response.bandType, Band::new);
            band.addToCache(response.values, response.dates);
            Timelines.getLayers().add(band);
            DrawController.setSelectedInterval(response.dates[0], response.dates[response.dates.length - 1]);
        });
    }

    static void addDownloads(Band band, List<Interval> intervals) {
        if ("".equals(band.getBandType().getBaseURL()))
            return;
        for (Interval interval : intervals) {
            Future<BandResponse> worker = EventQueueCallbackExecutor.pool.submit(
                    new BandDownload(band, interval.start, interval.end), new BandDownloadCallback(band));
            workerMap.put(band, worker);
        }
    }

    static void stopDownloads(Band band) {
        workerMap.get(band).forEach(worker -> worker.cancel(true));
        workerMap.removeAll(band);
    }

    static boolean isDownloadActive(Band band) {
        for (Future<BandResponse> worker : workerMap.get(band)) {
            if (!worker.isDone())
                return true;
        }
        return false;
    }

    private record BandDownload(Band band, long startTime, long endTime) implements Callable<BandResponse> {
{'fraction_non_alphanumeric': 0.10461653015636635, 'fraction_numerical': 0.0007446016381236039, 'mean_word_length': 3.5923076923076924, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6034819', 'n_tokens_mistral': 1551, 'n_tokens_neox': 1504, 'n_words': 294}