#include "bitmap.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

inline int f(const unsigned char *d)
{
	int tmp = d[3];
	tmp = (tmp << 8) | d[2];
	tmp = (tmp << 8) | d[1];
	tmp = (tmp << 8) | d[0];
	return tmp;
}

struct bitmap_t *bitmapOpen(const char *filename)
{
	unsigned char buf[128] = {0, };
	FILE *fp = fopen(filename, "rb");
	if (!fp)
		goto error;

	if (fread(buf, 1, 18, fp) != 18)
		goto close_fp;

	if (buf[0] != 'B' || buf[1] != 'M')
		goto close_fp;

	int dataStart = f(&buf[10]);
	int size = f(&buf[14]);

	if (fread(&buf[4], 1, size - 4, fp) != size - 4)
		goto close_fp;

	int width;
	int height;
	short bpp;
	short numCLUT;
	if (size == 40) {
		width = f(&buf[4]);
		height = f(&buf[8]);
		bpp = f(&buf[14]);
		numCLUT = f(&buf[32]);
	} else {
		goto close_fp;
	}

	if (bpp > 8)
		goto close_fp;

	int pitch = ((width * bpp + 31) >> 3) & ~3;

	unsigned char *clut = 0;
	if (numCLUT == 0)
		numCLUT = 2 << bpp;

	clut = memalign(64, 4 * (2 << bpp));
	if (!clut)
		goto close_fp;

	if (fread(clut, 1, numCLUT * 4, fp) != (numCLUT * 4))
		goto free_clut;

	// BMP is BGRA
	// clut[i * 4 + 0] Blue
	// clut[i * 4 + 1] Green
	// clut[i * 4 + 2] Red
	// clut[i * 4 + 3] reserved or Alpha
	//
	// but PSP texture is RGBA
	// swap(Blue, Red) is need
	int i;
	for(i = 0; i < numCLUT; ++i) {
		int tmp = clut[i * 4 + 0];
		clut[i * 4 + 0] = clut[i * 4 + 2];
		clut[i * 4 + 2] = tmp;
	}

	unsigned char *data = memalign(64, pitch * height);
	if (!data)
		goto free_clut;

	struct bitmap_t *image = malloc(sizeof(struct bitmap_t));
	if (!image)
		goto free_data;

	fseek(fp, dataStart, SEEK_SET);
	if (fread(data, 1, pitch * height, fp) != pitch * height)
		goto free_image;

	fclose(fp);

	image->width = width;
	image->height = height;
	image->pitch = pitch;
	image->bpp = bpp;
	image->clut = clut;
	image->data = data;

	return image;

free_image:
	free(image);
free_data:
	free(data);
free_clut:
	free(clut);
close_fp:
	fclose(fp);
error:
	return 0;
}

void bitmapClose(struct bitmap_t *image)
{
	if (!image)
		return;

	if (image->clut)
		free(image->clut);

	if (image->data)
		free(image->data);

	free(image);
}

