#include "image32.h"

#include <stdlib.h>

#include "error.h"
#include "file.h"
#include "png.h"
#include "vec2i.h"

static void png_read_fn(png_structp png, png_bytep out, png_size_t count);

struct sw_image32 *sw_image32_create(struct sw_vec2i size) {
	struct sw_image32 *image;

	image = malloc(sizeof(struct sw_image32));
	image->_data = malloc(sizeof(sw_color32) * size.x * size.y);
	image->size = size;

	return image;
}

void sw_image32_destroy(struct sw_image32 *image) {
	free(image->_data);
	free(image);
}

struct sw_image32 *sw_image32_load_png(char *path) {
	u32 size;
	u8 *data;
	struct sw_image32 *image;

	data = sw_file_load(path, &size);
	image = sw_image32_load_png_data(data, size);
	free(data);
	return image;
}

struct sw_image32 *sw_image32_load_png_data(u8 *data, u32 data_len) {
	struct sw_image32 *img;
	struct sw_filebuffer *buf;
	png_structp png;
	png_infop info;
	u32 width, height;
	u8 color_type, bit_depth;
	u8 **row_pointers;
	u32 i;

	buf = malloc(sizeof(struct sw_filebuffer));
	buf->_data = data;
	buf->pos = 0;
	buf->size = data_len;

	if(png_sig_cmp(data, 0, 8)) {
		sw_error("File isn't recognised as a PNG file");
	}

	/* TODO: add error handles? <_< */
	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
	info = png_create_info_struct(png);
	png_set_read_fn(png, buf, png_read_fn);
	png_read_info(png, info);

	width = png_get_image_width(png, info);
	height = png_get_image_height(png, info);
	color_type = png_get_color_type(png, info);
	bit_depth = png_get_bit_depth(png, info);

	sw_log(
	    "loading image of size %ux%u, color_type %u, bit_depth %u",
	    width,
	    height,
	    color_type,
	    bit_depth
	);

	if(color_type != PNG_COLOR_TYPE_RGBA) {
		sw_error("image32 can currently only load rgba files");
	}

	if(bit_depth != 8) {
		sw_error("image32 can currently only load 32 bit files");
	}

	img = sw_image32_create(sw_vec2i(width, height));

	row_pointers = malloc(sizeof(u8 *) * height);
	for(i = 0; i < height; ++i) {
		row_pointers[i] = (u8 *)(img->_data + width * i);
	}
	png_read_image(png, row_pointers);

	free(row_pointers);
	free(buf);

	/* TODO: cleanup of png structs? */

	return img;
}

sw_color32 sw_image32_get(struct sw_image32 *image, struct sw_vec2i pos) {
	return image->_data[pos.x + pos.y * image->size.x];
}

/* private */

static void png_read_fn(png_structp png, png_bytep out, png_size_t count) {
	struct sw_filebuffer *buf;

	buf = png_get_io_ptr(png);
	sw_filebuffer_read(buf, out, count);
}
