/*
    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/>.
*/

#pragma once
#if !defined(__MITSUBA_RENDER_BSDF_H_)
#define __MITSUBA_RENDER_BSDF_H_

#include <mitsuba/core/cobject.h>
#include <mitsuba/core/frame.h>
#include <mitsuba/core/properties.h>
#include <mitsuba/render/common.h>
#include <mitsuba/render/shader.h>

MTS_NAMESPACE_BEGIN

/**
 * \brief This data structured contains all information
 * that is required to sample or query a BSDF.
 *
 * \sa BSDF::f()
 * \sa BSDF::sample()
 * \ingroup librender
 * \ingroup libpython
 */
struct MTS_EXPORT_RENDER BSDFSamplingRecord {
public:
    /**
     * \brief Given a surface interaction and an incident direction,
     * construct a query record which can be used to sample an outgoing
     * direction.
     *
     * By default, all components will be sampled irregardless of
     * what measure they live on. For convenience, this function
     * uses the local incident direction vector contained in the
     * supplied intersection record. The mode of transport is
     * set to \ref ERadiance -- the \ref mode fie
     *
     * \param its
     *      An reference to the underlying intersection record
     *
     * \param sampler
     *      A source of (pseudo-) random numbers. Note that this sampler
     *      is only used when the scattering model for some reason needs
     *      more than the two unformly distributed numbers supplied in
     *      the \ref BSDF::sample() methods
     *
     * \param mode
     *      The transported mode (\ref ERadiance or \ref EImportance)
     */
    explicit inline BSDFSamplingRecord(const Intersection &its, Sampler *sampler,
            ETransportMode mode = ERadiance);

    /**
     * \brief Given a surface interaction an an incident/exitant direction
     * pair (wi, wo), create a query record to evaluate the BSDF or its
     * sampling density.
     *
     * For convenience, this function uses the local incident direction
     * vector contained in the supplied intersection record.
     *
     * \param its
     *      An reference to the underlying intersection record
     * \param wo
     *      An outgoing direction in local coordinates. This should
     *      be a normalized direction vector that points \a away from
     *      the scattering event.
     * \param mode
     *      The transported mode (\ref ERadiance or \ref EImportance)
     */
    inline BSDFSamplingRecord(const Intersection &its, const Vector &wo,
        ETransportMode mode = ERadiance);

    /**
     * \brief Given a surface interaction an an incident/exitant direction
     * pair (wi, wo), create a query record to evaluate the BSDF or its
     * sampling density.
     *
     * \param its
     *      An reference to the underlying intersection record
     * \param wi
     *      An incident direction in local coordinates. This should
     *      be a normalized direction vector that points \a away from
     *      the scattering event.
     * \param wo
     *      An outgoing direction in local coordinates. This should
     *      be a normalized direction vector that points \a away from
     *      the scattering event.
     * \param mode
     *      The transported mode (\ref ERadiance or \ref EImportance)
     *
     */
    inline BSDFSamplingRecord(const Intersection &its,
        const Vector &wi, const Vector &wo,
        ETransportMode mode = ERadiance);

    /**
     * \brief Reverse the direction of light transport in the record
     *
     * This function essentially swaps \c wi and \c wo and adjusts
     * \c mode appropriately, so that non-symmetric scattering
     * models can be queried in the reverse direction.
     */
    inline void reverse();

    /// Return a string representation
    std::string toString() const;
public:
    /// Reference to the underlying surface interaction
    const Intersection &its;
    
    /// Distance from emitter divided by emitters radius
    Float normalized_distance_travelled_from_emitter{};

    /**
     * \brief Pointer to a \ref Sampler instance (optional).
     *
     * Some BSDF implementations can significantly improve
     * the quality of their importance sampling routines
     * when having access to extra random numbers. This
     * attribute provides a means of providing this
     * capability to the BSDF.
     */
    Sampler *sampler;

    /**
     * \brief Normalized incident direction in local coordinates
     *
     * Mitsuba uses the convention that \c wi and \c wo
     * point away from the scattering event
     */
    Vector wi;

    /**
     * \brief Normalized outgoing direction in local coordinates
     *
     * Mitsuba uses the convention that \c wi and \c wo
     * point away from the scattering event
     */
    Vector wo;

    /// Relative index of refraction in the sampled direction
    Float eta;

    /** \brief Transported mode (radiance or importance)
     *
     * This information is required for rendering with non-reciprocal
     * BSDFs such as transmission through a dielectric material
     */
    ETransportMode mode;

    /**
     * \brief Bit mask containing the requested BSDF component types that
     * should be sampled/evaluated.
     *
     * Set to \c BSDF::EAll by default. After sampling has been performed,
     * the component type is stored inside \ref sampledType.
     *
     * \sa BSDF::EBSDFType
     */
    unsigned int typeMask;

    /**
     * \brief Integer value specifying the requested BSDF component index that
     * should be sampled/evaluated (for multi-lobed BSDFs).
     *
     * After sampling has been performed, the component index is stored
     * inside \ref sampledComponent.
     */
    int component;

    /**
     * \brief Stores the component type that was sampled by \ref BSDF::sample()
     * \sa BSDF::EBSDFType
     */
    unsigned int sampledType;

    /**
     * \brief Stores the component index that was sampled by \ref BSDF::sample()
     */
    int sampledComponent;
};


/**
 * \brief Abstract %BSDF base-class.
 *
 * This class implements an abstract interface to all BSDF plugins in Mitsuba.
 * It exposes functions for evaluating and sampling the model, and it allows
 * querying the probability density of the sampling method. Smooth
 * two-dimensional density functions, as well as degenerate one-dimensional
 * and discrete densities are all handled within the same framework.
 *
 * For improved flexibility with respect to the various rendering algorithms,
 * this class can sample and evaluate a complete BSDF, but it also allows to
 * pick and choose individual components of multi-lobed BSDFs based on their
 * properties and component indices. This selection is specified using a
 * special record that is provided along with every query.
 *
 * \ref BSDFSamplingRecord.
 * \ingroup librender
 * \ingroup libpython
 */
class MTS_EXPORT_RENDER BSDF : public ConfigurableObject, public HWResource {
public:
    /**
     * \brief This list of flags is used to classify the different
     * types of lobes that are implemented in a BSDF instance.
     *
     * They are also useful for picking out individual components
     * by setting combinations in \ref BSDFSamplingRecord::typeMask.
     */
    enum EBSDFType {
        // =============================================================
        //                      BSDF lobe types
        // =============================================================

        /// 'null' scattering event, i.e. particles do not undergo deflection
        ENull                 = 0x00001,
        /// Ideally diffuse reflection
        EDiffuseReflection    = 0x00002,
        /// Ideally diffuse transmission
        EDiffuseTransmission  = 0x00004,
        /// Glossy reflection
        EGlossyReflection     = 0x00008,
        /// Glossy transmission
        EGlossyTransmission   = 0x00010,
        /// Reflection into a discrete set of directions
        EDeltaReflection      = 0x00020,
        /// Transmission into a discrete set of directions
        EDeltaTransmission    = 0x00040,
        /// Reflection into a 1D space of directions
        EDelta1DReflection    = 0x00080,
        /// Transmission into a 1D space of directions
        EDelta1DTransmission  = 0x00100,
        
        EDiffractive          = 0x00200,

        // =============================================================
        //!                   Other lobe attributes
        // =============================================================
        /// The lobe is not invariant to rotation around the normal
        EAnisotropic          = 0x01000,
        /// The BSDF depends on the UV coordinates
        ESpatiallyVarying     = 0x02000,
        /// Flags non-symmetry (e.g. transmission in dielectric materials)
        ENonSymmetric         = 0x04000,
        /// Supports interactions on the front-facing side
        EFrontSide            = 0x08000,
        /// Supports interactions on the back-facing side
        EBackSide             = 0x10000,
        /// Uses extra random numbers from the supplied sampler instance
        EUsesSampler          = 0x20000
    };

    /// Convenient combinations of flags from \ref EBSDFType
    enum ETypeCombinations {
        /// Any reflection component (scattering into discrete, 1D, or 2D set of directions)
        EReflection   = EDiffuseReflection | EDeltaReflection
            | EDelta1DReflection | EGlossyReflection,
        /// Any transmission component (scattering into discrete, 1D, or 2D set of directions)
        ETransmission = EDiffuseTransmission | EDeltaTransmission
            | EDelta1DTransmission | EGlossyTransmission | ENull,
        /// Diffuse scattering into a 2D set of directions
        EDiffuse      = EDiffuseReflection | EDiffuseTransmission,
        /// Non-diffuse scattering into a 2D set of directions
        EGlossy       = EGlossyReflection | EGlossyTransmission,
        /// Scattering into a 2D set of directions
        ESmooth       = EDiffuse | EGlossy,
        /// Scattering into a discrete set of directions
        EDelta        = ENull | EDeltaReflection | EDeltaTransmission,
        /// Scattering into a 1D space of directions
        EDelta1D      = EDelta1DReflection | EDelta1DTransmission,
        /// Any kind of scattering
        EAll          = EDiffuse | EGlossy | EDelta | EDelta1D
    };

    /// Return the number of components of this BSDF
    inline int getComponentCount() const {
        return (int) m_components.size();
    }

    /**
     * \brief Return a listing of this BSDF's component types and
     * properties, combined using binary OR.
     * \sa EBSDFType
     */
    inline unsigned int getType() const {
        return m_combinedType;
    }

    /**
     * Returns the classification flags of a specific component
     * \sa EBSDFType
     */
    inline unsigned int getType(int component) const {
        return m_components[component];
    }

    /**
     * \brief Return the measure corresponding to a particular
     * component type
     */
    inline static EMeasure getMeasure(unsigned int componentType) {
        if (componentType & ESmooth) {
            return ESolidAngle;
        } else if (componentType & EDelta) {
            return EDiscrete;
        } else if (componentType & EDelta1D) {
            return ELength;
        } else {
            Log(EError, "getMeasure(): Invalid component type!");
            return ESolidAngle; // will never be reached
        }
    }

    /// Test whether this BSDF contains a certain type of component
    inline bool hasComponent(unsigned int type) const {
        return (type & m_combinedType) != 0;
    }

    /// Return whether this BSDF makes use of ray differentials
    inline bool usesRayDifferentials() const {
        return m_usesRayDifferentials;
    }

    /// Return the diffuse reflectance value (if any)
    virtual Spectrum getDiffuseReflectance(const Intersection &its) const;

    /// Return the specular reflectance value (if any)
    virtual Spectrum getSpecularReflectance(const Intersection &its) const {
        return Spectrum(0.0f);
    }

    /**
     * \brief Sample the BSDF and return the importance weight (i.e. the
     * value of the BSDF divided by the probability density of the sample).
     *
     * When the probability density is not explicitly required, this function
     * should be preferred, since it is potentially faster by making use of
     * cancellations during the division.
     *
     * If a component mask or a specific component index is specified, the
     * sample is drawn from the matching component, if it exists. Depending
     * on the provided transport type, either the BSDF or its adjoint version
     * is used.
     *
     * \param bRec    A BSDF query record
     * \param sample  A uniformly distributed sample on \f$[0,1]^2\f$
     *
     * \return The BSDF value divided by the probability density of the sample
     *         sample (multiplied by the cosine foreshortening factor when a
     *         non-delta component is sampled) A zero spectrum means that
     *         sampling failed.
     *
     * \remark This function is not exposed by the Python API. See the other
     *         sample function instead.
     *
     */
    virtual Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const = 0;

    /**
     * \brief Sample the BSDF and return the probability density \a and the
     * importance weight of the sample (i.e. the value of the BSDF divided
     * by the probability density)
     *
     * If a component mask or a specific component index is specified, the
     * sample is drawn from the matching component, if it exists. Depending
     * on the provided transport type, either the BSDF or its adjoint version
     * is used.
     *
     * When sampling a continuous/non-delta component, this method also
     * multiplies by the cosine foreshorening factor with respect to the
     * sampled direction.
     *
     * \param bRec    A BSDF query record
     * \param sample  A uniformly distributed sample on \f$[0,1]^2\f$
     * \param pdf     Will record the probability with respect to solid angles
     *                (or the discrete probability when a delta component is sampled)
     *
     * \return The BSDF value (multiplied by the cosine foreshortening
     *         factor when a non-delta component is sampled). A zero spectrum
     *         means that sampling failed.
     *
     * \remark From Python, this function is is called using the syntax
     *         <tt>value, pdf = bsdf.sample(bRec, sample)</tt>
     */
    virtual Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf,
        const Point2 &sample) const = 0;

    /**
     * \brief Evaluate the BSDF f(wi, wo) or its adjoint version f^{*}(wi, wo)
     *
     * This method allows to query the BSDF as a whole or pick out
     * individual components. When querying a smooth (i.e. non-degenerate)
     * component, it already multiplies the result by the cosine
     * foreshortening factor with respect to the outgoing direction.
     *
     * \param bRec
     *     A record with detailed information on the BSDF query
     *
     * \param measure
     *     Specifies the measure of the component. This is necessary
     *     to handle BSDFs, whose components live on spaces with
     *     different measures. (E.g. a diffuse material with an
     *     ideally smooth dielectric coating).
     */
    virtual Spectrum eval(const BSDFSamplingRecord &bRec,
        EMeasure measure = ESolidAngle) const = 0;

    /**
     * \brief Compute the probability of sampling \c bRec.wo (given
     * \c bRec.wi).
     *
     * This method provides access to the probability density that
     * would result when supplying the same BSDF query record to the
     * \ref sample() method. It correctly handles changes in probability
     * when only a subset of the components is chosen for sampling
     * (this can be done using the \ref BSDFSamplingRecord::component and
     * \ref BSDFSamplingRecord::typeMask fields).
     *
     * \param bRec
     *     A record with detailed information on the BSDF query
     *
     * \param measure
     *     Specifies the measure of the component. This is necessary
     *     to handle BSDFs, whose components live on spaces with
     *     different measures. (E.g. a diffuse material with an
     *     ideally smooth dielectric coating).
     */
    virtual Float pdf(const BSDFSamplingRecord &bRec,
        EMeasure measure = ESolidAngle) const = 0;

    /**
     * \brief For transmissive BSDFs: return the material's
     * relative index of refraction
     *
     * The default implementation returns <tt>1.0</tt>.
     *
     * \return interior IOR / exteriorIOR
     */
    virtual Float getEta() const;

    /**
     * \brief For rough BSDFs: return the root mean square
     * surface roughness of the given BSDF component
     *
     * An infinite value indicates a component that is ideally diffuse
     */
    virtual Float getRoughness(const Intersection &its, int index) const;

    /**
     * \brief Sometimes, BSDF models make use of a perturbed frame for
     * internal shading computations (e.g. bump maps). This function
     * exposes this internal frame.
     *
     * By default, it returns <tt>its.shFrame</tt>
     */
    virtual Frame getFrame(const Intersection &its) const;

    /**
     * \brief Sometimes, BSDF models make use of a perturbed frame for
     * internal shading computations (e.g. bump maps). This function
     * computes the derivative of this frame with respect to the UV
     * parameterization of the underlying shape.
     */
    virtual void getFrameDerivative(const Intersection &its,
            Frame &du, Frame &dv) const;

    // =============================================================
    //! @{ \name ConfigurableObject interface
    // =============================================================

    /// Configure the material (called after construction by the XML parser)
    virtual void configure();

    /// Add a child object
    virtual void addChild(const std::string &string, ConfigurableObject *obj);

    /// Add an unnamed child
    inline void addChild(ConfigurableObject *child) { addChild("", child); }

    /// Serialize this object to a stream
    virtual void serialize(Stream *stream, InstanceManager *manager) const;

    /// Set the parent object
    virtual void setParent(ConfigurableObject *parent);

    //! @}
    // =============================================================

    MTS_DECLARE_CLASS()
protected:
    /// Create a new BSDF instance
    BSDF(const Properties &props);

    /// Unserialize a BSDF instance
    BSDF(Stream *stream, InstanceManager *manager);

    /**
     * \brief Convenience function to ensure energy conservation
     *
     * This function determines the component-wise maximum of the
     * texture \c tex and checks if it is below \c max. If yes,
     * it returns the texture unmodified. Otherwise, it wraps
     * the texture into a \ref ScaleTexture instance (with a
     * scaling factor chosen so that the desired maximum \c max
     * is abided) and prints a warning.
     */
    Texture *ensureEnergyConservation(Texture *tex,
        const std::string &paramName, Float max) const;

    /**
     * \brief Convenience function to ensure energy conservation
     *
     * This function determines the component-wise maximum of the
     * sum \c tex1 + \c tex2 and checks if it is below \c max. If yes,
     * it returns the texture unmodified. Otherwise, it wraps
     * each the texture into a \ref ScaleTexture instance (with a
     * scaling factor chosen so that the desired maximum \c max
     * is abided) and prints a warning.
     */
    std::pair<Texture *, Texture *> ensureEnergyConservation(
        Texture *tex1, Texture *tex2, const std::string &paramName1,
        const std::string &paramName2, Float max) const;

    /// Virtual destructor
    virtual ~BSDF();
protected:
    std::vector<unsigned int> m_components;
    unsigned int m_combinedType;
    bool m_usesRayDifferentials;
    bool m_ensureEnergyConservation;
};

MTS_NAMESPACE_END

#endif /* __MITSUBA_RENDER_BSDF_H_ */
