#ifndef CC_MAPRENDERER_H
#define CC_MAPRENDERER_H
#include "Core.h"
#include "Constants.h"
/* Renders the blocks of the world by subdividing it into chunks.
   Also manages the process of building/deleting chunk meshes.
   Also sorts chunks so nearest chunks are rendered first, and calculates chunk visibility.
   Copyright 2014-2019 ClassiCube | Licensed under BSD-3
*/
struct IGameComponent;
extern struct IGameComponent MapRenderer_Component;

extern int MapRenderer_ChunksX, MapRenderer_ChunksY, MapRenderer_ChunksZ;
#define MapRenderer_Pack(cx, cy, cz) (((cz) * MapRenderer_ChunksY + (cy)) * MapRenderer_ChunksX + (cx))
/* TODO: Swap Y and Z? Make sure to update ChunkUpdater's ResetChunkCache and ClearChunkCache methods! */

/* Max used 1D atlases. (i.e. Atlas1D_Index(maxTextureLoc) + 1) */
extern int MapRenderer_1DUsedCount;
/* Number of chunks in the world, or ChunksX * ChunksY * ChunksZ */
extern int MapRenderer_ChunksCount;
/* Maximum number of chunk updates that can be performed in one frame. */
extern int MapRenderer_MaxUpdates;

/* Buffer for all chunk parts. There are (MapRenderer_ChunksCount * Atlas1D_Count) parts in the buffer,
with parts for 'normal' buffer being in lower half. */
extern struct ChunkPartInfo* MapRenderer_PartsNormal; /* TODO: THAT DESC SUCKS */
extern struct ChunkPartInfo* MapRenderer_PartsTranslucent;

/* Describes a portion of the data needed for rendering a chunk. */
struct ChunkPartInfo {
#ifdef CC_BUILD_GL11
	GfxResourceID Vb;
#endif
	int Offset;      /* -1 if no vertices at all */
	int SpriteCount; /* Sprite vertices count */
	cc_uint16 Counts[FACE_COUNT]; /* Counts per face */
};

/* Describes data necessary for rendering a chunk. */
struct ChunkInfo {	
	cc_uint16 CentreX, CentreY, CentreZ; /* Centre coordinates of the chunk */

	cc_uint8 Visible : 1;       /* Whether chunk is visible to the player */
	cc_uint8 Empty : 1;         /* Whether the chunk is empty of data */
	cc_uint8 PendingDelete : 1; /* Whether chunk is pending deletion */
	cc_uint8 AllAir : 1;        /* Whether chunk is completely air */
	cc_uint8 : 0;               /* pad to next byte*/

	cc_uint8 DrawXMin : 1;
	cc_uint8 DrawXMax : 1;
	cc_uint8 DrawZMin : 1;
	cc_uint8 DrawZMax : 1;
	cc_uint8 DrawYMin : 1;
	cc_uint8 DrawYMax : 1;
	cc_uint8 : 0;          /* pad to next byte */
#ifdef OCCLUSION
	public cc_bool Visited = false, Occluded = false;
	public byte OcclusionFlags, OccludedFlags, DistanceFlags;
#endif
#ifndef CC_BUILD_GL11
	GfxResourceID Vb;
#endif
	struct ChunkPartInfo* NormalParts;
	struct ChunkPartInfo* TranslucentParts;
};

void ChunkInfo_Reset(struct ChunkInfo* chunk, int x, int y, int z);
/* Gets the chunk at the given chunk coordinates in the world. */
/* NOTE: Does NOT check coordinates are within bounds. */
struct ChunkInfo* MapRenderer_GetChunk(int cx, int cy, int cz);
/* Renders the meshes of non-translucent blocks in visible chunks. */
void MapRenderer_RenderNormal(double delta);
/* Renders the meshes of translucent blocks in visible chunks. */
void MapRenderer_RenderTranslucent(double delta);
/* Potentially updates sort order of rendered chunks. */
/* Potentially builds meshes for several nearby chunks. */
/* NOTE: This should be called once per frame. */
void MapRenderer_Update(double deltaTime);

/* Marks the given chunk as needing to be rebuilt/redrawn. */
/* NOTE: Coordinates outside the map are simply ignored. */
void MapRenderer_RefreshChunk(int cx, int cy, int cz);
/* Deletes the vertex buffer associated with the given chunk. */
/* NOTE: This method also adjusts internal state, so do not bypass this. */
void MapRenderer_DeleteChunk(struct ChunkInfo* info);
/* Builds the mesh (and hence vertex buffer) for the given chunk. */
/* NOTE: This method also adjusts internal state, so do not bypass this. */
void MapRenderer_BuildChunk(struct ChunkInfo* info, int* chunkUpdates);

/* Refreshes chunks on the border of the map. */
/* NOTE: Only refreshes border chunks whose y is less than 'maxHeight'. */
void MapRenderer_RefreshBorders(int maxHeight);
/* Deletes all chunks and resets internal state. */
void MapRenderer_Refresh(void);
#endif
