/*
  Copyright (c) 2023 Alex Diener
  
  This software is provided 'as-is', without any express or implied
  warranty. In no event will the authors be held liable for any damages
  arising from the use of this software.
  
  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:
  
  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
  
  Alex Diener alex@ludobloom.com
*/

#include "gamemath/Rect4i.h"
#include "tileset/TileMapVisual.h"
#include "utilities/IOUtilities.h"
#include "stem_core.h"
#include <stdlib.h>

#define stemobject_implementation TileMapVisual

stemobject_vtable_begin();
stemobject_vtable_entry(dispose);
stemobject_vtable_end();

TileMapVisual * TileMapVisual_create(TilesetEditData * tileset, ImageCollection * imageCollection) {
	stemobject_create_implementation(init, tileset, imageCollection)
}

bool TileMapVisual_init(TileMapVisual * self, TilesetEditData * tileset, ImageCollection * imageCollection) {
	call_super(init, self);
	self->tileset = tileset;
	self->imageCollection = imageCollection;
	self->drawnTileCount = 0;
	self->drawnTileAllocatedCount = 0;
	self->drawnTiles = NULL;
	self->nextListOrder = 0;
	return true;
}

void TileMapVisual_dispose(TileMapVisual * self) {
	free(self->drawnTiles);
	call_super_virtual(dispose, self);
}

TileMapVisual * TileMapVisual_copy(TileMapVisual * self) {
	TileMapVisual * copy = TileMapVisual_create(self->tileset, self->imageCollection);
	copy->drawnTileCount = self->drawnTileCount;
	copy->drawnTileAllocatedCount = self->drawnTileCount;
	copy->drawnTiles = memdup(self->drawnTiles, self->drawnTileCount * sizeof(*self->drawnTiles));
	copy->nextListOrder = self->nextListOrder;
	return copy;
}

static Rect4f sliceTextureBounds(Rect4f atlasEntry, Rect4i totalBounds, Rect4i sliceBounds) {
	Rect4f textureBounds;
	textureBounds.xMin = atlasEntry.xMin + (atlasEntry.xMax - atlasEntry.xMin) * (sliceBounds.xMin - totalBounds.xMin) / (totalBounds.xMax - totalBounds.xMin);
	textureBounds.xMax = atlasEntry.xMax - (atlasEntry.xMax - atlasEntry.xMin) * (totalBounds.xMax - sliceBounds.xMax) / (totalBounds.xMax - totalBounds.xMin);
	textureBounds.yMin = atlasEntry.yMin + (atlasEntry.yMax - atlasEntry.yMin) * (totalBounds.yMax - sliceBounds.yMax) / (totalBounds.yMax - totalBounds.yMin);
	textureBounds.yMax = atlasEntry.yMax - (atlasEntry.yMax - atlasEntry.yMin) * (sliceBounds.yMin - totalBounds.yMin) / (totalBounds.yMax - totalBounds.yMin);
	return textureBounds;
}

static void addDrawnTile(TileMapVisual * self, TileMapVisual_drawnTile drawnTile) {
	if (self->drawnTileCount >= self->drawnTileAllocatedCount) {
		self->drawnTileAllocatedCount += self->drawnTileAllocatedCount + (self->drawnTileAllocatedCount == 0) * 64;
		self->drawnTiles = realloc(self->drawnTiles, self->drawnTileAllocatedCount * sizeof(*self->drawnTiles));
	}
	drawnTile.listOrder = self->nextListOrder++;
	self->drawnTiles[self->drawnTileCount++] = drawnTile;
}

static void addTileImage(TileMapVisual * self, TileEditData * tile, ImageCollectionEntry * imageEntry, Vector2i tilePosition) {
	if (tile == NULL || imageEntry == NULL) {
		return;
	}
	Rect4i tileBounds = Rect4i_fromPositionAndSize(tile->offset, VECTOR2i(imageEntry->size.x, imageEntry->size.y));
	TileMapVisual_drawnTile drawnTile = {.tilePosition = tilePosition, .shape = TILE_SHAPE_QUAD};
	drawnTile.size.x = tileBounds.xMax - tileBounds.xMin;
	drawnTile.size.y = tileBounds.yMax - tileBounds.yMin;
	drawnTile.offset.x = tileBounds.xMin;
	drawnTile.offset.y = tileBounds.yMin;
	drawnTile.textureBounds = imageEntry->atlasEntry;
	drawnTile.tileID = tile->identifier;
	drawnTile.drawPriority = 0;
	addDrawnTile(self, drawnTile);
}

void TileMapVisual_addTileInstanceGrid(TileMapVisual * self, TileInstanceGrid * tileGrid, Vector2i tileOffset) {
	Vector2u gridSize = tileGrid->size;
	TileInstance * tileInstances = tileGrid->tileInstances;
	for (unsigned int rowIndex = 0; rowIndex < gridSize.y; rowIndex++) {
		for (unsigned int columnIndex = 0; columnIndex < gridSize.x; columnIndex++) {
			Vector2i tilePosition = {tileOffset.x + columnIndex, tileOffset.y + rowIndex};
			TileEditData * tileData = TilesetEditData_getTile(self->tileset, tileInstances[rowIndex * gridSize.x + columnIndex].tileID);
			ImageCollectionEntry * imageEntry = NULL;
			if (tileData != NULL) {
				imageEntry = ImageCollection_getImage(self->imageCollection, tileData->imageID);
			}
			addTileImage(self, tileData, imageEntry, tilePosition);
		}
	}
}

static void addTileSlice(TileMapVisual * self, unsigned int relativeOriginX, unsigned int relativeOriginY, TileEditData * tile, ImageCollectionEntry * imageEntry, TileMapVisual_shape shape, Vector2i tilePosition) {
	if (tile == NULL || imageEntry == NULL) {
		return;
	}
	Vector2u tileSize = self->tileset->tileSize;
	TileMapVisual_drawnTile drawnTile = {.tilePosition = {tilePosition.x - relativeOriginX, tilePosition.y - relativeOriginY}, .shape = shape};
	Rect4i tileImageTotalBounds = Rect4i_fromPositionAndSize(tile->offset, VECTOR2i(imageEntry->size.x, imageEntry->size.y));
	Rect4i tileImageSlicedBounds = tileImageTotalBounds;
	if (relativeOriginX == 1) {
		if (tileImageSlicedBounds.xMin < (int) (tileSize.x - tileSize.x / 2)) {
			tileImageSlicedBounds.xMin = tileSize.x - tileSize.x / 2;
		}
	} else {
		if (tileImageSlicedBounds.xMax > (int) (tileSize.x - tileSize.x / 2)) {
			tileImageSlicedBounds.xMax = tileSize.x - tileSize.x / 2;
		}
	}
	if (relativeOriginY == 1) {
		if (tileImageSlicedBounds.yMin < (int) (tileSize.y - tileSize.y / 2)) {
			tileImageSlicedBounds.yMin = tileSize.y - tileSize.y / 2;
		}
	} else {
		if (tileImageSlicedBounds.yMax > (int) (tileSize.y - tileSize.y / 2)) {
			tileImageSlicedBounds.yMax = tileSize.y - tileSize.y / 2;
		}
	}
	if (tileImageSlicedBounds.xMax <= tileImageSlicedBounds.xMin || tileImageSlicedBounds.yMax <= tileImageSlicedBounds.yMin) {
		return;
	}
	drawnTile.size.x = tileImageSlicedBounds.xMax - tileImageSlicedBounds.xMin;
	drawnTile.size.y = tileImageSlicedBounds.yMax - tileImageSlicedBounds.yMin;
	drawnTile.offset.x = tileImageSlicedBounds.xMin;
	drawnTile.offset.y = tileImageSlicedBounds.yMin;
	drawnTile.textureBounds = sliceTextureBounds(imageEntry->atlasEntry, tileImageTotalBounds, tileImageSlicedBounds);
	drawnTile.tileID = tile->identifier;
	drawnTile.drawPriority = 0;
	addDrawnTile(self, drawnTile);
}

static void addTileAdjacencySlice(TileMapVisual * self, unsigned int sliceHalfTileX, unsigned int sliceHalfTileY, unsigned int relativeOriginX, unsigned int relativeOriginY, TileAdjacencyPair * adjacencyPair, ImageCollectionEntry * imageEntry, TileMapVisual_shape shape, Vector2i tilePosition, TileID tileID) {
	Vector2u tileSize = self->tileset->tileSize;
	TileMapVisual_drawnTile drawnTile = {.tilePosition = {tilePosition.x - relativeOriginX, tilePosition.y - relativeOriginY}, .shape = shape};
	Rect4i sliceTotalBounds = Rect4i_fromPositionAndSize(VECTOR2i_ZERO, VECTOR2i(imageEntry->size.x, imageEntry->size.y));
	Rect4i sliceBounds = RECT4i(tileSize.x * sliceHalfTileX / 2, tileSize.x * (sliceHalfTileX + 1) / 2, tileSize.y * sliceHalfTileY / 2, tileSize.y * (sliceHalfTileY + 1) / 2);
	drawnTile.size.x = sliceBounds.xMax - sliceBounds.xMin;
	drawnTile.size.y = sliceBounds.yMax - sliceBounds.yMin;
	drawnTile.offset.x = -drawnTile.size.x * relativeOriginX + tileSize.x * relativeOriginX;
	drawnTile.offset.y = -drawnTile.size.y * relativeOriginY + tileSize.y * relativeOriginY;
	drawnTile.textureBounds = sliceTextureBounds(imageEntry->atlasEntry, sliceTotalBounds, sliceBounds);
	drawnTile.tileID = tileID;
	drawnTile.drawPriority = 0;
	addDrawnTile(self, drawnTile);
}

void TileMapVisual_addTileInstanceGridWithBlendMap(TileMapVisual * self, TileInstanceGrid * tileGrid, TilesetAdjacencyBlendMap * blendMap, Vector2i tileOffset) {
	Vector2u gridSize = tileGrid->size;
	TileInstance * tileInstances = tileGrid->tileInstances;
	unsigned int rowMin = 0, rowMax = gridSize.y, columnMin = 0, columnMax = gridSize.x;
	for (unsigned int rowIndex = rowMin; rowIndex <= rowMax; rowIndex++) {
		TileID tileIDs[4] = {TILE_ID_NONE, TILE_ID_NONE, TILE_ID_NONE, TILE_ID_NONE};
		TileEditData * tiles[4] = {NULL, NULL, NULL, NULL};
		ImageCollectionEntry * tileImageEntries[4] = {NULL, NULL, NULL, NULL};
		for (unsigned int columnIndex = columnMin; columnIndex <= columnMax; columnIndex++) {
			Vector2i tilePosition = {tileOffset.x + columnIndex, tileOffset.y + rowIndex};
			tileIDs[0] = tileIDs[1];
			tileIDs[2] = tileIDs[3];
			tiles[0] = tiles[1];
			tiles[2] = tiles[3];
			tileImageEntries[0] = tileImageEntries[1];
			tileImageEntries[2] = tileImageEntries[3];
			if (columnIndex < gridSize.x) {
				if (rowIndex > 0) {
					tileIDs[1] = tileInstances[(rowIndex - 1) * gridSize.x + columnIndex].tileID;
				}
				if (rowIndex < gridSize.y) {
					tileIDs[3] = tileInstances[rowIndex * gridSize.x + columnIndex].tileID;
				}
				tiles[1] = TilesetEditData_getTile(self->tileset, tileIDs[1]);
				tiles[3] = TilesetEditData_getTile(self->tileset, tileIDs[3]);
				if (tiles[1] == NULL) {
					tileImageEntries[1] = NULL;
				} else {
					tileImageEntries[1] = ImageCollection_getImage(self->imageCollection, tiles[1]->imageID);
				}
				if (tiles[3] == NULL) {
					tileImageEntries[3] = NULL;
				} else {
					tileImageEntries[3] = ImageCollection_getImage(self->imageCollection, tiles[3]->imageID);
				}
			} else {
				tileIDs[1] = tileIDs[3] = TILE_ID_NONE;
				tiles[1] = tiles[3] = NULL;
				tileImageEntries[1] = tileImageEntries[3] = NULL;
			}
			TileAdjacencyPair * adjacencies[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
			if (blendMap != NULL) {
				if (rowIndex > 0 && columnIndex > 0 && columnIndex < gridSize.x) {
					adjacencies[0] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[0], tileIDs[1], NULL);
				}
				if (rowIndex > 0 && rowIndex < gridSize.y) {
					if (columnIndex > 0) {
						adjacencies[1] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[0], tileIDs[2], NULL);
						if (columnIndex < gridSize.x) {
							adjacencies[2] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[0], tileIDs[3], NULL);
							adjacencies[3] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[1], tileIDs[2], NULL);
						}
					}
					if (columnIndex < gridSize.x) {
						adjacencies[4] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[1], tileIDs[3], NULL);
					}
				}
				if (rowIndex < gridSize.y && columnIndex > 0 && columnIndex < gridSize.x) {
					adjacencies[5] = TilesetAdjacencyBlendMap_getTileAdjacencyPair(blendMap, tileIDs[2], tileIDs[3], NULL);
				}
			}
			ImageCollectionEntry * adjacencyImageEntries[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
			for (unsigned int adjacencyIndex = 0; adjacencyIndex < 6; adjacencyIndex++) {
				if (adjacencies[adjacencyIndex] != NULL) {
					adjacencyImageEntries[adjacencyIndex] = ImageCollection_getImage(self->imageCollection, adjacencies[adjacencyIndex]->sliceImageID);
					if (adjacencyImageEntries[adjacencyIndex] == NULL) {
						adjacencies[adjacencyIndex] = NULL;
					}
				}
			}
			
			if (columnIndex > columnMin && rowIndex > rowMin) {
				// Bottom right corner of above left tile
				if (adjacencies[0] != NULL && adjacencies[1] == adjacencies[0] && adjacencies[2] == adjacencies[0]) {
					if (adjacencies[0]->tileID1 == tileIDs[0]) {
						// Ab
						// bb
						addTileAdjacencySlice(self, 0, 2, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					} else {
						// Ba
						// aa
						addTileAdjacencySlice(self, 6, 4, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					}
				} else if (adjacencies[2] != NULL && adjacencies[2] == adjacencies[4] && adjacencies[2] == adjacencies[5]) {
					if (adjacencies[2]->tileID1 == tileIDs[0]) {
						// Aa
						// ab
						addTileAdjacencySlice(self, 4, 2, 1, 1, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					} else {
						// Bb
						// ba
						addTileAdjacencySlice(self, 2, 4, 1, 1, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					}
				} else if (adjacencies[0] != NULL && adjacencies[3] == adjacencies[0] && adjacencies[4] == adjacencies[0]) {
					if (adjacencies[0]->tileID1 == tileIDs[0]) {
						// Ab
						// aa
						addTileAdjacencySlice(self, 4, 4, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					} else {
						// Ba
						// bb
						addTileAdjacencySlice(self, 2, 2, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					}
				} else if (adjacencies[1] != NULL && adjacencies[3] == adjacencies[1] && adjacencies[5] == adjacencies[1]) {
					if (adjacencies[1]->tileID1 == tileIDs[0]) {
						// Aa
						// ba
						addTileAdjacencySlice(self, 6, 2, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					} else {
						// Bb
						// ab
						addTileAdjacencySlice(self, 0, 4, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
					}
				} else if (adjacencies[0] != NULL && adjacencies[1] != NULL) {
					if (adjacencies[1] != adjacencies[0]) {
						// Ab
						// ca
						if (adjacencies[0]->tileID1 == tileIDs[0]) {
							addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]);
						} else {
							addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]);
						}
						if (adjacencies[1]->tileID1 == tileIDs[0]) {
							addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]);
						} else {
							addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]);
						}
					} else {
						if (adjacencies[0]->tileID1 == tileIDs[0]) {
							// Ab
							// ba
							if (tileIDs[0] == tileIDs[3]) {
								addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
							} else {
								addTileAdjacencySlice(self, 6, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
							}
						} else {
							// Ba
							// ab
							if (adjacencies[0] != adjacencies[1] || tileIDs[0] == adjacencies[0]->tileID1) {
								addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
							} else {
								addTileAdjacencySlice(self, 0, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
							}
						}
					}
				} else if (adjacencies[0] != NULL) {
					if (adjacencies[1] != NULL) {
						if (adjacencies[0]->tileID1 == tileIDs[0]) {
							// Ab
							// c?
							addTileAdjacencySlice(self, 0, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]);
						} else {
							// Ba
							// c?
							addTileAdjacencySlice(self, 2, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]);
						}
						if (adjacencies[1]->tileID1 == tileIDs[0]) {
							// Ab
							// c?
							addTileAdjacencySlice(self, 6, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]);
						} else {
							// Ba
							// c?
							addTileAdjacencySlice(self, 4, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]);
						}
					} else {
						if (tileIDs[3] == tileIDs[0]) {
							if (adjacencies[0]->tileID1 == tileIDs[0]) {
								// Ab
								// ?a
								addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]);
								addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition);
							} else {
								// Ba
								// ?b
								addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[0]);
								addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition);
							}
						} else {
							if (adjacencies[0]->tileID1 == tileIDs[0]) {
								// Ab
								// ab
								addTileAdjacencySlice(self, 0, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
							} else {
								// Ba
								// ba
								addTileAdjacencySlice(self, 2, 0, 1, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
							}
						}
					}
				} else if (adjacencies[1] != NULL) {
					if (tileIDs[3] == tileIDs[0]) {
						if (adjacencies[1]->tileID1 == tileIDs[0]) {
							// A?
							// ba
							addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition);
							addTileAdjacencySlice(self, 2, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]);
						} else {
							// B?
							// ab
							addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition);
							addTileAdjacencySlice(self, 4, 6, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[0]);
						}
					} else {
						if (adjacencies[1]->tileID1 == tileIDs[0]) {
							// Aa
							// bb
							addTileAdjacencySlice(self, 6, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
						} else {
							// Bb
							// aa
							addTileAdjacencySlice(self, 4, 0, 1, 1, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[0]);
						}
					}
				} else if (tileImageEntries[0] != NULL) {
					addTileSlice(self, 1, 1, tiles[0], tileImageEntries[0], TILE_SHAPE_QUAD, tilePosition);
				}
			}
			
			if (columnIndex < columnMax && rowIndex > rowMin) {
				// Bottom left corner of above right tile
				if (adjacencies[0] != NULL && adjacencies[4] == adjacencies[0] && adjacencies[3] == adjacencies[0]) {
					if (adjacencies[0]->tileID1 == tileIDs[1]) {
						// bA
						// bb
						addTileAdjacencySlice(self, 3, 2, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					} else {
						// aB
						// aa
						addTileAdjacencySlice(self, 5, 4, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					}
				} else if (adjacencies[3] != NULL && adjacencies[3] == adjacencies[1] && adjacencies[3] == adjacencies[5]) {
					if (adjacencies[3]->tileID1 == tileIDs[1]) {
						// aA
						// ba
						addTileAdjacencySlice(self, 7, 2, 0, 1, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					} else {
						// bB
						// ab
						addTileAdjacencySlice(self, 1, 4, 0, 1, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					}
				} else if (adjacencies[0] != NULL && adjacencies[2] == adjacencies[0] && adjacencies[1] == adjacencies[0]) {
					if (adjacencies[0]->tileID1 == tileIDs[1]) {
						// bA
						// aa
						addTileAdjacencySlice(self, 7, 4, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					} else {
						// aB
						// bb
						addTileAdjacencySlice(self, 1, 2, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					}
				} else if (adjacencies[4] != NULL && adjacencies[2] == adjacencies[4] && adjacencies[5] == adjacencies[4]) {
					if (adjacencies[4]->tileID1 == tileIDs[1]) {
						// aA
						// ab
						addTileAdjacencySlice(self, 5, 2, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					} else {
						// bB
						// ba
						addTileAdjacencySlice(self, 3, 4, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
					}
				} else if (adjacencies[0] != NULL && adjacencies[4] != NULL) {
					if (adjacencies[4] != adjacencies[0]) {
						// bA
						// ac
						if (adjacencies[0]->tileID1 == tileIDs[1]) {
							addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]);
						} else {
							addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]);
						}
						if (adjacencies[4]->tileID1 == tileIDs[1]) {
							addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]);
						} else {
							addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]);
						}
					} else {
						if (adjacencies[0]->tileID1 == tileIDs[1]) {
							// bA
							// ab
							if (tileIDs[1] == tileIDs[2]) {
								addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
							} else {
								addTileAdjacencySlice(self, 5, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
							}
						} else {
							// aB
							// ba
							if (adjacencies[0] != adjacencies[4] || tileIDs[1] == adjacencies[0]->tileID1) {
								addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
							} else {
								addTileAdjacencySlice(self, 3, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
							}
						}
					}
				} else if (adjacencies[0] != NULL) {
					if (adjacencies[4] != NULL) {
						if (adjacencies[0]->tileID1 == tileIDs[1]) {
							// bA
							// ?c
							addTileAdjacencySlice(self, 3, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]);
						} else {
							// aB
							// ?c
							addTileAdjacencySlice(self, 0, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]);
						}
						if (adjacencies[4]->tileID1 == tileIDs[1]) {
							// bA
							// ?c
							addTileAdjacencySlice(self, 7, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]);
						} else {
							// aB
							// ?c
							addTileAdjacencySlice(self, 5, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]);
						}
					} else {
						if (tileIDs[2] == tileIDs[1]) {
							if (adjacencies[0]->tileID1 == tileIDs[1]) {
								// bA
								// a?
								addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]);
								addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition);
							} else {
								// aB
								// b?
								addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[1]);
								addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition);
							}
						} else {
							if (adjacencies[0]->tileID1 == tileIDs[1]) {
								// bA
								// ba
								addTileAdjacencySlice(self, 3, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
							} else {
								// aB
								// ab
								addTileAdjacencySlice(self, 1, 0, 0, 1, adjacencies[0], adjacencyImageEntries[0], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
							}
						}
					}
				} else if (adjacencies[4] != NULL) {
					if (tileIDs[2] == tileIDs[1]) {
						if (adjacencies[4]->tileID1 == tileIDs[1]) {
							// ?A
							// ab
							addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition);
							addTileAdjacencySlice(self, 1, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]);
						} else {
							// ?B
							// ba
							addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition);
							addTileAdjacencySlice(self, 7, 6, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[1]);
						}
					} else {
						if (adjacencies[4]->tileID1 == tileIDs[1]) {
							// aA
							// bb
							addTileAdjacencySlice(self, 7, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
						} else {
							// Bb
							// aa
							addTileAdjacencySlice(self, 5, 0, 0, 1, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[1]);
						}
					}
				} else if (tileImageEntries[1] != NULL) {
					addTileSlice(self, 0, 1, tiles[1], tileImageEntries[1], TILE_SHAPE_QUAD, tilePosition);
				}
			}
			
			if (columnIndex > columnMin && rowIndex < rowMax) {
				// Top right corner of below left tile
				if (adjacencies[5] != NULL && adjacencies[1] == adjacencies[5] && adjacencies[3] == adjacencies[5]) {
					if (adjacencies[5]->tileID1 == tileIDs[2]) {
						// bb
						// Ab
						addTileAdjacencySlice(self, 0, 5, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					} else {
						// aa
						// Ba
						addTileAdjacencySlice(self, 6, 3, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					}
				} else if (adjacencies[3] != NULL && adjacencies[3] == adjacencies[4] && adjacencies[3] == adjacencies[0]) {
					if (adjacencies[3]->tileID1 == tileIDs[3]) {
						// ab
						// Aa
						addTileAdjacencySlice(self, 4, 5, 1, 0, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					} else {
						// ba
						// Bb
						addTileAdjacencySlice(self, 2, 3, 1, 0, adjacencies[3], adjacencyImageEntries[3], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					}
				} else if (adjacencies[5] != NULL && adjacencies[2] == adjacencies[5] && adjacencies[4] == adjacencies[5]) {
					if (adjacencies[5]->tileID1 == tileIDs[2]) {
						// aa
						// Ab
						addTileAdjacencySlice(self, 4, 3, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					} else {
						// bb
						// Ba
						addTileAdjacencySlice(self, 2, 5, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					}
				} else if (adjacencies[1] != NULL && adjacencies[2] == adjacencies[1] && adjacencies[0] == adjacencies[1]) {
					if (adjacencies[1]->tileID1 == tileIDs[2]) {
						// ba
						// Aa
						addTileAdjacencySlice(self, 6, 5, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					} else {
						// ab
						// Bb
						addTileAdjacencySlice(self, 0, 3, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
					}
				} else if (adjacencies[5] != NULL && adjacencies[1] != NULL) {
					if (adjacencies[1] != adjacencies[5]) {
						// ca
						// Ab
						if (adjacencies[1]->tileID1 == tileIDs[2]) {
							addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]);
						} else {
							addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]);
						}
						if (adjacencies[5]->tileID1 == tileIDs[2]) {
							addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]);
						} else {
							addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]);
						}
					} else {
						if (adjacencies[5]->tileID1 == tileIDs[2]) {
							// ba
							// Ab
							if (tileIDs[2] == tileIDs[1]) {
								addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
							} else {
								addTileAdjacencySlice(self, 4, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
							}
						} else {
							// ab
							// Ba
							if (adjacencies[5] != adjacencies[1] || tileIDs[2] == adjacencies[5]->tileID1) {
								addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
							} else {
								addTileAdjacencySlice(self, 2, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
							}
						}
					}
				} else if (adjacencies[5] != NULL) {
					if (adjacencies[1] != NULL) {
						if (adjacencies[1]->tileID1 == tileIDs[2]) {
							// c?
							// Ab
							addTileAdjacencySlice(self, 4, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]);
						} else {
							// c?
							// Ba
							addTileAdjacencySlice(self, 6, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]);
						}
						if (adjacencies[5]->tileID1 == tileIDs[2]) {
							// c?
							// Ab
							addTileAdjacencySlice(self, 0, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]);
						} else {
							// c?
							// Ba
							addTileAdjacencySlice(self, 2, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]);
						}
					} else {
						if (tileIDs[1] == tileIDs[2]) {
							if (adjacencies[5]->tileID1 == tileIDs[2]) {
								// ?a
								// Ab
								addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition);
								addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]);
							} else {
								// ?b
								// Ba
								addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition);
								addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition, tileIDs[2]);
							}
						} else {
							if (adjacencies[5]->tileID1 == tileIDs[2]) {
								// ab
								// Ab
								addTileAdjacencySlice(self, 0, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
							} else {
								// ba
								// Ba
								addTileAdjacencySlice(self, 2, 1, 1, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
							}
						}
					}
				} else if (adjacencies[1] != NULL) {
					if (tileIDs[1] == tileIDs[2]) {
						if (adjacencies[1]->tileID1 == tileIDs[2]) {
							// A?
							// ba
							addTileAdjacencySlice(self, 0, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]);
							addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition);
						} else {
							// B?
							// ab
							addTileAdjacencySlice(self, 6, 7, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_TRIANGLE_UPPER_LEFT, tilePosition, tileIDs[2]);
							addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_TRIANGLE_LOWER_RIGHT, tilePosition);
						}
					} else {
						if (adjacencies[1]->tileID1 == tileIDs[2]) {
							// bb
							// Aa
							addTileAdjacencySlice(self, 4, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
						} else {
							// Bb
							// aa
							addTileAdjacencySlice(self, 6, 1, 1, 0, adjacencies[1], adjacencyImageEntries[1], TILE_SHAPE_QUAD, tilePosition, tileIDs[2]);
						}
					}
				} else if (tileImageEntries[2] != NULL) {
					addTileSlice(self, 1, 0, tiles[2], tileImageEntries[2], TILE_SHAPE_QUAD, tilePosition);
				}
			}
			
			if (columnIndex < columnMax && rowIndex < rowMax) {
				// Top left corner of below right tile
				if (adjacencies[5] != NULL && adjacencies[4] == adjacencies[5] && adjacencies[2] == adjacencies[5]) {
					if (adjacencies[5]->tileID1 == tileIDs[3]) {
						// bb
						// bA
						addTileAdjacencySlice(self, 3, 5, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					} else {
						// aa
						// aB
						addTileAdjacencySlice(self, 5, 3, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					}
				} else if (adjacencies[2] != NULL && adjacencies[2] == adjacencies[1] && adjacencies[2] == adjacencies[0]) {
					if (adjacencies[2]->tileID1 == tileIDs[3]) {
						// ba
						// aA
						addTileAdjacencySlice(self, 7, 5, 0, 0, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					} else {
						// ab
						// bB
						addTileAdjacencySlice(self, 1, 3, 0, 0, adjacencies[2], adjacencyImageEntries[2], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					}
				} else if (adjacencies[5] != NULL && adjacencies[3] == adjacencies[5] && adjacencies[1] == adjacencies[5]) {
					if (adjacencies[5]->tileID1 == tileIDs[3]) {
						// aa
						// bA
						addTileAdjacencySlice(self, 7, 3, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					} else {
						// bb
						// aB
						addTileAdjacencySlice(self, 1, 5, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					}
				} else if (adjacencies[4] != NULL && adjacencies[3] == adjacencies[4] && adjacencies[0] == adjacencies[4]) {
					if (adjacencies[4]->tileID1 == tileIDs[3]) {
						// ab
						// aA
						addTileAdjacencySlice(self, 5, 5, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					} else {
						// ba
						// bB
						addTileAdjacencySlice(self, 3, 3, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
					}
				} else if (adjacencies[5] != NULL && adjacencies[4] != NULL) {
					if (adjacencies[4] != adjacencies[5]) {
						// ac
						// bA
						if (adjacencies[4]->tileID1 == tileIDs[3]) {
							addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]);
						} else {
							addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]);
						}
						if (adjacencies[5]->tileID1 == tileIDs[3]) {
							addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]);
						} else {
							addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]);
						}
					} else {
						if (adjacencies[5]->tileID1 == tileIDs[3]) {
							// ab
							// bA
							if (tileIDs[3] == tileIDs[0]) {
								addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
							} else {
								addTileAdjacencySlice(self, 7, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
							}
						} else {
							// ba
							// aB
							if (adjacencies[5] != adjacencies[4] || tileIDs[3] == adjacencies[5]->tileID1) {
								addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
							} else {
								addTileAdjacencySlice(self, 1, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
							}
						}
					}
				} else if (adjacencies[5] != NULL) {
					if (adjacencies[4] != NULL) {
						if (adjacencies[4]->tileID1 == tileIDs[3]) {
							// ?c
							// bA
							addTileAdjacencySlice(self, 5, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]);
						} else {
							// ?c
							// aB
							addTileAdjacencySlice(self, 7, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]);
						}
						if (adjacencies[5]->tileID1 == tileIDs[3]) {
							// ?c
							// bA
							addTileAdjacencySlice(self, 3, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]);
						} else {
							// ?c
							// aB
							addTileAdjacencySlice(self, 1, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]);
						}
					} else {
						if (tileIDs[0] == tileIDs[3]) {
							if (adjacencies[5]->tileID1 == tileIDs[3]) {
								// a?
								// bA
								addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition);
								addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]);
							} else {
								// b?
								// aB
								addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition);
								addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition, tileIDs[3]);
							}
						} else {
							if (adjacencies[5]->tileID1 == tileIDs[3]) {
								// ba
								// bA
								addTileAdjacencySlice(self, 3, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
							} else {
								// ab
								// aB
								addTileAdjacencySlice(self, 1, 1, 0, 0, adjacencies[5], adjacencyImageEntries[5], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
							}
						}
					}
				} else if (adjacencies[4] != NULL) {
					if (tileIDs[0] == tileIDs[3]) {
						if (adjacencies[4]->tileID1 == tileIDs[3]) {
							// ab
							// ?A
							addTileAdjacencySlice(self, 3, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]);
							addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition);
						} else {
							// ba
							// ?B
							addTileAdjacencySlice(self, 5, 7, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_TRIANGLE_UPPER_RIGHT, tilePosition, tileIDs[3]);
							addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_TRIANGLE_LOWER_LEFT, tilePosition);
						}
					} else {
						if (adjacencies[4]->tileID1 == tileIDs[3]) {
							// bb
							// aA
							addTileAdjacencySlice(self, 5, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
						} else {
							// Bb
							// aa
							addTileAdjacencySlice(self, 7, 1, 0, 0, adjacencies[4], adjacencyImageEntries[4], TILE_SHAPE_QUAD, tilePosition, tileIDs[3]);
						}
					}
				} else if (tileImageEntries[3] != NULL) {
					addTileSlice(self, 0, 0, tiles[3], tileImageEntries[3], TILE_SHAPE_QUAD, tilePosition);
				}
			}
		}
	}
}

static void addTileImageWithAdjacencyBehaviors(TileMapVisual * self, TileID tileID, TileEditData * tile, ImageCollectionEntry * imageEntry, Vector2i tilePosition, unsigned int behaviorCount, TileAdjacencyBehavior ** behaviors) {
	if (tile != NULL && imageEntry != NULL) {
		Rect4i tileBounds = Rect4i_fromPositionAndSize(tile->offset, VECTOR2i(imageEntry->size.x, imageEntry->size.y));
		Rect4i sliceInsets = behaviors[0]->sliceInsets;
		for (unsigned int behaviorIndex = 1; behaviorIndex < behaviorCount; behaviorIndex++) {
			sliceInsets.xMin = fmaxf(sliceInsets.xMin, behaviors[behaviorIndex]->sliceInsets.xMin);
			sliceInsets.xMax = fmaxf(sliceInsets.xMax, behaviors[behaviorIndex]->sliceInsets.xMax);
			sliceInsets.yMin = fmaxf(sliceInsets.yMin, behaviors[behaviorIndex]->sliceInsets.yMin);
			sliceInsets.yMax = fmaxf(sliceInsets.yMax, behaviors[behaviorIndex]->sliceInsets.yMax);
		}
		Rect4i tileSlicedBounds = Rect4i_insetMargins(tileBounds, sliceInsets);
		if (tileSlicedBounds.xMax > tileSlicedBounds.xMin && tileSlicedBounds.yMax > tileSlicedBounds.yMin) {
			TileMapVisual_drawnTile drawnTile = {.tilePosition = tilePosition, .shape = TILE_SHAPE_QUAD};
			drawnTile.size.x = tileSlicedBounds.xMax - tileSlicedBounds.xMin;
			drawnTile.size.y = tileSlicedBounds.yMax - tileSlicedBounds.yMin;
			drawnTile.offset.x = tileSlicedBounds.xMin;
			drawnTile.offset.y = tileSlicedBounds.yMin;
			drawnTile.textureBounds = sliceTextureBounds(imageEntry->atlasEntry, tileBounds, tileSlicedBounds);
			drawnTile.tileID = tileID;
			drawnTile.drawPriority = 0;
			addDrawnTile(self, drawnTile);
		}
	}
	for (unsigned int behaviorIndex = 0; behaviorIndex < behaviorCount; behaviorIndex++) {
		for (unsigned int overlayIndex = 0; overlayIndex < behaviors[behaviorIndex]->overlayCount; overlayIndex++) {
			TileAdjacencyOverlay overlay = behaviors[behaviorIndex]->overlays[overlayIndex];
			ImageCollectionEntry * overlayImageEntry = ImageCollection_getImage(self->imageCollection, overlay.imageID);
			if (overlayImageEntry != NULL) {
				Rect4i overlayBounds = Rect4i_fromPositionAndSize(overlay.offset, VECTOR2i(overlayImageEntry->size.x, overlayImageEntry->size.y));
				Rect4i overlaySlicedBounds = Rect4i_insetMargins(overlayBounds, overlay.sliceInsets);
				if (overlaySlicedBounds.xMax > overlaySlicedBounds.xMin && overlaySlicedBounds.yMax > overlaySlicedBounds.yMin) {
					TileMapVisual_drawnTile drawnTile = {.tilePosition = tilePosition, .shape = TILE_SHAPE_QUAD};
					drawnTile.size.x = overlaySlicedBounds.xMax - overlaySlicedBounds.xMin;
					drawnTile.size.y = overlaySlicedBounds.yMax - overlaySlicedBounds.yMin;
					drawnTile.offset.x = overlaySlicedBounds.xMin;
					drawnTile.offset.y = overlaySlicedBounds.yMin;
					drawnTile.textureBounds = sliceTextureBounds(overlayImageEntry->atlasEntry, overlayBounds, overlaySlicedBounds);
					drawnTile.tileID = tileID;
					drawnTile.drawPriority = overlay.drawPriority;
					addDrawnTile(self, drawnTile);
				}
			}
		}
	}
}

void TileMapVisual_addTileInstanceGridWithBehaviorSet(TileMapVisual * self, TileAdjacencyBehaviorSet * behaviorSet, Rect4i tileRect, TileQueryCallback queryCallback, void * callbackContext) {
	for (int rowIndex = tileRect.yMin; rowIndex < tileRect.yMax; rowIndex++) {
		for (int columnIndex = tileRect.xMin; columnIndex < tileRect.xMax; columnIndex++) {
			Vector2i tilePosition = {columnIndex, rowIndex};
			TileID tileID = queryCallback(tilePosition, callbackContext);
			TileEditData * tileData = TilesetEditData_getTile(self->tileset, tileID);
			ImageCollectionEntry * imageEntry = NULL;
			if (tileData != NULL) {
				imageEntry = ImageCollection_getImage(self->imageCollection, tileData->imageID);
			}
			TileAdjacencyBehavior * behaviors[64];
			unsigned int behaviorCount = TileAdjacencyBehaviorSet_getApplicableBehaviors(behaviorSet, tilePosition, tileID, queryCallback, callbackContext, sizeof_count(behaviors), behaviors);
			if (behaviorCount == 0) {
				addTileImage(self, tileData, imageEntry, tilePosition);
			} else {
				addTileImageWithAdjacencyBehaviors(self, tileID, tileData, imageEntry, tilePosition, behaviorCount, behaviors);
			}
		}
	}
}

void TileMapVisual_clear(TileMapVisual * self) {
	self->drawnTileCount = 0;
	self->nextListOrder = 0;
}

Rect4i TileMapVisual_calculateExtendedBounds(TileMapVisual * self) {
	Rect4i result = RECT4i_EMPTY;
	Vector2u tileSize = self->tileset->tileSize;
	for (unsigned int drawnTileIndex = 0; drawnTileIndex < self->drawnTileCount; drawnTileIndex++) {
		TileMapVisual_drawnTile drawnTile = self->drawnTiles[drawnTileIndex];
		Rect4i tileRect;
		tileRect.xMin = drawnTile.tilePosition.x * tileSize.x + drawnTile.offset.x;
		tileRect.yMin = drawnTile.tilePosition.y * tileSize.y + drawnTile.offset.y;
		tileRect.xMax = tileRect.xMin + drawnTile.size.x;
		tileRect.yMax = tileRect.yMin + drawnTile.size.y;
		result = Rect4i_union(tileRect, result);
	}
	return result;
}

static int compareDrawnTiles(const void * lhsUntyped, const void * rhsUntyped) {
	const TileMapVisual_drawnTile * lhs = lhsUntyped, * rhs = rhsUntyped;
	if (lhs->drawPriority != rhs->drawPriority) {
		return (lhs->drawPriority > rhs->drawPriority) * 2 - 1;
	}
	if (lhs->tilePosition.y != rhs->tilePosition.y) {
		return (lhs->tilePosition.y > rhs->tilePosition.y) * 2 - 1;
	}
	if (lhs->tilePosition.x != rhs->tilePosition.x) {
		return (lhs->tilePosition.x > rhs->tilePosition.x) * 2 - 1;
	}
	return (lhs->listOrder > rhs->listOrder) * 2 - 1;
}

void TileMapVisual_sortByDrawPriority(TileMapVisual * self) {
	qsort(self->drawnTiles, self->drawnTileCount, sizeof(self->drawnTiles[0]), compareDrawnTiles);
}
