#pragma once
#include <core/fwd.h>
#include <render/intersection.h>
#include <core/logger.h>
struct Scene;
struct Camera;

struct MaterialVertex
{
    MaterialVertex() : type(EVInvalid) {}
    MaterialVertex(const Camera &camera) : type(EVSensor), pdf(1.) {}
    MaterialVertex(const Intersection &its, Float pdf, EVertex type = EVSurface);
    MaterialVertex(const DirectSamplingRecord &dRec);
    EVertex type = EVInvalid;
    Float pdf;
    int shape_id = -1;
    int triangle_id;
    int int_med_id = -1;
    int ext_med_id = -1;

    int medium_id = -1;
    Vector2 barycentric;
};

struct SpatialVertex : MaterialVertex
{
    SpatialVertex() : MaterialVertex() {}
    SpatialVertex(const Scene &scene, const MaterialVertex &v);
    void setZero();
    Vector3 p;
    Vector2 uv; // TODO
    Vector3 geo_n;
    Vector3 sh_n;
    Float J;
    Frame geo_frame;
    Frame sh_frame;
};

struct Vertex : SpatialVertex
{
    Vertex()
    {
        type = EVInvalid;
    }
    Vertex(const Camera &camera)
    {
        type = EVSensor;
        pdf = 1.;
    }
    Vertex(const Intersection &its, Float pdf, EVertex type = EVSurface);
    Vertex(const MediumSamplingRecord &mRec, int med_id, Float pdf);
    Vertex(const DirectSamplingRecord &dRec);
    void setZero()
    {
        p.setZero();
        geo_n.setZero();
        sh_n.setZero();
        J = 0;
        geo_frame.s.setZero();
        geo_frame.t.setZero();
        geo_frame.n.setZero();
        sh_frame.s.setZero();
        sh_frame.t.setZero();
        sh_frame.n.setZero();
        value.setZero();
        nee_bsdf.setZero();
        bsdf_bsdf.setZero();
    }
    int getTargetMedium(const Vector &wo) const
    {
        return wo.dot(geo_n) > 0 ? ext_med_id : int_med_id;
    }

    // pointer to nee vertex
    int l_nee_id = -1;
    int l_bsdf_id = -1;
    // ==== per-vertex contribution ====
    Spectrum value;     // can store bsdf, Le
    Spectrum nee_bsdf;  // emitter sampling bsdf
    Spectrum bsdf_bsdf; // bsdf sampling bsdf
};

// struct LightPath
// {
//     LightPath() {}
//     LightPath(const Array2i &pixel_idx) : pixel_idx(pixel_idx) {}
//     Array2i pixel_idx;
//     std::vector<Vertex> vertices;
//     std::vector<int> vs;

//     void reserve(int n)
//     {
//         vertices.reserve(n);
//         vs.reserve(n);
//     }
//     void resize(int n)
//     {
//         vertices.resize(n);
//     }
//     void clear()
//     {
//         vertices.clear();
//         vs.clear();
//     }

//     void append(const Vertex &v)
//     {
//         vertices.push_back(v);
//         vs.push_back(vertices.size() - 1);
//     }
//     void append_nee(const Vertex &v)
//     {
//         int cur_id = vs.back();
//         // FIXME
//         // Vertex &curV = vertices[cur_id];
//         vertices.push_back(v);
//         vertices[cur_id].l_nee_id = vertices.size() - 1;
//     }
//     void append_bsdf(const Vertex &v)
//     {
//         // FIXME
//         // Vertex &curV = vertices.at(vs.back());
//         int cur_id = vs.back();
//         vertices.push_back(v);
//         vertices[cur_id].l_bsdf_id = vertices.size() - 1;
//     }
//     void setZero()
//     {
//         for (auto &v : vertices)
//             v.setZero();
//     }
//     void setZero(int n)
//     {
//         assert(n <= vertices.size());
//         for (int i = 0; i < n; i++)
//             vertices[i].setZero();
//     }
//     std::string toString() const
//     {
//         Color::Modifier red(Color::FG_RED);
//         Color::Modifier def(Color::FG_DEFAULT);
//         std::stringstream ss;
//         ss << "vertices" << std::endl;
//         for (auto &v : vertices)
//         {
//             ss << red << "\t type: " << v.type << def << std::endl;
//             ss << "\t p: " << v.p[0] << " " << v.p[1] << " " << v.p[2] << std::endl;
//             ss << "\t pdf: " << v.pdf << std::endl;
//             ss << "\t J: " << v.J << std::endl;
//             ss << "\t value: " << v.value[0] << " " << v.value[1] << " " << v.value[2] << std::endl;
//             ss << "\t bsdf_bsdf: " << v.bsdf_bsdf[0] << " " << v.bsdf_bsdf[1] << " " << v.bsdf_bsdf[2] << std::endl;
//             ss << "\t nee_bsdf: " << v.nee_bsdf[0] << " " << v.nee_bsdf[1] << " " << v.nee_bsdf[2] << std::endl;
//             ss << "\t bsdf_id: " << v.l_bsdf_id << std::endl;
//             ss << "\t nee_id: " << v.l_nee_id << std::endl;
//             ss << "\t med_id: " << v.medium_id << std::endl;
//         }
//         ss << "vs" << std::endl;

//         for (auto &v : vs)
//         {
//             const Vertex &vv = vertices[v];
//             ss << "\t id: " << v << std::endl;
//             ss << "\t\t nee_id: " << vv.l_nee_id << std::endl;
//             ss << "\t\t bsdf_id: " << vv.l_bsdf_id << std::endl;
//         }
//         return ss.str();
//     }
// };

// struct LightPathAD
// {
//     LightPath val;
//     LightPath der;
//     LightPathAD() {}
//     LightPathAD(const LightPath &val) : val(val), der(val)
//     {
//         der.setZero();
//     }
//     void reserve(int n)
//     {
//         val.reserve(n);
//         der.resize(n);
//     }
//     void clear()
//     {
//         val.clear();
//         der.clear();
//     }
//     void zeroGrad()
//     {
//         der.setZero(val.vertices.size());
//     }
// };