#pragma once
#include <string>
#include <vector>
#include <deque>

#include <sys/sysinfo.h>
#include <embree4/rtcore.h>
#include <lexers/parsestream.h>
#include <algorithms/parallel_for.h>
#include <scenegraph/scenegraph.h>

// #include <lights/light.h>

#include "scene.hpp"

/* size of screen tiles */
#define TILE_SIZE_X 8
#define TILE_SIZE_Y 8

struct Sample {
    embree::Vec3fa P;
    embree::Vec3fa Ng;
    embree::Vec3fa Ns;
};

struct Data {
    RenderScene* scene;
    int spp;
    int max_path_length;

    /* accumulation buffer */
    embree::Vec3ff* accu;
    unsigned int accu_width;
    unsigned int accu_height;
    unsigned int accu_count;

    RTCScene g_scene;
};

inline void Data_Constructor(Data* This, int spp, int max_path_length) {
    This->g_scene = nullptr;
    This->scene = nullptr;
    This->accu = nullptr;

    This->spp = spp;
    This->max_path_length = max_path_length;

    This->accu = nullptr;
    This->accu_width = 0;
    This->accu_height = 0;
    This->accu_count = 0;
}

inline void Data_Destructor(Data* This) {
    rtcReleaseScene(This->g_scene);
    This->g_scene = nullptr;
}

struct RayStats {
    int numRays;
    int pad[32 - 1];
};

__forceinline void RayStats_addRay(RayStats &stats) { stats.numRays++; }
__forceinline void RayStats_addShadowRay(RayStats &stats) { stats.numRays++; }

template<typename Ty>
struct Averaged {
    Averaged(size_t N, double dt)
        : N(N), dt(dt) {
    }

    void add(double v) {
        values.push_front(std::make_pair(embree::getSeconds(), v));
        if (values.size() > N) values.resize(N);
    }

    Ty get() const {
        if (values.size() == 0) return embree::zero;
        double t_begin = values[0].first - dt;

        Ty sum(embree::zero);
        size_t num(0);
        for (size_t i = 0; i < values.size(); i++) {
            if (values[i].first >= t_begin) {
                sum += values[i].second;
                num++;
            }
        }
        if (num == 0) return 0;
        else return sum / Ty(num);
    }

    std::deque<std::pair<double, Ty>> values;
    size_t N;
    double dt;
};
