#pragma once
#include <google/protobuf/descriptor.h>
#include <saas/library/rtyt/lib/util/proto_utils.h>
#include <util/generic/vector.h>
#include <util/generic/yexception.h>

namespace NRTYT {

struct TRangeItem {
    TAtomicSharedPtr<::google::protobuf::Message> Row;
    ui32 TableIndex;
};

class TProtoRangesComparator {
    bool Compare(const TRangeItem& a, const TRangeItem& b) const {
        return Compare(a.Row.Get(), a.TableIndex, b.Row.Get(), b.TableIndex);
    }

    bool Compare(::google::protobuf::Message* first, ui32 firstIndex, ::google::protobuf::Message* second, ui32 secondIndex) const {
        if (firstIndex < 0 || firstIndex >= FieldDescriptors.size()) {
            ythrow yexception() << "Invalid usage of protobuf comparator, tableIndex " << firstIndex << " is out of range (0, " << FieldDescriptors.size() << ")";
        }
        if (secondIndex < 0 || secondIndex >= FieldDescriptors.size()) {
            ythrow yexception() << "Invalid usage of protobuf comparator, tableIndex " << secondIndex << " is out of range (0, " << FieldDescriptors.size() << ")";
        }
        for (ui32 descriptorIndex = 0; descriptorIndex < FieldDescriptors[firstIndex].size(); descriptorIndex++) {
            int currentComparison = CompareValues(first, second, FieldDescriptors[firstIndex][descriptorIndex], FieldDescriptors[secondIndex][descriptorIndex]);
            if (currentComparison) {
                return currentComparison < 0;
            }
        }
        return NeedCompareIndexes && (firstIndex < secondIndex);
    }

public:
    TProtoRangesComparator(const TVector<const ::google::protobuf::Descriptor*>& descriptors, const TVector<TString>& groupedColumns, bool needCompareIndexes = true, bool reverse = false)
            : NeedCompareIndexes(needCompareIndexes)
            , Reverse(reverse) {
        for (ui32 i = 0; i < descriptors.size(); i++) {
            FieldDescriptors.emplace_back(GetFieldDescriptorsByName(descriptors[i], groupedColumns));
        }
    }

    bool operator()(const TRangeItem& a, const TRangeItem& b) const {
        bool result = Compare(a, b);
        return Reverse ^ result;
    }

    bool operator()(::google::protobuf::Message* first, ui32 firstIndex, ::google::protobuf::Message* second, ui32 secondIndex) const {
        bool result = Compare(first, firstIndex, second, secondIndex);
        return Reverse ^ result;
    }
    
    void SetCompareIndexes(bool value) {
        NeedCompareIndexes = value;
    }

private:
    TVector<TVector<const ::google::protobuf::FieldDescriptor*>> FieldDescriptors;
    bool NeedCompareIndexes = true;
    bool Reverse = false;
};
} // namespace NRTYT
