
##! Functions for inspecting and manipulating broker data.

%%{
#include "zeek/broker/Data.h"
%%}

module Broker;

## Enumerates the possible types that :zeek:see:`Broker::Data` may be in
## terms of Zeek data types.
enum DataType %{
	NONE,
	BOOL,
	INT,
	COUNT,
	DOUBLE,
	STRING,
	ADDR,
	SUBNET,
	PORT,
	TIME,
	INTERVAL,
	ENUM,
	SET,
	TABLE,
	VECTOR,
%}

type Broker::Data: record;

type Broker::TableItem: record;

function Broker::__data%(d: any%): Broker::Data
	%{
	return zeek::Broker::detail::make_data_val(d);
	%}

function Broker::__data_type%(d: Broker::Data%): Broker::DataType
	%{
	return zeek::Broker::detail::get_data_type(d->AsRecordVal(), frame);
	%}

# For testing only.
function Broker::__opaque_clone_through_serialization%(d: any%): any
	%{
	auto x = d->AsOpaqueVal()->SerializeData();

	if ( ! x )
		{
		zeek::emit_builtin_error("cannot serialize object to clone");
        	return zeek::val_mgr->False();
		}

	return OpaqueVal::UnserializeData(x->AsView());
	%}

function Broker::__set_create%(%): Broker::Data
	%{
	return zeek::Broker::detail::make_data_val(broker::set());
	%}

function Broker::__set_clear%(s: Broker::Data%): bool
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::set>(s->AsRecordVal(),
	                                                               zeek::TYPE_TABLE, frame);
	v.clear();
	return zeek::val_mgr->True();
	%}

function Broker::__set_size%(s: Broker::Data%): count
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::set>(s->AsRecordVal(),
	                                                     zeek::TYPE_TABLE, frame);
	return zeek::val_mgr->Count(static_cast<uint64_t>(v.size()));
	%}

function Broker::__set_contains%(s: Broker::Data, key: any%): bool
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::set>(s->AsRecordVal(),
	                                                     zeek::TYPE_TABLE, frame);
	auto k = zeek::Broker::detail::val_to_data(key);

	if ( ! k )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for key argument");
		return zeek::val_mgr->False();
		}

	return zeek::val_mgr->Bool(v.find(*k) != v.end());
	%}

function Broker::__set_insert%(s: Broker::Data, key: any%): bool
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::set>(s->AsRecordVal(),
	                                                     zeek::TYPE_TABLE, frame);

	auto k = zeek::Broker::detail::val_to_data(key);

	if ( ! k )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for key argument");
		return zeek::val_mgr->False();
		}

	return zeek::val_mgr->Bool(v.insert(std::move(*k)).second);
	%}

function Broker::__set_remove%(s: Broker::Data, key: any%): bool
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::set>(s->AsRecordVal(),
	                                                     zeek::TYPE_TABLE, frame);
	auto k = zeek::Broker::detail::val_to_data(key);

	if ( ! k )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for key argument");
		return zeek::val_mgr->False();
		}

	return zeek::val_mgr->Bool(v.erase(*k) > 0);
	%}

function Broker::__set_iterator%(s: Broker::Data%): opaque of Broker::SetIterator
	%{
	return zeek::make_intrusive<zeek::Broker::detail::SetIterator>(s->AsRecordVal(), zeek::TYPE_TABLE, frame);
	%}

function Broker::__set_iterator_last%(it: opaque of Broker::SetIterator%): bool
	%{
	auto set_it = static_cast<zeek::Broker::detail::SetIterator*>(it);
	return zeek::val_mgr->Bool(set_it->it == set_it->dat.end());
	%}

function Broker::__set_iterator_next%(it: opaque of Broker::SetIterator%): bool
	%{
	auto set_it = static_cast<zeek::Broker::detail::SetIterator*>(it);

	if ( set_it->it == set_it->dat.end() )
		return zeek::val_mgr->False();

	++set_it->it;
	return zeek::val_mgr->Bool(set_it->it != set_it->dat.end());
	%}

function Broker::__set_iterator_value%(it: opaque of Broker::SetIterator%): Broker::Data
	%{
	auto set_it = static_cast<zeek::Broker::detail::SetIterator*>(it);
	auto rval = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);

	if ( set_it->it == set_it->dat.end() )
		{
		zeek::emit_builtin_error("attempt to retrieve value of invalid set iterator");
		return rval;
		}

	rval->Assign(0, zeek::make_intrusive<zeek::Broker::detail::DataVal>(*set_it->it));
	return rval;
	%}

function Broker::__table_create%(%): Broker::Data
	%{
	return zeek::Broker::detail::make_data_val(broker::table());
	%}

function Broker::__table_clear%(t: Broker::Data%): bool
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::table>(t->AsRecordVal(),
	                                                       zeek::TYPE_TABLE, frame);
	v.clear();
	return zeek::val_mgr->True();
	%}

function Broker::__table_size%(t: Broker::Data%): count
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::table>(t->AsRecordVal(),
	                                                       zeek::TYPE_TABLE, frame);
	return zeek::val_mgr->Count(static_cast<uint64_t>(v.size()));
	%}

function Broker::__table_contains%(t: Broker::Data, key: any%): bool
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::table>(t->AsRecordVal(),
	                                                       zeek::TYPE_TABLE, frame);

	auto k = zeek::Broker::detail::val_to_data(key);

	if ( ! k )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for key argument");
		return zeek::val_mgr->False();
		}

	return zeek::val_mgr->Bool(v.find(*k) != v.end());
	%}

function Broker::__table_insert%(t: Broker::Data, key: any, val: any%): Broker::Data
	%{
	auto& table = zeek::Broker::detail::require_data_type<broker::table>(t->AsRecordVal(),
	                                                           zeek::TYPE_TABLE, frame);

	auto k = zeek::Broker::detail::val_to_data(key);

	if ( ! k )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for key argument");
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
		}

	auto v = zeek::Broker::detail::val_to_data(val);

	if ( ! v )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for value argument");
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
		}

	try
		{
		auto& prev = table.at(*k);
		auto rval = zeek::Broker::detail::make_data_val(std::move(prev));
		prev = std::move(*v);
		return rval;
		}
	catch (const std::out_of_range&)
		{
		table[std::move(*k)] = std::move(*v);
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
		}
	%}

function Broker::__table_remove%(t: Broker::Data, key: any%): Broker::Data
	%{
	auto& table = zeek::Broker::detail::require_data_type<broker::table>(t->AsRecordVal(),
	                                                           zeek::TYPE_TABLE, frame);

	auto k = zeek::Broker::detail::val_to_data(key);

	if ( ! k )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for key argument");
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
		}

	auto it = table.find(*k);

	if ( it == table.end() )
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
	else
		{
		auto rval = zeek::Broker::detail::make_data_val(std::move(it->second));
		table.erase(it);
		return rval;
		}
	%}

function Broker::__table_lookup%(t: Broker::Data, key: any%): Broker::Data
	%{
	auto& table = zeek::Broker::detail::require_data_type<broker::table>(t->AsRecordVal(),
	                                                           zeek::TYPE_TABLE, frame);

	auto k = zeek::Broker::detail::val_to_data(key);

	if ( ! k )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for key argument");
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
		}

	auto it = table.find(*k);

	if ( it == table.end() )
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
	else
		return zeek::Broker::detail::make_data_val(it->second);
	%}

function Broker::__table_iterator%(t: Broker::Data%): opaque of Broker::TableIterator
	%{
	return zeek::make_intrusive<zeek::Broker::detail::TableIterator>(t->AsRecordVal(), zeek::TYPE_TABLE, frame);
	%}

function Broker::__table_iterator_last%(it: opaque of Broker::TableIterator%): bool
	%{
	auto ti = static_cast<zeek::Broker::detail::TableIterator*>(it);
	return zeek::val_mgr->Bool(ti->it == ti->dat.end());
	%}

function Broker::__table_iterator_next%(it: opaque of Broker::TableIterator%): bool
	%{
	auto ti = static_cast<zeek::Broker::detail::TableIterator*>(it);

	if ( ti->it == ti->dat.end() )
		return zeek::val_mgr->False();

	++ti->it;
	return zeek::val_mgr->Bool(ti->it != ti->dat.end());
	%}

function Broker::__table_iterator_value%(it: opaque of Broker::TableIterator%): Broker::TableItem
	%{
	auto ti = static_cast<zeek::Broker::detail::TableIterator*>(it);
	auto rval = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::TableItem);
	auto key_val = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
	auto val_val = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);
	rval->Assign(0, key_val);
	rval->Assign(1, val_val);

	if ( ti->it == ti->dat.end() )
		{
		zeek::emit_builtin_error("attempt to retrieve value of invalid table iterator");
		return rval;
		}

	key_val->Assign(0, zeek::make_intrusive<zeek::Broker::detail::DataVal>(ti->it->first));
	val_val->Assign(0, zeek::make_intrusive<zeek::Broker::detail::DataVal>(ti->it->second));
	return rval;
	%}

function Broker::__vector_create%(%): Broker::Data
	%{
	return zeek::Broker::detail::make_data_val(broker::vector());
	%}

function Broker::__vector_clear%(v: Broker::Data%): bool
	%{
	auto& vec = zeek::Broker::detail::require_data_type<broker::vector>(v->AsRecordVal(),
	                                                          zeek::TYPE_VECTOR, frame);
	vec.clear();
	return zeek::val_mgr->True();
	%}

function Broker::__vector_size%(v: Broker::Data%): count
	%{
	auto& vec = zeek::Broker::detail::require_data_type<broker::vector>(v->AsRecordVal(),
	                                                          zeek::TYPE_VECTOR, frame);
	return zeek::val_mgr->Count(static_cast<uint64_t>(vec.size()));
	%}

function Broker::__vector_insert%(v: Broker::Data, idx:count, d: any%): bool
	%{
	auto& vec = zeek::Broker::detail::require_data_type<broker::vector>(v->AsRecordVal(),
	                                                          zeek::TYPE_VECTOR, frame);
	auto item = zeek::Broker::detail::val_to_data(d);

	if ( ! item )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for item argument");
		return zeek::val_mgr->False();
		}

	idx = min(idx, static_cast<uint64_t>(vec.size()));
	vec.insert(vec.begin() + idx, std::move(*item));
	return zeek::val_mgr->True();
	%}

function Broker::__vector_replace%(v: Broker::Data, idx: count, d: any%): Broker::Data
	%{
	auto& vec = zeek::Broker::detail::require_data_type<broker::vector>(v->AsRecordVal(),
	                                                          zeek::TYPE_VECTOR, frame);
	auto item = zeek::Broker::detail::val_to_data(d);

	if ( ! item )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for item argument");
		return zeek::val_mgr->False();
		}

	if ( idx >= vec.size() )
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);

	auto rval = zeek::Broker::detail::make_data_val(std::move(vec[idx]));
	vec[idx] = std::move(*item);
	return rval;
	%}

function Broker::__vector_remove%(v: Broker::Data, idx: count%): Broker::Data
	%{
	auto& vec = zeek::Broker::detail::require_data_type<broker::vector>(v->AsRecordVal(),
	                                                          zeek::TYPE_VECTOR, frame);

	if ( idx >= vec.size() )
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);

	auto rval = zeek::Broker::detail::make_data_val(std::move(vec[idx]));
	vec.erase(vec.begin() + idx);
	return rval;
	%}

function Broker::__vector_lookup%(v: Broker::Data, idx: count%): Broker::Data
	%{
	auto& vec = zeek::Broker::detail::require_data_type<broker::vector>(v->AsRecordVal(),
	                                                          zeek::TYPE_VECTOR, frame);

	if ( idx >= vec.size() )
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);

	return zeek::Broker::detail::make_data_val(vec[idx]);
	%}

function Broker::__vector_iterator%(v: Broker::Data%): opaque of Broker::VectorIterator
	%{
	return zeek::make_intrusive<zeek::Broker::detail::VectorIterator>(v->AsRecordVal(), zeek::TYPE_VECTOR, frame);
	%}

function Broker::__vector_iterator_last%(it: opaque of Broker::VectorIterator%): bool
	%{
	auto vi = static_cast<zeek::Broker::detail::VectorIterator*>(it);
	return zeek::val_mgr->Bool(vi->it == vi->dat.end());
	%}

function Broker::__vector_iterator_next%(it: opaque of Broker::VectorIterator%): bool
	%{
	auto vi = static_cast<zeek::Broker::detail::VectorIterator*>(it);

	if ( vi->it == vi->dat.end() )
		return zeek::val_mgr->False();

	++vi->it;
	return zeek::val_mgr->Bool(vi->it != vi->dat.end());
	%}

function Broker::__vector_iterator_value%(it: opaque of Broker::VectorIterator%): Broker::Data
	%{
	auto vi = static_cast<zeek::Broker::detail::VectorIterator*>(it);
	auto rval = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);

	if ( vi->it == vi->dat.end() )
		{
		zeek::emit_builtin_error("attempt to retrieve value of invalid vector iterator");
		return rval;
		}

	rval->Assign(0, zeek::make_intrusive<zeek::Broker::detail::DataVal>(*vi->it));
	return rval;
	%}

function Broker::__record_create%(sz: count%): Broker::Data
	%{
	return zeek::Broker::detail::make_data_val(broker::vector(sz));
	%}

function Broker::__record_size%(r: Broker::Data%): count
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::vector>(r->AsRecordVal(),
	                                                        zeek::TYPE_RECORD, frame);
	return zeek::val_mgr->Count(static_cast<uint64_t>(v.size()));
	%}

function Broker::__record_assign%(r: Broker::Data, idx: count, d: any%): bool
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::vector>(r->AsRecordVal(),
	                                                       zeek::TYPE_RECORD, frame);
	if ( idx >= v.size() )
		return zeek::val_mgr->False();

	auto item = zeek::Broker::detail::val_to_data(d);

	if ( ! item )
		{
		zeek::emit_builtin_error("invalid Broker data conversion for item argument");
		return zeek::val_mgr->False();
		}

	v[idx] = std::move(*item);
	return zeek::val_mgr->True();
	%}

function Broker::__record_lookup%(r: Broker::Data, idx: count%): Broker::Data
	%{
	auto& v = zeek::Broker::detail::require_data_type<broker::vector>(r->AsRecordVal(),
	                                                        zeek::TYPE_RECORD, frame);

	if ( idx >= v.size() || broker::get_if<broker::none>(&v[idx]) )
		return zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);

	return zeek::Broker::detail::make_data_val(v[idx]);
	%}

function Broker::__record_iterator%(r: Broker::Data%): opaque of Broker::RecordIterator
	%{
	return zeek::make_intrusive<zeek::Broker::detail::RecordIterator>(r->AsRecordVal(), zeek::TYPE_RECORD, frame);
	%}

function Broker::__record_iterator_last%(it: opaque of Broker::RecordIterator%): bool
	%{
	auto ri = static_cast<zeek::Broker::detail::RecordIterator*>(it);
	return zeek::val_mgr->Bool(ri->it == ri->dat.end());
	%}

function Broker::__record_iterator_next%(it: opaque of Broker::RecordIterator%): bool
	%{
	auto ri = static_cast<zeek::Broker::detail::RecordIterator*>(it);

	if ( ri->it == ri->dat.end() )
		return zeek::val_mgr->False();

	++ri->it;
	return zeek::val_mgr->Bool(ri->it != ri->dat.end());
	%}

function Broker::__record_iterator_value%(it: opaque of Broker::RecordIterator%): Broker::Data
	%{
	auto ri = static_cast<zeek::Broker::detail::RecordIterator*>(it);
	auto rval = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::Broker::Data);

	if ( ri->it == ri->dat.end() )
		{
		zeek::emit_builtin_error("attempt to retrieve value of invalid record iterator");
		return rval;
		}

	if ( broker::get_if<broker::none>(&(*ri->it)) )
		return rval; // field isn't set

	rval->Assign(0, zeek::make_intrusive<zeek::Broker::detail::DataVal>(*ri->it));
	return rval;
	%}
