#ifndef __EVENTPIPE_STREAM_H__
#define __EVENTPIPE_STREAM_H__

#include "ep-rt-config.h"

#ifdef ENABLE_PERFTRACING
#include "ep-types.h"
#include "ep-rt.h"

#undef EP_IMPL_GETTER_SETTER
#ifdef EP_IMPL_STREAM_GETTER_SETTER
#define EP_IMPL_GETTER_SETTER
#endif
#include "ep-getter-setter.h"

// the enumeration has a specific set of values to keep it compatible with consumer library
// it's sibling is defined in https://github.com/Microsoft/perfview/blob/10d1f92b242c98073b3817ac5ee6d98cd595d39b/src/FastSerialization/FastSerialization.cs#L2295
typedef enum
{
	FAST_SERIALIZER_TAGS_ERROR                  = 0, // To improve debugability, 0 is an illegal tag.
	FAST_SERIALIZER_TAGS_NULL_REFERENCE         = 1, // Tag for a null object forwardReference.
	FAST_SERIALIZER_TAGS_OBJECT_REFERENCE       = 2, // Followed by StreamLabel
	                                                 // 3 used to belong to ForwardReference, which got removed in V3
	FAST_SERIALIZER_TAGS_BEGIN_OBJECT           = 4, // Followed by Type object, object data, tagged EndObject
	FAST_SERIALIZER_TAGS_BEGIN_PRIVATE_OBJECT   = 5, // Like beginObject, but not placed in interning table on deserialiation
	FAST_SERIALIZER_TAGS_END_OBJECT             = 6, // Placed after an object to mark its end.
	                                                 // 7 used to belong to ForwardDefinition, which got removed in V3
	FAST_SERIALIZER_TAGS_BYTE                   = 8,
	FAST_SERIALIZER_TAGS_INT16,
	FAST_SERIALIZER_TAGS_INT32,
	FAST_SERIALIZER_TAGS_INT64,
	FAST_SERIALIZER_TAGS_SKIP_REGION,
	FAST_SERIALIZER_TAGS_STRING,
	FAST_SERIALIZER_TAGS_BLOB,
	FAST_SERIALIZER_TAGS_LIMIT                       // Just past the last valid tag, used for asserts.
} FastSerializerTags;

/*
 * StreamWriter.
 */

typedef void (*StreamWriterFreeFunc)(void *stream);
typedef bool (*StreamWriterWriteFunc)(void *stream, const uint8_t *buffer, const uint32_t bytes_to_write, uint32_t *bytes_written);

struct _StreamWriterVtable {
	StreamWriterFreeFunc free_func;
	StreamWriterWriteFunc write_func;
};

#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _StreamWriter {
#else
struct _StreamWriter_Internal {
#endif
	StreamWriterVtable *vtable;
};

#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _StreamWriter {
	uint8_t _internal [sizeof (struct _StreamWriter_Internal)];
};
#endif

StreamWriter *
ep_stream_writer_init (
	StreamWriter *stream_writer,
	StreamWriterVtable *vtable);

void
ep_stream_writer_fini (StreamWriter *stream_writer);

bool
ep_stream_writer_write (
	StreamWriter *stream_writer,
	const uint8_t *buffer,
	const uint32_t bytes_to_write,
	uint32_t *bytes_written);

void
ep_stream_writer_free_vcall (StreamWriter *stream_writer);

bool
ep_stream_writer_write_vcall (
	StreamWriter *stream_writer,
	const uint8_t *buffer,
	const uint32_t bytes_to_write,
	uint32_t *bytes_written);

/*
 * FastSerializableObject.
 */

typedef void (*FastSerializableObjectFreeFunc)(void *object);
typedef void (*FastSerializableObjectFastSerializeFunc)(void *object, FastSerializer *fast_serializer);
typedef const ep_char8_t * (*FastSerializableObjectGetTypeNameFunc)(void *object);

struct _FastSerializableObjectVtable {
	FastSerializableObjectFreeFunc free_func;
	FastSerializableObjectFastSerializeFunc fast_serialize_func;
	FastSerializableObjectGetTypeNameFunc get_type_name_func;
};

#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FastSerializableObject {
#else
struct _FastSerializableObject_Internal {
#endif
	FastSerializableObjectVtable *vtable;
	int32_t object_version;
	int32_t min_reader_version;
	bool is_private;
};

#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FastSerializableObject {
	uint8_t _internal [sizeof (struct _FastSerializableObject_Internal)];
};
#endif

EP_DEFINE_GETTER(FastSerializableObject *, fast_serializable_object, FastSerializableObjectVtable *, vtable)

FastSerializableObject *
ep_fast_serializable_object_init (
	FastSerializableObject *fast_serializable_object,
	FastSerializableObjectVtable *vtable,
	int32_t object_version,
	int32_t min_reader_version,
	bool is_private);

void
ep_fast_serializable_object_fini (FastSerializableObject *fast_serializable_object);

void
ep_fast_serializable_object_fast_serialize (
	FastSerializableObject *fast_serializable_object,
	FastSerializer *fast_serializer);

const ep_char8_t *
ep_fast_serializable_object_get_type_name (FastSerializableObject *fast_serializable_object);

void
ep_fast_serializable_object_free_vcall (FastSerializableObject *fast_serializable_object);

const ep_char8_t *
ep_fast_serializable_object_get_type_name_vcall (FastSerializableObject *fast_serializable_object);

void
ep_fast_serializable_object_fast_serialize_vcall (
	FastSerializableObject *fast_serializable_object,
	FastSerializer *fast_serializer);

/*
 * FastSerializer.
 */

#define FAST_SERIALIZER_ALIGNMENT_SIZE 4

#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FastSerializer {
#else
struct _FastSerializer_Internal {
#endif
	StreamWriter *stream_writer;
	uint32_t required_padding;
	bool write_error_encountered;
};

#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FastSerializer {
	uint8_t _internal [sizeof (struct _FastSerializer_Internal)];
};
#endif

EP_DEFINE_GETTER(FastSerializer *, fast_serializer, StreamWriter *, stream_writer)
EP_DEFINE_GETTER(FastSerializer *, fast_serializer, uint32_t, required_padding)
EP_DEFINE_GETTER(FastSerializer *, fast_serializer, bool, write_error_encountered)

FastSerializer *
ep_fast_serializer_alloc (StreamWriter *stream_writer);

void
ep_fast_serializer_free (FastSerializer *fast_serializer);

void
ep_fast_serializer_write_buffer (
	FastSerializer *fast_serializer,
	const uint8_t *buffer,
	uint32_t buffer_len);

#define EP_FAST_SERIALIZER_WRITE_INT(BITS, SIGNEDNESS) \
static \
inline \
void \
ep_fast_serializer_write_##SIGNEDNESS##int##BITS##_t ( \
	FastSerializer *fast_serializer, \
	SIGNEDNESS##int##BITS##_t value) \
{ \
	value = ep_rt_val_##SIGNEDNESS##int##BITS##_t (value); \
	ep_fast_serializer_write_buffer (fast_serializer, (uint8_t *)&value, sizeof (value)); \
}

EP_FAST_SERIALIZER_WRITE_INT (16, )
EP_FAST_SERIALIZER_WRITE_INT (16, u)
EP_FAST_SERIALIZER_WRITE_INT (32, )
EP_FAST_SERIALIZER_WRITE_INT (32, u)
EP_FAST_SERIALIZER_WRITE_INT (64, )
EP_FAST_SERIALIZER_WRITE_INT (64, u)

#undef EP_FAST_SERIALIZER_WRITE_INT

static
inline
void
ep_fast_serializer_write_timestamp (
	FastSerializer *fast_serializer,
	ep_timestamp_t value)
{
	ep_fast_serializer_write_int64_t (fast_serializer, value);
}

void
ep_fast_serializer_write_system_time (
	FastSerializer *fast_serializer,
	const EventPipeSystemTime *system_time);

void
ep_fast_serializer_write_object (
	FastSerializer *fast_serializer,
	FastSerializableObject *fast_serializable_object);

void
ep_fast_serializer_write_string (
	FastSerializer *fast_serializer,
	const ep_char8_t *contents,
	uint32_t contents_len);

void
ep_fast_serializer_write_tag (
	FastSerializer *fast_serializer,
	FastSerializerTags tag,
	const uint8_t *payload,
	uint32_t payload_len);

/*
* FileStream.
*/

#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FileStream {
#else
struct _FileStream_Internal {
#endif
	ep_rt_file_handle_t rt_file;
};

#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FileStream {
	uint8_t _internal [sizeof (struct _FileStream_Internal)];
};
#endif

EP_DEFINE_GETTER(FileStream *, file_stream, ep_rt_file_handle_t, rt_file)
EP_DEFINE_SETTER(FileStream *, file_stream, ep_rt_file_handle_t, rt_file)

FileStream *
ep_file_stream_alloc (void);

void
ep_file_stream_free (FileStream *file_stream);

bool
ep_file_stream_open_write (
	FileStream *file_stream,
	const ep_char8_t *path);

bool
ep_file_stream_close (FileStream *file_stream);

bool
ep_file_stream_write (
	FileStream *file_stream,
	const uint8_t *buffer,
	uint32_t bytes_to_write,
	uint32_t *bytes_written);

/*
 * FileStreamWriter.
 */

#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FileStreamWriter {
#else
struct _FileStreamWriter_Internal {
#endif
	StreamWriter stream_writer;
	FileStream *file_stream;
};

#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _FileStreamWriter {
	uint8_t _internal [sizeof (struct _FileStreamWriter_Internal)];
};
#endif

EP_DEFINE_GETTER_REF(FileStreamWriter *, file_stream_writer, StreamWriter *, stream_writer)
EP_DEFINE_GETTER(FileStreamWriter *, file_stream_writer, FileStream *, file_stream)

FileStreamWriter *
ep_file_stream_writer_alloc (const ep_char8_t *output_file_path);

void
ep_file_stream_writer_free (FileStreamWriter *file_stream_writer);

bool
ep_file_stream_writer_write (
	FileStreamWriter *file_stream_writer,
	const uint8_t *buffer,
	uint32_t bytes_to_write,
	uint32_t *bytes_written);

/*
 * IpcStreamWriter.
 */

#if defined(EP_INLINE_GETTER_SETTER) || defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _IpcStreamWriter {
#else
struct _IpcStreamWriter_Internal {
#endif
	StreamWriter stream_writer;
	IpcStream *ipc_stream;
};

#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_STREAM_GETTER_SETTER)
struct _IpcStreamWriter {
	uint8_t _internal [sizeof (struct _IpcStreamWriter_Internal)];
};
#endif

EP_DEFINE_GETTER_REF(IpcStreamWriter *, ipc_stream_writer, StreamWriter *, stream_writer)
EP_DEFINE_GETTER(IpcStreamWriter *, ipc_stream_writer, IpcStream *, ipc_stream)

IpcStreamWriter *
ep_ipc_stream_writer_alloc (
	uint64_t id,
	IpcStream *stream);

void
ep_ipc_stream_writer_free (IpcStreamWriter *ipc_stream_writer);

bool
ep_ipc_stream_writer_write (
	IpcStreamWriter *ipc_stream_writer,
	const uint8_t *buffer,
	uint32_t bytes_to_write,
	uint32_t *bytes_written);

#endif /* ENABLE_PERFTRACING */
#endif /* __EVENTPIPE_STREAM_H__ */
