#ifndef MSUBSTATION_HPP_
#define MSUBSTATION_HPP_

#include <Serialize/Serialize.hpp>
#include <Serialize/Utils.hpp>
#include <optional>
#include <vector>
#include <Machine/Machine.hpp>
#include <ItemStack/ItemStack.hpp>
#include <Item/IInventory.hpp>
#include <Machine/IElectricMachine.hpp>
#include <Machine/IDataMachine.hpp>

/**
 * @brief The substation machine.
 */
class MSubstation :
    public Machine,
    public IInventory,
    public IElectricMachine,
    public IDataMachine
{
public:
    enum class Operator : uint8_t
    {
        Never = 0,
        EnergyLessThan = 1,
        EnergyMoreThan = 2,
    };

    struct Condition
    {
        Operator op;
        int energy;
    };

    struct Stats
    {
        int powerIn = 0;
        int powerOut = 0;

        inline bool operator==(const Stats &other) const
        {
            return powerIn == other.powerIn && powerOut == other.powerOut;
        }
    };

private:
    std::vector<ItemStack> stacks;
    Stats stats;
    std::optional<int> lastEnergyLevel;

    ItemStack& inputSlot();
    ItemStack& outputSlot();
    bool isConditionTrue(const Condition &cond);

    std::unordered_map<DataLink::Channel, Condition> channelConditions;
    int channelUpdatedCounter = 0;

public:
    static constexpr int capacity = 2000000;

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

    /**
     * @brief Get the energy stored (J).
     */
    int getEnergy();

    /**
     * @brief Set the condition for when the specified channel should be active.
     */
    void setCondition(DataLink::Channel channel, Operator op, int energy);

    /**
     * @brief Get the channel update counter.
     */
    int getChannelUpdateCounter();

    /**
     * @brief Get the map of channel conditions.
     */
    const std::unordered_map<DataLink::Channel, Condition>& getChannelConditions();

    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 int getNumSlots() override;
    virtual ItemStack getStackInSlot(int slot) override;
    virtual void setStackInSlot(int slot, ItemStack stack) override;
    virtual bool isAllowedStackInSlot(int slot, ItemStack stack) override;
    
    virtual void update() override;

    virtual bool canConnectElectricWire(Side side) override;
    virtual bool canDrainEnergyFromSide(Side side) override;

    virtual Side getDataInputSide() override;
    virtual DataLink::ChannelBitmap getActiveChannels() override;

    friend class SubstationController;
};

DERIVE_STATIC_CAST_SERIALIZER(MSubstation::Operator, uint8_t);
DERIVE_STRUCT_SERIALIZER(MSubstation::Condition, self.op, self.energy);
DERIVE_STRUCT_SERIALIZER(MSubstation::Stats, self.powerIn, self.powerOut);

#endif