#ifndef IBELT_MACHINE_HPP_
#define IBELT_MACHINE_HPP_

#include <Serialize/Utils.hpp>
#include <Side/Side.hpp>
#include <ItemStack/ItemStack.hpp>
#include <ItemFilter/ItemFilter.hpp>
#include <Belt/Belt.hpp>

/**
 * @brief Interface implemented by machines which interact with conveyor belts.
 */
class IBeltMachine
{
public:
    struct ExtractionModeSpec
    {
        uint32_t modeCode;
        std::string label;
    };

    virtual ~IBeltMachine() = default;

    /**
     * @brief Called when a stack is entering from a conveyor belt.
     *
     * The stack is passed by reference so that this function can modify it.
     * If the stack is non-empty after this call, the conveyor belt is clogged.
     *
     * By default, nothing happens and the stack is left alone.
     * 
     * @param side The side we are inserting from.
     * @param stack The stack we are trying to insert (will be modified).
     */
    virtual void onItemStackEntering(Side side, ItemStack &stack);

    /**
     * @brief Try to extract an item from the specified side.
     *
     * By default always returns an empty stack.
     * 
     * @param side The side we are extracting from.
     * @param filter The filter which the extracted item must match.
     * @param modeCode The extraction mode code, as defined by the machine; 0 if not defined.
     * @return The resulting stack: either empty, or with 1 item.
     */
    virtual ItemStack tryExtractItem(Side side, ItemFilter &filter, uint32_t modeCode);

    /**
     * @brief Returns true if an item extractor can be connected to the specified side.
     * 
     * By default always returns false.
     */
    virtual bool canConnectExtractor(Side side);

    /**
     * @brief Get the belts branching out, given the specified input side.
     *
     * By default does not output any branching belts.
     * 
     * @param side The side from which items would enter to branch out.
     * @param belts Push the branching belts into this vector.
     */
    virtual void getBeltsBranchingFrom(Side side, std::vector<std::shared_ptr<Belt>> &belts);

    /**
     * Get the list of extraction modes supported by this machine from the given side. By default
     * returns only one mode, with code 0 and the label "Any slot". Code 0 is always taken as the
     * default mode.
     */
    virtual void getExtractionModes(Side side, std::vector<ExtractionModeSpec> &specs);
};

DERIVE_STRUCT_SERIALIZER(
    IBeltMachine::ExtractionModeSpec,
    self.modeCode,
    self.label
);

#endif