/*!
    \file
    \copyright  (c) 2015 Jascha Wetzel. All rights reserved.
 */

#ifndef API_CONTAINER_H
#define API_CONTAINER_H

#include "api/base.h"
#include "api/client.h"
#include "api/vector.h"

#include <cstdint>
#include <memory>
#include <string>

namespace tfd {
namespace api {

/*!
    Instantiated by tfd::ChannelContainer.
    Represents a scalar voxel grid on a rectangular domain.
 */
struct Channel : Base
{
    /*!
        Read a value from the voxel grid.
        Uses tri-linear interpolation to obtain values at sub-grid coordinates.
     */
    virtual float sample_linear(float x, float y, float z) const = 0;

    /*!
        Read a value from the voxel grid.
        Uses cubic splines to obtain values at sub-grid coordinates.
     */
    virtual float sample_smooth(float x, float y, float z) const = 0;

    /*!
        Read a value from the voxel grid.
        Uses cubic interpolation to obtain values at sub-grid coordinates.
     */
    virtual float sample_sharp(float x, float y, float z) const = 0;
};

/*!
    Use tfd::ChannelContainer wrapper class instead of tfd::api::ChannelContainer interface.
 */
struct ChannelContainer : Base
{
    virtual Channel*
    get_channel(const char* name) = 0;

    virtual uint32_t width() const = 0;
    virtual uint32_t height() const = 0;
    virtual uint32_t depth() const = 0;

    virtual Vec3i grid_offset() const = 0;
    virtual double voxel_size() const = 0;
};

/*!
    Used by tfd::ChannelContainer wrapper class.
    Don't use directly.
 */
struct ChannelContainerFactory : Base
{
    virtual ChannelContainer*
    create(const char* cache_path, int frame) = 0;
};

} // namespace api

/*!
    A container of named voxel grid channels.
    This corresponds to a single .bcf cache file.
 */
class ChannelContainer
{
public:
    /*!
        \param cache_path   path to directory that contains .bcf cache files.
        \param frame        frame number to load from that cache directory.
     */
    ChannelContainer(const std::string& cache_path, int frame)
    {
        auto factory = api::create_shared_instance<api::ChannelContainerFactory>();
        iface_ = api::wrap_raw_iface_pointer(
            factory->create(cache_path.c_str(), frame)
        );
    }

    bool valid() const {
        return bool(iface_);
    }

    /*!
        Load a scalar voxel grid channel from the channel container.
        Returns nullptr if no channel with the given name exists.
     */
    std::shared_ptr<api::Channel>
    get_channel(const std::string& name)
    {
        return api::wrap_raw_iface_pointer(
            iface_->get_channel(name.c_str())
        );
    }

    /*!
        Container width in voxels.
     */
    uint32_t width() const {
        return iface_->width();
    }

    /*!
        Container height in voxels.
     */
    uint32_t height() const {
        return iface_->height();
    }

    /*!
        Container depth in voxels.
     */
    uint32_t depth() const {
        return iface_->depth();
    }

    /*!
        Voxel space coordinate of the container's minimum corner.
     */
    api::Vec3i grid_offset() const {
        return iface_->grid_offset();
    }

    /*!
        Side length of the cuboid voxels in object space.
     */
    double voxel_size() const {
        return iface_->voxel_size();
    }

    operator api::ChannelContainer*() {
        return iface_.get();
    }

private:
    std::shared_ptr<api::ChannelContainer>
        iface_;
};

} // namespace tfd

#endif // API_CONTAINER_H
