#include "image.h"
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#include "stb_image.h"

image::image(resource_manager& rm, ini_category const* ini, std::string const& path, std::vector<uint8_t> const& data) :
	_alpha(false), _borrowed_pointer(false)
{
	int w, h, n;
	auto d = reinterpret_cast<color*>(stbi_load_from_memory(data.data(), data.size(), &w, &h, &n, 4));
	_data = new color[w * h];
	for(int i = 0; i < w * h; ++i) {
		if((d[i] & 0xff000000u) == 0)
			_alpha = true;
		_data[i] = d[i];
		_data[i] = (d[i] & 0xff000000u) |
			((d[i] & 0x00ff0000u) >> 16) |
			(d[i] & 0x0000ff00u) |
			((d[i] & 0x000000ffu) << 16);
	}
	stbi_image_free(d);
	_bounds = recti(vec2i::zero, vec2i(w, h));
}

image::image(vec2i size) :
	_borrowed_pointer(false),
	_data(new color[size.size()]),
	_bounds(vec2i::zero, size),
	_alpha(true)
{}

image::image(vec2i size, color* data) :
	_borrowed_pointer(true),
	_data(data),
	_bounds(vec2i::zero, size),
	_alpha(true)
{}

image::image(image const& other) :
	_borrowed_pointer(false),
	_data(new color[other.size().size()]),
	_bounds(other.bounds()),
	_alpha(other._alpha)
{
	std::memcpy(_data, other._data, size().size() * 4);
}

image::~image() {
	if(!_borrowed_pointer && _data)
		delete[] _data;
}

vec2i const& image::size() const {
	return _bounds.size;
}

recti const& image::bounds() const {
	return _bounds;
}

color* image::raw_pointer() const {
	return _data;
}

color image::get(vec2i pos) const {
	return _data[pos.x + pos.y * _bounds.size.x];
}

color image::get_safe(vec2i pos) const {
	if(pos.x < 0 || pos.y < 0 || pos.x >= _bounds.size.x || pos.y >= _bounds.size.y)
		return 0x00000000;
	return _data[pos.x + pos.y * _bounds.size.x];
}

void image::clear(color color) {
	std::fill(_data, _data + size().size(), color);
}

void image::blend_to(color color, double amount) {
	auto bc = color_to_elements(color);
	auto inv_amount = 1.0 - amount;
	for(int i = 0; i < _bounds.size.size(); ++i) {
		auto c = color_to_elements(_data[i]);
		double r = inv_amount * std::get<0>(c) + amount * std::get<0>(bc);
		double g = inv_amount * std::get<1>(c) + amount * std::get<1>(bc);
		double b = inv_amount * std::get<2>(c) + amount * std::get<2>(bc);
		double a = inv_amount * std::get<3>(c) + amount * std::get<3>(bc);
		_data[i] = elements_to_color(r, g, b, a);
	}
}


std::tuple<double, double, double, double> color_to_elements(color color) {
	double r = (color & 0x00ff0000) >> 16;
	double g = (color & 0x0000ff00) >> 8;
	double b = color & 0x000000ff;
	double a = (color & 0xff000000) >> 24;
	return std::make_tuple(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
}

color elements_to_color(double r, double g, double b, double a) {
	int ri = r * 255;
	int gi = g * 255;
	int bi = b * 255;
	int ai = a * 255;
	return ai << 24 | ri << 16 | gi << 8 | bi;
}
