package com.exalttech.trex.ui.views.statistics;

import javafx.beans.value.ObservableValue;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.exalttech.trex.core.AsyncResponseManager;
import com.exalttech.trex.util.Util;


public class StatsLoader {
    public interface GlobalStatsChangedListener {
        void globalStatsChanged();
    }

    private static StatsLoader instance;

    public static StatsLoader getInstance() {
        if (instance == null) {
            instance = new StatsLoader();
        }
        return instance;
    }

    private final List<GlobalStatsChangedListener> globalStatsChangedListeners = new ArrayList<>();

    private Map<String, String> loadedStatsList = new HashMap<>();
    private Map<String, String> previousStatsList = new HashMap<>();
    private Map<String, String> shadowStatsList = null;

    private StatsLoader() {}

    public Map<String, String> getLoadedStatsList() {
        return loadedStatsList;
    }

    public Map<String, String> getPreviousStatsList() {
        return previousStatsList;
    }

    public Map<String, String> getShadowStatsList() {
        return shadowStatsList != null ? shadowStatsList : new HashMap<>();
    }

    private boolean validAsyncResponse(String jsonString) {
        return !Util.isNullOrEmpty(jsonString) && jsonString.contains("m_cpu_util");
    }

    public void start() {
        synchronized (loadedStatsList) {
            loadedStatsList.clear();
        }
        synchronized (previousStatsList) {
            previousStatsList.clear();
        }
        shadowStatsList = null;

        handleGlobalStatsChanged();

        AsyncResponseManager.getInstance().getTrexGlobalProperty().addListener(this::handleGlobalPropertyChanged);
    }

    public void reset() {
        shadowStatsList = loadedStatsList;

        handleGlobalStatsChanged();
    }

    public void addGlobalStatsChangedListener(final GlobalStatsChangedListener listener) {
        synchronized (globalStatsChangedListeners) {
            globalStatsChangedListeners.add(listener);
        }
    }

    public void removeGlobalStatsChangedListener(final GlobalStatsChangedListener listener) {
        synchronized (globalStatsChangedListeners) {
            globalStatsChangedListeners.remove(listener);
        }
    }

    private void handleGlobalPropertyChanged(
            ObservableValue<? extends String> observable,
            String oldValue,
            String newValue
    ) {
        if (newValue == null) {
            return;
        }

        String data = Util.fromJSONResult(newValue, "data");
        if (!validAsyncResponse(data)) {
            return;
        }

        previousStatsList = loadedStatsList;
        loadedStatsList = Util.getStatsFromJSONString(data);

        if (shadowStatsList == null) {
            shadowStatsList = loadedStatsList;
        }

        handleGlobalStatsChanged();
    }

    private void handleGlobalStatsChanged() {
        synchronized (globalStatsChangedListeners) {
            globalStatsChangedListeners.forEach(GlobalStatsChangedListener::globalStatsChanged);
        }
    }
}

@@ -994,31 +994,72 @@
  return 
-loadedStatsList
+getNormalizedStatsList(loadedStatsList, shadowStatsList)
 ;%0A    %7D%0A
@@ -2771,238 +2771,1153 @@
     
-previousStatsList = loadedStatsList;%0A        loadedStatsList = Util.getStatsFromJSONString(data);%0A%0A        if (shadowStatsList == null) %7B%0A            shadowStatsList = loadedStatsList;%0A        %7D%0A%0A        handleGlobalStatsChanged()
+Map%3CString, String%3E parsedData = Util.getStatsFromJSONString(data);%0A%0A        if (shadowStatsList == null) %7B%0A            shadowStatsList = parsedData;%0A        %7D%0A%0A        previousStatsList = loadedStatsList;%0A        loadedStatsList = parsedData;%0A%0A        handleGlobalStatsChanged();%0A    %7D%0A%0A    private Map%3CString, String%3E getNormalizedStatsList(final Map%3CString, String%3E stats,%0A                                                       final Map%3CString, String%3E initialStats) %7B%0A        final String TOTAL_QUEUE_FULL_FIELD_NAME = %22m_total_queue_full%22;%0A%0A        Map%3CString, String%3E result = new HashMap%3C%3E(stats);%0A%0A        if (!stats.containsKey(TOTAL_QUEUE_FULL_FIELD_NAME)) %7B%0A            return result;%0A        %7D%0A%0A        if (!initialStats.containsKey(TOTAL_QUEUE_FULL_FIELD_NAME)) %7B%0A            return result;%0A        %7D%0A%0A        final Long initial = Long.parseLong(initialStats.get(TOTAL_QUEUE_FULL_FIELD_NAME));%0A        final Long current = Long.parseLong(stats.get(TOTAL_QUEUE_FULL_FIELD_NAME));%0A        final Long relativeValue = current - initial;%0A%0A        result.put(TOTAL_QUEUE_FULL_FIELD_NAME, relativeValue.toString());%0A%0A        return result
 ;%0A  

Add ability to clear 'total queue full' value