
#include <Spectre/System/File.h>
#include <Spectre/System/Path.h>
#include <Spectre/System/Log.h>
#include <Spectre/Graphics/Image.h>
#include "ImageLoader.h"

#include "Image/IcoFormat.h"

// Disable some file formats that we don't use.
#define STBI_NO_PSD
#define STBI_NO_PIC
#define STBI_NO_GIF
#define STBI_NO_HDR
#define STBI_NO_PNM

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

#include <cstring>
#include <cstdlib>
#include <vector>

namespace sp {

bool ImageLoader::loadFromFile(const char *filename, Image& img)
{
	File file(filename);

	if (file.isOpen()) {
		std::vector<unsigned char> buf;
		file.read(buf);

		if (loadFromMemory(&buf[0], buf.size(), img) == false) {
			Log::warn("ImageLoader: could not load file '%s'. Reason: %s",
				filename, m_error);
			return false;
		}
		return true;
	}

	Log::warn("ImageLoader: could not open file '%s'. Reason: %s",
		filename, file.getErrorMessage().c_str());
	return false;
}

bool ImageLoader::loadFromMemory(const void *data, unsigned size, Image& img)
{
	// width, height, num components.
	int w, h, n_comp;
	const stbi_uc *ptr = (const stbi_uc *) data;

	if (ptr[0] == 0x0) {
		image::IcoFormat ico;
		bool ret = ico.decode(img, ptr, size);
		if (!ret) {
			m_error = stbi_failure_reason();
		}
		return ret;
	}

	unsigned char *pixels = stbi_load_from_memory(ptr, size, &w, &h, &n_comp, 4);

	if (pixels) {

		unsigned int len = w * h * 4;

		img.create(w, h, pixels);

		stbi_image_free(pixels);

		return true;
	}

	m_error = stbi_failure_reason();
	return false;
}

// TODO: Support more formats.
bool ImageLoader::saveToFile(const Image& img, const char *filename)
{
	std::string ext = Path::getExtension(filename);
	std::vector<unsigned char> encoded_data;

	if (ext == "png") {

		if (!encodePNG(img, encoded_data)) {
			Log::warn("ImageLoader: failed to save file '%s'. Reason: %s",
				filename, m_error);
		}

	} else {
		Log::warn("ImageLoader: Invalid file format");
		return false;
	}

	if (encoded_data.size() > 0) {

		File file(filename, File::Access::WRITE);
		if (file.isOpen()) {
			file.write(encoded_data);
		}
		return true;
	}

	return false;
}

bool ImageLoader::encodePNG(const Image& img, std::vector<unsigned char>& data)
{
	int buf_len;
	unsigned char *raw = (unsigned char*) img.getPixels();
	unsigned char *buf;

	buf = stbi_write_png_to_mem(raw, img.getStride(), img.getWidth(), img.getHeight(), img.getNumChannels(), &buf_len);
	if (buf && buf_len > 0) {

		data.resize(buf_len);
		memcpy(&data[0], buf, buf_len);
		STBIW_FREE(buf);

		return true;
	}
	m_error = stbi_failure_reason();
	return false;
}

} // namespace sp
