/********************************************************************
	Minecraft: Pocket Edition - Decompilation Project
	Copyright (C) 2023 iProgramInCpp
	
	The following code is licensed under the BSD 1 clause license.
	SPDX-License-Identifier: BSD-1-Clause
 ********************************************************************/

#include "world/level/Level.hpp"

bool LevelChunk::touchedSky = false;

LevelChunk::~LevelChunk()
{
	SAFE_DELETE(m_lightBlk);
	SAFE_DELETE(m_lightSky);
	SAFE_DELETE(m_tileData);
}

constexpr int MakeBlockDataIndex (int x, int y, int z)
{
	return (x << 11) | (z << 7) | y;
}

constexpr int MakeHeightMapIndex(int x, int z)
{
	return x | (z << 4);
}

// Temp function for checking oob accesses into levelchunks
void CheckPosition(int x, int y, int z)
{
	assert(x >= 0 && y >= 0 && z >= 0 && x < 16 && z < 16 && y < 128);
}

void LevelChunk::_init()
{
	field_4 = 0;
	m_bLoaded = false;
	m_tileData = nullptr;
	m_tileDataCnt = 0;
	m_lightSky = nullptr;
	m_lightSkyCnt = 0;
	m_lightBlk = nullptr;
	m_lightBlkCnt = 0;
	m_chunkX = 0;
	m_chunkZ = 0;
	field_234 = 0;
	m_bUnsaved = false;
	field_236 = 0;
	field_237 = 0;
	field_238 = 0;
	field_23C = 0;
	m_pBlockData = nullptr;
}

LevelChunk::LevelChunk(Level* pLevel, int x, int z)
{
	_init();

	m_pLevel = pLevel;
	m_chunkX = x;
	m_chunkZ = z;

	init();
}

LevelChunk::LevelChunk(Level* pLevel, TileID* pData, int x, int z)
{
	_init();

	m_pLevel = pLevel;

	// The game will blow its brains out if this if statement is executed
	// I have not the slightest idea as to why...
	/*if (pData)
	{*/
	m_tileDataCnt = 0x4000;
	field_4 = 0x8000;
	m_tileData = new uint8_t[m_tileDataCnt];
	memset(m_tileData, 0, m_tileDataCnt);

	//Space saving measure: Store 2 blocks' light field instead of only one block's, per byte.
	m_lightSkyCnt = 16 * 16 * 128 / 2;
	m_lightSky = new uint8_t[m_lightSkyCnt];
	memset(m_lightSky, 0, m_lightSkyCnt);

	m_lightBlkCnt = 16 * 16 * 128 / 2;
	m_lightBlk = new uint8_t[m_lightBlkCnt];
	memset(m_lightBlk, 0, m_lightBlkCnt);

	m_pBlockData = pData;
	//}

	m_pBlockData = pData;
	m_chunkX = x;
	m_chunkZ = z;

	init();
}

void LevelChunk::init()
{
	field_234 = 0;
	m_bUnsaved = false;
	field_237 = 0;
	field_238 = 0;
	field_23C = 0;
	memset(m_heightMap, 0, sizeof m_heightMap);
	memset(m_updateMap, 0, sizeof m_updateMap);
}

void LevelChunk::unload()
{
	m_bLoaded = false;
}

bool LevelChunk::isAt(int x, int z)
{
	return m_chunkX == x && m_chunkZ == z;
}

int LevelChunk::getHeightmap(int x, int z)
{
	CheckPosition(x, 64, z);
	return m_heightMap[MakeHeightMapIndex(x, z)];
}

void LevelChunk::recalcHeightmap()
{
	int x1 = 127;

	for (int i = 0; i < 16; i++)
	{
		for (int j = 0; j < 16; j++)
		{
			int index1 = MakeBlockDataIndex(i, 0, j);
			int index2 = 126;

			int x2 = 0;
			while (!Tile::lightBlock[m_pBlockData[index1 + index2]])
			{
				if (index2-- == 0)
				{
					x2 = 0;
					goto label_7;
				}
			}

			x2 = index2 + 1;
		label_7:
			if (x1 >= x2)
				x1 = x2;
			m_heightMap[MakeHeightMapIndex(i, j)] = x2;

			if (!m_pLevel->m_pDimension->field_E)
			{
				int x4 = 15;
				for (int x3 = 127; x3 > 0; x3--)
				{
					x4 -= Tile::lightBlock[m_pBlockData[index1 + x3]];
					if (x4 <= 0)
						break;

					int x = x3 + index1;
					int index = x >> 1, offs = x & 1;

					if (offs)
						m_lightSky[index] = (m_lightSky[index] & 0x0F) | (x4 << 4); // set the upper 4 bits to x4
					else
						m_lightSky[index] = (m_lightSky[index] & 0xF0) | x4; // set the lower 4 bits to x4
				}
			}
		}
	}

	field_228 = x1;

	for (int i = 0; i < 16; i++)
	{
		for (int j = 0; j < 16; j++)
		{
			lightGaps(i, j);
		}
	}
}

void LevelChunk::recalcHeightmapOnly()
{
	int x1 = 127;

	for (int i = 0; i < 16; i++)
	{
		for (int j = 0; j < 16; j++)
		{
			int index1 = MakeBlockDataIndex(i, 0, j);
			int index2 = 126;

			int x2 = 0;
			while (!Tile::lightBlock[m_pBlockData[index1 + index2]])
			{
				if (index2-- == 0)
				{
					x2 = 0;
					goto label_7;
				}
			}

			x2 = index2 + 1;
		label_7:
			if (x1 >= x2)
				x1 = x2;
			m_heightMap[MakeHeightMapIndex(i, j)] = x2;
		}
	}

	field_228 = x1;
}

void LevelChunk::lightGaps(int x, int z)
{
	CheckPosition(x, 64, z);
	uint8_t heightMap = getHeightmap(x, z);

	int coordX = x + 16 * m_chunkX;
	int coordZ = z + 16 * m_chunkZ;

	lightGap(coordX - 1, coordZ, heightMap);
	lightGap(coordX + 1, coordZ, heightMap);
	lightGap(coordX, coordZ - 1, heightMap);
	lightGap(coordX, coordZ + 1, heightMap);
}

void LevelChunk::lightGap(int gx, int gz, uint8_t heightMap)
{
	// @BUG: This is flawed. getHeightmap calls getChunk which creates another chunk and calls this over
	// and over again, creating a stack overflow. Since the level is limited a stack overflow is only
	// possible on devices with really tight stacks.
	uint8_t currHeightMap = m_pLevel->getHeightmap(gx, gz);
	if (currHeightMap > heightMap)
	{
		m_pLevel->updateLight(LightLayer::Sky, gx, heightMap, gz, gx, currHeightMap, gz);
		return;
	}
	if (currHeightMap < heightMap)
	{
		m_pLevel->updateLight(LightLayer::Sky, gx, currHeightMap, gz, gx, heightMap, gz);
		return;
	}
}

int LevelChunk::getBrightness(const LightLayer& ll, int x, int y, int z)
{
	CheckPosition(x, y, z);

	// why the hell is it doing it like that.
	if (&ll == &LightLayer::Sky)
	{
		int bitIdx = MakeBlockDataIndex(x, y, z);
		int index = bitIdx >> 1, offs = bitIdx & 1;
		if (offs)
			return m_lightSky[index] >> 4;
		else
			return m_lightSky[index] & 0xF;
	}

	if (&ll == &LightLayer::Block)
	{
		int bitIdx = MakeBlockDataIndex(x, y, z);
		int index = bitIdx >> 1, offs = bitIdx & 1;
		if (offs)
			return m_lightBlk[index] >> 4;
		else
			return m_lightBlk[index] & 0xF;
	}

	return 0;
}

void LevelChunk::setBrightness(const LightLayer& ll, int x, int y, int z, int brightness)
{
	CheckPosition(x, y, z);
	// why the hell is it doing it like that.
	if (&ll == &LightLayer::Sky)
	{
		int bitIdx = MakeBlockDataIndex(x, y, z);
		int index = bitIdx >> 1, offs = bitIdx & 1;
		if (offs)
			m_lightSky[index] = (m_lightSky[index] & 0x0F) | (brightness << 4);
		else
			m_lightSky[index] = (m_lightSky[index] & 0xF0) | (brightness & 0xF);

		return;
	}

	if (&ll == &LightLayer::Block)
	{
		int bitIdx = MakeBlockDataIndex(x, y, z);
		int index = bitIdx >> 1, offs = bitIdx & 1;
		if (offs)
			m_lightBlk[index] = (m_lightBlk[index] & 0x0F) | (brightness << 4);
		else
			m_lightBlk[index] = (m_lightBlk[index] & 0xF0) | (brightness & 0xF);

		return;
	}
}

int LevelChunk::getRawBrightness(int x, int y, int z, int skySubtract)
{
	CheckPosition(x, y, z);
	int bitIdx = MakeBlockDataIndex(x, y, z);
	int index = bitIdx >> 1, offs = bitIdx & 1;

	uint8_t bSky, bBlk;
	if (offs)
		bSky = m_lightSky[index] >> 4;
	else
		bSky = m_lightSky[index] & 0xF;

	if (m_lightSky)
		touchedSky = true;

	int br = bSky - skySubtract;

	if (offs)
		bBlk = m_lightBlk[index] >> 4;
	else
		bBlk = m_lightBlk[index] & 0xF;

	// if it's smaller than 0 it'll probably sort itself out
	if (br < bBlk)
		br = bBlk;
	
	return br;
}

void LevelChunk::addEntity(Entity* pEnt)
{
	field_238 = 1;

	int yCoord = int(floorf(pEnt->m_pos.y / 16));
	if (yCoord < 0) yCoord = 0;
	if (yCoord > 7) yCoord = 7;
	pEnt->m_bInAChunk = true;
	pEnt->m_chunkX = m_chunkX;
	pEnt->m_chunkY = yCoord;
	pEnt->m_chunkZ = m_chunkZ;

	m_entities[yCoord].push_back(pEnt);
}

void LevelChunk::clearUpdateMap()
{
	memset(m_updateMap, 0, sizeof m_updateMap);
	m_bUnsaved = 0;
}

void LevelChunk::deleteBlockData()
{
	if (m_pBlockData)
		delete[] m_pBlockData;

	m_pBlockData = nullptr;
}

void LevelChunk::removeEntity(Entity* pEnt)
{
	removeEntity(pEnt, pEnt->m_chunkY);
}

void LevelChunk::removeEntity(Entity* pEnt, int vec)
{
	if (vec < 0) vec = 0;
	if (vec > 7) vec = 7;

	std::vector<Entity*>::iterator it = std::find(m_entities[vec].begin(), m_entities[vec].end(), pEnt);

	if (it != m_entities[vec].end())
		m_entities[vec].erase(it);
}

bool LevelChunk::isSkyLit(int x, int y, int z)
{
	CheckPosition(x, 64, z);

	return m_heightMap[MakeHeightMapIndex(x, z)] <= y;
}

void LevelChunk::lightLava()
{

}

void LevelChunk::recalcBlockLights()
{

}

void LevelChunk::recalcHeight(int x, int y, int z)
{
	CheckPosition(x, 64, z);

	uint8_t& hmap = m_heightMap[MakeHeightMapIndex(x, z)];

	int index = MakeBlockDataIndex(x, 0, z);

	int x1 = hmap;
	if (x1 < y)
		x1 = y;

	if (x1 > 0)
	{
		while (!Tile::lightBlock[m_pBlockData[x1 - 1 + index]] && x1)
		{
			--x1;
		}
	}

	if (x1 != hmap)
	{
		m_pLevel->lightColumnChanged(x, z, x1, hmap);
		hmap = x1;
		if (field_228 <= x1)
		{
			int x6 = 127;
			for (int j = 0; j < 16; j++)
			{
				for (int i = 0; i < 16; i++)
				{
					int x = m_heightMap[MakeHeightMapIndex(j, i)];
					if (x6 >= x)
						x6 = x;
				}
			}

			field_228 = x6;
		}
		else
		{
			field_228 = x1;
		}

		int globalX = x + 16 * m_chunkX;
		int globalZ = z + 16 * m_chunkZ;
		if (hmap <= x1)
		{
			m_pLevel->updateLight(LightLayer::Sky, globalX, hmap, globalZ, globalX, x1, globalZ);

			if (hmap < x1)
			{
				for (int i = 0; i < x1 - hmap; i++)
				{
					int bitIdx = index | (i + hmap);
					int _idx = bitIdx >> 1;
					int _off = bitIdx & 1;

					if (_off)
						m_lightSky[_idx] &= 0xF0;
					else
						m_lightSky[_idx] &= 0x0F;
				}
			}
		}
		else
		{
			for (int i = 0; i < hmap; i++)
			{
				int v15 = (i | index) >> 1;
				//int v16 = (i | index) <<31;

				if ((i | index) & 1)
				{
					m_lightSky[v15] = (m_lightSky[v15] & 0xF0) | 0x0F;
				}
				else
				{
					m_lightSky[v15] = (m_lightSky[v15] & 0x0F) | 0xF0;
				}
			}
		}

		int x2 = x1;
		int x3 = 15;
		while (x3 > 0 && x2 > 0)
		{
			TileID tile = getTile(x, --x2, z);
			int bitIdx = x2 | index;

			int x4 = Tile::lightBlock[tile];
			if (!x4)
				x4 = 1; //@HUH: what is this?

			int x5 = x3 - x4;
			int _idx = bitIdx >> 1;
			int _off = (bitIdx & 1);

			x3 = x5;
			if (x3 < 0)
				x3 = 0;

			if (_off)
				m_lightSky[_idx] = (m_lightSky[_idx] & 0x0F) | ((x3) << 4);
			else
				m_lightSky[_idx] = (m_lightSky[_idx] & 0xF0) | x3;
		}

		if (x2 > 0)
		{
			for (; x2 > 0; x2--)
			{
				if (Tile::lightBlock[getTile(x, x2 - 1, z)])
					break;
			}
		}

		if (x2 != x1)
		{
			m_pLevel->updateLight(LightLayer::Sky, globalX - 1, x2, globalZ - 1, globalX + 1, x1, globalZ + 1);
		}
	}
}

void LevelChunk::skyBrightnessChanged()
{

}

void LevelChunk::load()
{
	m_bLoaded = true;
}

bool LevelChunk::shouldSave(bool b)
{
	if (field_236)
		return false;

	if (!b)
	{
		if (field_238)
		{
			if (field_23C + 599 < m_pLevel->getTime())
				return true;
		}

		return m_bUnsaved;
	}

	if (!field_238 || m_pLevel->getTime() == field_23C)
		return m_bUnsaved;

	return true;
}

void LevelChunk::markUnsaved()
{
	m_bUnsaved = true;
}

TileID LevelChunk::getTile(int x, int y, int z)
{
	CheckPosition(x, y, z);
	
	return m_pBlockData[MakeBlockDataIndex(x, y, z)];
}

int LevelChunk::countEntities()
{
	int n = 0;
	for (int i = 0; i < 8; i++)
		n += int(m_entities[i].size());
	return n;
}

void LevelChunk::getEntities(Entity* pEntExclude, const AABB& aabb, std::vector<Entity*>& out)
{
	int lowerBound = int(floorf((aabb.min.y - 2.0f) / 16.0f));
	int upperBound = int(floorf((aabb.max.y + 2.0f) / 16.0f));

	if (lowerBound < 0) lowerBound = 0;
	if (upperBound > 7) upperBound = 7;

	for (int b = lowerBound; b <= upperBound; b++)
	{
		for (std::vector<Entity*>::iterator it = m_entities[b].begin(); it != m_entities[b].end(); it++)
		{
			Entity* ent = *it;
			if (ent == pEntExclude) continue;
			
			if (!aabb.intersect(ent->m_hitbox)) continue;

			out.push_back(ent);
		}
	}
}

bool LevelChunk::setTile(int x, int y, int z, TileID tile)
{
	CheckPosition(x, y, z);

	int index = MakeBlockDataIndex(x, y, z);

	TileID oldTile = m_pBlockData[index];

	uint8_t height = m_heightMap[MakeHeightMapIndex(x, z)];

	if (oldTile == tile)
		return false;

	int globalX = x + 16 * m_chunkX;
	int globalZ = z + 16 * m_chunkZ;
	m_pBlockData[index] = tile;
	if (oldTile)
	{
		Tile::tiles[oldTile]->onRemove(m_pLevel, globalX, y, globalZ);
	}

	// clear the data value of the block
	if (index & 1)
		m_tileData[index >> 1] &= 0xF;
	else
		m_tileData[index >> 1] &= 0xF0;

	if (Tile::lightBlock[tile])
	{
		if (height <= y)
			recalcHeight(x, y + 1, z);
	}
	else if (height - 1 == y)
	{
		recalcHeight(x, y, z);
	}

	m_pLevel->updateLight(LightLayer::Sky,   globalX, y, globalZ, globalX, y, globalZ);
	m_pLevel->updateLight(LightLayer::Block, globalX, y, globalZ, globalX, y, globalZ);

	lightGaps(x, z);
	if (tile)
	{
		if (!m_pLevel->m_bIsMultiplayer)
			Tile::tiles[tile]->onPlace(m_pLevel, globalX, y, globalZ);
	}

	m_bUnsaved = true;
	m_updateMap[MakeHeightMapIndex(x, z)] |= 1 << (y >> 4);

	return true;
}

bool LevelChunk::setTileAndData(int x, int y, int z, TileID tile, int data)
{
	CheckPosition(x, y, z);

	assert((data & ~0xF) == 0);
	data &= 0xF;

	int index = MakeBlockDataIndex(x, y, z);

	TileID oldTile = m_pBlockData[index];

	uint8_t height = m_heightMap[MakeHeightMapIndex(x, z)];

	if (oldTile == tile)
	{
		// make sure we're at least updating the data. If not, simply return false
		if (getData(x, y, z) == data)
			return false;
	}

	int globalX = x + 16 * m_chunkX;
	int globalZ = z + 16 * m_chunkZ;
	m_pBlockData[index] = tile;
	if (oldTile)
	{
		Tile::tiles[oldTile]->onRemove(m_pLevel, globalX, y, globalZ);
	}

	// update the data value of the block
	if (index & 1)
		m_tileData[index >> 1] = (m_tileData[index >> 1] & 0x0F) | (data << 4);
	else
		m_tileData[index >> 1] = (m_tileData[index >> 1] & 0xF0) | (data);

	if (m_pLevel->m_pDimension->field_E)
	{
		m_pLevel->updateLight(LightLayer::Block, globalX, y, globalZ, globalX, y, globalZ);
		lightGaps(x, z);
	}

	if (Tile::lightBlock[tile])
	{
		if (height <= y)
			recalcHeight(x, y + 1, z);
	}
	else if (height - 1 == y)
	{
		recalcHeight(x, y, z);
	}

	m_pLevel->updateLight(LightLayer::Sky, globalX, y, globalZ, globalX, y, globalZ);
	m_pLevel->updateLight(LightLayer::Block, globalX, y, globalZ, globalX, y, globalZ);

	lightGaps(x, z);
	if (tile)
	{
		if (!m_pLevel->m_bIsMultiplayer)
			Tile::tiles[tile]->onPlace(m_pLevel, globalX, y, globalZ);
	}

	m_bUnsaved = true;
	m_updateMap[MakeHeightMapIndex(x, z)] |= 1 << (y >> 4);

	return true;
}

int LevelChunk::getData(int x, int y, int z)
{
	CheckPosition(x, y, z);

	int index = MakeBlockDataIndex(x, y, z);

	uint8_t data = m_tileData[index >> 1];
	if (index & 1)
		return data >> 4;
	return data & 0xF;
}

void LevelChunk::setData(int x, int y, int z, int data)
{
	CheckPosition(x, y, z);

	assert((data & ~0xF) == 0);
	data &= 0xF;

	int index = MakeBlockDataIndex(x, y, z);

	uint8_t& xdata = m_tileData[index >> 1];
	if (index & 1)
		xdata = (xdata & 0x0F) | (data << 4);
	else
		xdata = (xdata & 0xF0) | (data);
}

// seems to set block data in 8192 block (4*16*128) chunks for some reason ?
void LevelChunk::setBlocks(uint8_t* pData, int y)
{
	LOG_I("LevelChunk::setBlocks");
	for (int i = 0; i < 8192; i++)
	{
		m_pBlockData[8192 * y + i] = pData[i];
	}

	int x1 = 4 * y;
	int x2 = 4 * (y + 1);
	if (4 * y < x2)
	{
		for (int x3 = 4 * y; x3 < x2; x3++)
		{
			for (int j = 0; j < 16; j++)
			{
				recalcHeight(x3, 0, j);
			}
		}
	}

	int globalX = 16 * m_chunkX + x1;
	int globalZ = 16 * m_chunkZ;

	m_pLevel->updateLight(LightLayer::Sky,   globalX, 0, globalZ, globalX + 4, 128, globalZ + 16);
	m_pLevel->updateLight(LightLayer::Block, globalX, 0, globalZ, globalX + 4, 128, globalZ + 16);
	m_pLevel->setTilesDirty(globalX, 0, globalZ, globalX + 4, 128, globalZ); //@BUG: not setting all the way to globalZ+16?
}

int LevelChunk::setBlocksAndData(uint8_t* pData, int a3, int a4, int a5, int a6, int a7, int a8, int a9)
{
	LOG_I("LevelChunk::setBlocksAndData");

	if (a3 >= a6)
	{
		recalcHeightmapOnly();
		return a9;
	}

	// load the tile IDs
	int x2 = a7 - a4;
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* src = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* dst = &m_pBlockData[MakeBlockDataIndex(x1, a4, x3)];
			memcpy(dst, src, x2 * sizeof(TileID));
			src += x2 * sizeof(TileID);
			a9 += x2 * sizeof(TileID);
		}
	}

	recalcHeightmapOnly();

	int x5 = x2 / 2;

	// load the tile data
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* src = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* dst = &m_tileData[MakeBlockDataIndex(x1, a4, x3) >> 1];
			memcpy(dst, src, x5);
			src += x5;
			a9 += x5;
		}
	}

	// load the block lights
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* src = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* dst = &m_lightBlk[MakeBlockDataIndex(x1, a4, x3) >> 1];
			memcpy(dst, src, x5);
			src += x5;
			a9 += x5;
		}
	}

	// load the sky lights
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* src = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* dst = &m_lightSky[MakeBlockDataIndex(x1, a4, x3) >> 1];
			memcpy(dst, src, x5);
			src += x5;
			a9 += x5;
		}
	}

	return a9;
}

int LevelChunk::getBlocksAndData(uint8_t* pData, int a3, int a4, int a5, int a6, int a7, int a8, int a9)
{
	if (a3 >= a6)
	{
		recalcHeightmapOnly();
		return a9;
	}

	// load the tile IDs
	int x2 = a7 - a4;
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* dst = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* src = &m_pBlockData[MakeBlockDataIndex(x1, a4, x3)];
			memcpy(dst, src, x2 * sizeof(TileID));
			dst += x2 * sizeof(TileID);
			a9 += x2 * sizeof(TileID);
		}
	}

	recalcHeightmapOnly();

	int x5 = x2 / 2;

	// load the tile data
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* dst = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* src = &m_tileData[MakeBlockDataIndex(x1, a4, x3) >> 1];
			memcpy(dst, src, x5);
			dst += x5;
			a9 += x5;
		}
	}

	// load the block lights
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* dst = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* src = &m_lightBlk[MakeBlockDataIndex(x1, a4, x3) >> 1];
			memcpy(dst, src, x5);
			dst += x5;
			a9 += x5;
		}
	}

	// load the sky lights
	for (int x1 = a3; x1 < a6; x1++)
	{
		if (a5 >= a8) continue;

		uint8_t* dst = &pData[a9];

		for (int x3 = a5; x3 < a8; x3++)
		{
			uint8_t* src = &m_lightSky[MakeBlockDataIndex(x1, a4, x3) >> 1];
			memcpy(dst, src, x5);
			dst += x5;
			a9 += x5;
		}
	}

	return a9;
}

Random LevelChunk::getRandom(TLong l)
{
	Random random;

	int levelSeed = m_pLevel->getSeed();
	int chunkSeed = m_chunkX * (4987142 * m_chunkX + 5947611) + m_chunkZ * (4392871 * m_chunkZ + 389711);

	random.init_genrand((levelSeed + chunkSeed) ^ l);

	return random;
}

bool LevelChunk::isEmpty()
{
	return false;
}

// EmptyLevelChunk

EmptyLevelChunk::EmptyLevelChunk(Level* pLevel, TileID* pBlockData, int x, int z)
	:LevelChunk(pLevel, pBlockData, x, z)
{
}

int EmptyLevelChunk::getHeightmap(int x, int z)
{
	return 0;
}

void EmptyLevelChunk::recalcHeightmap()
{

}

void EmptyLevelChunk::recalcHeightmapOnly()
{

}

int EmptyLevelChunk::getRawBrightness(int x, int y, int z, int skySubtract)
{
	return 7;
}

void EmptyLevelChunk::addEntity(Entity*)
{

}

void EmptyLevelChunk::removeEntity(Entity*)
{

}

void EmptyLevelChunk::removeEntity(Entity*, int vec)
{

}

bool EmptyLevelChunk::isSkyLit(int x, int y, int z)
{
	return false;
}

void EmptyLevelChunk::lightLava()
{

}

void EmptyLevelChunk::recalcBlockLights()
{

}

void EmptyLevelChunk::skyBrightnessChanged()
{

}

void EmptyLevelChunk::load()
{

}

void EmptyLevelChunk::unload()
{

}

void EmptyLevelChunk::markUnsaved()
{

}

TileID EmptyLevelChunk::getTile(int x, int y, int z)
{
	return Tile::invisible_bedrock->m_ID;
}

bool EmptyLevelChunk::setTile(int x, int y, int z, TileID tile)
{
	return true;
}

bool EmptyLevelChunk::setTileAndData(int x, int y, int z, TileID tile, int data)
{
	return true;
}

int EmptyLevelChunk::getData(int x, int y, int z)
{
	return 0;
}

void EmptyLevelChunk::setData(int x, int y, int z, int data)
{

}

void EmptyLevelChunk::recalcHeight(int, int, int)
{

}

bool EmptyLevelChunk::isEmpty()
{
	return true;
}

