#ifndef UTILIZATION_MONITOR_H
#define UTILIZATION_MONITOR_H

#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <boost/atomic.hpp>

#include <cstddef>
#include <cstdint>
#include <string>

enum CpuColumn {
        COL_USER = 0,
        COL_NICE,
        COL_SYSTEM,
        COL_IDLE,
        COL_IOWAIT,
        COL_IRQ,
        COL_SOFTIRQ,
        COL_STEAL,
        COL_GUEST,
        COL_GUEST_NICE,
        COL_COUNT
    };

class UtilizationMonitor {
public:
    UtilizationMonitor(unsigned int interval_seconds = 60,
                       double a = 0.7,
                       double beta = 0.8);

    ~UtilizationMonitor();

    void start();
    void stop();

    double getEwmaUtilization();
    double getEwmaRawUtilization();
    bool isRunning();

private:

    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;
    };

    void monitorLoop();

    bool readPerCpuStats(unsigned long long**& matrix, int& cpu_count);

    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);

    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, const bool raw);

private:
    boost::atomic<bool> stop_flag;
    boost::atomic<bool> running;
    unsigned int interval_seconds;
    boost::thread worker_thread;

    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::mutex data_mutex;
};

#endif
