#if INTERFACE

#ifdef Escape
    #undef Escape
#endif
#include "glfw_keycodes_to_string.h"
#include "../PerlinNoise.hpp"

#ifdef WIN32
    #include "external/fix_win32_compatibility.h"
#endif

#include "raylib.h"
#include "raymath.h"
#include <stdint.h>
#include <bitset>

#endif

#include <cstdint>
#include <list>
#define FOV 90

#define WIDTH 1024.0
#define HEIGHT 600.0

#define ASPECT WIDTH / HEIGHT
#define CHUNK_SIZE 16 // W & H
#define WORLD_HEIGHT 128
#define CHUNK_DATA_SIZE CHUNK_SIZE*CHUNK_SIZE*WORLD_HEIGHT
#define CHUNK_SIZE_SQR CHUNK_SIZE*CHUNK_SIZE

#define MAX_WORLD_SIZE 16
#define MAX_WORLD_AREA MAX_WORLD_SIZE*MAX_WORLD_SIZE

#define WORLD_SIZE_BLOCKS MAX_WORLD_SIZE * CHUNK_SIZE
#define ATLAS_SIZE_W 16
#define ATLAS_SIZE_H 16

#define ITEM_ATLAS_SIZE 16
#define ITEM_SIZE 16

#define PLAYER_HEIGHT 1.75f
#define PLAYER_RADIUS 0.25f
#define GRAVITY 20.0f
#define MOVE_SPEED 6.0f
#define JUMP_SPEED 8.0f
#define DELTA_SMOOTH 0.016f
#define MOUSE_SENS 0.0025f
#define PITCH_MIN -1.47f     // ~-83 degrees
#define PITCH_MAX 1.47f     // ~ 83 degrees
#define SMOOTH_FACTOR 12.0f  // larger = snappier

#define BLOCK_AIR 0

#define BLOCK_STONE 1
#define BLOCK_GRASS 2
#define BLOCK_DIRT 3
#define BLOCK_SAND 4
#define BLOCK_WATERS 5

#define MAX_BLOCK_ID 28
#define CLOUD_FADE_BLOCKS CHUNK_SIZE*8
//Networking
#define PORT 25565
#define MAX_MESSAGE_LENGTH 64
//Notes:
//The server uses a player ID of 255
//
//Server->Client
//                         Byte 0
#define NET_ARG_NONE        0x00 // Invalid
#define NET_ARG_CHUNKF      0x01 // Byte 1 - chunk ID, uint8_t blocks[16*64*16], uint8_t highestBlock[16*16]
#define NET_ARG_BLOCK       0x02 // Player ID, block X, block Y, block Z, block type
#define NET_ARG_PLRCON      0x03 // Byte 1 - ID
#define NET_ARG_PLRDCN      0x04 // Byte 1 - ID
#define NET_ARG_PLRID       0x05 // Byte 1 - Server-assigned ID to the player
#define NET_ARG_ECHOMOVE    0x06 // Byte 1 - player ID, bytes 2-3 - echoed player X, bytes 4-5 - echoed player Y, bytes 6-7 - echoed player Z, byte 8 - player yaw
#define NET_ARG_PLRREG      0x07 // Same as ECHOMOVE, used as an answer to REQPLAYERS
#define NET_ARG_ECHOYAW     0x08 // Byte 1 - ID, byte 2 - yaw
#define NET_ARG_MESSAGEE    0x09 // Byte 1 - sender ID, byte 2 - length, rest is message (128b max)
//Client->Server
//                         Byte 0
#define NET_ARG_PLAYERMOVE  0x80 // Byte 1-2 - player X, byte 3-4 - player Y, byte 5-6 - player Z, byte 7 - player yaw
#define NET_ARG_BLOCKDELTA  0x81 // Block X, block Y, block Z, block type
#define NET_ARG_REQWORLD    0x82 // No additional data
#define NET_ARG_REQPLAYERS  0x83 // No additional data
#define NET_ARG_PLAYERYAW   0x84 // Byte 1 - yaw
#define NET_ARG_MESSAGEC    0x85 // All is message (64b max)
//
//Errors
#define NET_ERROR_DSCN      0x00
#define NET_ERROR_NOTFOUND  0x01
#define NET_ERROR_FAILED    0x02
#define NET_ERROR_INTERNAL  0x03

#define GAME_NAME "CubeGame"
#define GAME_VERSION_INT 0

#define DEFAULT_FONT_SIZE 16
#define TPS 30.0
#define TPS_DIV 1.0 / TPS
//Rendering
#define RENDER_DISTANCE 16
#define RENDER_DISTANCE_SQUARED RENDER_DISTANCE*RENDER_DISTANCE

//Game stuff
#define MAX_HP 100
#define PATH_APPEND "resources/"
#define PATH_APPEND_USER "user/"

#define ITEMS 9
#define INVENTORY_WIDTH 8
#define INVENTORY_HEIGHT 6
#define INVENTORY_SIZE INVENTORY_WIDTH*INVENTORY_HEIGHT

enum BlockType {
    B_Wood,
    B_Stone,
    B_Grass,
    B_Dirt
};

struct BlockDef {
    const std::string name;
    BlockRendering render;
    uint8_t textureTop;
    uint8_t textureSide;
    uint8_t textureBottom;
    int hardness;
    int min_tier;
    BlockType type;
    // Constructor with initializer list; textureSide/textureBottom default to textureTop if not provided.
    BlockDef(
             std::string _name,
             BlockRendering render_ = R_None,
             BlockType btype = B_Stone,
             int _hardness = 1,
             int _min_tier = 0,
             uint8_t textureTop_ = 255,
             uint8_t textureSide_ = 255,
             uint8_t textureBottom_ = 255)
        : name(_name),
          render(render_),
          hardness(_hardness),
          min_tier(_min_tier),
          textureTop(textureTop_),
          textureSide(textureSide_ == 255 ? textureTop_ : textureSide_),
          textureBottom(textureBottom_ == 255 ? textureTop_ : textureBottom_),
          type(btype)
    {}
};

enum BlockRendering {
    R_None,
    R_Normal,
    R_TriSide,
    R_Translucent,
    R_TranslucentTriSide,
    R_TranslucentAllSides
};

enum GameState {
    GAME_STATE_LOADING,
    GAME_STATE_GAME,
    GAME_STATE_MAINMENU,
    GAME_STATE_WORLDSELECT,
    GAME_STATE_MULTIPLAYERSELECT,
    GAME_STATE_TRANSITION,
    GAME_STATE_WORLD_CREATE,
    GAME_STATE_OPTIONS
};

struct Chunk {
    uint8_t x;
    uint8_t z;
    bool isDirty;
    uint8_t blocks[CHUNK_DATA_SIZE];
    uint16_t opaqueMask[CHUNK_DATA_SIZE/16];
    uint16_t airMask[CHUNK_DATA_SIZE/16];
    uint8_t highestBlock[CHUNK_SIZE*CHUNK_SIZE];
    
};
#ifndef SERVER
struct ChunkRenderData {
    uint8_t sides[CHUNK_DATA_SIZE];
};

struct MeshData {
    float *positions;   // xyz xyz ...
    float *normals;     // xyz ...
    float *uvs;         // uv uv ...
    unsigned char *colors; // rgba rgba ...
    unsigned short *indices;  // triangles
    unsigned short vertexCount;
    unsigned short indexCount;
};

struct ChunkMeshData {
    uint8_t *positions;   // xyz xyz ...
    uint8_t *luminosity; // l l ...
    float *uvs;         // uv uv ...
    unsigned short *indices;  // triangles
    unsigned short vertexCount;
    unsigned short indexCount;
};

struct UVCorners {
    Vector2 corner0;
    Vector2 corner1;
    Vector2 corner2;
    Vector2 corner3;
};

struct Quad2D {
    int x;
    int y;
    int w;
    int h;
};
#endif
struct Vector3I {
    int x;
    int y;
    int z;
};
struct Entity {
    uint8_t type;
    uint8_t id;
    uint8_t damage_flash;
    uint8_t data[16];
    float smoothing;
    float health;
    float lifetime;
    Vector3 position;
    Vector3 velocity;
    unsigned short yaw;
    bool free;
};

struct NetPlayer
{
    ENetPeer* peer;
    int addr;
    int port;
    int id;
    int yaw;
    float x;
    float y;
    float z;
};

struct MemInfo {
    uint64_t resident;
    uint64_t virtualSize;
    uint64_t privateBytes;
};

enum ItemType {
    IType_Generic,
    IType_Block,
    IType_Edible,
    IType_Mining,
    IType_Weapon,
    IType_Special_Tag
};
enum EntityPhysics {
    Phys_Falling,
    Phys_Floating
};
struct Item {
    std::string id;
    ItemType type;
    int texture;
    int tier;
    Item(
            std::string _id,
            ItemType _type,
            int _texture,
            int _tier = 0)
        : id(_id),
        type(_type),
        texture(_texture),
        tier(_tier)
    {}
};
struct InventoryItem {
    Item* item = nullptr;
    int damage = -1;
    int amount = 0;
};
struct RecipeItem {
    ItemType type;
    std::string id;
    int quantity = 1;
};
struct Recipe {
    std::string requiredStation;
    std::list<RecipeItem> inputs;
    std::list<RecipeItem> outputs;
};
struct Particle {
    Vector3 position;
    Vector3 velocity;
    EntityPhysics physicsType;
    Texture2D texture;
    int lifetime;
    int data[8];
};