// Copyright (c) 2013 Jascha Wetzel. All rights reserved.

#ifndef LIB_TFD_H
#define LIB_TFD_H

#include "api/shader.h"

#include <c4d.h>

#include <memory>

#if API_VERSION < 15000
typedef LONG Int32;
typedef SVector Vector32;
typedef LVector Vector64;
typedef SVector4 Vector4d32;
typedef LMatrix Matrix64;
#endif

namespace tfd
{

using std::shared_ptr;

class ChannelC4D;
class ShaderC4D;

const Int32
    ID_TFD_FLUID_CONTAINER = 1023131,
    LIB_API_VERSION = 6;

/*!
    TFD fluid container object in C4D's scene graph.
    BaseObject::GetType() will return tfd::ID_TFD_FLUID_CONTAINER for these objects.
 */
class TFDFluidContainer : public BaseObject
{
    TFDFluidContainer();
    ~TFDFluidContainer();

public:
    /*!
        Returns a transform from world space to voxel space and the fluid container's bounding box.

        \param m        matrix object to receive the world-to-voxel transform
        \param bbox_neg left/bottom/back corner of the bounding box in world-space
        \param bbox_pos right/top/front corner of the bounding box in world-space
     */
    void get_world_to_voxel(
        Matrix64& m,
        Vector64& bbox_neg,
        Vector64& bbox_pos
    );

    /*!
        Get a handle to a fluid channel for use with sample_channel().

        \param channel  name of the channel to take the sample from. Currently valid names are
        - "density"
        - "temperature"
        - "fuel"
        - "burn"
        - "solid"
        - "velocity.u"
        - "velocity.v"
        - "velocity.w"

        \return handle to the fluid channel.
    */
    shared_ptr<ChannelC4D>
    get_channel(const ::String& channel);

    /*!
        Take a sample from the fluid container.

        \param field    channel to sample.
        \param p        point in voxel-space to take the sample at.
        \param interp   type of interpolation to use.

        \return interpolated value from the voxel grid.
    */
    float sample_channel(
        shared_ptr<ChannelC4D>& field,
        const Vector64& p,
        Interpolation interp
    );

    /*!
        Get a handle to a shader for use with sample_shader().

        \param shader   name of shader to take the sample from. Currently valid names are
        - "smoke"
        - "fire"

        \return handle to the shader.
    */
    shared_ptr<ShaderC4D>
    get_shader(const ::String& shader);

    /*!
        Take a shaded sample from the fluid container.

        \param shader   shader handle retrieved via get_shader().
        \param p        point in voxel-space to take the sample at.
        \param shader   name of shader to take the sample from. Currently valid names are
        - "smoke"
        - "fire"

        \return RGBA value of the shaded sample.
     */
    Vector4d32
    sample_shader(
        shared_ptr<ShaderC4D>& shader,
        const Vector64& p
    );

    /*!
        Get the path to the selected cache directory of this container.
     */
    ::String get_cache_directory() const;

    /*!
        Get the combined shader parameters for use with TFD's host-independent API.
     */
    void get_shader_parameters(api::ShaderParameters&) const;

    /*!
        Get frame number mapped by frame_offset/step rendering parameters.
     */
    int get_cache_frame_number() const;
};

struct TurbulenceFDLibrary : public C4DLibrary
{
    void (*get_world_to_voxel)(TFDFluidContainer*, Matrix64&, Vector64&, Vector64&);
    ChannelC4D* (*get_channel)(TFDFluidContainer*, const ::String&);
    float (*sample_channel)(ChannelC4D*, const Vector64&, Interpolation);
    ShaderC4D* (*get_shader)(TFDFluidContainer*, const ::String&);
    Vector4d32 (*sample_shader)(ShaderC4D*, const Vector64&);
    void (*delete_ChannelC4D)(ChannelC4D* f);
    void (*delete_ShaderC4D)(ShaderC4D* f);
    ::String (*get_cache_directory)(const TFDFluidContainer*);
    void (*get_shader_parameters)(const TFDFluidContainer*, api::ShaderParameters*);
    int (*get_cache_frame_number)(const TFDFluidContainer*);
};

} // namespace tfd

#endif // LIB_TFD_H
