#ifndef EEPROM_MANAGER
#define EEPROM_MANAGER

#include <Arduino.h>
#include <EEPROM.h>

class EepromUnit;

class EepromManager final
{
public:
    explicit EepromManager();

    friend class EepromUnit;

    /*!
     * \brief Function to register / allocate a part of the EEPROM
     * 
     * This function ALWAYS has to be called in THE SAME ORDER for each unit.
     * Otherwise THE DATA WILL BE LOST!!
     */
    EepromUnit *registerEempromUnit(size_t size);

private:
    int currentAddressEnding;

    template <typename T>
    const T &writeToEeprom(int address, size_t size, const T &t)
    {

        if (sizeof(T) > size) {
            Serial.println("[Error][EepromManager] writing: Size should be: " + String(size) + " but was: " + String(sizeof(T)));
            return t;
        }

        const T &res = EEPROM.put(address, t);
        EEPROM.commit();
        return res;
    }

    template <typename T>
    T &readFromEeprom(int address, size_t size, T &t)
    {
        if (sizeof(T) > size) {
            Serial.println("[Error][EepromManager] reading: Size should be: " + String(size) + " but was: " + String(sizeof(T)));
            return t;
        }

        T &res = EEPROM.get(address, t);

        return res;
    }
};

class EepromUnit final
{
public:
    friend class EepromManager;

    template <typename T>
    const T &write(const T &t)
    {
        return this->manager->writeToEeprom(this->address, this->size, t);
    };

    template <typename T>
    T &read(T &t)
    {
        return this->manager->readFromEeprom(this->address, this->size, t);
    };

private:
    explicit EepromUnit(EepromManager *manager, int address, size_t size);

    EepromManager *manager;
    int address;
    size_t size;
};

#endif // EEPROM_MANAGER