// See the file "COPYING" in the main distribution directory for copyright.

#ifndef val_h
#define val_h

// BRO values.

#include <vector>
#include <list>

#include "net_util.h"
#include "Type.h"
#include "Dict.h"
#include "CompHash.h"
#include "BroString.h"
#include "Attr.h"
#include "Timer.h"
#include "ID.h"
#include "Scope.h"
#include "StateAccess.h"
#include "IPAddr.h"

class Val;
class Func;
class BroFile;
class RE_Matcher;
class PrefixTable;
class SerialInfo;

class PortVal;
class AddrVal;
class SubNetVal;

class IntervalVal;
class PatternVal;
class TableVal;
class RecordVal;
class ListVal;
class StringVal;
class EnumVal;
class MutableVal;

class StateAccess;

class VectorVal;

class TableEntryVal;
declare(PDict,TableEntryVal);

typedef union {
	// Used for bool, int, enum.
	bro_int_t int_val;

	// Used for count, counter, port, subnet.
	bro_uint_t uint_val;

	// Used for addr
	IPAddr* addr_val;

	// Used for subnet
	IPPrefix* subnet_val;

	// Used for double, time, interval.
	double double_val;

	BroString* string_val;
	Func* func_val;
	BroFile* file_val;
	RE_Matcher* re_val;
	PDict(TableEntryVal)* table_val;
	val_list* val_list_val;

	vector<Val*>* vector_val;

} BroValUnion;

class Val : public BroObj {
public:
	Val(bool b, TypeTag t)
		{
		val.int_val = b;
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val(int32 i, TypeTag t)
		{
		val.int_val = bro_int_t(i);
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val(uint32 u, TypeTag t)
		{
		val.uint_val = bro_uint_t(u);
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val(int64 i, TypeTag t)
		{
		val.int_val = i;
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val(uint64 u, TypeTag t)
		{
		val.uint_val = u;
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val(double d, TypeTag t)
		{
		val.double_val = d;
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val(Func* f);

	// Note, will unref 'f' when it's done, closing it unless
	// class has ref'd it.
	Val(BroFile* f);

	Val(BroType* t, bool type_type) // Extra arg to differentiate from protected version.
		{
		type = new TypeType(t->Ref());
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val()
		{
		val.int_val = 0;
		type = base_type(TYPE_ERROR);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	virtual ~Val();

	Val* Ref()			{ ::Ref(this); return this; }
	virtual Val* Clone() const;

	int IsZero() const;
	int IsOne() const;

	bro_int_t InternalInt() const;
	bro_uint_t InternalUnsigned() const;
	double InternalDouble() const;

	bro_int_t CoerceToInt() const;
	bro_uint_t CoerceToUnsigned() const;
	double CoerceToDouble() const;

	// Returns a new Val with the "size" of this Val.  What constitutes
	// size depends on the Val's type.
	virtual Val* SizeVal() const;

	// Bytes in total value object.
	virtual unsigned int MemoryAllocation() const;

	// Add this value to the given value (if appropriate).
	// Returns true if succcessful.  is_first_init is true only if
	// this is the *first* initialization of the value, not
	// if it's a subsequent += initialization.
	virtual int AddTo(Val* v, int is_first_init) const;

	// Remove this value from the given value (if appropriate).
	virtual int RemoveFrom(Val* v) const;

	BroType* Type()			{ return type; }
	const BroType* Type() const	{ return type; }

#define CONST_ACCESSOR(tag, ctype, accessor, name) \
	const ctype name() const \
		{ \
		CHECK_TAG(type->Tag(), tag, "Val::CONST_ACCESSOR", type_name) \
		return val.accessor; \
		}

	// Needed for g++ 4.3's pickiness.
#define CONST_ACCESSOR2(tag, ctype, accessor, name) \
	ctype name() const \
		{ \
		CHECK_TAG(type->Tag(), tag, "Val::CONST_ACCESSOR", type_name) \
		return val.accessor; \
		}

	CONST_ACCESSOR2(TYPE_BOOL, bool, int_val, AsBool)
	CONST_ACCESSOR2(TYPE_INT, bro_int_t, int_val, AsInt)
	CONST_ACCESSOR2(TYPE_COUNT, bro_uint_t, uint_val, AsCount)
	CONST_ACCESSOR2(TYPE_COUNTER, bro_uint_t, uint_val, AsCounter)
	CONST_ACCESSOR2(TYPE_DOUBLE, double, double_val, AsDouble)
	CONST_ACCESSOR2(TYPE_TIME, double, double_val, AsTime)
	CONST_ACCESSOR2(TYPE_INTERVAL, double, double_val, AsInterval)
	CONST_ACCESSOR2(TYPE_ENUM, int, int_val, AsEnum)
	CONST_ACCESSOR(TYPE_STRING, BroString*, string_val, AsString)
	CONST_ACCESSOR(TYPE_FUNC, Func*, func_val, AsFunc)
	CONST_ACCESSOR(TYPE_TABLE, PDict(TableEntryVal)*, table_val, AsTable)
	CONST_ACCESSOR(TYPE_RECORD, val_list*, val_list_val, AsRecord)
	CONST_ACCESSOR(TYPE_FILE, BroFile*, file_val, AsFile)
	CONST_ACCESSOR(TYPE_PATTERN, RE_Matcher*, re_val, AsPattern)
	CONST_ACCESSOR(TYPE_VECTOR, vector<Val*>*, vector_val, AsVector)

	const IPPrefix& AsSubNet() const
		{
		CHECK_TAG(type->Tag(), TYPE_SUBNET, "Val::SubNet", type_name)
		return *val.subnet_val;
		}

	BroType* AsType() const
		{
		CHECK_TAG(type->Tag(), TYPE_TYPE, "Val::Type", type_name)
		return type;
		}

	const IPAddr& AsAddr() const
		{
		if ( type->Tag() != TYPE_ADDR )
			BadTag("Val::AsAddr", type_name(type->Tag()));
		return *val.addr_val;
		}

#define ACCESSOR(tag, ctype, accessor, name) \
	ctype name() \
		{ \
		CHECK_TAG(type->Tag(), tag, "Val::ACCESSOR", type_name) \
		return val.accessor; \
		}

	// Accessors for mutable values are called AsNonConst* and
	// are protected to avoid external state changes.
	// ACCESSOR(TYPE_STRING, BroString*, string_val, AsString)
	ACCESSOR(TYPE_FUNC, Func*, func_val, AsFunc)
	ACCESSOR(TYPE_FILE, BroFile*, file_val, AsFile)
	ACCESSOR(TYPE_PATTERN, RE_Matcher*, re_val, AsPattern)
	ACCESSOR(TYPE_VECTOR, vector<Val*>*, vector_val, AsVector)

	const IPPrefix& AsSubNet()
		{
		CHECK_TAG(type->Tag(), TYPE_SUBNET, "Val::SubNet", type_name)
		return *val.subnet_val;
		}

	const IPAddr& AsAddr()
		{
		if ( type->Tag() != TYPE_ADDR )
			BadTag("Val::AsAddr", type_name(type->Tag()));
		return *val.addr_val;
		}

	// Gives fast access to the bits of something that is one of
	// bool, int, count, or counter.
	bro_int_t ForceAsInt() const		{ return val.int_val; }
	bro_uint_t ForceAsUInt() const		{ return val.uint_val; }

#define CONVERTER(tag, ctype, name) \
	ctype name() \
		{ \
		CHECK_TAG(type->Tag(), tag, "Val::CONVERTER", type_name) \
		return (ctype)(this); \
		}

	CONVERTER(TYPE_PATTERN, PatternVal*, AsPatternVal)
	CONVERTER(TYPE_PORT, PortVal*, AsPortVal)
	CONVERTER(TYPE_SUBNET, SubNetVal*, AsSubNetVal)
	CONVERTER(TYPE_ADDR, AddrVal*, AsAddrVal)
	CONVERTER(TYPE_TABLE, TableVal*, AsTableVal)
	CONVERTER(TYPE_RECORD, RecordVal*, AsRecordVal)
	CONVERTER(TYPE_LIST, ListVal*, AsListVal)
	CONVERTER(TYPE_STRING, StringVal*, AsStringVal)
	CONVERTER(TYPE_VECTOR, VectorVal*, AsVectorVal)
	CONVERTER(TYPE_ENUM, EnumVal*, AsEnumVal)

#define CONST_CONVERTER(tag, ctype, name) \
	const ctype name() const \
		{ \
		CHECK_TAG(type->Tag(), tag, "Val::CONVERTER", type_name) \
		return (const ctype)(this); \
		}

	CONST_CONVERTER(TYPE_PATTERN, PatternVal*, AsPatternVal)
	CONST_CONVERTER(TYPE_PORT, PortVal*, AsPortVal)
	CONST_CONVERTER(TYPE_SUBNET, SubNetVal*, AsSubNetVal)
	CONST_CONVERTER(TYPE_ADDR, AddrVal*, AsAddrVal)
	CONST_CONVERTER(TYPE_TABLE, TableVal*, AsTableVal)
	CONST_CONVERTER(TYPE_RECORD, RecordVal*, AsRecordVal)
	CONST_CONVERTER(TYPE_LIST, ListVal*, AsListVal)
	CONST_CONVERTER(TYPE_STRING, StringVal*, AsStringVal)
	CONST_CONVERTER(TYPE_VECTOR, VectorVal*, AsVectorVal)

	bool IsMutableVal() const
		{
		return IsMutable(type->Tag());
		}

	const MutableVal* AsMutableVal() const
		{
		if ( ! IsMutableVal() )
			BadTag("Val::AsMutableVal", type_name(type->Tag()));
		return (MutableVal*) this;
		}

	MutableVal* AsMutableVal()
		{
		if ( ! IsMutableVal() )
			BadTag("Val::AsMutableVal", type_name(type->Tag()));
		return (MutableVal*) this;
		}

	void Describe(ODesc* d) const;
	virtual void DescribeReST(ODesc* d) const;

	bool Serialize(SerialInfo* info) const;
	static Val* Unserialize(UnserialInfo* info, TypeTag type = TYPE_ANY)
		{ return Unserialize(info, type, 0); }
	static Val* Unserialize(UnserialInfo* info, const BroType* exact_type)
		{ return Unserialize(info, exact_type->Tag(), exact_type); }

	DECLARE_SERIAL(Val);

#ifdef DEBUG
	// For debugging, we keep a reference to the global ID to which a
	// value has been bound *last*.
	ID* GetID() const
		{
		return bound_id ? global_scope()->Lookup(bound_id) : 0;
		}

	void SetID(ID* id)
		{
		delete [] bound_id;
		bound_id = id ? copy_string(id->Name()) : 0;
		}
#endif

protected:
	Val(BroString* s, TypeTag t)
		{
		val.string_val = s;
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	virtual void ValDescribe(ODesc* d) const;
	virtual void ValDescribeReST(ODesc* d) const;

	Val(TypeTag t)
		{
		type = base_type(t);
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	Val(BroType* t)
		{
		type = t->Ref();
#ifdef DEBUG
		bound_id = 0;
#endif
		}

	ACCESSOR(TYPE_TABLE, PDict(TableEntryVal)*, table_val, AsNonConstTable)
	ACCESSOR(TYPE_RECORD, val_list*, val_list_val, AsNonConstRecord)

	// Just an internal helper.
	static Val* Unserialize(UnserialInfo* info, TypeTag type,
			const BroType* exact_type);

	BroValUnion val;
	BroType* type;

#ifdef DEBUG
	// For debugging, we keep the name of the ID to which a Val is bound.
	const char* bound_id;
#endif

};

class MutableVal : public Val {
public:
	// Each MutableVal gets a globally unique ID that can be used to
	// reference it no matter if it's directly bound to any user-visible
	// ID. This ID is inserted into the global namespace.
	ID* UniqueID() const	{ return id ? id : Bind(); }

	// Returns true if we've already generated a unique ID.
	bool HasUniqueID() const	{ return id; }

	// Transfers the unique ID of the given value to this value. We keep our
	// old ID as an alias.
	void TransferUniqueID(MutableVal* mv);

	// MutableVals can have properties (let's refrain from calling them
	// attributes!).  Most properties are recursive.  If a derived object
	// can contain MutableVals itself, the object has to override
	// {Add,Remove}Properties(). RecursiveProp(state) masks out all non-
	// recursive properties. If this is non-null, an overriden method must
	// call itself with RecursiveProp(state) as argument for all contained
	// values.  (In any case, don't forget to call the parent's method.)
	typedef char Properties;

	static const int PERSISTENT = 0x01;
	static const int SYNCHRONIZED = 0x02;

	// Tracked by NotifierRegistry, not recursive.
	static const int TRACKED = 0x04;

	int RecursiveProps(int prop) const	{ return prop & ~TRACKED; }

	Properties GetProperties() const	{ return props; }
	virtual bool AddProperties(Properties state);
	virtual bool RemoveProperties(Properties state);

	// Whether StateAccess:LogAccess needs to be called.
	bool LoggingAccess() const
		{
#ifndef DEBUG
		return props & (SYNCHRONIZED|PERSISTENT|TRACKED);
#else
		return debug_logger.IsVerbose() ||
			(props & (SYNCHRONIZED|PERSISTENT|TRACKED));
#endif
		}

	virtual uint64 LastModified() const 	{ return last_modified; }

	// Mark value as changed.
	void Modified()
		{
		last_modified = IncreaseTimeCounter();
		}

protected:
	MutableVal(BroType* t) : Val(t)
		{ props = 0; id = 0; last_modified = SerialObj::ALWAYS; }
	MutableVal()	{ props = 0; id = 0; last_modified = SerialObj::ALWAYS; }
	~MutableVal();

	friend class ID;
	friend class Val;

	void SetID(ID* arg_id)	{ Unref(id); id = arg_id; }

	DECLARE_SERIAL(MutableVal);

private:
	ID* Bind() const;

	mutable ID* id;
	list<ID*> aliases;
	Properties props;
	uint64 last_modified;
};

#define Microseconds 1e-6
#define Milliseconds 1e-3
#define Seconds 1.0
#define Minutes (60*Seconds)
#define Hours (60*Minutes)
#define Days (24*Hours)

class IntervalVal : public Val {
public:
	IntervalVal(double quantity, double units);

protected:
	IntervalVal()	{}

	void ValDescribe(ODesc* d) const;

	DECLARE_SERIAL(IntervalVal);
};


// We have four different port name spaces: TCP, UDP, ICMP, and UNKNOWN.
// We distinguish between them based on the bits specified in the *_PORT_MASK
// entries specified below.
#define NUM_PORT_SPACES 4
#define PORT_SPACE_MASK 0x30000

#define TCP_PORT_MASK	0x10000
#define UDP_PORT_MASK	0x20000
#define ICMP_PORT_MASK	0x30000

class PortVal : public Val {
public:
	// Constructors - both take the port number in host order.
	PortVal(uint32 p, TransportProto port_type);
	PortVal(uint32 p);	// used for already-massaged port value.

	Val* SizeVal() const	{ return new Val(val.uint_val, TYPE_INT); }

	// Returns the port number in host order (not including the mask).
	uint32 Port() const;

	// Tests for protocol types.
	int IsTCP() const;
	int IsUDP() const;
	int IsICMP() const;

	TransportProto PortType() const
		{
		if ( IsTCP() )
			return TRANSPORT_TCP;
		else if ( IsUDP() )
			return TRANSPORT_UDP;
		else if ( IsICMP() )
			return TRANSPORT_ICMP;
		else
			return TRANSPORT_UNKNOWN;
		}

protected:
	friend class Val;
	PortVal()	{}

	void ValDescribe(ODesc* d) const;

	DECLARE_SERIAL(PortVal);
};

class AddrVal : public Val {
public:
	AddrVal(const char* text);
	~AddrVal();

	Val* SizeVal() const;

	// Constructor for address already in network order.
	AddrVal(uint32 addr);          // IPv4.
	AddrVal(const uint32 addr[4]); // IPv6.
	AddrVal(const IPAddr& addr);

	unsigned int MemoryAllocation() const;

protected:
	friend class Val;
	AddrVal()	{}
	AddrVal(TypeTag t) : Val(t)	{ }
	AddrVal(BroType* t) : Val(t)	{ }

	DECLARE_SERIAL(AddrVal);
};

class SubNetVal : public Val {
public:
	SubNetVal(const char* text);
	SubNetVal(const char* text, int width);
	SubNetVal(uint32 addr, int width); // IPv4.
	SubNetVal(const uint32 addr[4], int width); // IPv6.
	SubNetVal(const IPAddr& addr, int width);
	SubNetVal(const IPPrefix& prefix);
	~SubNetVal();

	Val* SizeVal() const;

	const IPAddr& Prefix() const;
	int Width() const;
	IPAddr Mask() const;

	bool Contains(const IPAddr& addr) const;

	unsigned int MemoryAllocation() const;

protected:
	friend class Val;
	SubNetVal()	{}

	void ValDescribe(ODesc* d) const;

	DECLARE_SERIAL(SubNetVal);
};

class StringVal : public Val {
public:
	StringVal(BroString* s);
	StringVal(const char* s);
	StringVal(const string& s);
	StringVal(int length, const char* s);

	Val* SizeVal() const
		{ return new Val(val.string_val->Len(), TYPE_COUNT); }

	int Len()		{ return AsString()->Len(); }
	const u_char* Bytes()	{ return AsString()->Bytes(); }
	const char* CheckString() { return AsString()->CheckString(); }

	// Note that one needs to de-allocate the return value of
	// ExpandedString() to avoid a memory leak.
	// char* ExpandedString(int format = BroString::EXPANDED_STRING)
	// 	{ return AsString()->ExpandedString(format); }

	StringVal* ToUpper();

	unsigned int MemoryAllocation() const;

protected:
	friend class Val;
	StringVal()	{}

	void ValDescribe(ODesc* d) const;

	DECLARE_SERIAL(StringVal);
};

class PatternVal : public Val {
public:
	PatternVal(RE_Matcher* re);
	~PatternVal();

	int AddTo(Val* v, int is_first_init) const;

	void SetMatcher(RE_Matcher* re);

	unsigned int MemoryAllocation() const;

protected:
	friend class Val;
	PatternVal()	{}

	void ValDescribe(ODesc* d) const;

	DECLARE_SERIAL(PatternVal);
};

// ListVals are mainly used to index tables that have more than one 
// element in their index.
class ListVal : public Val {
public:
	ListVal(TypeTag t);
	~ListVal();

	TypeTag BaseTag() const		{ return tag; }

	Val* SizeVal() const	{ return new Val(vals.length(), TYPE_COUNT); }

	int Length() const		{ return vals.length(); }
	Val* Index(const int n)		{ return vals[n]; }
	const Val* Index(const int n) const	{ return vals[n]; }

	// Returns an RE_Matcher() that will match any string that
	// includes embedded within it one of the patterns listed
	// (as a string, e.g., "foo|bar") in this ListVal.
	//
	// Assumes that all of the strings in the list are NUL-terminated
	// and do not have any embedded NULs.
	//
	// The return RE_Matcher has not yet been compiled.
	RE_Matcher* BuildRE() const;

	void Append(Val* v);

	// Returns a Set representation of the list (which must be homogeneous).
	TableVal* ConvertToSet() const;

	const val_list* Vals() const	{ return &vals; }
	val_list* Vals()		{ return &vals; }

	void Describe(ODesc* d) const;

	unsigned int MemoryAllocation() const;

protected:
	friend class Val;
	ListVal()	{}

	DECLARE_SERIAL(ListVal);

	val_list vals;
	TypeTag tag;
};

extern double bro_start_network_time;

class TableEntryVal {
public:
	TableEntryVal(Val* v)
		{
		val = v;
		last_access_time = network_time;
		expire_access_time = last_read_update =
			int(network_time - bro_start_network_time);
		}
	~TableEntryVal()	{ }

	Val* Value()	{ return val; }
	void Ref()	{ val->Ref(); }
	void Unref()	{ ::Unref(val); }

	// Returns/sets time of last expiration relevant access to this value.
	double ExpireAccessTime() const
		{ return bro_start_network_time + expire_access_time; }
	void SetExpireAccess(double time)
		{ expire_access_time = int(time - bro_start_network_time); }

	// Returns/sets time of when we propagated the last OP_READ_IDX
	// for this item.
	double LastReadUpdate() const
		{ return bro_start_network_time + last_read_update; }
	void SetLastReadUpdate(double time)
		{ last_read_update = int(time - bro_start_network_time); }

protected:
	friend class TableVal;

	Val* val;
	double last_access_time;

	// The next two entries store seconds since Bro's start.  We use
	// ints here to save a few bytes, as we do not need a high resolution
	// for these anyway.
	int expire_access_time;
	int last_read_update;
};

class TableValTimer : public Timer {
public:
	TableValTimer(TableVal* val, double t);
	~TableValTimer();

	virtual void Dispatch(double t, int is_expire);

	TableVal* Table()	{ return table; }

protected:
	TableVal* table;
};

class CompositeHash;
class TableVal : public MutableVal {
public:
	TableVal(TableType* t, Attributes* attrs = 0);
	~TableVal();

	// Returns true if the assignment typechecked, false if not.
	// Second version takes a HashKey and Unref()'s it when done.
	// If we're a set, new_val has to be nil.
	// If we aren't a set, index may be nil in the second version.
	int Assign(Val* index, Val* new_val, Opcode op = OP_ASSIGN);
	int Assign(Val* index, HashKey* k, Val* new_val, Opcode op = OP_ASSIGN);

	Val* SizeVal() const	{ return new Val(Size(), TYPE_COUNT); }

	// Add the entire contents of the table to the given value,
	// which must also be a TableVal.
	// Returns true if the addition typechecked, false if not.
	// If is_first_init is true, then this is the *first* initialization
	// (and so should be strictly adding new elements).
	int AddTo(Val* v, int is_first_init) const;

	// Same but allows suppression of state operations.
	int AddTo(Val* v, int is_first_init, bool propagate_ops) const;

	// Remove the entire contents.
	void RemoveAll();

	// Remove the entire contents of the table from the given value.
	// which must also be a TableVal.
	// Returns true if the addition typechecked, false if not.
	int RemoveFrom(Val* v) const;

	// Expands any lists in the index into multiple initializations.
	// Returns true if the initializations typecheck, false if not.
	int ExpandAndInit(Val* index, Val* new_val);

	// Returns the element's value if it exists in the table,
	// nil otherwise.  Note, "index" is not const because we
	// need to Ref/Unref it when calling the default function.
	Val* Lookup(Val* index, bool use_default_val = true);

	// Sets the timestamp for the given index to network time.
	// Returns false if index does not exist.
	bool UpdateTimestamp(Val* index);

	// Returns the index corresponding to the given HashKey.
	ListVal* RecoverIndex(const HashKey* k) const;

	// Returns the element if it was in the table, false otherwise.
	Val* Delete(const Val* index);
	Val* Delete(const HashKey* k);

	// Returns a ListVal representation of the table (which must be a set).
	ListVal* ConvertToList(TypeTag t=TYPE_ANY) const;
	ListVal* ConvertToPureList() const;	// must be single index type

	void SetAttrs(Attributes* attrs);
	Attr* FindAttr(attr_tag t) const
		{ return attrs ? attrs->FindAttr(t) : 0; }
	Attributes* Attrs()	{ return attrs; }

	// Returns the size of the table.
	int Size() const	{ return AsTable()->Length(); }
	int RecursiveSize() const;

	void Describe(ODesc* d) const;

	void InitTimer(double delay);
	void DoExpire(double t);

	unsigned int MemoryAllocation() const;

	void ClearTimer(Timer* t)
		{
		if ( timer == t )
			timer = 0;
		}

	HashKey* ComputeHash(const Val* index) const
		{ return table_hash->ComputeHash(index, 1); }

protected:
	friend class Val;
	friend class StateAccess;
	TableVal()	{}

	void Init(TableType* t);

	void CheckExpireAttr(attr_tag at);
	int ExpandCompoundAndInit(val_list* vl, int k, Val* new_val);
	int CheckAndAssign(Val* index, Val* new_val, Opcode op = OP_ASSIGN);

	bool AddProperties(Properties arg_state);
	bool RemoveProperties(Properties arg_state);

	// Calculates default value for index.  Returns 0 if none.
	Val* Default(Val* index);

	// Calls &expire_func and returns its return interval;
	// takes ownership of the reference.
	double CallExpireFunc(Val *idx);

	// Propagates a read operation if necessary.
	void ReadOperation(Val* index, TableEntryVal *v);

	DECLARE_SERIAL(TableVal);

	TableType* table_type;
	CompositeHash* table_hash;
	Attributes* attrs;
	double expire_time;
	Expr* expire_expr;
	TableValTimer* timer;
	IterCookie* expire_cookie;
	PrefixTable* subnets;
	Val* def_val;
};

class RecordVal : public MutableVal {
public:
	RecordVal(RecordType* t);
	~RecordVal();

	Val* SizeVal() const
		{ return new Val(record_type->NumFields(), TYPE_COUNT); }

	void Assign(int field, Val* new_val, Opcode op = OP_ASSIGN);
	Val* Lookup(int field) const;	// Does not Ref() value.
	Val* LookupWithDefault(int field) const;	// Does Ref() value.

	/**
	 * Looks up the value of a field by field name.  If the field doesn't
	 * exist in the record type, it's an internal error: abort.
	 * @param field name of field to lookup.
	 * @param with_default whether to rely on field's &default attribute when
	 * the field has yet to be initialized.
	 * @return the value in field \a field.  It is Ref()'d only if
	 * \a with_default is true.
	 */
	Val* Lookup(const char* field, bool with_default = false) const;

	void Describe(ODesc* d) const;

	// This is an experiment to associate a BroObj within the
	// event engine to a record value in bro script.
	void SetOrigin(BroObj* o)	{ origin = o; }
	BroObj* GetOrigin() const	{ return origin; }

	// Returns a new value representing the value coerced to the given
	// type. If coercion is not possible, returns 0. The non-const
	// version may return the current value ref'ed if its type matches
	// directly.
	//
	// *aggr* is optional; if non-zero, we add to it. See
	// Expr::InitVal(). We leave it out in the non-const version to make
	// the choice unambigious.
	//
	// The *allow_orphaning* parameter allows for a record to be demoted
	// down to a record type that contains less fields.
	RecordVal* CoerceTo(const RecordType* other, Val* aggr, bool allow_orphaning = false) const;
	RecordVal* CoerceTo(RecordType* other, bool allow_orphaning = false);

	unsigned int MemoryAllocation() const;
	void DescribeReST(ODesc* d) const;

protected:
	friend class Val;
	RecordVal()	{}

	bool AddProperties(Properties arg_state);
	bool RemoveProperties(Properties arg_state);

	DECLARE_SERIAL(RecordVal);

	RecordType* record_type;
	BroObj* origin;
};

class EnumVal : public Val {
public:
	EnumVal(int i, EnumType* t) : Val(t)
		{
		val.int_val = i;
		type = t;
		}

	Val* SizeVal() const	{ return new Val(val.int_val, TYPE_INT); }

protected:
	friend class Val;
	EnumVal()	{}

	void ValDescribe(ODesc* d) const;

	DECLARE_SERIAL(EnumVal);
};


class VectorVal : public MutableVal {
public:
	VectorVal(VectorType* t);
	~VectorVal();

	Val* SizeVal() const
		{ return new Val(uint32(val.vector_val->size()), TYPE_COUNT); }

	// Returns false if the type of the argument was wrong.
	// The vector will automatically grow to accomodate the index.
	// 'assigner" is the expression that is doing the assignment;
	// it's just used for pinpointing errors.
	//
	// Note: does NOT Ref() the element! Remember to do so unless
	//       the element was just created and thus has refcount 1.
	//
	bool Assign(unsigned int index, Val* element, Opcode op = OP_ASSIGN);
	bool Assign(Val* index, Val* element, Opcode op = OP_ASSIGN)
		{
		return Assign(index->AsListVal()->Index(0)->CoerceToUnsigned(),
				element, op);
		}

	// Assigns the value to how_many locations starting at index.
	bool AssignRepeat(unsigned int index, unsigned int how_many,
			  Val* element);

	// Returns nil if no element was at that value.
	// Lookup does NOT grow the vector to this size.
	// The Val* variant assumes that the index Val* has been type-checked.
	Val* Lookup(unsigned int index) const;
	Val* Lookup(Val* index)
		{
		bro_uint_t i = index->AsListVal()->Index(0)->CoerceToUnsigned();
		return Lookup(static_cast<unsigned int>(i));
		}

	unsigned int Size() const { return val.vector_val->size(); }

	// Is there any way to reclaim previously-allocated memory when you
	// shrink a vector?  The return value is the old size.
	unsigned int Resize(unsigned int new_num_elements);

	// Won't shrink size.
	unsigned int ResizeAtLeast(unsigned int new_num_elements);

protected:
	friend class Val;
	VectorVal()	{ }

	bool AddProperties(Properties arg_state);
	bool RemoveProperties(Properties arg_state);
	void ValDescribe(ODesc* d) const;

	DECLARE_SERIAL(VectorVal);

	VectorType* vector_type;
};

// Base class for values with types that are managed completely internally,
// with no further script-level operators provided (other than bif
// functions). See OpaqueVal.h for derived classes.
class OpaqueVal : public Val {
public:
	OpaqueVal(OpaqueType* t);
	virtual ~OpaqueVal();

protected:
	friend class Val;
	OpaqueVal() { }

	DECLARE_SERIAL(OpaqueVal);
};

// Checks the given value for consistency with the given type.  If an
// exact match, returns it.  If promotable, returns the promoted version,
// Unref()'ing the original.  If not a match, generates an error message
// and returns nil, also Unref()'ing v.  If is_init is true, then
// the checking is done in the context of an initialization.
extern Val* check_and_promote(Val* v, const BroType* t, int is_init);

// Given a pointer to where a Val's core (i.e., its BRO value) resides,
// returns a corresponding newly-created or Ref()'d Val.  ptr must already
// be properly aligned.  Returns the size of the core in bytes in 'n'.
// If t corresponds to a variable-length type, n must give the size on entry.
Val* recover_val(void* ptr, BroType* t, int& n);

extern int same_val(const Val* v1, const Val* v2);
extern int same_atomic_val(const Val* v1, const Val* v2);
extern bool is_atomic_val(const Val* v);
extern void describe_vals(const val_list* vals, ODesc* d, int offset=0);
extern void delete_vals(val_list* vals);

// True if the given Val* has a vector type.
inline bool is_vector(Val* v)	{ return  v->Type()->Tag() == TYPE_VECTOR; }

#endif
