#include <World/LocalWorld/SinglePlayerWorld/SinglePlayerWorld.hpp>
#include <ChunkWindow/ChunkWindow.hpp>

SinglePlayerWorld::SinglePlayerWorld(std::string worldDir) : LocalWorld(worldDir)
{
    if (!fatalError.empty())
    {
        return;
    }

    uint64_t id = allocEntityId();

    std::shared_ptr<Player> player = std::make_shared<Player>(*this, "main:player", id, "");
    if (areCheatsEnabled)
    {
        player->perms.setAll();
    }

    player->clientPrefs = {0xFF, 0xFF};

    std::vector<uint8_t> bytes;
    if (planner.read("singleplayer.dat", dataStore, bytes))
    {
        player->deserializeState(bytes.data(), bytes.size());
    }
    
    entities[id] = player;
    this->thePlayer = player;

    // Attach the gadget controllers.
    for (size_t i = 0; i < Player::numEquipmentSlots; i++)
    {
        player->reattachGadget(i);
    }
}

std::shared_ptr<Player> SinglePlayerWorld::getThePlayer()
{
    return thePlayer;
}

void SinglePlayerWorld::fastUpdate(ChunkWindow *chunkWindow)
{
    if (chunkWindow == nullptr)
    {
        return;
    }

    BoundingBox<cint32> playerBox = thePlayer->getViewBox();
    chunkWindow->setVisibleBox(playerBox);
}

void SinglePlayerWorld::sendMessageToEntity(uint64_t id, const void *msg, size_t size)
{
    deliverEntityMessage(id, msg, size);
}

void SinglePlayerWorld::sendCreateEntityMessageToClients(std::shared_ptr<Entity> entity)
{
    // NOP
}

void SinglePlayerWorld::sendDeleteEntityMessageToClients(uint64_t id)
{
    // NOP
}

void SinglePlayerWorld::onPlayerUpdateOver(Player &player)
{
    // Save to the single-player file.
    player.serializeState(planner.newFile("singleplayer.dat"));
}

void SinglePlayerWorld::drawBlockSelector(GraphicsContext &context, WorldDisplay &worldDisplay, Player &player)
{
    player.drawBlockSelector(context, worldDisplay);
}

void SinglePlayerWorld::onPlayerMessage(Player &player, const void *msg, size_t size)
{
    const Player::MessageHeader *header = reinterpret_cast<const Player::MessageHeader*>(msg);
    if (header->type == Player::MessageType::SendChat && size > 4)
    {
        const char *msgAsChars = reinterpret_cast<const char*>(msg);

        char *buffer = new char[size - 4 + 1];
        buffer[size - 4] = 0;
        std::memcpy(buffer, msgAsChars + 4, size - 4);

        std::string cmd(buffer);
        delete[] buffer;

        if (cmd.size() == 0)
        {
            return;
        }

        for (char& c : cmd)
        {
            if (c == '\n')
            {
                c = ' ';
            }
        }

        if (cmd[0] == '/')
        {
            cmd = cmd.substr(1);
        }

        processCommand(player, cmd);
    }
    else
    {
        LocalWorld::onPlayerMessage(player, msg, size);
    }
}

void SinglePlayerWorld::playSound(Sound &sound, glm::ivec3 blockPos, glm::vec3 vel)
{
    sound.play(glm::vec3(blockPos) + glm::vec3(0.5f), vel);
}

void SinglePlayerWorld::close()
{
    LocalWorld::close();
    thePlayer = nullptr;
}

std::shared_ptr<Player> SinglePlayerWorld::getPlayer(const std::string &userName)
{
    return thePlayer;
}