// $Id: Val.h,v 1.64 2003/08/31 00:38:11 vern Exp $
//
// Copyright (c) 1995, 1996, 1997, 1998, 1999, 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#ifndef val_h
#define val_h

// BRO values.

#include "net_util.h"
#include "Type.h"
#include "Dict.h"
#include "CompHash.h"
#include "BroString.h"
#include "Attr.h"
#include "Timer.h"

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

class PortVal;
class AddrVal;
class NetVal;
class SubNetVal;

class IntervalVal;
class PatternVal;
class TimerVal;
class AnyVal;
class TableVal;
class UnionVal;
class RecordVal;
class ListVal;
class StringVal;

class TableEntryVal;
declare(PDict,TableEntryVal);

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

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

	// Used for addr, net
	addr_type addr_val;

	// Used for subnet
	subnet_type 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;

} BroValUnion;

class Val : public BroObj {
public:
	Val(int i, TypeTag t)
		{
		val.int_val = i;
		type = base_type(t);
		attribs = 0;
		}

	Val(uint32 u, TypeTag t)
		{
		val.uint_val = u;
		type = base_type(t);
		attribs = 0;
		}

	Val(double d, TypeTag t)
		{
		val.double_val = d;
		type = base_type(t);
		attribs = 0;
		}

	Val(Func* f);
	Val(BroFile* f);

	Val()
		{
		val.int_val = 0;
		type = base_type(TYPE_ERROR);
		attribs = 0;
		}

	virtual ~Val();

	Val* Ref()			{ ::Ref(this); return this; }

	RecordVal* GetAttribs(bool instantiate);
	void SetAttribs(RecordVal* arg_attribs)
		{
		Unref((Val*) attribs);
		attribs = arg_attribs;
		}

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

	int InternalInt() const;
	uint32 InternalUnsigned() const;
	double InternalDouble() const;

	int CoerceToInt() const;
	uint32 CoerceToUnsigned() const;
	double CoerceToDouble() const;

	int Size() const;	// bytes in total value object

	// 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 \
		{ \
		if ( type->Tag() != tag ) \
			BadTag("Val::ACCESSOR"); \
		return val.accessor; \
		}

	CONST_ACCESSOR(TYPE_BOOL, int, int_val, AsBool)
	CONST_ACCESSOR(TYPE_INT, int, int_val, AsInt)
	CONST_ACCESSOR(TYPE_COUNT, uint32, uint_val, AsCount)
	CONST_ACCESSOR(TYPE_COUNTER, uint32, uint_val, AsCounter)
	CONST_ACCESSOR(TYPE_DOUBLE, double, double_val, AsDouble)
	CONST_ACCESSOR(TYPE_TIME, double, double_val, AsTime)
	CONST_ACCESSOR(TYPE_INTERVAL, double, double_val, AsInterval)
	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 subnet_type* AsSubNet() const
		    {
		    if ( type->Tag() != TYPE_SUBNET )
			    BadTag("Val::SubNet");
		    return &val.subnet_val;
		    }

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

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

	ACCESSOR(TYPE_STRING, BroString*, string_val, AsString)
	ACCESSOR(TYPE_FUNC, Func*, func_val, AsFunc)
	ACCESSOR(TYPE_TABLE, PDict(TableEntryVal)*, table_val, AsTable)
	ACCESSOR(TYPE_RECORD, val_list*, val_list_val, AsRecord)
	ACCESSOR(TYPE_FILE, BroFile*, file_val, AsFile)
	ACCESSOR(TYPE_PATTERN, RE_Matcher*, re_val, AsPattern)

	subnet_type* AsSubNet()
		    {
		    if ( type->Tag() != TYPE_SUBNET )
			    BadTag("Val::SubNet");
		    return &val.subnet_val;
		    }

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

#define CONVERTER(tag, ctype, name) \
	ctype name() \
		{ \
		if ( type->Tag() != tag ) \
			BadTag("Val::CONVERTER"); \
		return (ctype)(this); \
		}

	CONVERTER(TYPE_PATTERN, PatternVal*, AsPatternVal)
	CONVERTER(TYPE_PORT, PortVal*, AsPortVal)
	CONVERTER(TYPE_NET, NetVal*, AsNetVal)
	CONVERTER(TYPE_SUBNET, SubNetVal*, AsSubNetVal)
	CONVERTER(TYPE_TABLE, TableVal*, AsTableVal)
	CONVERTER(TYPE_RECORD, RecordVal*, AsRecordVal)
	CONVERTER(TYPE_LIST, ListVal*, AsListVal)
	CONVERTER(TYPE_STRING, StringVal*, AsStringVal)

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

	CONST_CONVERTER(TYPE_PATTERN, PatternVal*, AsPatternVal)
	CONST_CONVERTER(TYPE_PORT, PortVal*, AsPortVal)
	CONST_CONVERTER(TYPE_NET, NetVal*, AsNetVal)
	CONST_CONVERTER(TYPE_SUBNET, SubNetVal*, AsSubNetVal)
	CONST_CONVERTER(TYPE_TABLE, TableVal*, AsTableVal)
	CONST_CONVERTER(TYPE_RECORD, RecordVal*, AsRecordVal)
	CONST_CONVERTER(TYPE_LIST, ListVal*, AsListVal)
	CONST_CONVERTER(TYPE_STRING, StringVal*, AsStringVal)

	void Describe(ODesc* d) const;

	// Serialize this value to fd.
	bool Serialize(Serializer* s) const;
	// If type != nil, no type-checking is done and
	// the type of the created Val only contains the information
	// needed to serialize it again.
	static Val* Unserialize(Serializer* s, BroType* type);

protected:
	Val(BroString* s, TypeTag t)
		{
		val.string_val = s;
		type = base_type(t);
		attribs = 0;
		}

	virtual void ValDescribe(ODesc* d) const;

	Val(TypeTag t)		{ type = base_type(t); attribs = 0; }
	Val(BroType* t)		{ type = t->Ref(); attribs = 0; }

	// Value specific parts of (un-)serialization.
	virtual bool DoSerialize(Serializer* s) const { return true; }
	virtual bool DoUnserialize(Serializer* s) { return true; }

	BroValUnion val;
	BroType* type;
	RecordVal* attribs;
};


#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:
	void ValDescribe(ODesc* d) const;
};

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

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

	// True if TCP port, false otherwise.
	int IsTCP() const;

protected:
	void ValDescribe(ODesc* d) const;
};

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

	// Constructor for address already in network order.
	AddrVal(uint32 addr);
	AddrVal(const uint32* addr);

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

	void Init(uint32 addr);
	void Init(const uint32* addr);
};

class NetVal : public AddrVal {
public:
	NetVal(const char* text);
	NetVal(uint32 addr);
	NetVal(const uint32* addr);

protected:
	void ValDescribe(ODesc* d) const;
};

class SubNetVal : public Val {
public:
	SubNetVal(const char* text);
	SubNetVal(const char* text, int width);
	SubNetVal(uint32 addr, int width);	// for address already massaged
	SubNetVal(const uint32* addr, int width);	// ditto

	int Width() const	{ return val.subnet_val.width; }
	addr_type Mask() const;

	bool Contains(const uint32 addr) const;
	bool Contains(const uint32* addr) const;

protected:
	void Init(const char* text, int width);
	void Init(uint32 addr, int width);
	void Init(const uint32 *addr, int width);

	void ValDescribe(ODesc* d) const;

	bool DoSerialize(Serializer* s) const;
	bool DoUnserialize(Serializer* s);

	uint32 mask;
};

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

	StringVal* ToUpper();

protected:
	void ValDescribe(ODesc* d) const;
};

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

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

	void SetMatcher(RE_Matcher* re);

protected:
	void ValDescribe(ODesc* d) const;
};

class ListVal : public Val {
public:
	ListVal(TypeTag t);
	~ListVal();

	TypeTag BaseTag() const		{ return tag; }

	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 offset of where str includes one of the strings in this
	// ListVal (which had better be a list of strings), nil if none.
	//
	// Assumes that all of the strings in the list are NUL-terminated
	// and do not have any embedded NULs.
	const char* IncludedInString(const char* str) const;

	// 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;

	bool DoSerialize(Serializer* s) const;
	bool DoUnserialize(Serializer* s);

protected:
	val_list vals;
	TypeTag tag;
};

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

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

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

protected:
	friend class TableVal;

	Val* val;
	double last_access_time;
	double expire_access_time;
};

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

	virtual void Dispatch(double t, int is_expire);

	TableVal* Table()    { return table; }

protected:
	TableVal* table;
};

class CompositeHash;
class TableVal : public Val {
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);
	int Assign(Val* index, HashKey* k, Val* new_val);

	// 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;

	// 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.
	Val* Lookup(const Val* index) const;

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

	// 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; }

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

	void Describe(ODesc* d) const;

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

protected:
	void CheckExpireAttr(attr_tag at);
	int ExpandCompoundAndInit(val_list* vl, int k, Val* new_val);
	int CheckAndAssign(Val* index, Val* new_val);
	HashKey* ComputeHash(const Val* index) const
		{ return table_hash->ComputeHash(index, 1); }

	bool DoSerialize(Serializer* s) const;
	bool DoUnserialize(Serializer* s);

	const TableType* table_type;
	CompositeHash* table_hash;
	Attributes* attrs;
	double expire_time;
	Func* expire_func;
	TableValTimer* timer;
	PrefixTable* subnets;
};

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

	void Assign(int field, Val* new_val);
	Val* Lookup(int field) 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; }

protected:
	bool DoSerialize(Serializer* s) const;
	bool DoUnserialize(Serializer* s);

	RecordType* record_type;
	BroObj* origin;
};

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

protected:
	void ValDescribe(ODesc* d) const;
};

// 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, const BroType* t, int& n);

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

#endif
