#include <Block/SpriteBlock/SteamGenerator/SteamGenerator.hpp>
#include <Entity/PhysicsEntity/CameraEntity/Player/Player.hpp>
#include <Milestone/ElectronicsMilestone/ElectronicsMilestone.hpp>
#include <World/LocalWorld/LocalWorld.hpp>
#include <Controller/InventoryController/SteamGeneratorController/SteamGeneratorController.hpp>

std::vector<SteamGenerator::Sprite> SteamGenerator::makeSprites()
{
    std::vector<Sprite> sprites;

    for (uint8_t meta = 0; meta < numStates; meta++)
    {
        Side outputSide(meta & 3);
        Side frontSide = outputSide.opposite();
        const char *frontTexture = (meta & metaActive) ? "steamGenerator/frontOn" : "steamGenerator/front";
        const char *activeStateName = (meta & metaActive) ? "On" : "Off";

        Sprite sprite = {
            {
                "brownBricks",
                "brownBricks",
                "brownBricks",
                "brownBricks",
                "brownBricks",
                "steamGenerator/top"
            },
            {{0, 0, 0, 8, 8, 8}},
            formatString("output$+$+", outputSide, activeStateName),
        };

        sprite.texturesOnSides[outputSide.asNumber()] = "steamGenerator/output";
        sprite.texturesOnSides[frontSide.asNumber()] = frontTexture;

        sprites.push_back(sprite);
    }

    return sprites;
}

SteamGenerator::SteamGenerator(std::string name) :
    SpriteBlock(
        name,
        "Steam generator",
        90.0f,
        "steamGenerator/front",
        makeSprites()
    )
{
    withStoneSounds();
}

bool SteamGenerator::canManuallyDig()
{
    return true;
}

void SteamGenerator::onInteract(LocalWorld &world, Player &player, glm::ivec3 pos, Side side, const RGBABytes &collisionKey)
{
    if (player.isHoldingScrewdriver())
    {
        uint8_t meta = *world.getBlockMeta(pos);
        uint8_t newDir = (meta & 3) + 1;
        uint8_t newRest = meta & ~3;
        world.setBlockMeta(pos, (newDir & 3) | newRest, true);

        return;
    }

    player.achieve(ElectronicsMilestone::ID, ElectronicsMilestone::MakeSteamGenerator);
    player.connectController<SteamGeneratorController>(
        "main:SteamGeneratorController",
        world.getMachine(pos)
    );
}

void SteamGenerator::getHarvestedStacks(LocalWorld &world, glm::ivec3 pos, std::vector<ItemStack> &stacks)
{
    SpriteBlock::getHarvestedStacks(world, pos, stacks);
    
    std::shared_ptr<IInventory> inv = std::dynamic_pointer_cast<IInventory>(world.getMachine(pos));
    if (inv != nullptr)
    {
        int i;
        for (i=0; i<inv->getNumSlots(); i++)
        {
            ItemStack stack = inv->getStackInSlot(i);
            if (!stack.isEmpty())
            {
                stacks.push_back(stack);
            }
        }
    }
}

glm::vec3 SteamGenerator::getLightColor(uint8_t meta)
{
    if (meta & metaActive)
    {
        return glm::vec3(0.5f, 0.2f, 0.0f);
    }

    return glm::vec3(0.0f);
}

float SteamGenerator::getLightVariance(uint8_t meta)
{
    if (meta & metaActive)
    {
        return 0.1f;
    }

    return 0.0f;
}

glm::ivec3 SteamGenerator::getLightOffset(uint8_t meta)
{
    Side outputSide(meta & 3);
    Side frontSide = outputSide.opposite();

    return glm::ivec3(4) + 4 * frontSide.out();
}

Sound& SteamGenerator::getBackgroundSound(uint8_t meta)
{
    if (meta & metaActive)
    {
        return Sound::fire;
    }

    return Sound::NONE;
}

bool SteamGenerator::canPlace(LocalWorld &world, IWorldActor &actor, glm::ivec3 pos, Side side, uint8_t &meta)
{
    meta = actor.getSideFacing2D().opposite().asNumber();
    return true;
}