#include <math.h>
#include "pvr.h"
#include "rspix.h"
#include <kmg/kmg.h>

// Constructor - Loads a KMG image from a given path.
PVR::cKmgImg::cKmgImg(const char *fn)
{	
	KmgToImg(fn);
}

// Destructor
PVR::cKmgImg::~cKmgImg()
{		
	kos_img_free(&m_KosImg, 0);
}
	

int16_t PVR::cKmgImg::Load(RFile *pcf)
{
	int16_t sReturn = SUCCESS;

	if (pcf && pcf->IsOpen())
	{
		// Load image dependent on version.
		sReturn = KmgToImg(pcf);
	}
	else
	{
		sReturn = FAILURE;
	}

	return sReturn;
}

/* Heavily based on libkmg todo: attribution? */
int PVR::cKmgImg::KmgToImg(const char *fn)
{
	file_t f;
	kmg_header_t hdr;
	int dep;
	void *map;

	/* Open the file */
	f = fs_open(fn, O_RDONLY);
	if (f == FILEHND_INVALID)
	{
		dbglog(DBG_ERROR, "kmg_to_img: can't open file '%s'\n", fn);
		return -1;
	}

	/* Read the header */
	if (fs_read(f, &hdr, sizeof(hdr)) != sizeof(hdr))
	{
		fs_close(f);
		dbglog(DBG_ERROR, "kmg_to_img: can't read header from file '%s'\n", fn);
		return -2;
	}

	/* Verify a few things */
	if (hdr.magic != KMG_MAGIC || hdr.version != KMG_VERSION ||
		hdr.platform != KMG_PLAT_DC)
	{
		fs_close(f);
		dbglog(DBG_ERROR, "kmg_to_img: file '%s' is incompatible:\n"
						  "   magic %08lx version %d platform %d\n",
			   fn, hdr.magic, (int)hdr.version, (int)hdr.platform);
		return -3;
	}

	/* See if we can mmap the thing. If so, then we'll just make it
	   a non-owned buffer that way and save some transfers. */
	map = fs_mmap(f);
	if (map)
	{
		m_KosImg.data = ((uint8 *)map) + sizeof(hdr);
	}

	/* Setup the kimg struct */
	m_KosImg.w = hdr.width;
	m_KosImg.h = hdr.height;

	dep = 0;
	if (hdr.format & KMG_DCFMT_VQ)
		dep |= PVR_TXRLOAD_FMT_VQ;
	if (hdr.format & KMG_DCFMT_TWIDDLED)
		dep |= PVR_TXRLOAD_FMT_TWIDDLED;

	switch (hdr.format & KMG_DCFMT_MASK)
	{
	case KMG_DCFMT_RGB565:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_RGB565, dep);
		break;

	case KMG_DCFMT_ARGB4444:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_ARGB4444, dep);
		break;

	case KMG_DCFMT_ARGB1555:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_ARGB1555, dep);
		break;

	case KMG_DCFMT_YUV422:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_YUV422, dep);
		break;

	case KMG_DCFMT_BUMP:
		/* XXX */
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_RGB565, dep);
		break;

	case KMG_DCFMT_4BPP_PAL:
	case KMG_DCFMT_8BPP_PAL:
	default:
		assert_msg(0, "currently-unsupported KMG pixel format");
		fs_close(f);
		free(m_KosImg.data);
		return -5;
	}

	m_KosImg.byte_count = hdr.byte_count;

	/* And load the rest of it if necessary */
	if (map)
	{
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_I(m_KosImg.fmt) | KOS_IMG_NOT_OWNER,
							  KOS_IMG_FMT_D(m_KosImg.fmt));
	}
	else
	{
		m_KosImg.data = malloc(hdr.byte_count);
		if (!m_KosImg.data)
		{
			dbglog(DBG_ERROR, "kmg_to_img: can't malloc(%d) while loading '%s'\n",
				   (int)hdr.byte_count, fn);
			fs_close(f);
			return -4;
		}
		if (fs_read(f, m_KosImg.data, m_KosImg.byte_count) != m_KosImg.byte_count)
		{
			dbglog(DBG_ERROR, "kmg_to_img: can't read %d bytes while loading '%s'\n",
				   (int)hdr.byte_count, fn);
			fs_close(f);
			free(m_KosImg.data);
			return -6;
		}
	}

	/* Ok, all done */
	fs_close(f);

	/* If the byte count is not a multiple of 32, bump it up as well.
	   This is for DMA/SQ usage. */
	m_KosImg.byte_count = (m_KosImg.byte_count + 31) & ~31;

	return 0;
}

/* Heavily based on libkmg todo: attribution? */
int PVR::cKmgImg::KmgToImg(RFile *pcf)
{
	kmg_header_t hdr;
	int dep;

	if (pcf && pcf->IsOpen())
	{
		/* Read the header */
		if (pcf->Read(&hdr, sizeof(hdr)) != sizeof(hdr))
		{			
			return -1;
		}

		/* Verify a few things */
		if (hdr.magic != KMG_MAGIC || hdr.version != KMG_VERSION ||
			hdr.platform != KMG_PLAT_DC)
		{			
			return -1;
		}
	}
	
	/* Setup the kimg struct */
	m_KosImg.w = hdr.width;
	m_KosImg.h = hdr.height;

	dep = 0;
	if (hdr.format & KMG_DCFMT_VQ)
		dep |= PVR_TXRLOAD_FMT_VQ;
	if (hdr.format & KMG_DCFMT_TWIDDLED)
		dep |= PVR_TXRLOAD_FMT_TWIDDLED;

	switch (hdr.format & KMG_DCFMT_MASK)
	{
	case KMG_DCFMT_RGB565:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_RGB565, dep);
		break;

	case KMG_DCFMT_ARGB4444:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_ARGB4444, dep);
		break;

	case KMG_DCFMT_ARGB1555:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_ARGB1555, dep);
		break;

	case KMG_DCFMT_YUV422:
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_YUV422, dep);
		break;

	case KMG_DCFMT_BUMP:
		/* XXX */
		m_KosImg.fmt = KOS_IMG_FMT(KOS_IMG_FMT_RGB565, dep);
		break;

	case KMG_DCFMT_4BPP_PAL:
	case KMG_DCFMT_8BPP_PAL:
	default:		
		return -5;
	}

	m_KosImg.byte_count = hdr.byte_count;

	uint8_t *data = (uint8_t *)malloc(m_KosImg.byte_count);

	if (!data)
	{		
		return -4;
	}

	if (pcf->Read(data, (int32_t)m_KosImg.byte_count) != (int32_t)m_KosImg.byte_count)
	{		
		free(data);
		return -6;
	}

	m_KosImg.data = data;

	/* If the byte count is not a multiple of 32, bump it up as well.
	   This is for DMA/SQ usage. */
	m_KosImg.byte_count = (m_KosImg.byte_count + 31) & ~31;

	return 0;
}
