#ifndef DATA_CABLE_COMPONENT_HPP_
#define DATA_CABLE_COMPONENT_HPP_

#include <MachineComponent/MachineComponent.hpp>
#include <Machine/IDataMachine.hpp>
#include <ItemStack/ItemStack.hpp>
#include <DataLink/DataLink.hpp>
#include <Utilities/Flags.hpp>
#include <Serialize/Serialize.hpp>

/**
 * @brief Represents the data component of machines.
 */
class DataCableComponent final : public MachineComponent
{
public:
    /**
     * @brief Describes a data input rule.
     */
    struct InputRule
    {
        /**
         * @brief The input channel for which this rule applies.
         */
        DataLink::Channel channel;

        /**
         * @brief The destination state of the channel to which this rule applies.
         * 
         * `false` means the rule applies when the input channel turns OFF, whereas
         * `true` when it turns ON.
         */
        bool destState;

        /**
         * @brief The verb which is passed to the machine if the above change is detected.
         */
        std::string verb;
    };

private:
    DataLink::ChannelBitmap prevInputs;
    DataLink::ChannelBitmap outputs;
    uint8_t sideBitmap = 0;
    std::vector<InputRule> inputRules;

    IDataMachine &dataMachine;

public:
    static std::string tagName;

    DataCableComponent(Machine &machine);

    /**
     * @brief Returns the states of each output cable.
     */
    DataLink::ChannelBitmap getOutputs();

    /**
     * @brief Returns the states of each input cable.
     */
    DataLink::ChannelBitmap getLastKnownInputs();

    /**
     * @brief Sync the specified input rules vector with our rules.
     * 
     * Returns `true` if `inputRules` was updated.
     */
    bool syncInputRules(std::vector<InputRule> &inputRules);

    /**
     * @brief Add a new input rule to the list.
     */
    void addInputRule(InputRule rule);

    /**
     * @brief Remove the input rule with the specified index.
     */
    void removeInputRule(uint32_t index);

    /**
     * @brief Set the channel for the input rule with the given index.
     */
    void setInputRuleChannel(uint32_t index, DataLink::Channel channel);

    /**
     * @brief Set the dest state for the rule with the given index.
     */
    void setInputRuleDestState(uint32_t index, bool destState);

    /**
     * @brief Set the verb for the rule with the given index.
     */
    void setInputRuleVerb(uint32_t index, std::string verb);

    /**
     * Returns the input side.
     */
    Side getInputSide();

    /**
     * Returns the output side.
     */
    Side getOutputSide();

    virtual void serializeState(std::vector<uint8_t> &outBytes) override;
    virtual void deserializeState(const uint8_t *bytes, size_t size) override;
    virtual void onPreUpdate() override;
    virtual void onRemoving() override;
    virtual void saveConfig(std::vector<ItemStack> &consumedItems, std::vector<uint8_t> &data) override;
    virtual void loadConfig(const std::vector<uint8_t> &data, bool consumed) override;
};

DERIVE_STRUCT_SERIALIZER(DataCableComponent::InputRule, self.channel, self.destState, self.verb);

#endif