// $Id: Serializer.cc,v 1.3 2003/09/02 04:16:06 vern Exp $

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>

#include "Serializer.h"
#include "Scope.h"
#include "Logger.h"
#include "Func.h"
#include "Event.h"
#include "EventRegistry.h"
#include "SerializationFormat.h"

Serializer::Serializer(SerializationFormat* arg_format)
	{
	if ( arg_format )
		format = arg_format;
	else
		format = new BinarySerializationFormat();

	io = 0;
	verbose = 0;
	error_descr = 0;
	}

Serializer::~Serializer()
	{
	delete format;
	}

void Serializer::StartSerialization()
	{
	format->StartWrite();
	}

bool Serializer::EndSerialization()
	{
	ChunkedIO::Chunk* chunk = new ChunkedIO::Chunk;
	chunk->len = format->EndWrite(&chunk->data);
	return io->Write(chunk);
	}

bool Serializer::Serialize(ID* id)
	{
	error_descr = fmt("serializing ID %s", id->Name());

	if ( ! id->IsGlobal() )
		{
		Error("non-global identifiers cannot be serialized");
		return false;
		}

	if ( id->IsConst() || id->IsEnumConst() || id->AsType() )
		{
		Error("const values and types cannot be serialzed");
		return false;
		}

#ifdef DEBUG_SERIALIZER
	fprintf(stderr, ">>> Start serializing ID %s\n", id->Name());
#endif

	StartSerialization();

	WriteOpenTag("id");
	Write('i');
	Write(id->Name(), "name");
	id->ID_Val()->Serialize(this);
	WriteCloseTag("id");
	WriteSeparator();

	if ( ! EndSerialization() )
		{
		Error(io->Error());
		return false;
		}

#ifdef DEBUG_SERIALIZER
	fprintf(stderr, "<<< End serializing ID %s\n", id->Name());
#endif

	return true;
	}

bool Serializer::Serialize(const char* event, val_list* args)
	{
	error_descr = fmt("serializing event %s", event);

#ifdef DEBUG_SERIALIZER
	fprintf(stderr, ">>> Start serializing event %s\n", event);
#endif

	StartSerialization();

	WriteOpenTag("event");
	int a = args->length();
	Write('e');
	Write(event, "name");
	Write(network_time, "time");
	Write(a);

	loop_over_list(*args, i) (*args)[i]->Serialize(this);

	WriteCloseTag("event");
	WriteSeparator();

	if ( ! EndSerialization() )
		{
		Error(io->Error());
		return false;
		}

#ifdef DEBUG_SERIALIZER
	fprintf(stderr, "<<< End serializing event %s\n", event);
#endif

	return true;
	}

int Serializer::Unserialize(bool block)
	{
	error_descr = "unserializing";

	ChunkedIO::Chunk* chunk = 0;

	while ( ! chunk )
		{
		if ( ! io->Read(&chunk) )
			{
			if ( io->Eof() )
				return 0;
			Error(io->Error());
			return -1;
			}

		if ( ! chunk && ! block )
			return 0;
		}

	format->StartRead(chunk->data, chunk->len);

	char type;
	if ( ! format->Read(&type) )
		return false;

	bool result;
	switch ( type ) {
	case 'i':
		result = UnserializeID();
		break;

	case 'e':
		result = UnserializeEvent();
		break;

	default:
		Error(fmt("unknown serialization type %x", (int) type));
		result = false;
	}

	format->EndRead();
	return result;
	}

bool Serializer::UnserializeID()
	{
	char* name;
	if ( ! Read(&name, 0) )
		return false;

	error_descr = fmt("unserializing ID %s", name);

	ID* current = global_scope()->Lookup(name);

	Val* val = Val::Unserialize(this, current ? current->Type() : 0);
	if ( ! val )
		return false;

	if ( current )
		{
		if ( verbose )
			{
			ODesc d;
			val->Describe(&d);
			fprintf(stderr, "ID %s = %s\n", name, d.Description());
			}

		delete [] name;

		GotID(current, val);
		}

	else
		{
		ID* id = new ID(name, SCOPE_GLOBAL, 1);
		id->SetVal(val);
		UnknownID(id);

		if ( verbose )
			fprintf(stderr, "ID %s exists, but type is unknown\n", name);
		}

	return true;
	}

bool Serializer::UnserializeEvent()
	{
	char* name;
	int len;
	double time;

	if ( ! (Read(&name, 0) && Read(&time) && Read(&len)) )
		return false;

	error_descr = fmt("unserializing event %s", name);

	FuncType* functype = 0;
	type_list* types = 0;

	EventHandler* handler = event_registry->Lookup(name);

	if ( handler )
		functype = handler->FType();
	else
		Error("unknown event, ignoring");

	if ( functype )
		{
		types = functype->ArgTypes()->Types();

		if ( types->length() != len )
			{
			Error("wrong number of arguments, ignoring");
			types = 0;
			}
		}

	ODesc d;
	val_list* args = new val_list;
	for ( int i = 0; i < len; ++i )
		{
		Val* v = Val::Unserialize(this, types ? (*types)[i] : 0);

		if ( ! v )
			{
			delete [] name;
			return false;
			}

		if ( verbose && types )
			v->Describe(&d);

		args->append(v);
		}

	if ( verbose )
		fprintf(stderr, "Event [%.8f] %s(%s)\n",
				time, name, types ? d.Description() : "<ignored>");

	if ( types )
		GotEvent(name, time, handler, args);
	else
		delete_vals(args);

	delete [] name;

	return true;
	}

void Serializer::Error(const char* str)
	{
	char buffer[1024];
	snprintf(buffer, sizeof(buffer), "%s%s%s",
		error_descr ? error_descr : "", error_descr ? ": " : "", str);
	ReportError(buffer);
	}

FileSerializer::FileSerializer(SerializationFormat* format): Serializer(format)
	{
	file = 0;
	fd = -1;
	io = 0;
	}

FileSerializer::~FileSerializer()
	{
	delete [] file;
	delete io;

	if ( fd >= 0 )
		close(fd);
	}

bool FileSerializer::Open(const char* file, bool pure)
	{
	if ( ! OpenFile(file, false) )
		return false;

	if ( pure )
		io->MakePure();

	if ( ! PrepareForWriting() )
		return false;

	return true;
	}

bool FileSerializer::Close()
	{
	CloseFile();
	return true;
	}

bool FileSerializer::OpenFile(const char* arg_file, bool readonly)
	{
	CloseFile();

	file = copy_string(arg_file);
	fd = open(file, readonly ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC, 0600);

	if ( fd < 0 )
		{
		if ( readonly && errno == ENOENT )
			{
			CloseFile();
			return true;
			}

		Error(fmt("Can't open file %s for %s: %s",
				file, (readonly ? "reading" : "writing"),
				strerror(errno)));
		return false;
		}

	io = new ChunkedIOFd(fd);

	return io != 0;
	}

void FileSerializer::CloseFile()
	{
	if ( fd >= 0 )
		close(fd);
	fd = -1;

	delete [] file;
	file = 0;

	delete io;
	io = 0;
	}

bool FileSerializer::PrepareForWriting()
	{
	if ( ! io->IsPure() )
		{
		// Write file header.
		uint32 magic = htonl(MAGIC);
		uint16 version = htons(DATA_FORMAT_VERSION);
		uint32 time = htonl((uint32)::time(0));

		if ( write(fd, &magic, sizeof(magic)) != sizeof(magic ) ||
		     write(fd, &version, sizeof(version)) != sizeof(version) ||
		     write(fd, &time, sizeof(time)) != sizeof(time))
			{
			Error(fmt("Can't write file header to %s: %s",
					file, strerror(errno)));
			return false;
			}
		}

	return true;
	}

bool FileSerializer::ReadHeader()
	{
	uint32 magic;
	uint16 version;
	uint32 time;

	if ( read(fd, &magic, sizeof(magic)) != sizeof(magic ) ||
	     read(fd, &version, sizeof(version)) != sizeof(version) ||
	     read(fd, &time, sizeof(time)) != sizeof(time) )
		{
		Error(fmt("Can't read file header from %s: %s",
				file, strerror(errno)));
		return false;
		}

	version = ntohs(version);
	time = ntohl(time);

	if ( verbose )
		{
		time_t teatime = (time_t) time;
		fprintf(stderr, "Date: %s", ctime(&teatime));
		}

	if ( magic != htonl(MAGIC) )
		{
		Error(fmt("%s is not a bro state file", file));
		CloseFile();
		return false;
		}

	if ( version != DATA_FORMAT_VERSION )
		{
		Error(fmt("wrong data format, expected version %d but got version %d", DATA_FORMAT_VERSION, version));
		CloseFile();
		return false;
		}

	return true;
	}

bool FileSerializer::Read(const char* file)
	{
	if ( ! OpenFile(file, true) )
		return false;

	if ( fd < 0 )
		// Not existent, but that's ok.
		return true;

	if ( ! ReadHeader() )
		return false;

	int i;
	while ( (i = Unserialize(true)) > 0 )
		;

	CloseFile();

	return i == 0;
	}

void FileSerializer::ReportError(const char* str)
	{
	run_time(str);
	}

void FileSerializer::UnknownID(ID* id)
	{
	fprintf(stderr, "Unknown ID %s\n", id->Name());
	}

void FileSerializer::GotID(ID* id, Val* val)
	{
	ODesc d;
	val->Describe(&d);
	fprintf(stdout, "ID %s = %s\n", id->Name(), d.Description());
	}

void FileSerializer::GotEvent(const char* event, double time,
				EventHandlerPtr func, val_list* args)
	{
	ODesc d;
	for ( int i = 0; i < args->length(); i++ )
		(*args)[i]->Describe(&d);

	fprintf(stdout, "Event [%.8f] %s(%s)\n", time, event, d.Description());
	}

ConversionSerializer::ConversionSerializer(SerializationFormat* in,
						SerializationFormat* out)
: FileSerializer(in)
	{
	serout = new FileSerializer(out);
	}

ConversionSerializer::~ConversionSerializer()
	{
	delete serout;
	}

bool ConversionSerializer::Convert(const char* file_in, const char* file_out)
	{
	if ( ! serout->Open(file_out, true) )
		return false;

	if ( ! Read(file_in) )
		return false;

	if ( ! serout->Close() )
		return false;

	return true;
	}

void ConversionSerializer::UnknownID(ID* id)
	{
	serout->Serialize(id);
	}

void ConversionSerializer::GotEvent(const char* event, double time,
					EventHandlerPtr func, val_list* args)
	{
	serout->Serialize(event, args);
	}

void ConversionSerializer::GotID(ID* id, Val* val)
	{
	// We first set the id to the value so that we can then
	// serialize the id...
	// Copy attributes from current val.
	Attributes* attr = id->Attrs();
	if ( attr )
		Ref(attr);
	id->SetVal(val);
	id->SetAttrs(attr);
	Unref(attr);

	serout->Serialize(id);
	}

PersistenceSerializer::PersistenceSerializer()
	{
	}

PersistenceSerializer::~PersistenceSerializer()
	{
	}

bool PersistenceSerializer::ReadAll(const char* file)
	{
	return Read(file);
	}

bool PersistenceSerializer::WriteAll(const char* file)
	{
	if ( ! ids.length() )
		return true;

	bro_logger->Log("Checkpointing state...");

	timeval tvstart;
	gettimeofday(&tvstart, 0);

	if ( ! OpenFile(file, false) )
		return false;

	if ( ! PrepareForWriting() )
		return false;

	loop_over_list(ids, i)
		{
		if ( ! Serialize(ids[i]) )
			{
			CloseFile();
			return false;
			}
		}

	CloseFile();

	timeval tvend;
	gettimeofday(&tvend, 0);
	bro_logger->Log(fmt("Checkpointing done (%f secs).",
				(tvend.tv_sec + tvend.tv_usec / 1e6) -
				(tvstart.tv_sec + tvstart.tv_usec / 1e6)));

	return true;
	}

void PersistenceSerializer::UnknownID(ID* id)
	{
	RegisterID(id);
	}

void PersistenceSerializer::GotEvent(const char* event, double time,
					EventHandlerPtr func, val_list* args)
	{
	mgr.QueueEvent(func, args);
	}

void PersistenceSerializer::GotID(ID* id, Val* val)
	{
	// Copy attributes from current val.
	Attributes* attr = id->Attrs();
	if ( attr )
		Ref(attr);
	id->SetVal(val);
	id->SetAttrs(attr);
	Unref(attr);
	}

EventPlayer::EventPlayer()
	{
	stream_time = 0;
	replay_time = 0;
	ne_time = 0;
	}

bool EventPlayer::Replay(const char* file)
	{
	if ( ! OpenFile(file, true) )
		return false;

	if ( fd < 0 )
		{
		Error(fmt("%s does not exist", file));
		return false;
		}

	return ReadHeader();
	}

EventPlayer::~EventPlayer()
	{
	CloseFile();
	}

void EventPlayer::GotEvent(const char* event, double time, EventHandlerPtr func, val_list* args)
	{
	ne_time = time;
	ne_handler = func;
	ne_args = args;
	}

double EventPlayer::NextTimestamp()
	{
	if ( ne_time )
		return ne_time;

	if ( ! io )
		return 0;

	// Read next event if we don't have one waiting.
	if ( ! ne_time )
		Unserialize();

	if ( ! ne_time )
		return 0;

	if ( ! network_time )
		{
		// Network time not initialized yet.
		stream_time = replay_time = ne_time;
		return ne_time;
		}

	if ( ! stream_time )
		{
		// Init base times.
		stream_time = ne_time;
		replay_time = network_time;
		}

	// Scale time.
	ne_time = ne_time - stream_time + network_time;
	return ne_time;
	}

void EventPlayer::DispatchNextEvent()
	{
	if ( ! ( io && ne_time ) )
		return;

	Event(ne_handler, ne_args).Dispatch();

	ne_time = 0;
	}

bool EventPlayer::IsActive()
	{
	if ( ! io )
		return false;

	return ! io->Eof();
	}
