#pragma once

#include <saas/rtyserver/common/common_rty.h>
#include <saas/rtyserver/indexer_core/guarded_document.h>
#include <saas/rtyserver/modifier/document_modifier.h>
#include <saas/rtyserver/indexer_disk/metrics.h>
#include <util/generic/ptr.h>

struct TRTYServerConfig;
class TIndexStorage;

class TIndexersPack {
private:
    TMap<ui32, TAtomicSharedPtr<IIndexer> > Indexers;
    TMaybe<ui32> MemoryIndexerID;
    THashMap<ui32, TString> IndexersName;
    const TRTYServerConfig& Config;
    TIndexStorage& IndexStorage;
public:
    enum class TIndexerType {
        itDisk,
        itMemory
    };
    using TPtr = TAtomicSharedPtr<TIndexersPack>;

public:
    TIndexersPack(const TRTYServerConfig& config, TIndexStorage& storage);

    void Spawn(const TString& indexerName, ui32 shard);
    void SpawnAllIndexers(ui32 shard);

    TVector<TAtomicSharedPtr<IIndexer> > ResetIndexers(bool force, ui32 shard, bool timeToLiveSec, bool memoryIndexer, const TString& realmName);
    TVector<TAtomicSharedPtr<IIndexer> > GetIndexers();
    bool IsMemoryIndexer(ui32 indexerID);
    TString GetFirstIndexerName();

    void Index(TQueuedDocument document, ui32 indexerThreadID, TIndexerMetrics* indexerMetrics, const TString& serviceName, TModifyResult& modifyResult);
    void UpdateTimestampsOnDelete(TQueuedDocument& document, ui32 indexerThreadID);
};

class TIndexersList {
    enum TStatus {ACTIVE, STOP, RIGID_STOP};
    TVector<TAtomicSharedPtr<TIndexersPack>> Indexers;
    TVector<TAtomicSharedPtr<TRWMutex>> MutexesIndexThread;
    TMutex Mutex;
    int ShardsCount;
    int ThreadsCount;
    std::atomic<TStatus> Status;

    size_t ShardsNumber() {
        return Indexers.size();
    }

public:
    int GetShardsCount() { return ShardsCount; }
    int GetThreadsCount() { return ThreadsCount; }

    TRWMutex& GetMutex(int shard) {
        return *MutexesIndexThread[shard];
    }

    TAtomicSharedPtr<TIndexersPack> Get(ui32 i) {
        TGuard<TMutex> g(Mutex);
        CHECK_WITH_LOG(i < Indexers.size());
        return Indexers[i];
    }

    size_t Size() {
        return Indexers.size();
    }

    inline void Stop(bool rigidStop) {
        Status = rigidStop ? RIGID_STOP : STOP;
    }

    inline bool IsActive() {
        return Status == ACTIVE;
    }

    inline bool IsRigidStop() {
        return Status == RIGID_STOP;
    }

    TIndexersList(const TRTYServerConfig& config, TIndexStorage& storage, int shards, int threads)
        : Indexers(shards), MutexesIndexThread(shards), ShardsCount(shards), ThreadsCount(threads), Status(ACTIVE)
    {
        for (int icreate = 0; icreate < ShardsCount; icreate++) {
            Indexers[icreate] = new TIndexersPack(config, storage);
            MutexesIndexThread[icreate] = new TRWMutex;
        }
    }

    ~TIndexersList()
    {
    }
};

class TReadShardIndexGuard {
private:
    TIndexersList& Indexers;
    int Shard;
public:
    TReadShardIndexGuard(TIndexersList& indexers, int shard): Indexers(indexers), Shard(shard) {
        Indexers.GetMutex(shard).AcquireRead();
    }
    ~TReadShardIndexGuard() {
        Indexers.GetMutex(Shard).ReleaseRead();
    }
};

class TWriteShardIndexGuard {
private:
    TIndexersList& Indexers;
    int Shard;
public:
    TWriteShardIndexGuard(TIndexersList& indexers, int shard): Indexers(indexers), Shard(shard) {
        Indexers.GetMutex(shard).AcquireWrite();
    }
    ~TWriteShardIndexGuard() {
        Indexers.GetMutex(Shard).ReleaseWrite();
    }
};
