#include "3dmodelio/TexturePacker.h"
#include "unittest/TestSuite.h"
#include <limits.h>
#include <stdio.h>

static void verifyInit(TexturePacker * object, enum BitmapPixelFormat pixelFormat, unsigned int line) {
	TestCase_assert(object->vtable == &TexturePacker_class, "Expected %p but got %p (line %u)", &TexturePacker_class, object->vtable, line);
	TestCase_assert(object->vtable->dispose == TexturePacker_dispose, "Expected %p but got %p (line %u)", TexturePacker_dispose, object->vtable->dispose, line);
	TestCase_assert(object->pixelFormat == pixelFormat, "Expected %d but got %d (line %u)", pixelFormat, object->pixelFormat, line);
	TestCase_assert(object->entryCount == 0, "Expected 0 but got %u (line %u)", object->entryCount, line);
	TestCase_assert(object->entries == NULL, "Expected NULL but got %p (line %u)", object->entries, line);
}

static void testInit(void) {
	TexturePacker object, * objectPtr;
	bool success;
	
	memset(&object, 0x00, sizeof(object));
	stemobject_assign_vtable(object, TexturePacker);
	success = TexturePacker_init(&object, BITMAP_PIXEL_FORMAT_RGBA_8888);
	TestCase_assertBoolTrue(success);
	verifyInit(&object, BITMAP_PIXEL_FORMAT_RGBA_8888, __LINE__);
	TexturePacker_dispose(&object);
	
	memset(&object, 0xFF, sizeof(object));
	stemobject_assign_vtable(object, TexturePacker);
	success = TexturePacker_init(&object, BITMAP_PIXEL_FORMAT_RGB_888);
	TestCase_assertBoolTrue(success);
	verifyInit(&object, BITMAP_PIXEL_FORMAT_RGB_888, __LINE__);
	TexturePacker_dispose(&object);
	
	objectPtr = TexturePacker_create(BITMAP_PIXEL_FORMAT_RGBA_8888);
	TestCase_assertPointerNonNULL(objectPtr);
	if (objectPtr == NULL) { return; } // Suppress clang warning
	verifyInit(objectPtr, BITMAP_PIXEL_FORMAT_RGBA_8888, __LINE__);
	TexturePacker_dispose(objectPtr);
}

static void printPixels(const unsigned char * pixels, unsigned int width, unsigned int height, BitmapPixelFormat pixelFormat) {
	unsigned int componentCount = BitmapImage_pixelFormatBytes(pixelFormat);
	for (unsigned int rowIndex = 0; rowIndex < height; rowIndex++) {
		fprintf(stderr, "  ");
		for (unsigned int columnIndex = 0; columnIndex < width; columnIndex++) {
			for (unsigned int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
				fprintf(stderr, "%02X", pixels[(rowIndex * width + columnIndex) * componentCount + componentIndex]);
			}
			fputc(' ', stderr);
		}
		fputc('\n', stderr);
	}
}

static void assertBitmapImage(unsigned int line, BitmapImage * image, const unsigned char * expectedPixels, BitmapPixelFormat pixelFormat, unsigned int width, unsigned int height, bool dispose) {
	TestCase_assert(image != NULL, "Expected non-NULL but got NULL (line %u)", line);
	TestCase_assert(image->pixelFormat == pixelFormat, "Expected %d but got %d (line %u)", pixelFormat, image->pixelFormat, line);
	TestCase_assert(image->width == width, "Expected %u but got %u (line %u)", width, image->width, line);
	TestCase_assert(image->height == height, "Expected %u but got %u (line %u)", height, image->height, line);
	unsigned int componentCount = BitmapImage_pixelFormatBytes(pixelFormat);
	for (unsigned int rowIndex = 0; rowIndex < height; rowIndex++) {
		for (unsigned int columnIndex = 0; columnIndex < width; columnIndex++) {
			for (unsigned int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
				if (image->pixels[(rowIndex * width + columnIndex) * componentCount + componentIndex] != expectedPixels[(rowIndex * width + columnIndex) * componentCount + componentIndex]) {
					fprintf(stderr, "Expected image:\n");
					printPixels(expectedPixels, width, height, pixelFormat);
					fprintf(stderr, "Actual image:\n");
					printPixels(image->pixels, width, height, pixelFormat);
					TestCase_assert(false, "Image data mismatch at %u, %u (line %u)", columnIndex, rowIndex, line);
					return;
				}
			}
		}
	}
	if (dispose) {
		BitmapImage_dispose(image);
	}
}

static void testExtractEntryImage(void) {
	BitmapImage * sourceImage, * extractedImage;
	unsigned char pixels1[] = {
		1, 2,
		3, 4
	};
	unsigned char pixels1Flipped[] = {
		3, 4,
		1, 2
	};
	unsigned char pixels2[] = {
		1, 2, 3,
		4, 5, 6,
		7, 8, 9
	};
	unsigned char pixels2UpperLeft[] = {
		1, 2,
		4, 5
	};
	unsigned char pixels2UpperRight[] = {
		2, 3,
		5, 6
	};
	unsigned char pixels2LowerLeft[] = {
		4, 5,
		7, 8
	};
	unsigned char pixels2LowerRight[] = {
		5, 6,
		8, 9
	};
	unsigned char pixels2UpperLeftFlipped[] = {
		4, 5,
		1, 2
	};
	unsigned char pixels2UpperRightFlipped[] = {
		5, 6,
		2, 3
	};
	unsigned char pixels2LowerLeftFlipped[] = {
		7, 8,
		4, 5
	};
	unsigned char pixels2LowerRightFlipped[] = {
		8, 9,
		5, 6
	};
	
	sourceImage = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, pixels1, false);
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, false, false, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, false, true, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, false, false, true, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, false, true, true, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, true, false, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, true, true, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, true, false, true, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, true, true, true, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, false, false, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, false, true, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, false, false, false, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, false, true, false, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, true, false, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, true, true, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, true, false, false, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0.0f, 1.0f, 0.0f, 1.0f), sourceImage, true, true, false, true);
	assertBitmapImage(__LINE__, extractedImage, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	BitmapImage_dispose(sourceImage);
	
	sourceImage = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, pixels2, false);
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, false, false, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2UpperLeft, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(1, 3, 1, 3), sourceImage, false, false, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2LowerRight, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, true, false, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2LowerLeftFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(1, 3, 1, 3), sourceImage, true, false, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2UpperRightFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, false, true, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2UpperLeftFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(1, 3, 1, 3), sourceImage, false, true, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2LowerRightFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, true, true, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2LowerLeft, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(1, 3, 1, 3), sourceImage, true, true, false, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2UpperRight, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, false, false, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2LowerLeft, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(1, 3, 1, 3), sourceImage, false, false, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2UpperRight, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(0, 2, 0, 2), sourceImage, true, false, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2UpperLeftFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	
	extractedImage = TexturePacker_extractEntryImage(RECT4f(1, 3, 1, 3), sourceImage, true, false, true, false);
	assertBitmapImage(__LINE__, extractedImage, pixels2LowerRightFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	BitmapImage_dispose(sourceImage);
}

static void testAddImage(void) {
	unsigned char pixels1[] = {
		1, 2,
		3, 4
	};
	unsigned char pixels1Flipped[] = {
		3, 4,
		1, 2
	};
	unsigned char pixels2[] = {
		1, 2, 3,
		4, 5, 6,
		7, 8, 9
	};
	unsigned char pixels2Flipped[] = {
		7, 8, 9,
		4, 5, 6,
		1, 2, 3
	};
	
	TexturePacker * texturePacker = TexturePacker_create(BITMAP_PIXEL_FORMAT_GRAY_8);
	bool result = TexturePacker_addImage(texturePacker, BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, pixels1, false), true, "1", false, 0, 0);
	TestCase_assertBoolFalse(result);
	TestCase_assertPointerNonNULL(texturePacker->entries);
	TestCase_assertUIntEqual(texturePacker->entryCount, 1);
	TestCase_assertStringEqual(texturePacker->entries[0].name, "1");
	TestCase_assertUIntEqual(texturePacker->entries[0].width, 2);
	TestCase_assertUIntEqual(texturePacker->entries[0].height, 2);
	TestCase_assertUIntEqual(texturePacker->entries[0].margin, 0);
	TestCase_assertIntEqual(texturePacker->entries[0].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[0].image, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, false);
	
	result = TexturePacker_addImage(texturePacker, BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, pixels1, false), true, "2", true, 1, 0);
	TestCase_assertBoolFalse(result);
	TestCase_assertUIntEqual(texturePacker->entryCount, 2);
	TestCase_assertStringEqual(texturePacker->entries[1].name, "2");
	TestCase_assertUIntEqual(texturePacker->entries[1].width, 2);
	TestCase_assertUIntEqual(texturePacker->entries[1].height, 2);
	TestCase_assertUIntEqual(texturePacker->entries[1].margin, 1);
	TestCase_assertIntEqual(texturePacker->entries[1].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[1].image, pixels1Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, false);
	
	result = TexturePacker_addImage(texturePacker, BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, pixels2, false), true, "image 3", false, 0, 1);
	TestCase_assertBoolFalse(result);
	TestCase_assertUIntEqual(texturePacker->entryCount, 3);
	TestCase_assertStringEqual(texturePacker->entries[2].name, "image 3");
	TestCase_assertUIntEqual(texturePacker->entries[2].width, 3);
	TestCase_assertUIntEqual(texturePacker->entries[2].height, 3);
	TestCase_assertUIntEqual(texturePacker->entries[2].margin, 0);
	TestCase_assertIntEqual(texturePacker->entries[2].internalMargin, 1);
	assertBitmapImage(__LINE__, texturePacker->entries[2].image, pixels2, BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, false);
	
	result = TexturePacker_addImage(texturePacker, BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, pixels2, false), true, "image 4", true, 1, 1);
	TestCase_assertBoolFalse(result);
	TestCase_assertUIntEqual(texturePacker->entryCount, 4);
	TestCase_assertStringEqual(texturePacker->entries[3].name, "image 4");
	TestCase_assertUIntEqual(texturePacker->entries[3].width, 3);
	TestCase_assertUIntEqual(texturePacker->entries[3].height, 3);
	TestCase_assertUIntEqual(texturePacker->entries[3].margin, 1);
	TestCase_assertIntEqual(texturePacker->entries[3].internalMargin, 1);
	assertBitmapImage(__LINE__, texturePacker->entries[3].image, pixels2Flipped, BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, false);
	
	result = TexturePacker_addImage(texturePacker, BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, pixels1, false), true, "image 3", false, 1, 0);
	TestCase_assertBoolTrue(result);
	TestCase_assertUIntEqual(texturePacker->entryCount, 4);
	TestCase_assertStringEqual(texturePacker->entries[2].name, "image 3");
	TestCase_assertUIntEqual(texturePacker->entries[2].width, 2);
	TestCase_assertUIntEqual(texturePacker->entries[2].height, 2);
	TestCase_assertUIntEqual(texturePacker->entries[2].margin, 1);
	TestCase_assertIntEqual(texturePacker->entries[2].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[2].image, pixels1, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, false);
	TexturePacker_dispose(texturePacker);
}

static void testAddAllEntriesFromAtlas(void) {
	unsigned char pixelsFull[] = {
		1, 2, 3,
		4, 5, 6,
		7, 8, 9
	};
	unsigned char pixelsUpperLeft[] = {
		1, 2,
		4, 5
	};
	unsigned char pixelsLowerRight[] = {
		5, 6,
		8, 9
	};
	unsigned char pixelsFullFlipped[] = {
		7, 8, 9,
		4, 5, 6,
		1, 2, 3
	};
	unsigned char pixelsLowerLeftFlipped[] = {
		7, 8,
		4, 5
	};
	unsigned char pixelsUpperRightFlipped[] = {
		5, 6,
		2, 3
	};
	struct TextureAtlasData_entry entries[3] = {
		{"1", RECT4f(0, 2, 0, 2)},
		{"2", RECT4f(1, 3, 1, 3)},
		{"3", RECT4f(0, 3, 0, 3)}
	};
	TextureAtlasData * atlasData = TextureAtlasData_create(NULL, 3, entries);
	TexturePacker * texturePacker = TexturePacker_create(BITMAP_PIXEL_FORMAT_GRAY_8);
	BitmapImage * atlasImage = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, pixelsFull, false);
	
	bool result = TexturePacker_addAllEntriesFromAtlas(texturePacker, atlasData, atlasImage, false, 0);
	TestCase_assertBoolFalse(result);
	TestCase_assertPointerNonNULL(texturePacker->entries);
	TestCase_assertUIntEqual(texturePacker->entryCount, 3);
	
	TestCase_assertStringEqual(texturePacker->entries[0].name, "1");
	TestCase_assertUIntEqual(texturePacker->entries[0].width, 2);
	TestCase_assertUIntEqual(texturePacker->entries[0].height, 2);
	TestCase_assertUIntEqual(texturePacker->entries[0].margin, 0);
	TestCase_assertIntEqual(texturePacker->entries[0].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[0].image, pixelsUpperLeft, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, false);
	
	TestCase_assertStringEqual(texturePacker->entries[1].name, "2");
	TestCase_assertUIntEqual(texturePacker->entries[1].width, 2);
	TestCase_assertUIntEqual(texturePacker->entries[1].height, 2);
	TestCase_assertUIntEqual(texturePacker->entries[1].margin, 0);
	TestCase_assertIntEqual(texturePacker->entries[1].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[1].image, pixelsLowerRight, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, false);
	
	TestCase_assertStringEqual(texturePacker->entries[2].name, "3");
	TestCase_assertUIntEqual(texturePacker->entries[2].width, 3);
	TestCase_assertUIntEqual(texturePacker->entries[2].height, 3);
	TestCase_assertUIntEqual(texturePacker->entries[2].margin, 0);
	TestCase_assertIntEqual(texturePacker->entries[2].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[2].image, pixelsFull, BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, false);
	
	result = TexturePacker_addAllEntriesFromAtlas(texturePacker, atlasData, atlasImage, true, 1);
	TestCase_assertBoolTrue(result);
	TestCase_assertUIntEqual(texturePacker->entryCount, 3);
	
	TestCase_assertStringEqual(texturePacker->entries[0].name, "1");
	TestCase_assertUIntEqual(texturePacker->entries[0].width, 2);
	TestCase_assertUIntEqual(texturePacker->entries[0].height, 2);
	TestCase_assertUIntEqual(texturePacker->entries[0].margin, 1);
	TestCase_assertIntEqual(texturePacker->entries[0].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[0].image, pixelsLowerLeftFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, false);
	
	TestCase_assertStringEqual(texturePacker->entries[1].name, "2");
	TestCase_assertUIntEqual(texturePacker->entries[1].width, 2);
	TestCase_assertUIntEqual(texturePacker->entries[1].height, 2);
	TestCase_assertUIntEqual(texturePacker->entries[1].margin, 1);
	TestCase_assertIntEqual(texturePacker->entries[1].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[1].image, pixelsUpperRightFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, false);
	
	TestCase_assertStringEqual(texturePacker->entries[2].name, "3");
	TestCase_assertUIntEqual(texturePacker->entries[2].width, 3);
	TestCase_assertUIntEqual(texturePacker->entries[2].height, 3);
	TestCase_assertUIntEqual(texturePacker->entries[2].margin, 1);
	TestCase_assertIntEqual(texturePacker->entries[2].internalMargin, 0);
	assertBitmapImage(__LINE__, texturePacker->entries[2].image, pixelsFullFlipped, BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, false);
	
	BitmapImage_dispose(atlasImage);
	TexturePacker_dispose(texturePacker);
	TextureAtlasData_dispose(atlasData);
}

static void testPack(void) {
	unsigned char pixels[] = {
		1, 2, 3,
		4, 5, 6,
		7, 8, 9
	};
	unsigned char pixelsUpperLeft[] = {
		1, 2,
		4, 5
	};
	unsigned char pixelsLowerRight[] = {
		5, 6,
		8, 9
	};
	struct TextureAtlasData_entry entries[3] = {
		{"1", RECT4f(0, 2, 0, 2)},
		{"2", RECT4f(1, 3, 1, 3)},
		{"3", RECT4f(0, 3, 0, 3)}
	};
	TextureAtlasData * inputData = TextureAtlasData_create(NULL, 3, entries);
	TexturePacker * texturePacker = TexturePacker_create(BITMAP_PIXEL_FORMAT_GRAY_8);
	BitmapImage * inputImage = BitmapImage_createWithPixelsNoCopy(BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, pixels, false);
	TexturePacker_addAllEntriesFromAtlas(texturePacker, inputData, inputImage, false, 0);
	BitmapImage_dispose(inputImage);
	TextureAtlasData_dispose(inputData);
	
	TexturePacker_pack(texturePacker, false);
	TestCase_assert(texturePacker->packedWidth > 0, "Expected nonzero but got 0");
	TestCase_assert(texturePacker->packedHeight > 0, "Expected nonzero but got 0");
	
	BitmapImage * atlasImage = TexturePacker_rasterize(texturePacker, false);
	TestCase_assertPointerNonNULL(atlasImage);
	TestCase_assertIntEqual(atlasImage->pixelFormat, BITMAP_PIXEL_FORMAT_GRAY_8);
	
	TextureAtlasData * atlasData = TexturePacker_createTextureAtlasData(texturePacker, NULL);
	TestCase_assertPointerNonNULL(atlasData);
	TestCase_assertUIntEqual(atlasData->entryCount, 3);
	unsigned int entry1Index = UINT_MAX, entry2Index = UINT_MAX, entry3Index = UINT_MAX;
	for (unsigned int entryIndex = 0; entryIndex < atlasData->entryCount; entryIndex++) {
		if (atlasData->entries[entryIndex].name[0] == '1') {
			entry1Index = entryIndex;
		} else if (atlasData->entries[entryIndex].name[0] == '2') {
			entry2Index = entryIndex;
		} else if (atlasData->entries[entryIndex].name[0] == '3') {
			entry3Index = entryIndex;
		}
	}
	TestCase_assert(entry1Index < UINT_MAX, "Couldn't find entry \"1\" after packing");
	TestCase_assert(entry2Index < UINT_MAX, "Couldn't find entry \"2\" after packing");
	TestCase_assert(entry3Index < UINT_MAX, "Couldn't find entry \"3\" after packing");
	TestCase_assertStringEqual(atlasData->entries[entry1Index].name, "1");
	TestCase_assertStringEqual(atlasData->entries[entry2Index].name, "2");
	TestCase_assertStringEqual(atlasData->entries[entry3Index].name, "3");
	
	BitmapImage * image = TexturePacker_extractEntryImage(atlasData->entries[entry1Index].bounds, atlasImage, false, false, false, false);
	assertBitmapImage(__LINE__, image, pixelsUpperLeft, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	image = TexturePacker_extractEntryImage(atlasData->entries[entry2Index].bounds, atlasImage, false, false, false, false);
	assertBitmapImage(__LINE__, image, pixelsLowerRight, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	image = TexturePacker_extractEntryImage(atlasData->entries[entry3Index].bounds, atlasImage, false, false, false, false);
	assertBitmapImage(__LINE__, image, pixels, BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, true);
	
	BitmapImage_dispose(atlasImage);
	atlasImage = TexturePacker_rasterize(texturePacker, true);
	image = TexturePacker_extractEntryImage(atlasData->entries[entry1Index].bounds, atlasImage, true, false, false, false);
	assertBitmapImage(__LINE__, image, pixelsUpperLeft, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	image = TexturePacker_extractEntryImage(atlasData->entries[entry2Index].bounds, atlasImage, true, false, false, false);
	assertBitmapImage(__LINE__, image, pixelsLowerRight, BITMAP_PIXEL_FORMAT_GRAY_8, 2, 2, true);
	image = TexturePacker_extractEntryImage(atlasData->entries[entry3Index].bounds, atlasImage, true, false, false, false);
	assertBitmapImage(__LINE__, image, pixels, BITMAP_PIXEL_FORMAT_GRAY_8, 3, 3, true);
	
	BitmapImage_dispose(atlasImage);
	TextureAtlasData_dispose(atlasData);
	TexturePacker_dispose(texturePacker);
}

TEST_SUITE(TexturePackerTest,
           testInit,
           testExtractEntryImage,
           testAddImage,
           testAddAllEntriesFromAtlas,
           testPack)
