#ifndef UTILIZATION_MONITOR_FIXED_HPP
#define UTILIZATION_MONITOR_FIXED_HPP

#include <boost/atomic.hpp>
#include <boost/thread.hpp>

class UtilizationMonitorFixed {
public:
    UtilizationMonitorFixed(unsigned int interval_seconds, double a, double beta);
    ~UtilizationMonitorFixed();

    void start();
    void stop();
    bool isRunning();

    double getEwmaUtilization();
    double getEwmaRawUtilization();

private:
    enum CpuStatColumn {
        COL_USER = 0,
        COL_NICE,
        COL_SYSTEM,
        COL_IDLE,
        COL_IOWAIT,
        COL_IRQ,
        COL_SOFTIRQ,
        COL_STEAL,
        COL_GUEST,
        COL_GUEST_NICE,
        COL_COUNT
    };

    struct CpuUsageResult {
        int cpu_index;
        unsigned long long running_delta;
        unsigned long long iowait_delta;
        unsigned long long idle_delta;
        unsigned long long total_delta;
        double running_percent;
        double iowait_percent;
        double idle_percent;
        double utilization_percent;
        double mean_utilization_percent;
    };

    boost::atomic<bool> stop_flag;
    boost::atomic<bool> running;
    unsigned int interval_seconds;

    unsigned long long** previous;
    unsigned long long** current;
    int cpu_count;

    double* per_cpu_mean_util;
    int per_cpu_mean_count;

    double a;
    double beta;
    double total_util_ewma;
    double total_util_ewma_raw;
    bool ewma_initialized;

    boost::thread worker_thread;
    boost::mutex data_mutex;

    unsigned long long** allocateMatrix(int rows);
    void freeMatrix(unsigned long long** matrix, int rows);

    double* allocateDoubleArray(int n);
    void freeDoubleArray(double* arr);
    void ensureMeanStorage(int n);

    unsigned long long getRunningTime(unsigned long long* row);
    unsigned long long getIoWaitTime(unsigned long long* row);
    unsigned long long getIdleTime(unsigned long long* row);
    unsigned long long getTotalTime(unsigned long long* row);
    unsigned long long getBusyTime(unsigned long long* row);

    bool readPerCpuStats(unsigned long long**& matrix, int& cpu_count);

    CpuUsageResult calculateUsage(unsigned long long* prev_row,
                                  unsigned long long* curr_row,
                                  int cpu_index);

    void updatePerCpuMeans(CpuUsageResult* results,
                           int count,
                           unsigned long long sample_index);

    double computeTotalUtilizationEwma(CpuUsageResult* results,
                                       int count,
                                       bool raw = false);

    void monitorLoop();
};

#endif // UTILIZATION_MONITOR_FIXED_HPP
