#include <AssetManager/AssetManager.hpp>
#include <BlockSprite/BlockSprite.hpp>
#include <VoxelSprite/VoxelSprite.hpp>
#include <filesystem>

BlockSprite::Frame::Frame() :
    colorMap(glm::ivec3(8)),
    lightSpecMap(glm::ivec3(8)),
    biasMap(glm::ivec3(8)),
    refractMap(glm::ivec3(8))
{
    for (int z = 0; z < 8; z++)
    {
        for (int y = 0; y < 8; y++)
        {
            for (int x = 0; x < 8; x++)
            {
                colorMap.at(x, y, z) = VoxelSprite::Color{{0x00, 0x00, 0x00, 0x00}};
                lightSpecMap.at(x, y, z) = VoxelSprite::Color{{0x00, 0x00, 0x00, 0x00}};
                biasMap.at(x, y, z) = VoxelSprite::Color{{0x00, 0x00, 0x00, 0x00}};
                refractMap.at(x, y, z) = VoxelSprite::Color{{0x00, 0x1A, 0x00, 0x00}};
            }
        }
    }
}

BlockSprite::Frame& BlockSprite::addFrame()
{
    size_t index = frames.size();
    frames.push_back(Frame());
    return frames[index];
}

bool BlockSprite::isFullyOpaque()
{
    for (auto &frame : frames)
    {
        for (int z = 0; z < 8; z++)
        {
            for (int y = 0; y < 8; y++)
            {
                for (int x = 0; x < 8; x++)
                {
                    if (x != 0 && x != 7 && y != 0 && y != 7 && z != 0 && z != 7)
                    {
                        continue;
                    }
                    
                    VoxelSprite::Color &color = frame.colorMap.at(x, y, z);
                    if (color.alpha != 0xFF)
                    {
                        return false;
                    }
                }
            }
        }
    }

    return true;
}

void BlockSprite::dumpToDir(std::string dirname)
{
    for (size_t i = 0; i < frames.size(); i++)
    {
        Frame &frame = frames[i];

        std::string frameDir = formatString("$+/frame$+", dirname, i);
        std::filesystem::create_directories(frameDir);

        VoxelSprite lightMap(glm::ivec3(8));
        VoxelSprite specularMap(glm::ivec3(8));

        glm::ivec3 coords;
        for (coords.z = 0; coords.z < 8; coords.z++)
        {
            for (coords.y = 0; coords.y < 8; coords.y++)
            {
                for (coords.x = 0; coords.x < 8; coords.x++)
                {
                    uint8_t alpha = frame.colorMap.at(coords).alpha;
                    VoxelSprite::Color &lightMapColor = lightMap.at(coords);
                    VoxelSprite::Color &specularMapColor = specularMap.at(coords);
                    lightMapColor = frame.lightSpecMap.at(coords);
                    specularMapColor.red = specularMapColor.green = specularMapColor.blue = lightMapColor.alpha;
                    lightMapColor.alpha = alpha;
                    specularMapColor.alpha = alpha;
                }
            }
        }
        
        frame.colorMap.writeToVSP(formatString("$+/$+-colormap.vsp", frameDir, i));
        lightMap.writeToVSP(formatString("$+/$+-lightmap.vsp", frameDir, i));
        specularMap.writeToVSP(formatString("$+/$+-specularmap.vsp", frameDir, i));
        frame.biasMap.writeToVSP(formatString("$+/$+-biasmap.vsp", frameDir, i));
        frame.refractMap.writeToVSP(formatString("$+/$+-refractmap.vsp", frameDir, i));
    }

    std::string collisionMaskPath = formatString("$+/collisionMask.vsp", dirname);
    collisionMask.writeToVSP(collisionMaskPath);
}

void BlockSprite::readFromDir(std::string dirname)
{
    for (size_t i = 0;; i++)
    {
        std::string frameDir = formatString("$+/frame$+", dirname, i);

        std::string colorMapPath = assets.resolve(formatString("$+/$+-colormap.vsp", frameDir, i));
        if (!std::filesystem::exists(colorMapPath))
        {
            break;
        }

        Frame &frame = addFrame();
        if (!frame.colorMap.loadFromVSP(colorMapPath))
        {
            crash(formatString("Failed to load required voxel sprite file '$+'!", colorMapPath));
        }

        VoxelSprite lightMap;
        VoxelSprite specularMap;

        if (lightMap.loadFromVSP(assets.resolve(formatString("$+/$+-lightmap.vsp", frameDir, i))))
        {
            glm::ivec3 pos;
            for (pos.z = 0; pos.z < 8; pos.z++)
            {
                for (pos.y = 0; pos.y < 8; pos.y++)
                {
                    for (pos.x = 0; pos.x < 8; pos.x++)
                    {
                        VoxelSprite::Color &imageColor = lightMap.at(pos);
                        VoxelSprite::Color &spriteColor = frame.lightSpecMap.at(pos);

                        spriteColor.red = imageColor.red;
                        spriteColor.green = imageColor.green;
                        spriteColor.blue = imageColor.blue;
                    }
                }
            }
        }

        if (specularMap.loadFromVSP(assets.resolve(formatString("$+/$+-specularmap.vsp", frameDir, i))))
        {
            glm::ivec3 pos;
            for (pos.z = 0; pos.z < 8; pos.z++)
            {
                for (pos.y = 0; pos.y < 8; pos.y++)
                {
                    for (pos.x = 0; pos.x < 8; pos.x++)
                    {
                        VoxelSprite::Color &imageColor = specularMap.at(pos);
                        VoxelSprite::Color &spriteColor = frame.lightSpecMap.at(pos);

                        spriteColor.alpha = imageColor.green;
                    }
                }
            }
        }

        frame.biasMap.loadFromVSP(assets.resolve(formatString("$+/$+-biasmap.vsp", frameDir, i)));
        frame.refractMap.loadFromVSP(assets.resolve(formatString("$+/$+-refractmap.vsp", frameDir, i)));
    }

    std::string collisionMaskPath = formatString("$+/collisionMask.vsp", dirname);
    collisionMask.loadFromVSP(assets.resolve(collisionMaskPath));
}