#pragma once
#include <core/fwd.h>
#include <core/ptr.h>
#include "macro.h"

enum EMeasure
{
    /// Invalid measure
    EMInvalid = 0,
    /// Solid angle measure
    EMSolidAngle = 1,
    /// Length measure
    EMLength = 2,
    /// Area measure
    EMArea = 3,
    /// Discrete measure
    EMDiscrete = 4
};

struct PositionSamplingRecord
{
    Vector p, n;
    Vector2 uv;
    Float J;
    Float pdf;
    EMeasure measure;
    // NOTE: for algo1
    int shape_id;
    int tri_id;
    Vector2 barycentric;
};

struct DirectSamplingRecord : public PositionSamplingRecord
{
    Vector ref;
    /*  a reference point within a medium or on a transmissive surface will set dRec.refN = 0  */
    Vector refN;
    Vector dir; // direction pointing from ref to p
    Float dist;
    Float G;

    // int interactions = 0;

    DirectSamplingRecord(const Vector &ref) : ref(ref), refN(Vector::Zero()) {}
    DirectSamplingRecord(const Vector &ref, const Vector &refN) : ref(ref), refN(refN) {}
    DirectSamplingRecord(const Intersection &refIts);
};

struct EdgeSamplingRecord
{
    Float t;    // [0,1]
    Vector ref; // point on boundary
    Float pdf;
    int edge_id;
    int shape_id; // shape to which the sampled edge belongs
    int med_id;
};

struct EdgeRaySamplingRecord : EdgeSamplingRecord
{
    Vector dir;
};

struct BoundarySamplingRecord : public EdgeRaySamplingRecord
{
    //
    bool onSurface_S = true;
    // for surface vertex
    int shape_id_S = -1;
    int tri_id_S = -1;
    Vector3 barycentric3_S;
    // for volume vertex
    int med_id_S = -1;
    int tet_id_S = -1;
    Vector4 barycentric4_S;
    Float pdf_S = 1.; // pdf of sampling a volume vertex, or the probability of sampling a surface vertex

    bool onSurface_D = true;
    int shape_id_D = -1;
    int tri_id_D = -1;
    Vector3 barycentric3_D;
    int med_id_D = -1;
    int tet_id_D = -1;
    Vector4 barycentric4_D;
    Float pdf_D = 1.;
};

struct BoundarySegmentInfo
{
    Vector xS_0, xS_1, xS_2,
        xB_0, xB_1,
        xD_0, xD_1, xD_2;

    Float maxCoeff() const
    {
        Float res = xS_0.cwiseAbs().maxCoeff();
        res = std::max(res, xS_1.cwiseAbs().maxCoeff());
        res = std::max(res, xS_2.cwiseAbs().maxCoeff());
        res = std::max(res, xB_0.cwiseAbs().maxCoeff());
        res = std::max(res, xB_1.cwiseAbs().maxCoeff());
        res = std::max(res, xD_0.cwiseAbs().maxCoeff());
        res = std::max(res, xD_1.cwiseAbs().maxCoeff());
        res = std::max(res, xD_2.cwiseAbs().maxCoeff());
        return res;
    }

    Float maxCoeff(bool S, bool B, bool D) const
    {
        Float res = 0.0;
        if (S)
        {
            res = std::max(res, xS_0.cwiseAbs().maxCoeff());
            res = std::max(res, xS_0.cwiseAbs().maxCoeff());
            res = std::max(res, xS_0.cwiseAbs().maxCoeff());
        }

        if (B)
        {
            res = std::max(res, xB_0.cwiseAbs().maxCoeff());
            res = std::max(res, xB_1.cwiseAbs().maxCoeff());
        }

        if (D)
        {
            res = std::max(res, xD_0.cwiseAbs().maxCoeff());
            res = std::max(res, xD_1.cwiseAbs().maxCoeff());
            res = std::max(res, xD_2.cwiseAbs().maxCoeff());
        }
        return res;
    }

    void setZero()
    {
        xS_0.setZero();
        xS_1.setZero();
        xS_2.setZero();

        xB_0.setZero();
        xB_1.setZero();

        xD_0.setZero();
        xD_1.setZero();
        xD_2.setZero();
    }
};