#ifndef MITEM_EXTRACTOR_HPP_
#define MITEM_EXTRACTOR_HPP_

#include <Item/Item.hpp>
#include <Machine/Machine.hpp>
#include <Machine/IElectricMachine.hpp>
#include <Machine/IBeltMachine.hpp>
#include <Machine/IDataMachine.hpp>
#include <FilterType/FilterType.hpp>
#include <ItemFilter/ItemFilter.hpp>
#include <memory>

/**
 * @brief The machine for the item extractor block.
 */
class MItemExtractor :
    public Machine,
    public IElectricMachine,
    public IBeltMachine,
    public IDataMachine
{
private:
    bool switchedOn = false;
    FilterType filterType = FilterType::All;
    std::vector<ItemStack> filterStacks;
    int filterUpdateCounter = 0;
    std::unique_ptr<ItemFilter> currentFilter;
    uint32_t currentModeCode = 0;
    bool modeSpecsLoaded = false;
    std::vector<IBeltMachine::ExtractionModeSpec> modeSpecs;

    std::unique_ptr<ItemFilter> computeFilter();
    void markFilterUpdated();

public:
    struct Verbs
    {
        static constexpr std::string_view TurnOn = "TurnOn";
        static constexpr std::string_view TurnOff = "TurnOff";
    };

    static constexpr int itemSpacing = 4;
    static constexpr int itemInitialOffset = 5;
    static constexpr int energyPerItem = 5;

    MItemExtractor(LocalWorld &world, glm::ivec3 pos);

    /**
     * @brief Returns true if the extractor is switched on.
     */
    bool isSwitchedOn();

    /**
     * @brief Returns the current filter type.
     */
    FilterType getFilterType();

    /**
     * @brief Sets whether the extractor is switched on.
     */
    void setSwitch(bool on);

    /**
     * @brief Set the filter type for this extractor.
     */
    void setFilterType(FilterType type);

    /**
     * Get the current filter update count.
     */
    int getFilterUpdateCounter();

    /**
     * Get the names of filtered items.
     */
    std::vector<ItemStack> getFilterItemStacks();

    /**
     * @brief Add the following stack to the filter list.
     */
    void addItemFilter(ItemStack stack);

    /**
     * @brief Remove the item filter with the specified index.
     */
    void removeItemFilter(int index);

    bool areModeSpecsLoaded();
    const std::vector<IBeltMachine::ExtractionModeSpec>& getModeSpecs();
    uint32_t getModeCode();
    void setModeCode(uint32_t modeCode);

    virtual void serializeState(std::vector<uint8_t> &outBytes) override;
    virtual void deserializeState(const uint8_t *data, size_t size) override;
    virtual void saveMainConfig(MachineConfig &config) override;
    virtual void loadMainConfig(const MachineConfig &config, bool consumed) override;
    virtual bool canConnectElectricWire(Side side) override;
    virtual void update() override;
    virtual void onDataInputVerb(std::string verb) override;
    virtual Side getDataInputSide() override;
};

#endif