/*
    This file is part of Mitsuba, a physically based rendering system.

    Copyright (c) 2007-2014 by Wenzel Jakob and others.

    Mitsuba is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License Version 3
    as published by the Free Software Foundation.

    Mitsuba is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <mitsuba/render/emitter.h>
#include <mitsuba/render/medium.h>
#include <mitsuba/core/track.h>
#include <mitsuba/render/shape.h>

MTS_NAMESPACE_BEGIN

AbstractEmitter::AbstractEmitter(const Properties &props)
 : ConfigurableObject(props), m_shape(NULL), m_type(0) {
    m_worldTransform = props.getAnimatedTransform("toWorld", Transform());
}

AbstractEmitter::AbstractEmitter(Stream *stream, InstanceManager *manager)
 : ConfigurableObject(stream, manager) {
    m_worldTransform = new AnimatedTransform(stream);
    m_medium = static_cast<Medium *>(manager->getInstance(stream));
    m_shape = static_cast<Shape *>(manager->getInstance(stream));
    m_type = stream->readUInt();
 }

AbstractEmitter::~AbstractEmitter() {
}

void AbstractEmitter::serialize(Stream *stream, InstanceManager *manager) const {
    ConfigurableObject::serialize(stream, manager);
    m_worldTransform->serialize(stream);
    manager->serialize(stream, m_medium.get());
    manager->serialize(stream, m_shape);
    stream->writeUInt(m_type);
}

void AbstractEmitter::addChild(const std::string &name, ConfigurableObject *child) {
    if (child->getClass()->derivesFrom(MTS_CLASS(Medium))) {
        Assert(m_medium == NULL);
        m_medium = static_cast<Medium *>(child);
    } else {
        ConfigurableObject::addChild(name, child);
    }
}

ref<Shape> AbstractEmitter::createShape(const Scene *scene) {
    return NULL;
}

Spectrum AbstractEmitter::samplePosition(PositionSamplingRecord &pRec,
        const Point2 &sample, const Point2 *extra) const {
    NotImplementedError("samplePosition");
}

Spectrum AbstractEmitter::sampleDirection(DirectionSamplingRecord &dRec,
        PositionSamplingRecord &pRec, const Point2 &sample,
        const Point2 *extra) const {
    NotImplementedError("sampleDirection");
}

Spectrum AbstractEmitter::sampleDirect(DirectSamplingRecord &dRec, const Point2 &sample) const {
    NotImplementedError("sampleDirect");
}

Spectrum AbstractEmitter::evalPosition(const PositionSamplingRecord &pRec) const {
    NotImplementedError("evalPosition");
}

Spectrum AbstractEmitter::evalDirection(const DirectionSamplingRecord &dRec,
        const PositionSamplingRecord &pRec) const {
    NotImplementedError("evalDirection");
}

Float AbstractEmitter::pdfPosition(const PositionSamplingRecord &pRec) const {
    NotImplementedError("pdfPosition");
}

Float AbstractEmitter::pdfDirection(const DirectionSamplingRecord &dRec,
        const PositionSamplingRecord &pRec) const {
    NotImplementedError("pdfDirection");
}

Float AbstractEmitter::pdfDirect(const DirectSamplingRecord &dRec) const {
    NotImplementedError("pdfDirect");
}

Emitter::Emitter(const Properties &props)
 : AbstractEmitter(props) {
    // Importance sampling weight (used by the luminaire sampling code in \ref Scene)
    m_samplingWeight = props.getFloat("samplingWeight", 1.0f);
    m_scale = props.getFloat("scale", 1.f);
}

Emitter::Emitter(Stream *stream, InstanceManager *manager)
 : AbstractEmitter(stream, manager) {
    m_samplingWeight = stream->readFloat();
    m_scale = stream->readFloat();
}

void Emitter::serialize(Stream *stream, InstanceManager *manager) const {
    AbstractEmitter::serialize(stream, manager);

    stream->writeFloat(m_samplingWeight);
    stream->writeFloat(m_scale);
}

Spectrum Emitter::sampleRay(Ray &ray,
        const Point2 &spatialSample,
        const Point2 &directionalSample,
        Float time) const {
    NotImplementedError("sampleRay");
}

Spectrum Emitter::eval(const Intersection &its, const Vector &d) const {
    NotImplementedError("eval");
}

Spectrum Emitter::evalEnvironment(const RayDifferential &ray) const {
    NotImplementedError("evalEnvironment");
}

bool Emitter::fillDirectSamplingRecord(DirectSamplingRecord &dRec,
        const Ray &ray) const {
    NotImplementedError("fillDirectSamplingRecord");
}

Emitter::~Emitter() { }

Emitter *Emitter::getElement(size_t index) {
    return NULL;
}

bool Emitter::isCompound() const {
    return false;
}

ref<Bitmap> Emitter::getBitmap(const Vector2i &sizeHint) const {
    NotImplementedError("getBitmap");
}

MTS_IMPLEMENT_CLASS(Emitter, false, AbstractEmitter)
MTS_IMPLEMENT_CLASS(AbstractEmitter, true, ConfigurableObject)
MTS_NAMESPACE_END
