#ifndef ELECTRIC_COMPONENT_HPP_
#define ELECTRIC_COMPONENT_HPP_

#include <MachineComponent/MachineComponent.hpp>
#include <Machine/IElectricMachine.hpp>
#include <ItemStack/ItemStack.hpp>
#include <ElectricLink/ElectricLink.hpp>

/**
 * @brief Represents the electric component for machines.
 */
class ElectricComponent : public MachineComponent
{
private:
    int energy = 0;
    uint8_t sideBitmap = 0;
    IElectricMachine &electricMachine;

public:
    static std::string tagName;

    ElectricComponent(Machine &machine);

    /**
     * @brief If we are below `maxEnergy`, try to fill up to that amount by charging from `inputSide`.
     */
    void rechargeFrom(Side inputSide, int maxEnergy);

    /**
     * @brief If we are below `maxEnergy`, try to fill up to that amount by charging from an energy item.
     * 
     * @param stack The stack which could contain
     * @param maxEnergy Max energy to be stored.
     */
    void rechargeFrom(ItemStack &stack, int maxEnergy);

    /**
     * @brief If we are below `maxEnergy`, try to fill up to that amount by charing from a link.
     *
     * The link is allowed to be `nullptr` in which case nothing happens.
     */
    void rechargeFrom(std::shared_ptr<ElectricLink> link, int maxEnergy);

    /**
     * @brief Recharge by at most the specified `amount`, reaching a total of at most `maxEnergy`.
     */
    void rechargeBy(int amount, int maxEnergy);

    /**
     * @brief Returns the current energy level.
     */
    int getEnergy();

    /**
     * @brief Add the specified amount of energy to the machine.
     */
    void addEnergy(int amount);

    /**
     * @brief Try to drain the specified amount of energy, and return the amount actually drained.
     */
    int drainEnergy(int amount);

    /**
     * @brief Drain as much energy as possible into the stack.
     * 
     * @param stack A stack which may contain an energy item.
     */
    void drainEnergyInto(ItemStack &stack);

    /**
     * @brief Try to drain the specified amount of energy, and return the amount actually drained.
     * 
     * @param side The side of the machine to drain from (returns 0 if not allowed).
     * @param amount The maximum amount to drain (J).
     */
    int drainEnergyFromSide(Side side, int amount);

    /**
     * @brief Try to consume the specified amount of energy.
     * 
     * Returns true if successfully consumed, otherwise no energy was drained.
     */
    bool tryConsume(int amount);

    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;
};

#endif