#include <render/bitmap.h>
#include <fstream>
#include <core/utils.h>

#define TINYEXR_USE_MINIZ 0
#include <core/miniz.h>
#define TINYEXR_IMPLEMENTATION
#include <core/tinyexr.h>

Bitmap::Bitmap(const ArrayXd &data, const Vector2i &res)
{
    m_data = from_tensor_to_spectrum_list(
        data, res.prod());
    m_res = res;
}

void Bitmap::fill(const Spectrum &value)
{
    m_res = Vector2i(1, 1);
    m_data.resize(1);
    m_data[0] = value;
}

void Bitmap::setZero()
{
    for (auto &v : m_data)
        v.setZero();
}

void Bitmap::load(const char *filename)
{
    float *out; // width * height * RGBA
    int width;
    int height;
    const char *err = NULL; // or nullptr in C++11

    int ret = LoadEXR(&out, &width, &height, filename, &err);
    int size = width * height;

    if (ret != TINYEXR_SUCCESS)
    {
        if (err)
        {
            fprintf(stderr, "ERR : %s\n", err);
            FreeEXRErrorMessage(err); // release memory of error message.
        }
    }

    m_data.resize(size);
    int offset = 0;
    for (int i = 0; i < height; ++i)
        for (int j = 0; j < width; ++j)
        {
            m_data[offset] = Spectrum(out[offset * 4 + 0],
                                      out[offset * 4 + 1],
                                      out[offset * 4 + 2]);
            ++offset;
        }
    free(out); // release memory of image data
    m_res = Vector2i(width, height);
}

Spectrum Bitmap::eval(const Vector2 &_uv) const
{
    if (m_res.x() == 1 && m_res.y() == 1)
        return m_data[0];
    Array2 uv = _uv.array();
    uv.y() = -uv.y();
    uv -= uv.floor();
    uv *= m_res.cast<Float>().array() - Array2(1, 1);
    Array2i pos = uv.cast<int>();
    Array2 w1 = uv - uv.floor();
    Array2 w0 = Array2(1, 1) - w1;
    pos = pos.min(m_res.array() - Array2i(2, 2));
    int index = pos.y() * m_res.x() + pos.x();
    Spectrum v00 = m_data[index],
             v10 = m_data[index + 1],
             v01 = m_data[index + m_res.x()],
             v11 = m_data[index + m_res.x() + 1];

    Spectrum v0 = v00 * w0.x() + v10 * w1.x(),
             v1 = v01 * w0.x() + v11 * w1.x();
    Spectrum ret = w0.y() * v0 + w1.y() * v1;
    assert(ret.allFinite());
    return ret;
}

ArrayXd Bitmap::getData() const
{
    return from_spectrum_list_to_tensor(m_data, m_res.prod());
}

void Bitmap::setData(const ArrayXd &data)
{
    m_data = from_tensor_to_spectrum_list(data, m_res.prod());
}

std::string Bitmap::toString() const
{
    std::stringstream ss;
    ss << "Bitmap[" << m_res << "]";
    return ss.str();
}
