#include "disk_manager.h"
#include "const.h"

class TKeysIndexManagerDisk::TDiskIterator : public TKeysIndexManager::TIterator {
public:
    TDiskIterator(const IKeysAndPositions& kp, const YxRecord* rec, const IDocProcessor* filter)
        : Filter(filter)
    {
        PosIterator.Init(kp, rec->Offset, rec->Length, rec->Counter, RH_DEFAULT);
        FindFiltered();
    }

    bool Valid() const override {
        return PosIterator.Valid();
    }

    void Next() override {
        ++PosIterator;
        FindFiltered();
    }

    void SkipTo(SUPERLONG pos) override {
        PosIterator.SkipTo(pos);
        FindFiltered();
    }

    SUPERLONG GetPosition() const override {
        return PosIterator.Current();
    }

    TPtr Clone() const override {
        return MakeIntrusive<TDiskIterator>(*this);
    }

private:
    inline void FindFiltered() {
        while (Filter && PosIterator.Valid() && Filter->DocumentBanned(PosIterator.Doc())) {
            ++PosIterator;
        }
    }
    const IDocProcessor* Filter;
    TPosIterator<> PosIterator;
};

bool TKeysIndexManagerDisk::DoOpen() {
    try {
        Reader.Reset(new TRTYKIReader(Path, "keys"));
        Reader->Open();
        TUnbufferedFileInput fi(Path + "/" + NRTYServer::KeysBloomFileName);
        ExistsKeys.Load(&fi);
    } catch (...) {
        NOTICE_LOG << "Can't open special keys data for " << Path << " : " << CurrentExceptionMessage() << Endl;
        return false;
    }
    return Reader && Reader->IsOpen();
}

bool TKeysIndexManagerDisk::DoClose() {
    CHECK_WITH_LOG(!!Reader);
    if (Reader->IsOpen()) {
        Reader->Close();
    }
    Reader.Destroy();
    ExistsKeys.Clear();
    return true;
}

bool TKeysIndexManagerDisk::GetData(const TString& keyName, const TString& value, const IDocProcessor* filter, TSet<SUPERLONG>& result) const {
    TKeysIndexManager::TIterator::TPtr it = CreateIterator(keyName, value, filter);
    for (;it && it->Valid(); it->Next()) {
        result.insert(it->GetPosition());
    }
    return !!it;
}

bool TKeysIndexManagerDisk::HasKey(const TString& keyName, const TString& value) const {
    return ExistsKeys.Has(TString::Join(keyName, '@', value));
}

void TKeysIndexManagerDisk::GetKeyValues(const TString& keyName, TSet<TString>& result) const {
    if (!Reader)
        return;
    TRequestContext rc;
    const IKeysAndPositions& kap = Reader->GetYR()->YMain();
    TString key = keyName + "@";
    for (i32 number = kap.LowerBound(key.data(), rc), block = rc.GetBlockNumber(); const YxRecord* record = kap.EntryByNumber(rc, number, block); ++number) {
        if (!TStringBuf(record->TextPointer).StartsWith(key)) {
            break;
        }
        result.insert(record->TextPointer + key.length());
    }
}

TKeysIndexManager::TIterator::TPtr TKeysIndexManagerDisk::CreateIterator(const TString& keyName, const TString& value, const IDocProcessor* filter) const {
    if (!Reader)
        return nullptr;
    const TString key(TString::Join(keyName, '@', value));
    if (!ExistsKeys.Has(key))
        return nullptr;
    TRequestContext rc;
    const YxRecord* rec = ExactSearch(&Reader->GetYR()->YMain(), rc, key.data());
    if (!rec)
        return nullptr;
    return MakeIntrusive<TDiskIterator>(Reader->GetYR()->YMain(), rec, filter);
}
