#ifndef TILE_BASE_CONFIG_CONFIGURATION_H
#define TILE_BASE_CONFIG_CONFIGURATION_H

#pragma once
#include "tile/base/optional.h"
#include "tile/base/ref_ptr.h"
#include "tile/base/slice.h"
#include "tile/base/thread/mutex.h"
#include "tile/sigslot/sigslot.h"
#include <cstdint>

namespace tile {
class Configuration : public RefCounted<Configuration> {
public:
    using Keys = std::vector<std::string>;
    using Ptr  = RefPtr<Configuration>;

    // events
    // Key, Value
    sigslot::signal2<const Slice &, const Slice &> OnChanging;
    // Key, Value
    sigslot::signal2<const Slice &, const Slice &> OnChanged;
    // Key
    sigslot::signal1<const Slice &> OnRemoving;
    // Key
    sigslot::signal1<const Slice &> OnRemoved;

    bool Has(const Slice &key) const;
    void Remove(const Slice &key);
    void EnableEvents(bool enable = true);
    bool EventsEnabled() const;

    Keys keys(const Slice &root = "") const;

#define TILE_DECLARE_CONFIG_GETTER(type, name)                                                                         \
    std::optional<type> Get##name(const Slice &key) const;                                                             \
    type Get##name(const Slice &key, type default_value) const;

#define TILE_DECLARE_CONFIG_SETTER(type, name) virtual void Set##name(const Slice &key, type value);

    std::string GetRawString(const Slice &key, const Slice &default_value) const;
    // std::stirng GetString()
    // int GetInt()
    // unsigned int GetUInt()
    // int16_t GetInt16()
    // int32_t GetInt32()
    // int64_t GetInt64()
    // uint16_t GetUInt16()
    // uint32_t GetUInt32()
    // uint64_t GetUInt64()
    // double GetDouble()
    // bool GetBool()

    // getters
    TILE_DECLARE_CONFIG_GETTER(std::string, String)
    TILE_DECLARE_CONFIG_GETTER(int, Int)
    TILE_DECLARE_CONFIG_GETTER(unsigned int, UInt)
    TILE_DECLARE_CONFIG_GETTER(int16_t, Int16)
    TILE_DECLARE_CONFIG_GETTER(int32_t, Int32)
    TILE_DECLARE_CONFIG_GETTER(int64_t, Int64)
    TILE_DECLARE_CONFIG_GETTER(uint16_t, UInt16)
    TILE_DECLARE_CONFIG_GETTER(uint32_t, UInt32)
    TILE_DECLARE_CONFIG_GETTER(uint64_t, UInt64)
    TILE_DECLARE_CONFIG_GETTER(double, Double)
    TILE_DECLARE_CONFIG_GETTER(bool, Bool)

protected:
    // setters
    // SetString(const Slice &key, const std::string &value)
    // SetInt(const Slice &key, int value)
    // SetUInt(const Slice &key, unsigned int value)
    // SetInt16(const Slice &key, int16_t value)
    // SetInt32(const Slice &key, int32_t value)
    // SetInt64(const Slice &key, int64_t value)
    // SetUInt16(const Slice &key, uint16_t value)
    // SetUInt32(const Slice &key, uint32_t value)
    // SetUInt64(const Slice &key, uint64_t value)
    // SetDouble(const Slice &key, double value)
    // SetBool(const Slice &key, bool value)
    TILE_DECLARE_CONFIG_SETTER(const std::string &, String)
    TILE_DECLARE_CONFIG_SETTER(int, Int)
    TILE_DECLARE_CONFIG_SETTER(unsigned int, UInt)
    TILE_DECLARE_CONFIG_SETTER(int16_t, Int16)
    TILE_DECLARE_CONFIG_SETTER(int32_t, Int32)
    TILE_DECLARE_CONFIG_SETTER(int64_t, Int64)
    TILE_DECLARE_CONFIG_SETTER(uint16_t, UInt16)
    TILE_DECLARE_CONFIG_SETTER(uint32_t, UInt32)
    TILE_DECLARE_CONFIG_SETTER(uint64_t, UInt64)
    TILE_DECLARE_CONFIG_SETTER(double, Double)
    TILE_DECLARE_CONFIG_SETTER(bool, Bool)

#undef TILE_DECLARE_CONFIG_GETTER
#undef TILE_DECLARE_CONFIG_SETTER

protected:
    class ScopedLock {
    public:
        explicit ScopedLock(const Configuration &config) : config_(config) { config_.mutex_.Lock(); }

        ~ScopedLock() { config_.mutex_.Unlock(); }

    private:
        const Configuration &config_;
    };

    virtual bool GetRaw(const Slice &key, std::string *value) const = 0;
    virtual bool SetRaw(const Slice &key, const Slice &value)       = 0;
    virtual void RemoveRaw(const Slice &key)                        = 0;
    virtual Keys Enumerate(const Slice &range) const                = 0;
    void SetRawWithEvent(const Slice &key, const std::string &value);
    virtual ~Configuration();

    friend class std::default_delete<Configuration>;
    friend class LayeredConfiguration;

private:
    mutable Mutex mutex_;
    bool events_enabled_{false};
};
}// namespace tile

#endif// TILE_BASE_CONFIG_CONFIGURATION_H
