#pragma once
#include "ptr.h"
#include "fwd.h"
#include "frameAD.h"
#include "pmf.h"
#include "utils.h"
#include <math.h>

class ReconstructionFilter
{
public:
    /// Evaluate the filter function
    virtual Float eval(Float x) const = 0;
    virtual FloatAD evalAD(const FloatAD &x) const = 0;
    virtual Float sample(Float rnd, Float &pdf) const = 0;
};

class TentFilter : public ReconstructionFilter
{
public:
    TentFilter(){};
    TentFilter(Float padding) : m_padding(padding), m_invPadding(1. / padding){};
    virtual Float eval(Float x) const
    {
        x -= 0.5;
        Float k = 0.5 * m_invPadding;
        return clamp((0.5 + m_padding) * k - abs(k * x), 0., 1.);
    }
    virtual FloatAD evalAD(const FloatAD &x) const
    {
        Float k = 0.5 * m_invPadding;
        FloatAD y = (0.5 + m_padding) * k - k * (x-0.5).abs();
        return clamp(y, FloatAD(0.), FloatAD(1.));
    }
    virtual Float sample(Float rnd, Float &pdf) const {
        Float s1 = m_padding;
        Float s2 = s1 + 1.0 - 2.0 * m_padding;
        Float ret;
        if (rnd <= s1)
            ret = std::sqrt(4.0 * m_padding * rnd) - m_padding;
        else if (rnd <= s2)
            ret = rnd - s1 + m_padding;
        else
        {
            rnd -= s2;
            ret = -std::sqrt(4.0 * m_padding * rnd) + m_padding + 1.0;
        }
        pdf = eval(ret);
        return ret;
    }

public:
    Float m_padding;
    Float m_invPadding;
};