#ifndef MNUCLEAR_REACTOR_HPP_
#define MNUCLEAR_REACTOR_HPP_

#include <Machine/IBeltMachine.hpp>
#include <Machine/Machine.hpp>
#include <ItemStack/ItemStack.hpp>
#include <BlockRegistry/BlockRegistry.hpp>
#include <FluidRegistry/FluidRegistry.hpp>
#include <Item/IInventory.hpp>
#include <Item/IEnergyContainingItem.hpp>
#include <Item/IFluidContainingItem.hpp>
#include <Machine/IElectricMachine.hpp>
#include <Machine/IDataMachine.hpp>
#include <vector>
#include <Explosion/Explosion.hpp>

class INuclearReactorComponent;

/**
 * @brief The nuclear reactor machine.
 */
class MNuclearReactor :
    public Machine,
    public IInventory,
    public IElectricMachine,
    public IBeltMachine,
    public IFluidMachine,
    public IDataMachine
{
private:
    static constexpr double explosionPower = 1000.0;
    static constexpr int explosionRadius = 16;

    std::vector<INuclearReactorComponent*> components;
    std::vector<ItemStack> stacks;
    std::vector<int> slotTemperatures;
    bool isSwitchedOnPriv = false;
    bool exploding = false;

    uint8_t newMeta;
    std::unique_ptr<Explosion> explosion;

    ItemStack& rechargeSlot();
    bool updateComponents();
    void updateHeat();
    void updateExplosion();

public:
    int uraniumBurned = 0;
    int plutoniumCaptureCells = 0;

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

    static constexpr int energyCapacity = 35000;
    static constexpr int criticalTemperature = 5000;
    static constexpr float airHeatConductivity = 0.1f;

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

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

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

    /**
     * @brief Returns the amount of energy stored in the battery.
     */
    int getEnergy();

    /**
     * @brief Return the vector of slot temperatures.
     */
    const std::vector<int>& getSlotTemperatures();

    /**
     * @brief Set the temperature of the specified cell.
     */
    void setCellTemperature(glm::ivec2 cellPos, int temperature);

    /**
     * @brief Heat the specified cell by the specified temperature difference.
     */
    void heatCell(glm::ivec2 cellPos, int dt);

    /**
     * @brief Get the temperature of a cell; returns 300 K for out-of-bounds coordinates.
     */
    int getCellTemperature(glm::ivec2 cellPos);

    /**
     * @brief Get the heat conductivity of a cell; returns air conductivity for out-of-bounds coordinates.
     */
    float getCellHeatConductivity(glm::ivec2 cellPos);

    /**
     * @brief Try to pump in and consume the specified fluid. Returns true if successful.
     */
    bool tryConsumeFluid(const std::string &name, int amount);

    /**
     * @brief Generate the specified number of J.
     */
    void generateEnergy(int amount);

    /**
     * @brief Triggers the meltdown alarm for the next tick.
     */
    void meltdown();

    /**
     * @brief Explode the reactor.
     */
    void explode();

    virtual void serializeState(std::vector<uint8_t> &outBytes) override;
    virtual void deserializeState(const uint8_t *data, size_t size) 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 void onItemStackEntering(Side side, ItemStack &stack) override;
    virtual ItemStack tryExtractItem(Side side, ItemFilter &filter, uint32_t modeCode) override;
    virtual bool canConnectExtractor(Side side) override;
    virtual bool canConnectFluidPipe(Side side) override;
    virtual bool canDrainEnergyFromSide(Side side) override;
    virtual Side getDataInputSide() override;
    virtual void onDataInputVerb(std::string verb) override;
    virtual int getMaxFluidToRelease(std::optional<Side> side, std::string fluidName) override;
    virtual void saveMainConfig(MachineConfig &config) override;
    virtual void loadMainConfig(const MachineConfig &config, bool consumed) override;
};

#endif