#pragma once
#ifndef AREA_EMITTER_H__
#define AREA_EMITTER_H__

#include "emitter.h"

struct AreaLight : Emitter {
    AreaLight() {}

    AreaLight(int shape_id, const Spectrum3f &intensity)
        : Emitter(shape_id), intensity(intensity.cast<Float>()) {}

    AreaLight(int shape_id, const Spectrum3f &intensity, const ptr<float> dIntensity)
        : Emitter(shape_id), intensity(intensity.cast<Float>())
    {
        initVelocities(Eigen::Map<Eigen::Array<float, nder, 3, Eigen::RowMajor> >(dIntensity.get(), nder, 3).cast<Float>());
    }

    inline void initVelocities(const Eigen::Array<Float, nder, 3> &dIntensity) {
        intensity.der = dIntensity;
    }

    inline int getShapeID() const { return shape_id; }
    Spectrum getIntensity() const { return intensity.val; }

    // Return the radiant emittance for the given surface intersection
    Spectrum eval(const Intersection &its, const Vector &d) const;
    SpectrumAD evalAD(const IntersectionAD &its, const VectorAD &d) const;
    Spectrum eval(const Vector &norm, const Vector &d) const;
    SpectrumAD evalAD(const VectorAD &norm, const VectorAD &d) const;

    Float evalDirection(const Vector& norm, const Vector& d) const;
    FloatAD evalDirectionAD(const VectorAD& norm, const VectorAD& d) const;
    Float sampleDirection(const Array2 &rnd, Vector& dir, Float *pdf = nullptr) const;

    std::string toString() const {
        std::ostringstream oss;
        oss << "AreaLight[" << std::endl
            << "  intensity = " << "(" << intensity(0) << "," << intensity(1) << "," << intensity(2) << ")"
            << "  shape_id = " << shape_id << std::endl
            << "]" << std::endl;
        return oss.str();
    }

    SpectrumAD intensity;
};

#endif //AREA_EMITTER_H__
