#ifndef CC_BLOCK_H
#define CC_BLOCK_H
#include "PackedCol.h"
#include "Vectors.h"
#include "Constants.h"
#include "BlockID.h"
/* Stores properties and data for blocks.
   Also performs automatic rotation of directional blocks.
   Copyright 2014-2025 ClassiCube | Licensed under BSD-3
*/
CC_BEGIN_HEADER

struct IGameComponent;
extern struct IGameComponent Blocks_Component;

/* Describes how a block is rendered in the world. */
enum DrawType {
	DRAW_OPAQUE,            /* Completely covers blocks behind (e.g. dirt). */
	DRAW_TRANSPARENT,       /* Blocks behind show (e.g. glass). Pixels either fully visible or invisible. */
	DRAW_TRANSPARENT_THICK, /* Same as Transparent, but all neighbour faces show. (e.g. leaves) */
	DRAW_TRANSLUCENT,       /* Blocks behind show (e.g. water). Pixels blend with other blocks behind. */
	DRAW_GAS,               /* Does not show (e.g. air). Can still be collided with. */
	DRAW_SPRITE             /* Block renders as an X (e.g. sapling). Pixels either fully visible or invisible. */
};

/* Describes the interaction a block has with a player when they collide with it. */
enum CollideType {
	COLLIDE_NONE,         /* No interaction when player collides. */
	COLLIDE_LIQUID,       /* 'swimming'/'bobbing' interaction when player collides. */
	COLLIDE_SOLID,        /* Block completely stops the player when they are moving. */
	COLLIDE_ICE,          /* Block is solid and partially slidable on. */
	COLLIDE_SLIPPERY_ICE, /* Block is solid and fully slidable on. */
	COLLIDE_WATER,        /* Water style 'swimming'/'bobbing' interaction when player collides. */
	COLLIDE_LAVA,         /* Lava style 'swimming'/'bobbing' interaction when player collides. */
	COLLIDE_CLIMB         /* Rope/Ladder style climbing interaction when player collides. */
};

CC_VAR extern struct _BlockLists {
	/* Whether this block is a liquid. (Like water/lava) */
	cc_bool IsLiquid[BLOCK_COUNT];
	/* Whether this block prevents lights from passing through it. */
	cc_bool BlocksLight[BLOCK_COUNT];
	/* Whether this block is fully bright/light emitting. (Like lava) */
	cc_uint8 Brightness[BLOCK_COUNT];
	/* Fog colour when player is inside this block. */
	/* NOTE: Only applies if fog density is not 0. */
	PackedCol FogCol[BLOCK_COUNT];
	/* How thick fog is when player is inside this block. */
	float FogDensity[BLOCK_COUNT];
	/* Basic collision type of this block. (gas, liquid, or solid) */
	cc_uint8 Collide[BLOCK_COUNT];
	/* Extended collision type of this block, usually same as basic. */
	/* NOTE: Not always the case. (e.g. ice, water, lava, rope differ) */
	cc_uint8 ExtendedCollide[BLOCK_COUNT];
	/* Speed multiplier when player is touching this block. */
	/* Can be < 1 to slow player down, or > 1 to speed up. */
	float SpeedMultiplier[BLOCK_COUNT];
	/* Bit flags of which faces of this block uses light colour from neighbouring blocks. */
	/*   e.g. a block with Min.x of 0.0 uses light colour at X-1,Y,Z for XMIN face. */
	/*   e.g. a block with Min.x of 0.1 uses light colour at X,Y,Z   for XMIN face. */
	cc_uint8 LightOffset[BLOCK_COUNT];
	/* Draw method used when rendering this block. See DrawType enum. */
	cc_uint8 Draw[BLOCK_COUNT];
	/* Sound played when the player manually destroys this block. See SoundType enum. */
	cc_uint8 DigSounds[BLOCK_COUNT];
	/* Sound played when the player walks on this block. See SoundType enum. */
	cc_uint8 StepSounds[BLOCK_COUNT];
	/* Whether fog colour is used to apply a tint effect to this block. */
	cc_bool Tinted[BLOCK_COUNT];
	/* Whether this block has an opaque draw type, min of (0,0,0), and max of (1,1,1) */
	cc_bool FullOpaque[BLOCK_COUNT];
	/* Offset/variation mode of this block. (only when drawn as a sprite) */
	/* Some modes slightly randomly offset blocks to produce nicer looking clumps. */
	cc_uint8 SpriteOffset[BLOCK_COUNT];

	/* Coordinates of min corner of this block for collisions. */
	Vec3 MinBB[BLOCK_COUNT];
	/* Coordinates of max corner of this block for collisions. */
	Vec3 MaxBB[BLOCK_COUNT];
	/* Coordinates of min corner of this block for rendering. */
	/* e.g. ice is very slightly offset horizontally. */
	Vec3 RenderMinBB[BLOCK_COUNT];
	/* Coordinates of max corner of this block for rendering. */
	/* e.g. ice is very slightly offset horizontally. */
	Vec3 RenderMaxBB[BLOCK_COUNT];

	/* Texture ids of each face of blocks. */
	TextureLoc Textures[BLOCK_COUNT * FACE_COUNT];
	/* Whether this block is allowed to be placed. */
	cc_bool CanPlace[BLOCK_COUNT];
	/* Whether this block is allowed to be deleted. */
	cc_bool CanDelete[BLOCK_COUNT];

	/* Bit flags of faces hidden of two neighbouring blocks. */
	cc_uint8 Hidden[BLOCK_COUNT * BLOCK_COUNT];
	/* Bit flags of which faces of this block can stretch with greedy meshing. */
	cc_uint8 CanStretch[BLOCK_COUNT];
	/* Gravity of particles spawned when this block is broken */
	float ParticleGravity[BLOCK_COUNT];
} Blocks;

#define Block_Tint(col, block)\
if (Blocks.Tinted[block]) col = PackedCol_Tint(col, Blocks.FogCol[block]);

/* Most blocks which 'stop' light actually stop the light starting at block below */
/*  except for e.g. upside down slabs which 'stop' the light at same block level */
/* The difference can be seen by placing a lower and upper slab block on a wall, */
/*  and comparing whether the block directly behind them is in shadow or not */
#define LIGHT_FLAG_SHADES_FROM_BELOW 6
cc_uint8 Block_ReadBrightness(cc_uint8 fullBright);
cc_uint8 Block_WriteFullBright(cc_uint8 brightness);

/* Returns whether the given block has been changed from default */
cc_bool Block_IsCustomDefined(BlockID block);
/* Updates state and raises event after the given block has been defined */
void Block_DefineCustom(BlockID block, cc_bool checkSprite);
/* Resets the given block to default */
void Block_UndefineCustom(BlockID block);
/* Resets all the properties of the given block to default */
void Block_ResetProps(BlockID block);

/* Gets the name of the given block */
/* NOTE: Name points directly within underlying buffer, you MUST NOT persist this string */
CC_API STRING_REF  cc_string Block_UNSAFE_GetName(      BlockID block);
typedef STRING_REF cc_string (*FP_Block_UNSAFE_GetName)(BlockID block);
/* Sets the name of the given block. */
void Block_SetName(BlockID block, const cc_string* name);
/* Finds the ID of the block whose name caselessly matches given name */
CC_API int Block_FindID(const cc_string* name);
/* Attempts to parse given name as a numerical block ID */
/* Falls back to Block_FindID if this fails */
CC_API int Block_Parse(const cc_string* name);

/* Sets the textures of the side faces of the given block */
void Block_SetSide(TextureLoc texLoc, BlockID blockId);
/* The texture for the given face of the given block */
#define Block_Tex(block, face) Blocks.Textures[(block) * FACE_COUNT + (face)]

/* Whether the given face of this block is occluded/hidden */
#define Block_IsFaceHidden(block, other, face) (Blocks.Hidden[((block) * BLOCK_COUNT) + (other)] & (1 << (face)))

/* Whether blocks can be automatically rotated */
extern cc_bool AutoRotate_Enabled;
/* Attempts to find the rotated block based on the user's orientation and offset on selected block */
/* If no rotated block is found, returns given block */
BlockID AutoRotate_RotateBlock(BlockID block);
/* Returns non 0 if both blocks belong to the same autorotate group */
cc_bool AutoRotate_BlocksShareGroup(BlockID block, BlockID blockOther);

CC_END_HEADER
#endif
