#include "pkm_helper.h"
#include "etc1_utils.h"

static int stbi__pkm_test(stbi__context *s)
{
	//	check the magic number
	if (stbi__get8(s) != 'P') {
		stbi__rewind(s);
		return 0;
	}

	if (stbi__get8(s) != 'K') {
		stbi__rewind(s);
		return 0;
	}

	if (stbi__get8(s) != 'M') {
		stbi__rewind(s);
		return 0;
	}

	if (stbi__get8(s) != ' ') {
		stbi__rewind(s);
		return 0;
	}

	if (stbi__get8(s) != '1') {
		stbi__rewind(s);
		return 0;
	}

	if (stbi__get8(s) != '0') {
		stbi__rewind(s);
		return 0;
	}

	stbi__rewind(s);
	return 1;
}

#ifndef STBI_NO_STDIO

int      stbi__pkm_test_filename        		(char const *filename)
{
   int r;
   FILE *f = fopen(filename, "rb");
   if (!f) return 0;
   r = stbi__pkm_test_file(f);
   fclose(f);
   return r;
}

int      stbi__pkm_test_file        (FILE *f)
{
   stbi__context s;
   int r,n = ftell(f);
   stbi__start_file(&s,f);
   r = stbi__pkm_test(&s);
   fseek(f,n,SEEK_SET);
   return r;
}
#endif

int      stbi__pkm_test_memory      (stbi_uc const *buffer, int len)
{
   stbi__context s;
   stbi__start_mem(&s,buffer, len);
   return stbi__pkm_test(&s);
}

int      stbi__pkm_test_callbacks      (stbi_io_callbacks const *clbk, void *user)
{
   stbi__context s;
   stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
   return stbi__pkm_test(&s);
}

static int stbi__pkm_info(stbi__context *s, int *x, int *y, int *comp )
{
	PKMHeader header;
	unsigned int width, height;

	stbi__getn( s, (stbi_uc*)(&header), sizeof(PKMHeader) );

	if ( 0 != strcmp( header.aName, "PKM 10" ) ) {
		stbi__rewind(s);
		return 0;
	}

	width = (header.iWidthMSB << 8) | header.iWidthLSB;
	height = (header.iHeightMSB << 8) | header.iHeightLSB;

	*x = s->img_x = width;
	*y = s->img_y = height;
	*comp = s->img_n = 3;

	stbi__rewind(s);

	return 1;
}

int stbi__pkm_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp )
{
	stbi__context s;
	stbi__start_mem(&s,buffer, len);
	return stbi__pkm_info( &s, x, y, comp );
}

int stbi__pkm_info_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp)
{
	stbi__context s;
	stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
	return stbi__pkm_info( &s, x, y, comp );
}

#ifndef STBI_NO_STDIO
int stbi__pkm_info_from_path(char const *filename,     int *x, int *y, int *comp)
{
   int res;
   FILE *f = fopen(filename, "rb");
   if (!f) return 0;
   res = stbi__pkm_info_from_file( f, x, y, comp );
   fclose(f);
   return res;
}

int stbi__pkm_info_from_file(FILE *f,                  int *x, int *y, int *comp)
{
   stbi__context s;
   int res;
   long n = ftell(f);
   stbi__start_file(&s, f);
   res = stbi__pkm_info(&s, x, y, comp);
   fseek(f, n, SEEK_SET);
   return res;
}
#endif

static stbi_uc * stbi__pkm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
	stbi_uc *pkm_data = NULL;
	stbi_uc *pkm_res_data = NULL;
	PKMHeader header;
	unsigned int width;
	unsigned int height;
	unsigned int align = 0;
	unsigned int bpr;
	unsigned int size;
	unsigned int compressedSize;

	int res;

	stbi__getn( s, (stbi_uc*)(&header), sizeof(PKMHeader) );

	if ( 0 != strcmp( header.aName, "PKM 10" ) ) {
		return NULL;
	}

	width = (header.iWidthMSB << 8) | header.iWidthLSB;
	height = (header.iHeightMSB << 8) | header.iHeightLSB;

	*x = s->img_x = width;
	*y = s->img_y = height;
	*comp = s->img_n = 3;

	compressedSize = etc1_get_encoded_data_size(width, height);

	pkm_data = (stbi_uc *)malloc(compressedSize);
	stbi__getn( s, pkm_data, compressedSize );

	bpr = ((width * 3) + align) & ~align;
	size = bpr * height;
	pkm_res_data = (stbi_uc *)malloc(size);

	res = etc1_decode_image((const etc1_byte*)pkm_data, (etc1_byte*)pkm_res_data, width, height, 3, bpr);

	free( pkm_data );

	if ( 0 == res ) {
		if( (req_comp <= 4) && (req_comp >= 1) ) {
			//	user has some requirements, meet them
			if( req_comp != s->img_n ) {
				pkm_res_data = stbi__convert_format( pkm_res_data, s->img_n, req_comp, s->img_x, s->img_y );
				*comp = req_comp;
			}
		}

		return (stbi_uc *)pkm_res_data;
	} else {
		free( pkm_res_data );
	}

	return NULL;
}

#ifndef STBI_NO_STDIO
stbi_uc *stbi__pkm_load_from_file   (FILE *f,                  int *x, int *y, int *comp, int req_comp)
{
	stbi__context s;
	stbi__start_file(&s,f);
	return stbi__pkm_load(&s,x,y,comp,req_comp);
}

stbi_uc *stbi__pkm_load_from_path             (char const*filename,           int *x, int *y, int *comp, int req_comp)
{
   stbi_uc *data;
   FILE *f = fopen(filename, "rb");
   if (!f) return NULL;
   data = stbi__pkm_load_from_file(f,x,y,comp,req_comp);
   fclose(f);
   return data;
}
#endif

stbi_uc *stbi__pkm_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
{
   stbi__context s;
   stbi__start_mem(&s,buffer, len);
   return stbi__pkm_load(&s,x,y,comp,req_comp);
}

stbi_uc *stbi__pkm_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
{
	stbi__context s;
   stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
   return stbi__pkm_load(&s,x,y,comp,req_comp);
}
