#pragma once

#include <saas/library/storage/abstract.h>

#include <util/folder/path.h>
#include <util/generic/map.h>
#include <util/generic/ptr.h>
#include <util/generic/list.h>
#include <util/system/rwlock.h>

namespace NSaas {
    class TLocalStorage : public IVersionedStorage {
    public:
        TLocalStorage(const TOptions& options);
        ~TLocalStorage();
        virtual bool RemoveNode(const TString& key, bool withHistory = false) const override;
        virtual bool ExistsNode(const TString& key) const override;
        virtual bool GetVersion(const TString& key, i64& version) const override;
        virtual bool GetNodes(const TString& key, TVector<TString>& result, bool withDirs = false) const override;
        virtual bool GetValue(const TString& key, TString& result, i64 version = -1, bool lock = true) const override;
        virtual bool SetValue(const TString& key, const TString& value, bool storeHistory = true, bool lock = true, i64* version = nullptr) const override;
        virtual bool CreatePersistentSequentialNode(const TString& key, const TString& data) const override;
        static IVersionedStorage::TFactory::TRegistrator<TLocalStorage> Registrator;

    protected:
        virtual TAbstractLock::TPtr NativeWriteLockNode(const TString& path, TDuration timeout = TDuration::Seconds(100000)) const override;
        virtual TAbstractLock::TPtr NativeReadLockNode(const TString& path, TDuration timeout = TDuration::Seconds(100000)) const override;

    private:
        class TLock;
        class TNode : public TAtomicRefCount<TNode> {
        public:
            typedef TIntrusivePtr<TNode> TPtr;
            typedef TAtomicSharedPtr<TReadGuard> TReadLock;
            typedef TAtomicSharedPtr<TWriteGuard> TWriteLock;
            typedef TList<TReadLock> TReadLockList;
            typedef TList<TWriteLock> TWriteLockList;

            TNode(const TLocalStorage& storage, const TFsPath& path);
            bool RemoveChild(const TString& child);
            bool GetVersion(i64& version) const;
            void GetChildren(TVector<TString>& result, bool withDirs) const;
            bool GetValue(TString& result, i64 version) const;
            void SetValue(const TString& value, bool storeHistory, i64* version);
            TPtr GetChild(const TFsPath& path, TReadLockList* rl, TWriteLockList* wl);
            TPtr AddOrGetChild(const TFsPath& path, TReadLockList* rl, TWriteLockList* wl);
            void Serialize() const;
            void Deserialize();
            void Lock(TReadLockList* rl, TWriteLockList* wl) const;
       private:
            TPtr GetNearChild(const TStringBuf& name);
            TPtr AddOrGetNearChild(const TStringBuf& name);
            typedef TMap<TString, TPtr> TChildren;
            TChildren Children;
            TVector<TString> Data;
            i64 Version = -1;
            TRWMutex Mutex;
            TFsPath Path;
            const TLocalStorage& Storage;
        };
        TFsPath FullPath(const TString& key) const;
        mutable TNode::TPtr RootNode;
        TRWMutex RootMutex;
    };
}
