#include "lib/self_test.h"

#include "lib/res/graphics/tex.h"
#include "lib/res/graphics/tex_codec.h"
#include "lib/res/graphics/tex_internal.h"	// tex_encode

class TestTex : public CxxTest::TestSuite 
{
	void generate_encode_decode_compare(uint w, uint h, uint flags, uint bpp,
		const char* filename)
	{
		// generate test data
		const size_t size = w*h*bpp/8;
		u8* img = new u8[size];
		for(size_t i = 0; i < size; i++)
			img[i] = rand() & 0xFF;

		// wrap in Tex
		Tex t;
		TS_ASSERT_OK(tex_wrap(w, h, bpp, flags, img, &t));

		// encode to file format
		DynArray da;
		TS_ASSERT_OK(tex_encode(&t, filename, &da));
		memset(&t, 0, sizeof(t));

		// decode from file format
		MEM_DTOR dtor = 0;	// we'll free da manually
		TS_ASSERT_OK(tex_decode(da.base, da.cur_size, dtor, &t));

		// make sure pixel format gets converted completely to plain
		TS_ASSERT_OK(tex_transform_to(&t, 0));

		// compare img
		TS_ASSERT_SAME_DATA(tex_get_data(&t), img, size);

		// cleanup
		TS_ASSERT_OK(tex_free(&t));
		TS_ASSERT_OK(da_free(&da));
		delete[] img;
	}

public:

	// this also covers BGR and orientation transforms.
	void test_encode_decode()
	{
		// for each codec
		const TexCodecVTbl* c = 0;
		for(;;)
		{
			c = tex_codec_next(c);
			if(!c)
				break;

			// get an extension that this codec will support
			// (so that tex_encode uses this codec)
			char extension[30] = {'.'};
			strcpy_s(extension+1, 29, c->name);
			// .. make sure the c->name hack worked
			const TexCodecVTbl* correct_c;
			TS_ASSERT_OK(tex_codec_for_filename(extension, &correct_c));
			TS_ASSERT_EQUALS(c, correct_c);

			// for each test width/height combination
			const uint widths [] = { 4, 5, 4, 256, 384 };
			const uint heights[] = { 4, 4, 5, 256, 256 };
			for(size_t i = 0; i < ARRAY_SIZE(widths); i++)
			{
				// for each bit depth
				for(uint bpp = 8; bpp <= 32; bpp += 8)
				{
					uint flags = 0;
					if(!strcmp(extension, ".dds"))
						flags |= (TEX_DXT&3);	// DXT3
					if(bpp == 8)
						flags |= TEX_GREY;
					else if(bpp == 32)
						flags |= TEX_ALPHA;

					// normal
					generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
					// top-down
					flags &= ~TEX_ORIENTATION; flags |= TEX_TOP_DOWN;
					generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
					// bottom up
					flags &= ~TEX_ORIENTATION; flags |= TEX_BOTTOM_UP;
					generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);

					flags &= ~TEX_ORIENTATION;
					flags |= TEX_BGR;

					// bgr, normal
					generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
					// bgr, top-down
					flags &= ~TEX_ORIENTATION; flags |= TEX_TOP_DOWN;
					generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
					// bgr, bottom up
					flags &= ~TEX_ORIENTATION; flags |= TEX_BOTTOM_UP;
					generate_encode_decode_compare(widths[i], heights[i], flags, bpp, extension);
				}	// for bpp
			}	// for width/height
		}	 // foreach codec
	}

	// have mipmaps be created for a test image; check resulting size and pixels
	void test_mipmap_create()
	{
		u8 img[] = { 0x10,0x20,0x30, 0x40,0x60,0x80, 0xA0,0xA4,0xA8, 0xC0,0xC1,0xC2 };
		// assumes 2x2 box filter algorithm with rounding
		const u8 mipmap[] = { 0x6C,0x79,0x87 };
		Tex t;
		TS_ASSERT_OK(tex_wrap(2, 2, 24, 0, img, &t));
		TS_ASSERT_OK(tex_transform_to(&t, TEX_MIPMAPS));
		const u8* const out_img = tex_get_data(&t);
		TS_ASSERT_EQUALS(tex_img_size(&t), 12+3);
		TS_ASSERT_SAME_DATA(out_img, img, 12);
		TS_ASSERT_SAME_DATA(out_img+12, mipmap, 3);
	}

	void test_img_size()
	{
		char dummy_img[100*100*4];	// required

		Tex t;
		TS_ASSERT_OK(tex_wrap(100, 100, 32, TEX_ALPHA, dummy_img, &t));
		TS_ASSERT_EQUALS(tex_img_size(&t), 40000);

		// DXT rounds up to 4x4 blocks; DXT1a is 4bpp
		Tex t2;
		TS_ASSERT_OK(tex_wrap(97, 97, 4, DXT1A, dummy_img, &t2));
		TS_ASSERT_EQUALS(tex_img_size(&t2),  5000);
	}
};
