// $Id: Serializer.h,v 1.1 2003/08/31 00:38:11 vern Exp $

#ifndef SERIALIZER_H
#define SERIALIZER_H

#include "ID.h"
#include "List.h"
#include "Expr.h"
#include "ChunkedIO.h"
#include "SerializationFormat.h"

// #define DEBUG_SERIALIZER

class Serializer {
public:
	// Turns on output during unserialization.
	void Verbose()	{ verbose = 1; }

	// Input/output methods.
	bool Read(int* v)	{ return format->Read(v); }
	bool Read(uint32* v)	{ return format->Read(v); }
	bool Read(char* v)	{ return format->Read(v); }
	bool Read(bool* v)	{ return format->Read(v); }
	bool Read(double* d)	{ return format->Read(d); }
	bool Read(char** str, int* len)	{ return format->Read(str, len); }

	void Write(int v, const char* tag = 0)	{ format->Write(v, tag); }
	void Write(uint32 v, const char* tag = 0)	{ format->Write(v, tag); }
	void Write(char v, const char* tag = 0)	{ format->Write(v, tag); }
	void Write(bool v, const char* tag = 0)	{ format->Write(v, tag); }
	void Write(double d, const char* tag = 0)	{ format->Write(d, tag); }
	void Write(const char* s, const char* tag = 0)
		{ format->Write(s, tag); }
	void Write(const char* buf, int len, const char* tag = 0)
		{ format->Write(buf, len, tag); }
	void WriteOpenTag(const char* tag)	{ format->WriteOpenTag(tag); }
	void WriteCloseTag(const char* tag)	{ format->WriteCloseTag(tag); }
	void WriteSeparator()	{ format->WriteSeparator(); }

	void Error(const char* msg);

	// FIXME: Why do we get a compiler error in ConversionSerializer if
	// we make these protected?
	bool Serialize(ID* id);
	bool Serialize(const char* event, val_list* args);

protected:
	// Format defaults to binary serialization.
	Serializer(SerializationFormat* format = 0);
	virtual ~Serializer();

	// Reads next object.
	// If 'block' is true, wait until an object can be read.
	// Returns 0 if no more object available, -1 on error.
	int Unserialize(bool block = false);

	// Callback for IDs not currently known.
	virtual void UnknownID(ID* id)	{};

	// Callback for error messages.
	virtual void ReportError(const char* msg) = 0;

	// Callback for unserialized IDs; id points to ID in global scope,
	// val is unserialized value.
	virtual void GotID(ID* id, Val* val) = 0;

	// Callback for unserialized events.
	virtual void GotEvent(const char* event, double time,
				EventHandlerPtr func, val_list* args) = 0;

	// Magic to recognize state files.
	static const uint32 MAGIC = 0x42525354;

	// This will be increased whenever there is an incompatible change
	// in the data format.
	static const int DATA_FORMAT_VERSION = 4;

	int verbose;
	ChunkedIO* io;

private:
	void StartSerialization();
	bool EndSerialization();

	bool Serializer::UnserializeID();
	bool Serializer::UnserializeEvent();

	SerializationFormat* format;

	const char* error_descr;	// used in error messages
};

// Write values/events to file or fd.
class FileSerializer : public Serializer {
public:
	FileSerializer(SerializationFormat* format = 0);
	virtual ~FileSerializer();

	// Opens the file for serialization.
	bool Open(const char* file, bool pure = false);
	bool Close();

	// Reads the file and prints content to stderr.
	bool Read(const char* file);

protected:
	virtual void UnknownID(ID* id);
	virtual void ReportError(const char* msg);
	virtual void GotID(ID* id, Val* val);
	virtual void GotEvent(const char* event, double time,
				EventHandlerPtr func, val_list* args);

	bool OpenFile(const char* file, bool readonly);
	void CloseFile();
	bool ReadFile(const char* file);
	bool PrepareForWriting();
	bool ReadHeader();

	const char* file;
	int fd;
};

// Converts from one serialization format into another.
class ConversionSerializer:public FileSerializer {
public:
	ConversionSerializer(SerializationFormat* in, SerializationFormat* out);
	virtual ~ConversionSerializer();

	bool Convert(const char* file_in, const char* file_out);

protected:
	virtual void UnknownID(ID* id);
	virtual void GotID(ID* id, Val* val);
	virtual void GotEvent(const char* event, double time,
				EventHandlerPtr func, val_list* args);

	FileSerializer* serout;
};


// Implements persistance for values.
class PersistenceSerializer : public FileSerializer {
public:
	PersistenceSerializer();
	virtual ~PersistenceSerializer();

	// Reads all data.
	bool ReadAll(const char* file);

	// Writes all registered IDs plus data whose declaration
	// was unknown during last ReadAll(). All existing data
	// in file in deleted.
	bool WriteAll(const char* file);

	// Registers the ID to be saved by WriteAll().
	void RegisterID(ID* id)	{ ids.append(id); }

protected:
	virtual void UnknownID(ID* id);
	virtual void GotID(ID* id, Val* val);
	virtual void GotEvent(const char* event, double time,
				EventHandlerPtr func, val_list* args);

private:
	declare(PList, ID);
	typedef PList(ID) id_list;

	id_list ids;
};


// Abstract interface class for external sources providing a stream of events.
class EventSource {
public:
	// Returns time of the oldest event (0 if none available).
	virtual double NextTimestamp() = 0;

	// Dispatches the oldest event and removes it.
	virtual void DispatchNextEvent() = 0;

	// Returns true if there are more events to expect from this source.
	virtual bool IsActive() = 0;
};

// Plays a file of events back (not implemeted yet).
class EventPlayer : public FileSerializer, public EventSource {
public:
	EventPlayer();
	virtual ~EventPlayer();

	bool Replay(const char* file);

	virtual double NextTimestamp();
	virtual void DispatchNextEvent();
	virtual bool IsActive();

protected:
	virtual void UnknownID(ID* id)	{}
	virtual void GotID(ID* id, Val* val)	{}
	virtual void GotEvent(const char* event, double time,
				EventHandlerPtr func, val_list* args);

	double stream_time;	// time of first captured event
	double replay_time;	// network time of replay start

	// Next event waiting to be dispatched.
	double ne_time;
	EventHandlerPtr ne_handler;
	val_list* ne_args;

};

extern EventPlayer* event_player;
extern FileSerializer* event_serializer;
extern PersistenceSerializer* persistence_serializer;

declare(PList, EventSource);
typedef PList(EventSource) event_source_list;
extern event_source_list external_event_sources;

#endif
