#pragma once

#include <render/medium.h>
#include <core/ptr.h>
#include <sstream>
#include <string>

struct Homogeneous : Medium
{
    Medium *clone() const override
    {
        return new Homogeneous(*this);
    }
    void merge(Medium *other) override
    {
        Homogeneous *m = dynamic_cast<Homogeneous *>(other);
        sigma_t += m->sigma_t;
        albedo += m->albedo;
        sigma_s += m->sigma_s;
        sampling_weight += m->sampling_weight;
    }

    inline Homogeneous(Float sigma_t, const Spectrum &albedo, int phase_id)
        : Medium(phase_id), sigma_t(sigma_t), albedo(albedo)
    {
        sigma_s = sigma_t * this->albedo.transpose();
        sampling_weight = albedo.maxCoeff();
    }

    void setZero() override
    {
        sigma_t = 0.f;
        sigma_s = 0.f;
        albedo.setZero();
        sampling_weight = 0.f;
    }

    bool sampleDistance(const Ray &ray, const Float &tmax,
                        const Array2 &rnd2, RndSampler *sampler,
                        Vector &p_scatter, Spectrum &throughput) const override;

    bool sampleDistance(const Ray &ray, const Float &tmax,
                        RndSampler *sampler,
                        MediumSamplingRecord &mRec) const override;

    Float evalTransmittance(const Ray &ray, const Float &tmin,
                            const Float &tmax, RndSampler *sampler) const;

    inline bool isHomogeneous() const override { return true; }

    inline Spectrum sigS(const Vector &x) const { return sigma_s; }
    inline Float sigT(const Vector &x) const { return sigma_t; };
    inline std::string toString() const
    {
        std::ostringstream oss;
        oss << "Homogeneous Medium...";
        return oss.str();
    }

    Float sigma_t;
    Spectrum albedo;

    Spectrum sigma_s;
    Float sampling_weight;

    PSDR_DECLARE_CLASS(Homogeneous)
    PSDR_IMPLEMENT_VIRTUAL_CLASS(Homogeneous)
};
