#ifndef SERVER
#include <stdint.h>
#include <stdbool.h>
#include "common.hpp"
#include <cstring>
#include <iostream>
#include <map>
#include <rlgl.h>
#define PIXUNIT 1.0f / 16.0f
InventoryItem hotbar[ITEMS];
InventoryItem inventory[INVENTORY_SIZE];
static uint8_t selected = 0;
static Texture2D item_atlas;
extern Texture2D terrain;
extern Texture2D playerTex;
const std::string LocaleGet(std::string loc);
std::vector<Item> registry = {
    Item{ "block", IType_Block, 0},
    Item{ "apple", IType_Edible, 0},
    Item{ "stick", IType_Generic, 1},
    Item{ "stone_pickaxe", IType_Mining, 2, 1},
    Item{ "copper_pickaxe", IType_Mining, 4, 2},
    Item{ "silver_pickaxe", IType_Mining, 3, 3},
    Item{ "gold_pickaxe", IType_Mining, 5, 4},
    Item{ "copper_ingot", IType_Generic, 6},
    Item{ "silver_ingot", IType_Generic, 7},
    Item{ "gold_ingot", IType_Generic, 8},
};
bool inventoryOpen = false;
InventoryItem* movingItem = nullptr;
static Image itemAtlasImg;
extern std::list<Recipe> recipes;
int GetNumericIDFromString(std::string s);

void DrawItem(InventoryItem item, int x, int y) {
    if(item.item != nullptr) {
        int atl_size = ITEM_ATLAS_SIZE;
        int tex = item.item->texture;
        Texture2D atlas = item_atlas;
        if(item.item->type == IType_Block) {
            atlas = terrain;
            atl_size = ATLAS_SIZE_W;
            tex = BLOCKS[item.damage].textureTop;
        }
        DrawTexturePro(atlas, 
            {
                (float)(tex % atl_size) * ITEM_SIZE, 
                (float)(tex / atl_size) * ITEM_SIZE, 
                ITEM_SIZE, 
                ITEM_SIZE
            },
            {
                (float)x+6, (float)y+6, 44, 44
            },
            {0, 0},
            0,
            WHITE);
        char text[12] = "???\0";
        sprintf(text, "x%d", item.amount);
        if(item.amount != 0) DrawText(text, x, y, DEFAULT_FONT_SIZE / 2, WHITE);
    }
}
void InventoryRemoveItem(int slot) {
    if(inventory[slot].amount-1 > 0) {
        inventory[slot].amount--;
    }
    else {
        inventory[slot].item = nullptr;
        inventory[slot].damage = 0;
        inventory[slot].amount = 0;
    }
}
void HotbarRemoveItem(int slot) {
    if(hotbar[slot].amount-1 > 0) {
        hotbar[slot].amount--;
    }
    else {
        hotbar[slot].item = nullptr;
        hotbar[slot].damage = 0;
        hotbar[slot].amount = 0;
    }
}
void HotbarRemoveSelected() {
    HotbarRemoveItem(selected);
}
void HotbarAddItem(Item* registry, int damage) {
    for (int i = 0; i < ITEMS; i++)
    {
        InventoryItem item = hotbar[i];
        if((item.item == registry && item.damage == damage)) {
            hotbar[i].item = registry;
            hotbar[i].damage = damage;
            hotbar[i].amount += 1;
            return;
        }
    }
    for (int i = 0; i < INVENTORY_SIZE; i++)
    {
        InventoryItem item = inventory[i];
        if((item.item == registry && item.damage == damage)) {
            inventory[i].item = registry;
            inventory[i].damage = damage;
            inventory[i].amount += 1;
            return;
        }
    }
    for (int i = 0; i < ITEMS; i++)
    {
        InventoryItem item = hotbar[i];
        if(item.item == nullptr) {
            hotbar[i].item = registry;
            hotbar[i].damage = damage;
            hotbar[i].amount += 1;
            return;
        }
    }
    for (int i = 0; i < INVENTORY_SIZE; i++)
    {
        InventoryItem item = inventory[i];
        if(item.item == nullptr) {
            inventory[i].item = registry;
            inventory[i].damage = damage;
            inventory[i].amount += 1;
            return;
        }
    }
}
void HotbarAddItemLots(Item* registry, int damage, int amount) {
    for (int i = 0; i < ITEMS; i++)
    {
        InventoryItem item = hotbar[i];
        if((item.item == registry && item.damage == damage)) {
            hotbar[i].item = registry;
            hotbar[i].damage = damage;
            hotbar[i].amount += amount;
            return;
        }
    }
    for (int i = 0; i < INVENTORY_SIZE; i++)
    {
        InventoryItem item = inventory[i];
        if((item.item == registry && item.damage == damage)) {
            inventory[i].item = registry;
            inventory[i].damage = damage;
            inventory[i].amount += amount;
            return;
        }
    }
    for (int i = 0; i < ITEMS; i++)
    {
        InventoryItem item = hotbar[i];
        if(item.item == nullptr) {
            hotbar[i].item = registry;
            hotbar[i].damage = damage;
            hotbar[i].amount += amount;
            return;
        }
    }
    for (int i = 0; i < INVENTORY_SIZE; i++)
    {
        InventoryItem item = inventory[i];
        if(item.item == nullptr) {
            inventory[i].item = registry;
            inventory[i].damage = damage;
            inventory[i].amount += amount;
            return;
        }
    }
}
Item* GetItemByID(std::string s) {
    for (Item& i : registry) {
        if (i.id == s) return &i;
    }
    return nullptr;
}
int GetItemNumID(std::string s) {
    int o = 0;
    for (Item& i : registry) {
        if (i.id == s) return o;
        o++;
    }
    return -1;
}
void InitHotbar() {
    /*for (int i = 0; i < ITEMS; i++)
    {
        HotbarRemoveItem(i);
    }*/
    //HotbarAddItem(&registry[0], GetNumericIDFromString("workbench"));
    item_atlas = LoadTexture(Pathify("items.png"));
    itemAtlasImg = LoadImageFromTexture(item_atlas);
}

bool IDTagMatch(std::string tag, std::string id);
static bool GetTaggedItem(std::string tag, InventoryItem* item) {
    InventoryItem temp;
    int id;
    bool isBlock;
    bool found;
    for(int blk = 0; blk < MAX_BLOCK_ID; blk++) {
        if(IDTagMatch(tag, BLOCKS[blk].name)) {
            isBlock = true;
            id = GetNumericIDFromString(BLOCKS[blk].name);
            found = true;
            break;
        };
    }
    if(!found) {
        for(Item item : registry) {
            if(IDTagMatch(tag, item.id)) {
                isBlock = false;
                id = GetItemNumID(item.id);
                found = true;
                break;
            };
        }
    }
    if(!found) {
        return false;
    }
    if(isBlock) {
        temp.item = &(registry[0]);
        temp.damage = id;
    }
    else {
        temp.item = &(registry[id]);
    }
    memcpy(item, &temp, sizeof(InventoryItem));
    return true;
}
static bool FindTaggedItem(std::string tag, InventoryItem* item) {
    for(int blk = 0; blk < INVENTORY_SIZE; blk++) {
        InventoryItem itm = inventory[blk];
        if(itm.item != nullptr) {
            if(GetTaggedItem(tag, &itm)) {
                memcpy(item, &itm, sizeof(InventoryItem));
                return true;
            }
        }
    }
    return false;
}
void DrawHotbar() {
    if(inventoryOpen) UIBegin();
        const int width = 52 * ITEMS;
        DrawRectangle(GetScreenWidth()/2-width/2, GetScreenHeight()-48-2, width, 50, ColorAlpha(BLACK, 0.25f));
        for (int i = 0; i < ITEMS; i++)
        {
            int x = GetScreenWidth()/2-width/2 + i * 52;
            int y = GetScreenHeight()-50;
            if(inventoryOpen) {
                if(ButtonInventory(x, y, 52, 52)) {
                    if(movingItem != nullptr && hotbar[i].item == nullptr) {
                        memcpy(&hotbar[i], movingItem, sizeof(InventoryItem));
                        movingItem->item = nullptr;
                        movingItem->amount = 0;
                        movingItem->damage = 0;
                    }
                    else {
                        movingItem = &hotbar[i];
                    }
                };
            }
            
            if(i == selected) {
                DrawRectangle(x, y, 52, 52, BLACK);
            }
            if(&hotbar[i] != movingItem) {
                DrawItem(hotbar[i], x, y);
            }
        }
        if(inventoryOpen) { 
            const int width2 = 52 * INVENTORY_WIDTH;
            const int inv_height = 52 * INVENTORY_HEIGHT;
            DrawRectangle(GetScreenWidth()/2-width2/2, GetScreenHeight()-56-inv_height, width2, inv_height, ColorAlpha(BLACK, 0.25f));
            for (int i = 0; i < INVENTORY_SIZE; i++)
            {
                int x = GetScreenWidth()/2-width2/2 + (i % INVENTORY_WIDTH) * 52;
                int y = GetScreenHeight()-56-inv_height + (i / INVENTORY_WIDTH) * 52;
                if((i + (i / INVENTORY_WIDTH)) % 2 == 0) {
                    DrawRectangle(x, y, 52, 52, ColorAlpha(BLACK, 0.1f));
                }
                if(ButtonInventory(x, y, 52, 52)) {
                    if(movingItem != nullptr && inventory[i].item == nullptr) {
                        memcpy(&inventory[i], movingItem, sizeof(InventoryItem));
                        movingItem->item = nullptr;
                        movingItem->amount = 0;
                        movingItem->damage = 0;
                    }
                    else {
                        movingItem = &inventory[i];
                    }
                };
                if(&inventory[i] != movingItem) {
                    DrawItem(inventory[i], x, y);
                }
            }
            std::list<Recipe> validRecipes;
            for (Recipe r : recipes)
            {
                if(r.requiredStation == "hands") {
                    std::vector<int> items;
                    for (RecipeItem ingredient : r.inputs) {
                        for (int i = 0; i < INVENTORY_SIZE; i++)
                        {
                            InventoryItem item = inventory[i];
                            if(item.item != nullptr) {
                                bool blockCond = item.item->type == IType_Block && ingredient.type == IType_Block && item.damage == GetNumericIDFromString(ingredient.id);
                                bool genericCond = item.item->type != IType_Block && ingredient.type == IType_Generic && item.item->id == ingredient.id;
                                bool tagBlockCond = item.item->type == IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, BLOCKS[item.damage].name);
                                bool tagItemCond = item.item->type != IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, item.item->id);
                                if(blockCond || genericCond || tagBlockCond || tagItemCond) {
                                    items.push_back(i);
                                }
                            }
                        }
                    }
                    if(items.size() == r.inputs.size()) {
                        validRecipes.push_back(r);
                    }
                }
            }
            int _y = 0;
            for (Recipe r : validRecipes)
            {
                int x = GetScreenWidth()/2-width2/2 - 300;
                int y = GetScreenHeight()-56-inv_height - _y * 52;
                DrawRectangle(x, y, 260, 52, Fade(BLACK, 0.25f));
                int _x = 0;
                for (RecipeItem ingredient : r.inputs) {
                    InventoryItem temp;
                    if(ingredient.type == IType_Block) {
                        temp.item = &(registry[0]);
                        temp.damage = GetNumericIDFromString(ingredient.id);
                    }
                    else if (ingredient.type == IType_Generic) {
                        temp.item = GetItemByID(ingredient.id);
                    }
                    else {
                        GetTaggedItem(ingredient.id, &temp);
                    }
                    temp.amount = ingredient.quantity;
                    DrawItem(temp, x + _x * 52, y);
                    _x++;
                }
                _x = 1;
                for (RecipeItem ingredient : r.outputs) {
                    InventoryItem temp;
                    if(ingredient.type == IType_Block) {
                        temp.item = &(registry[0]);
                        temp.damage = GetNumericIDFromString(ingredient.id);
                    }
                    else {
                        temp.item = GetItemByID(ingredient.id);
                    }
                    temp.amount = ingredient.quantity;
                    DrawItem(temp, x + 300 - 40 - _x * 52, y);
                    _x++;
                }
                if(ButtonInventory(x, y, 260, 52)) {
                    std::map<int, int> items;
                    for (RecipeItem ingredient : r.inputs) {
                        for (int i = 0; i < INVENTORY_SIZE; i++)
                        {
                            InventoryItem item = inventory[i];
                            if(item.item != nullptr) {
                                bool blockCond = item.item->type == IType_Block && ingredient.type == IType_Block && item.damage == GetNumericIDFromString(ingredient.id);
                                bool genericCond = item.item->type != IType_Block && ingredient.type == IType_Generic && item.item->id == ingredient.id;
                                bool tagBlockCond = item.item->type == IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, BLOCKS[item.damage].name);
                                bool tagItemCond = item.item->type != IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, item.item->id);
                                if(blockCond || genericCond || tagBlockCond || tagItemCond) {
                                    items[i] = ingredient.quantity;
                                }
                            }
                        }
                    }
                    if(items.size() == r.inputs.size()) {
                        for(auto [i, amount] : items) {
                            for (int _ = 0; _ < amount; _++)
                            {
                                InventoryRemoveItem(i);
                            }
                        }
                        for (RecipeItem ingredient : r.outputs) {
                            if(ingredient.type == IType_Block) {
                                for (int _ = 0; _ < ingredient.quantity; _++) { 
                                    HotbarAddItem(&registry[0], GetNumericIDFromString(ingredient.id)); 
                                }
                            }
                            else {
                                for (int _ = 0; _ < ingredient.quantity; _++) { 
                                    HotbarAddItem(GetItemByID(ingredient.id), 255); 
                                }
                            }
                        }
                    }
                }
                _y++;
            }
        }
        if(inventoryOpen) {
            const int width2 = 52 * INVENTORY_WIDTH;
            const int inv_height = 52 * INVENTORY_HEIGHT;
            DrawRectangle(GetScreenWidth()/2-width2/2, 4, width2, inv_height, ColorAlpha(BLACK, 0.25f));
            for (int i = 0; i < INVENTORY_SIZE; i++)
            {
                InventoryItem item;
                if(i < MAX_BLOCK_ID) {
                    item.item = &registry[0];
                    item.damage = i;
                }
                else if (i < registry.size()+MAX_BLOCK_ID-1) {
                    item.item = &registry[i-MAX_BLOCK_ID+1];
                }
                int x = GetScreenWidth()/2-width2/2 + (i % INVENTORY_WIDTH) * 52;
                int y = 4 + (i / INVENTORY_WIDTH) * 52;
                if((i + (i / INVENTORY_WIDTH)) % 2 == 0) {
                    DrawRectangle(x, y, 52, 52, ColorAlpha(BLACK, 0.1f));
                }
                if(ButtonInventory(x, y, 52, 52)) {
                    if(movingItem != nullptr) {
                        movingItem = nullptr;
                    }
                    else {
                        HotbarAddItem(item.item, item.damage);
                    }
                };
                DrawItem(item, x, y);
            }
        }
        if(movingItem != nullptr && movingItem->item != nullptr) {
            DrawItem(*movingItem, GetMouseX(), GetMouseY());
            std::string name = "item."+movingItem->item->id;
            if(movingItem->item->type == IType_Block) {
                name = "item."+BLOCKS[movingItem->damage].name;
            }
            DrawTextB(LocaleGet(name).c_str(), GetMouseX()-16, GetMouseY()+56, 16, WHITE);
        }
    if(inventoryOpen) UIHandleControls();
}
InventoryItem HotbarGetSelected() {
    return hotbar[selected];
}
void DrawSelectedItem() {
    InventoryItem item = hotbar[selected];
    Vector3 center = {-0.75f, -0.45f, 0.5f};
    rlPushMatrix();
    rlTranslatef(center.x, center.y-0.25f, center.z);
    float swing = powf(player_attack_time,1);
    float swing3 = sinf(swing * swing * PI);
    float swing2 = sinf(sqrt(swing) * PI);
    rlRotatef(-swing3 * 50, 1, 0, 0);
    rlRotatef(-swing2 * 80, 0, 1, 0);

    swing = powf(player_place_time,2);
    swing2 = sinf(swing * PI);
    rlRotatef(swing2 * 80, 0, 1, 0);

    rlRotatef(-45, 1, 0, 0);
    rlRotatef(25, 0, 0, 1);
    DrawCubeTexture(playerTex, {0, -12*PIXUNIT/2, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 44,20,4,4);
    if(item.item != nullptr) {
        if(item.item->type == IType_Block) {
            DrawCubeBlock(terrain, {0, -12*PIXUNIT - 0.25f, 0}, 0.5f, 0.5f, 0.5f, BLOCKS[item.damage]);
        }
        else {
            UVCorners left = GetUVTex(item.item->texture);
            rlSetTexture(item_atlas.id);
            rlBegin(RL_QUADS);
                rlColor4ub(255, 255, 255, 255);
                rlNormal3f(1.0f, 0.0f, 0.0f);
                const float size = 0.5f;
                rlTexCoord2f(left.corner0.x, left.corner0.y); rlVertex3f(0, -12*PIXUNIT - size, 0.25f - size);
                rlTexCoord2f(left.corner1.x, left.corner1.y); rlVertex3f(0, -12*PIXUNIT + size, 0.25f - size);
                rlTexCoord2f(left.corner2.x, left.corner2.y); rlVertex3f(0, -12*PIXUNIT + size, 0.25f + size);
                rlTexCoord2f(left.corner3.x, left.corner3.y); rlVertex3f(0, -12*PIXUNIT - size, 0.25f + size);
            rlEnd();
        }
    }
    rlPopMatrix();
}
bool InputGetKeyDown(const std::string name);
bool InputGetKeyPressed(const std::string name);
void CloseInventory() {
    inventoryOpen = false;
    movingItem = nullptr;
    DisableCursor();
}
void UpdateHotbar() {
    if(GetMouseWheelMoveV().y > 0.1f) {
        selected++;
        if(selected >= ITEMS) {
            selected = 0;
        }
    }
    if(GetMouseWheelMoveV().y < -0.1f) {
        selected--;
        if(selected >= ITEMS) {
            selected = ITEMS-1;
        }
    }
    if(IsKeyPressed(KEY_ONE)) {
        selected = 0;
    }
    if(IsKeyPressed(KEY_TWO)) {
        selected = 1;
    }
    if(IsKeyPressed(KEY_THREE)) {
        selected = 2;
    }
    if(IsKeyPressed(KEY_FOUR)) {
        selected = 3;
    }
    if(IsKeyPressed(KEY_FIVE)) {
        selected = 4;
    }
    if(IsKeyPressed(KEY_SIX)) {
        selected = 5;
    }
    if(IsKeyPressed(KEY_SEVEN)) {
        selected = 6;
    }
    if(IsKeyPressed(KEY_EIGHT)) {
        selected = 7;
    }
    if(IsKeyPressed(KEY_NINE)) {
        selected = 8;
    }
    if(IsKeyPressed(KEY_KP_ADD)) {
        hotbar[selected].damage++;
    }
    if(IsKeyPressed(KEY_KP_SUBTRACT)) {
        hotbar[selected].damage--;
    }
    if(InputGetKeyPressed("inventory")) {
        inventoryOpen = !inventoryOpen;
        if(inventoryOpen) {
            EnableCursor();
        }
        else {
            CloseInventory();
        }
    }
    if(IsKeyPressed(KEY_F9)) {
        HotbarAddItem(GetItemByID("gold_pickaxe"), 255); 
        HotbarAddItemLots(GetItemByID("gold_ingot"), 255, INT32_MAX); 
        HotbarAddItemLots(GetItemByID("copper_ingot"), 255, INT32_MAX); 
        HotbarAddItemLots(GetItemByID("silver_ingot"), 255, INT32_MAX); 
        HotbarAddItemLots(GetItemByID("block"), GetNumericIDFromString("stone"), INT32_MAX); 
        HotbarAddItemLots(GetItemByID("block"), GetNumericIDFromString("planks"), INT32_MAX); 
        HotbarAddItemLots(GetItemByID("apple"), 255, INT32_MAX); 
    }
}
#endif