#include "pw/core/image.hpp"
#include "pw/core/debug.hpp"

#include <limits>
#include <random>
#include <algorithm>

namespace pw {

image::image(const ::pw::size &s,
             image::pixel_layout t,
             const data_t *ptr)
{
    if (!this->create(s,t,ptr)) {
        debug::w() << __PRETTY_FUNCTION__ << " create failed";
    }
}

bool image::create(const ::pw::size &s,
                   image::pixel_layout t,
                   const data_t *ptr)
{
	size_t n = bytes_per_pixel(t) * s.area();
    _size = s;
    _layout = t;

    if (ptr != nullptr) {

        const auto a = reinterpret_cast<const image::data_t*>(ptr);
        _data.assign(a,a + n);

    } else {

		_data.resize(n);
    }

    return !_data.empty();
}

void image::generate_noise()
{
    std::random_device rd;
    std::mt19937_64 gen(rd());
    std::uniform_int_distribution<std::uint8_t> dis;

    std::for_each(_data.begin(),_data.end(),[&](auto& v) {v = static_cast<std::byte>(dis(gen)); });


      _change_count++;
}

void image::release(bool release_memory)
{
    _data.clear();
    _size = pw::size();

    if (release_memory) _data.shrink_to_fit();
}

bool image::is_valid() const
{
    return !_data.empty() && (0 != _size.area());
}

uint32_t image::bytes_per_pixel(image::pixel_layout t)
{
    switch (t) {
    case pw::image::RGB8:
    case pw::image::RGBA8:
    case pw::image::LUM:
        return components(t) * sizeof(data_t);
	case pw::image::DEPTH:
        return components(t) * sizeof(float);
    default:
        debug::w() << __PRETTY_FUNCTION__ << " unhandled pixel_layout";
    }

    return std::numeric_limits<uint32_t>::max();
}

uint32_t image::components(image::pixel_layout t)
{
    switch (t) {
    case pw::image::RGB8:
        return 3; // * sizeof(data_t);
    case pw::image::RGBA8:
        return 4; // * sizeof(data_t);
    case pw::image::LUM:
        return 1; // * sizeof(data_t);
    case pw::image::DEPTH:
        return 1; // * sizeof(float);
    default:
        debug::w() << __PRETTY_FUNCTION__ << " unhandled pixel_layout";
    }
    return 0;
}

::pw::size image::size() const
{
    return _size;
}

image::pixel_layout image::layout() const
{
    return _layout;
}

void image::set_layout(const pixel_layout &layout)
{
    _layout = layout;
}

uint64_t image::change_count() const
{
    return _change_count;
}

void image::set_change_count(const uint64_t &change_count)
{
    _change_count = change_count;
}

}
