#ifndef SPRITE_BLOCK_HPP_
#define SPRITE_BLOCK_HPP_

#include <cstddef>
#include <unordered_map>
#include <vector>
#include <Block/Block.hpp>
#include <BoundingBox/BoundingBox.hpp>
#include <BlockAtlas/BlockAtlas.hpp>
#include <BlockSpriteDesigner/BlockSpriteDesigner.hpp>

/**
 * @brief Represents a block made of many subcubes.
 */
class SpriteBlock : public Block
{
protected:
    /**
     * @brief Represents a sprite.
     */
    struct Sprite
    {
        /**
         * @brief Maps a side number to the name of the texture.
         */
        std::array<std::string, 6> texturesOnSides;

        /**
         * @brief Vector of segments. Each bounding box is expressed as ints in the range [0..8].
         */
        std::vector<BoundingBox<int>> segments;

        /**
         * @brief The name of this state, used to identify the sprite directory.
         */
        std::string stateName;

        /**
         * @brief Overriding textures for specific segment indexes.
         */
        std::unordered_map<size_t, std::array<std::string, 6>> segmentTextureOverrides;
    };

    /**
     * @brief Construct a sprite block from a vector of sprites.
     */
    SpriteBlock(std::string name, std::string displayName, float hardness, std::string iconName, std::vector<Sprite> sprites);

    /**
     * @brief Construct a sprite block from a vector of state names.
     */
    SpriteBlock(std::string name, std::string displayName, float hardness, const std::vector<std::string> &stateNames);

    /**
     * @brief Called when the block sprite designer is initializing.
     *
     * By default this does nothing, but you can override it to define block-specific variables,
     * functions, etc for the script engine, for example.
     */
    virtual void onDesignerInitializing(BlockSpriteDesigner &designer, ScriptEngine &engine);

private:
    std::vector<BlockAtlas::SpriteID> spriteIds;
    std::vector<std::vector<BoundingBox<double>>> boxes;
    std::vector<std::string> stateNames;
    bool areAllSpritesFull = true;

public:
    std::vector<Sprite> sprites;
    std::vector<std::shared_ptr<BlockSprite>> generatedSprites;
    std::string iconName;

    virtual void loadBlockSprites(BlockAtlas &atlas, Bitmap &icon, VoxelSprite &defaultSprite) override;
    virtual BlockAtlas::SpriteID getBlockSprite(glm::ivec3 pos, uint8_t meta, World &world) override;
    virtual bool isFullyOpaque() override;
    virtual void getCollisionBoxes(World &world, glm::ivec3 pos, std::vector<BoundingBox<wfixed>> &boxes) override;
    virtual bool isIntersectedByRay(World &world, glm::ivec3 pos, wfvec3 start, wfvec3 dir, Side &side, RGBABytes &collisionKey) override;

    friend class ConveyorBelt;      // TODO: Shouldn't need it here.
};

#endif