#include "protobufparser.h"
#include <tuple>
#include "core/cjson/protobufbuilder.h"
#include "core/schema.h"

namespace reindexer {

ProtobufValue ProtobufParser::ReadValue() {
	bool isArray = false;
	const uint64_t tag = object_.ser.GetVarUint();
	int tagType = (tag & kTypeMask);
	int tagName = (tag >> kTypeBit);
	TagsPath currPath{object_.tagsPath};
	currPath.push_back(tagName);
	KeyValueType itemType = object_.schema.GetFieldType(currPath, isArray);
	if (itemType.Is<KeyValueType::Undefined>()) {
		throw Error(errParseProtobuf, "Field [%d] type is unknown: [%s]", tagName, itemType.Name());
	}
	switch (tagType) {
		case PBUF_TYPE_VARINT:
			if (itemType.Is<KeyValueType::Bool>()) {
				return {Variant(bool(object_.ser.GetVarUint())), tagName, itemType, isArray};
			} else {
				return {Variant(int64_t(object_.ser.GetVarUint())), tagName, itemType, isArray};
			}
		case PBUF_TYPE_FLOAT32:
		case PBUF_TYPE_FLOAT64:
			return {Variant(double(object_.ser.GetDouble())), tagName, itemType, isArray};
		case PBUF_TYPE_LENGTHENCODED:
			return {Variant(p_string(object_.ser.GetPVString())), tagName, itemType, isArray};
		default:
			throw Error(errParseProtobuf, "Type [%d] unexpected while decoding Protobuf", tagType);
	}
}

Variant ProtobufParser::ReadArrayItem(KeyValueType fieldType) {
	return fieldType.EvaluateOneOf([&](KeyValueType::Int64) { return Variant(int64_t(object_.ser.GetVarUint())); },
								   [&](KeyValueType::Int) { return Variant(int(object_.ser.GetVarUint())); },
								   [&](KeyValueType::Double) { return Variant(object_.ser.GetDouble()); },
								   [&](KeyValueType::Bool) { return Variant(object_.ser.GetBool()); },
								   [&](OneOf<KeyValueType::Null, KeyValueType::Composite, KeyValueType::Tuple, KeyValueType::Undefined,
											 KeyValueType::String, KeyValueType::Uuid>) -> Variant {
									   throw Error(errParseProtobuf, "Error parsing packed indexed array: unexpected type [%s]",
												   fieldType.Name());
								   });
}

bool ProtobufParser::IsEof() const { return !(object_.ser.Pos() < object_.ser.Len()); }

ProtobufValue::ProtobufValue() : value(), tagName(0), itemType(KeyValueType::Undefined{}), isArray(false) {}
ProtobufValue::ProtobufValue(Variant&& _value, int _tagName, KeyValueType _itemType, bool _isArray)
	: value(std::move(_value)), tagName(_tagName), itemType(_itemType), isArray(_isArray) {}

}  // namespace reindexer
