#include "ep-rt-config.h"

#ifdef ENABLE_PERFTRACING
#if !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES)

#define EP_IMPL_EVENT_INSTANCE_GETTER_SETTER
#include "ep.h"
#include "ep-event.h"
#include "ep-event-instance.h"
#include "ep-stream.h"
#include "ep-rt.h"

/*
 * Forward declares of all static functions.
 */

static
void
sequence_point_fini (EventPipeSequencePoint *sequence_point);

/*
 * EventPipeEventInstance.
 */

EventPipeEventInstance *
ep_event_instance_alloc (
	EventPipeEvent *ep_event,
	uint32_t proc_num,
	uint64_t thread_id,
	const uint8_t *data,
	uint32_t data_len,
	const uint8_t *activity_id,
	const uint8_t *related_activity_id)
{
	EventPipeEventInstance *instance = ep_rt_object_alloc (EventPipeEventInstance);
	ep_raise_error_if_nok (instance != NULL);

	ep_raise_error_if_nok (ep_event_instance_init (
		instance,
		ep_event,
		proc_num,
		thread_id,
		data,
		data_len,
		activity_id,
		related_activity_id) != NULL);

ep_on_exit:
	return instance;

ep_on_error:
	ep_event_instance_free (instance);
	instance = NULL;
	ep_exit_error_handler ();
}

EventPipeEventInstance *
ep_event_instance_init (
	EventPipeEventInstance *event_instance,
	EventPipeEvent *ep_event,
	uint32_t proc_num,
	uint64_t thread_id,
	const uint8_t *data,
	uint32_t data_len,
	const uint8_t *activity_id,
	const uint8_t *related_activity_id)
{
	EP_ASSERT (event_instance != NULL);

#ifdef EP_CHECKED_BUILD
	event_instance->debug_event_start = 0xDEADBEEF;
	event_instance->debug_event_end = 0xC0DEC0DE;
#endif

	event_instance->ep_event = ep_event;
	event_instance->proc_num = proc_num;
	event_instance->thread_id = thread_id;

	if (activity_id)
		memcpy (&(event_instance->activity_id), activity_id, EP_ACTIVITY_ID_SIZE);

	if (related_activity_id)
		memcpy (&(event_instance->related_activity_id), related_activity_id, EP_ACTIVITY_ID_SIZE);

	event_instance->data = data;
	event_instance->data_len = data_len;

	event_instance->timestamp = ep_perf_timestamp_get ();
	EP_ASSERT (event_instance->timestamp > 0);

	ep_event_instance_ensure_consistency (event_instance);

	return event_instance;
}

void
ep_event_instance_fini (EventPipeEventInstance *ep_event_instance)
{
	;
}

void
ep_event_instance_free (EventPipeEventInstance *ep_event_instance)
{
	ep_return_void_if_nok (ep_event_instance != NULL);

	ep_event_instance_fini (ep_event_instance);
	ep_rt_object_free (ep_event_instance);
}

bool
ep_event_instance_ensure_consistency (const EventPipeEventInstance *ep_event_instance)
{
#ifdef EP_CHECKED_BUILD
	EP_ASSERT (ep_event_instance->debug_event_start == 0xDEADBEEF);
	EP_ASSERT (ep_event_instance->debug_event_end == 0xC0DEC0DE);
#endif

	return true;
}

uint32_t
ep_event_instance_get_aligned_total_size (
	const EventPipeEventInstance *ep_event_instance,
	EventPipeSerializationFormat format)
{
	EP_ASSERT (ep_event_instance != NULL);

	// Calculate the size of the total payload so that it can be written to the file.
	uint32_t payload_len = 0;

	if (format == EP_SERIALIZATION_FORMAT_NETPERF_V3) {
		payload_len =
			// Metadata ID
			sizeof (ep_event_instance->metadata_id) +
			// Thread ID
			sizeof (uint32_t) +
			// TimeStamp
			sizeof (ep_event_instance->timestamp) +
			// Activity ID
			EP_ACTIVITY_ID_SIZE +
			// Related Activity ID
			EP_ACTIVITY_ID_SIZE +
			// Data payload length
			sizeof (ep_event_instance->data_len) +
			// Event payload data
			ep_event_instance->data_len +
			// Prepended stack payload size in bytes
			sizeof (uint32_t) +
			// Stack payload size
			ep_stack_contents_instance_get_size (&ep_event_instance->stack_contents_instance);
	} else if (format == EP_SERIALIZATION_FORMAT_NETTRACE_V4) {
		payload_len =
			// Metadata ID
			sizeof (ep_event_instance->metadata_id) +
			// Sequence number (implied by the buffer containing the event instance)
			sizeof (uint32_t) +
			// Thread ID
			sizeof (ep_event_instance->thread_id) +
			// Capture Thread ID (implied by the buffer containing the event instance)
			sizeof (uint64_t) +
			// ProcNumber
			sizeof (ep_event_instance->proc_num) +
			// Stack intern table id
			sizeof (uint32_t) +
			// TimeStamp
			sizeof (ep_event_instance->timestamp) +
			// Activity ID
			EP_ACTIVITY_ID_SIZE +
			// Related Activity ID
			EP_ACTIVITY_ID_SIZE +
			// Data payload length
			sizeof (ep_event_instance->data_len) +
			// Event payload data
			ep_event_instance->data_len;
	} else {
		EP_ASSERT (!"Unrecognized format");
	}

	// round up to FAST_SERIALIZER_ALIGNMENT_SIZE bytes
	if (payload_len % FAST_SERIALIZER_ALIGNMENT_SIZE != 0)
		payload_len += FAST_SERIALIZER_ALIGNMENT_SIZE - (payload_len % FAST_SERIALIZER_ALIGNMENT_SIZE);

	return payload_len;
}

#ifdef EP_CHECKED_BUILD
#include "ep-json-file.h"
#define MAX_JSON_FILE_MESSAGE_BUFFER_SIZE 512
void
ep_event_instance_serialize_to_json_file (
	EventPipeEventInstance *ep_event_instance,
	EventPipeJsonFile *json_file)
{
	ep_return_void_if_nok (ep_event_instance != NULL);
	ep_return_void_if_nok (json_file != NULL);


	ep_char8_t buffer [MAX_JSON_FILE_MESSAGE_BUFFER_SIZE];
	int32_t characters_written = -1;
	characters_written = ep_rt_utf8_string_snprintf (
		buffer,
		ARRAY_SIZE (buffer),
		"Provider=%s/EventID=%d/Version=%d",
		ep_provider_get_provider_name (ep_event_get_provider (ep_event_instance->ep_event)),
		ep_event_get_event_id (ep_event_instance->ep_event),
		ep_event_get_event_version (ep_event_instance->ep_event));

	if (characters_written > 0 && characters_written < (int32_t)ARRAY_SIZE (buffer))
		ep_json_file_write_event_data (json_file, ep_event_instance->timestamp, ep_rt_uint64_t_to_thread_id_t (ep_event_instance->thread_id), buffer, &ep_event_instance->stack_contents_instance);
}
#else
void
ep_event_instance_serialize_to_json_file (
	EventPipeEventInstance *ep_event_instance,
	EventPipeJsonFile *json_file)
{
	;
}
#endif

/*
 * EventPipeSequencePoint.
 */

static
void
sequence_point_fini (EventPipeSequencePoint *sequence_point)
{
	EP_ASSERT (sequence_point != NULL);

	// Each entry in the map owns a ref-count on the corresponding thread
	if (dn_umap_size (sequence_point->thread_sequence_numbers) != 0) {
		DN_UMAP_FOREACH_KEY_BEGIN (EventPipeThreadSessionState *, key, sequence_point->thread_sequence_numbers) {
			ep_thread_release (ep_thread_session_state_get_thread (key));
		} DN_UMAP_FOREACH_END;
	}

	dn_umap_free (sequence_point->thread_sequence_numbers);
}


EventPipeSequencePoint *
ep_sequence_point_alloc (void)
{
	EventPipeSequencePoint *instance = ep_rt_object_alloc (EventPipeSequencePoint);
	ep_raise_error_if_nok (instance != NULL);
	ep_raise_error_if_nok (ep_sequence_point_init (instance) != NULL);

ep_on_exit:
	return instance;

ep_on_error:
	ep_sequence_point_free (instance);
	instance = NULL;
	ep_exit_error_handler ();
}

EventPipeSequencePoint *
ep_sequence_point_init (EventPipeSequencePoint *sequence_point)
{
	EP_ASSERT (sequence_point != NULL);

	sequence_point->timestamp = 0;
	sequence_point->thread_sequence_numbers = dn_umap_alloc ();
	return sequence_point->thread_sequence_numbers ? sequence_point : NULL;
}

void
ep_sequence_point_fini (EventPipeSequencePoint *sequence_point)
{
	ep_return_void_if_nok (sequence_point != NULL);
	sequence_point_fini (sequence_point);
}

void
ep_sequence_point_free (EventPipeSequencePoint *sequence_point)
{
	ep_return_void_if_nok (sequence_point != NULL);

	sequence_point_fini (sequence_point);
	ep_rt_object_free (sequence_point);
}

#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */
#endif /* ENABLE_PERFTRACING */

#if !defined(ENABLE_PERFTRACING) || (defined(EP_INCLUDE_SOURCE_FILES) && !defined(EP_FORCE_INCLUDE_SOURCE_FILES))
extern const char quiet_linker_empty_file_warning_eventpipe_event_instance;
const char quiet_linker_empty_file_warning_eventpipe_event_instance = 0;
#endif
