#include "mingeo_index.h"

namespace {
    // Increment this value every time when you want the inverted index to be dropped and rebuilt from the l2 records.
    constexpr ui32 GeoInvIndexVersion = 2;
}

using NRTYGeo::TGeoIndexTree;

template <>
struct TSerializer<TGeoIndexTree::TRecord> {
    using TFloat = double;
    static void Save(IOutputStream* s, const TGeoIndexTree::TRecord& record) {
        ::Save(s, record.DocId);

        for (auto&& vl : record.Coords) {
            ::Save(s, vl);
        }
    }

    static void Load(IInputStream* s, TGeoIndexTree::TRecord& record) {
        ::Load(s, record.DocId);

        for (auto& vl : record.Coords) {
            ::Load(s, vl);
        }
    }
};

namespace NRTYGeo {
    void TGeoIndexFormatter::Save(const TFsPath& dest, const TGeoIndex& index) {
        TFileOutput fout(dest);
        fout.SetFinishPropagateMode(true);
        ::Save<ui32>(&fout, 0u); // reserved for CRC
        ::Save<ui32>(&fout, GeoInvIndexVersion); // index format version

        TVector<TPartKey> parts;
        for (auto&& [part, _] : index.Trees) {
            parts.push_back(part);
        }
        Sort(parts);

        ::Save(&fout, parts);
        for (const TPartKey& part: parts) {
            const auto pTree = index.Trees.find(part);
            Y_ASSERT(pTree != index.Trees.end());

            const TVector<TGeoIndexTree::TRecord>& data = pTree->second->GetData();
            ::Save(&fout, data);
        }

        fout.Flush();
        fout.Finish();

        //TODO(yrum): calculate and store a checksum here (see examples in infra/yasm/server/persistence), because TIndexHashChecker does not handle checksum mismatch
    }

    bool TGeoIndexFormatter::Check(const TFsPath& src) {
        if (!src.Exists()) {
            return false;
        }

        ui32 ver;
        ui32 checksum;
        try {
            TFileInput fin(src);
            ::Load<ui32>(&fin, checksum);
            ::Load<ui32>(&fin, ver);
        } catch (...) {
            ERROR_LOG << "TGeoIndexFormatter::Check() failed: " << CurrentExceptionMessage() << Endl;
            return false;
        }

        if (ver != GeoInvIndexVersion) {
            INFO_LOG << "File " << src << " is built with a deprecated version of MinGeo index" << Endl;
            return false;
        }
        return true;
    }

    void TGeoIndexFormatter::Load(const TFsPath& src, TGeoIndex& index) {
        ui32 ver;
        ui32 checksum;
        TRY
            TFileInput fin(src);
            ::Load<ui32>(&fin, checksum);
            ::Load<ui32>(&fin, ver);
            Y_ENSURE(ver == GeoInvIndexVersion, "Unsupported version of MinGeo index");
            TVector<TPartKey> parts;
            ::Load(&fin, parts);

            TGeoIndexBuilder bld;
            for (const TPartKey& part : parts) {
                TGeoIndexTreeBuilder& child = bld.GetChildBuilder(part.first, part.second);
                TVector<TGeoIndexTree::TRecord> data;
                ::Load(&fin, data);
                bool loaded = child.Assign(std::move(data));
                Y_ENSURE(loaded, "Bad MinGeo data in " << src);
            }
            bld.Finalize(index);
        CATCH_AND_RETHROW("TGeoIndexFormatter::Load()");
    }
}
