{#
 # The MIT License (MIT)
 #
 # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
 # "Software"), to deal in the Software without restriction, including
 # without limitation the rights to use, copy, modify, merge, publish,
 # distribute, sublicense, and/or sell copies of the Software, and to
 # permit persons to whom the Software is furnished to do so, subject to
 # the following conditions:
 #
 # The above copyright notice and this permission notice shall be
 # included in all copies or substantial portions of the Software.
 #
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #}
{% import 'common.j2' as common %}
{% import 'c/common.j2' as c_common %}
{% import 'c/barectf.c-macros.j2' as macros %}
{% set prefix = common.prefix %}
{% set ucprefix = common.ucprefix %}
{% set ctx_struct_name = c_common.ctx_struct_name %}
{% set cg_opts = cfg.options.code_generation_options %}
{% set const_params = true %}
{% include 'license-header.j2' %}


#include <stdint.h>
#include <string.h>
#include <assert.h>

#include "{{ header_file_name }}"
#include "{{ bitfield_header_file_name }}"

#define _ALIGN(_at_var, _align)						\
	do {								\
		(_at_var) = ((_at_var) + ((_align) - 1)) & -(_align);	\
	} while (0)

#ifdef __cplusplus
# define _TO_VOID_PTR(_value)		static_cast<void *>(_value)
# define _FROM_VOID_PTR(_type, _value)	static_cast<_type *>(_value)
#else
# define _TO_VOID_PTR(_value)		((void *) (_value))
# define _FROM_VOID_PTR(_type, _value)	((_type *) (_value))
#endif

#define _BITS_TO_BYTES(_x)	((_x) >> 3)
#define _BYTES_TO_BITS(_x)	((_x) << 3)

union _f2u {
	float f;
	uint32_t u;
};

union _d2u {
	double f;
	uint64_t u;
};

uint32_t {{ prefix }}packet_size(const void * const vctx)
{
	return _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->packet_size;
}

int {{ prefix }}packet_is_full(const void * const vctx)
{
	const struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx);

	return ctx->at == ctx->packet_size;
}

int {{ prefix }}packet_is_empty(const void * const vctx)
{
	const struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx);

	return ctx->at <= ctx->off_content;
}

uint32_t {{ prefix }}packet_events_discarded(const void * const vctx)
{
	return _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->events_discarded;
}

uint32_t {{ prefix }}discarded_event_records_count(const void * const vctx)
{
	return {{ prefix }}packet_events_discarded(vctx);
}

uint32_t {{ prefix }}packet_sequence_number(const void * const vctx)
{
	return _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->sequence_number;
}

uint8_t *{{ prefix }}packet_buf(const void * const vctx)
{
	return _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->buf;
}

uint8_t *{{ prefix }}packet_buf_addr(const void * const vctx)
{
	return {{ prefix }}packet_buf(vctx);
}

uint32_t {{ prefix }}packet_buf_size(const void * const vctx)
{
	const struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx);

	return _BITS_TO_BYTES(ctx->packet_size);
}

void {{ prefix }}packet_set_buf(void * const vctx, uint8_t * const buf,
		const uint32_t buf_size)
{
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);

	ctx->buf = buf;

	if (ctx->at == ctx->packet_size) {
		/* Keep full packet state */
		ctx->at = _BYTES_TO_BITS(buf_size);
	}

	ctx->packet_size = _BYTES_TO_BITS(buf_size);
}

int {{ prefix }}packet_is_open(const void * const vctx)
{
	return _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->packet_is_open;
}

int {{ prefix }}is_in_tracing_section(const void * const vctx)
{
	return _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->in_tracing_section;
}

volatile const int *{{ prefix }}is_in_tracing_section_ptr(const void * const vctx)
{
	return &_FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->in_tracing_section;
}

int {{ prefix }}is_tracing_enabled(const void * const vctx)
{
	return _FROM_VOID_PTR(const struct {{ ctx_struct_name }}, vctx)->is_tracing_enabled;
}

void {{ prefix }}enable_tracing(void * const vctx, const int enable)
{
	_FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx)->is_tracing_enabled = enable;
}

static
void _write_c_str(struct {{ ctx_struct_name }} * const ctx, const char * const src)
{
	const uint32_t sz = strlen(src) + 1;

	memcpy(&ctx->buf[_BITS_TO_BYTES(ctx->at)], src, sz);
	ctx->at += _BYTES_TO_BITS(sz);
}

static
int _reserve_er_space(void * const vctx, const uint32_t er_size)
{
	int ret;
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);

	/* Event _cannot_ fit? */
	if (er_size > (ctx->packet_size - ctx->off_content)) {
		goto no_space;
	}

	/* Packet is full? */
	if ({{ prefix }}packet_is_full(ctx)) {
		/* Yes: is the back end full? */
		if (ctx->cbs.is_backend_full(ctx->data)) {
			/* Yes: discard event record */
			goto no_space;
		}

		/* Back-end is _not_ full: open new packet */
		ctx->use_cur_last_event_ts = 1;
		ctx->cbs.open_packet(ctx->data);
		ctx->use_cur_last_event_ts = 0;
	}

	/* Event fits the current packet? */
	if (er_size > (ctx->packet_size - ctx->at)) {
		/* No: close packet now */
		ctx->use_cur_last_event_ts = 1;
		ctx->cbs.close_packet(ctx->data);
		ctx->use_cur_last_event_ts = 0;

		/* Is the back end full? */
		if (ctx->cbs.is_backend_full(ctx->data)) {
			/* Yes: discard event record */
			goto no_space;
		}

		/* Back-end is _not_ full: open new packet */
		ctx->use_cur_last_event_ts = 1;
		ctx->cbs.open_packet(ctx->data);
		ctx->use_cur_last_event_ts = 0;
		assert(er_size <= (ctx->packet_size - ctx->at));
	}

	ret = 1;
	goto end;

no_space:
	ctx->events_discarded++;
	ret = 0;

end:
	return ret;
}

static
void _commit_er(void * const vctx)
{
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);

	/* Is the packet full? */
	if ({{ prefix }}packet_is_full(ctx)) {
		/* Yes: close it now */
		ctx->cbs.close_packet(ctx->data);
	}
}

{% include 'c/ctx-init-func-proto.j2' %}

{
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);
	ctx->cbs = cbs;
	ctx->data = data;
	ctx->buf = buf;
	ctx->packet_size = _BYTES_TO_BITS(buf_size);
	ctx->at = 0;
	ctx->events_discarded = 0;
	ctx->sequence_number = 0;
	ctx->packet_is_open = 0;
	ctx->in_tracing_section = 0;
	ctx->is_tracing_enabled = 1;
	ctx->use_cur_last_event_ts = 0;
}

{% for dst in cfg.trace.type.data_stream_types | sort %}
	{% set def_clk_type = dst.default_clock_type %}
	{% set sctx_name %}{{ prefix }}{{ dst.name }}{% endset %}
	{% set this_ds_ops = ds_ops[dst] %}
	{% include 'c/open-func-proto.j2' %}

{
	{{ macros.open_close_func_preamble(dst, dst.features.packet_features.beginning_timestamp_field_type) | indent_tab }}

	/*
	 * This function is either called by a tracing function, or
	 * directly by the platform.
	 *
	 * If it's called by a tracing function, then
	 * `ctx->in_tracing_section` is 1, so it's safe to open
	 * the packet here (alter the packet), even if tracing was
	 * disabled in the meantime because we're already in a tracing
	 * section (which finishes at the end of the tracing function
	 * call).
	 *
	 * If it's called directly by the platform, then if tracing is
	 * disabled, we don't want to alter the packet, and return
	 * immediately.
	 */
	if (!ctx->is_tracing_enabled && !saved_in_tracing_section) {
		ctx->in_tracing_section = 0;
		goto end;
	}

	/* We can alter the packet */
	ctx->in_tracing_section = 1;

	/* Do not open a packet that is already open */
	if (ctx->packet_is_open) {
		ctx->in_tracing_section = saved_in_tracing_section;
		goto end;
	}

	ctx->at = 0;
	{% set pkt_header_op = this_ds_ops.pkt_header_op %}
	{% if pkt_header_op %}

	{{ pkt_header_op.serialize_str(dst=dst) | indent_tab }}
	{% endif %}

	{{ this_ds_ops.pkt_ctx_op.serialize_str(dst=dst) | indent_tab }}

	/* Save content beginning's offset */
	ctx->off_content = ctx->at;

	/* Mark current packet as open */
	ctx->packet_is_open = 1;

	/* Not tracing anymore */
	ctx->in_tracing_section = saved_in_tracing_section;

end:
	return;
}

	{% include 'c/close-func-proto.j2' %}

{
	{{ macros.open_close_func_preamble(dst, dst.features.packet_features.end_timestamp_field_type) | indent_tab }}

	/*
	 * This function is either called by a tracing function, or
	 * directly by the platform.
	 *
	 * If it's called by a tracing function, then
	 * `ctx->in_tracing_section` is 1, so it's safe to close
	 * the packet here (alter the packet), even if tracing was
	 * disabled in the meantime, because we're already in a tracing
	 * section (which finishes at the end of the tracing function
	 * call).
	 *
	 * If it's called directly by the platform, then if tracing is
	 * disabled, we don't want to alter the packet, and return
	 * immediately.
	 */
	if (!ctx->is_tracing_enabled && !saved_in_tracing_section) {
		ctx->in_tracing_section = 0;
		goto end;
	}

	/* We can alter the packet */
	ctx->in_tracing_section = 1;

	/* Do not close a packet that is not open */
	if (!ctx->packet_is_open) {
		ctx->in_tracing_section = saved_in_tracing_section;
		goto end;
	}

	/* Save content size */
	ctx->content_size = ctx->at;
	{% set name = 'timestamp_end' %}
	{% if name in dst._pkt_ctx_ft.members %}
		{% set op = ds_op_pkt_ctx_op(dst, name) %}

	/* Go back to `timestamp_end` field offset */
	ctx->at = sctx->off_{{ op | op_src_var_name }};

		{% set src = 'ts' %}
		{% filter indent_tab(indent_first=true) %}
			{% include 'c/serialize-write-saved-int-statements.j2' %}

		{% endfilter %}
	{% endif %}
	{% set name = 'content_size' %}
	{% if name in dst._pkt_ctx_ft.members %}
		{% set op = ds_op_pkt_ctx_op(dst, name) %}

	/* Go back to `content_size` field offset */
	ctx->at = sctx->off_{{ op | op_src_var_name }};

		{% set src %}ctx->{{ name }}{% endset %}
		{% filter indent_tab(indent_first=true) %}
			{% include 'c/serialize-write-saved-int-statements.j2' %}

		{% endfilter %}
	{% endif %}
	{% set name = 'events_discarded' %}
	{% if name in dst._pkt_ctx_ft.members %}
		{% set op = ds_op_pkt_ctx_op(dst, name) %}

	/* Go back to `events_discarded` field offset */
	ctx->at = sctx->off_{{ op | op_src_var_name }};

		{% set src %}ctx->{{ name }}{% endset %}
		{% filter indent_tab(indent_first=true) %}
			{% include 'c/serialize-write-saved-int-statements.j2' %}

		{% endfilter %}
	{% endif %}

	/* Go back to end of packet */
	ctx->at = ctx->packet_size;

	/* Mark packet as closed */
	ctx->packet_is_open = 0;
	{% if 'packet_seq_num' in dst._pkt_ctx_ft.members %}
	/* Increment sequence number for next packet */
	ctx->sequence_number++;
	{% endif %}

	/* Not tracing anymore */
	ctx->in_tracing_section = saved_in_tracing_section;

end:
	return;
}

	{% if dst._er_header_ft %}
static void _serialize_er_header_{{ dst.name }}(void * const vctx,
	const uint32_t ert_id)
{
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);
		{% if def_clk_type and dst.features.event_record_features.timestamp_field_type %}
	struct {{ sctx_name }}_ctx * const sctx = _FROM_VOID_PTR(struct {{ sctx_name }}_ctx, vctx);
	const {{ cg_opts.clock_type_c_types[def_clk_type] }} ts = sctx->cur_last_event_ts;
		{% endif %}

	{{ this_ds_ops.er_header_op.serialize_str(dst=dst) | indent_tab }}
}

	{% endif %}
	{% if dst.event_record_common_context_field_type %}
static void _serialize_er_common_ctx_{{ dst.name }}(void * const vctx{{ dst | serialize_er_common_ctx_func_params_str(const_params) }})
{
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);

	{{ this_ds_ops.er_common_ctx_op.serialize_str(dst=dst) | indent_tab }}
}

	{% endif %}
	{# internal serialization functions #}
	{% for ert in dst.event_record_types | sort %}
static void _serialize_er_{{ dst.name }}_{{ ert.name }}(void * const vctx{{ (dst, ert) | trace_func_params_str(const_params) }})
{
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);
		{% if dst._er_header_ft %}

	/* Serialize header */
	_serialize_er_header_{{ dst.name }}(ctx, {{ ert.id }});
		{% endif %}
		{% if dst.event_record_common_context_field_type %}

	/* Serialize common context */
			{% set params = macros.ft_call_params(root_ft_prefixes.ERCC, dst.event_record_common_context_field_type) %}
	_serialize_er_common_ctx_{{ dst.name }}(ctx{{ params }});
		{% endif %}
		{% set this_er_ops = this_ds_ops.er_ops[ert] %}
		{% if this_er_ops.spec_ctx_op %}

	{{ this_er_ops.spec_ctx_op.serialize_str(dst=dst, ert=ert) | indent_tab }}
		{% endif %}
		{% if this_er_ops.payload_op %}

	{{ this_er_ops.payload_op.serialize_str(dst=dst, ert=ert) | indent_tab }}
		{% endif %}
}

	{% endfor %}
	{# internal size functions #}
	{% for ert in dst.event_record_types | sort %}
		{% set this_er_ops = this_ds_ops.er_ops[ert] %}
static uint32_t _er_size_{{ dst.name }}_{{ ert.name }}(void * const vctx{{ (dst, ert) | trace_func_params_str(const_params, only_dyn=true) }})
{
	struct {{ ctx_struct_name }} * const ctx = _FROM_VOID_PTR(struct {{ ctx_struct_name }}, vctx);
	uint32_t at = ctx->at;
		{% if this_ds_ops.er_header_op %}

	{{ this_ds_ops.er_header_op.size_str(dst=dst) | indent_tab }}
		{% endif %}
		{% if this_ds_ops.er_common_ctx_op %}

	{{ this_ds_ops.er_common_ctx_op.size_str(dst=dst) | indent_tab }}
		{% endif %}
		{% if this_er_ops.spec_ctx_op %}

	{{ this_er_ops.spec_ctx_op.size_str(dst=dst, ert=ert) | indent_tab }}
		{% endif %}
		{% if this_er_ops.payload_op %}

	{{ this_er_ops.payload_op.size_str(dst=dst, ert=ert) | indent_tab }}
		{% endif %}

	return at - ctx->at;
}

	{% endfor %}
	{# public tracing functions #}
	{% for ert in dst.event_record_types | sort %}
		{% include 'c/trace-func-proto.j2' %}

{
	struct {{ ctx_struct_name }} * const ctx = &sctx->parent;
	uint32_t er_size;

		{% if def_clk_type %}
	/* Save timestamp */
	sctx->cur_last_event_ts = ctx->cbs.{{ def_clk_type.name }}_clock_get_value(ctx->data);

		{% endif %}
	if (!ctx->is_tracing_enabled) {
		goto end;
	}

	/* We can alter the packet */
	ctx->in_tracing_section = 1;

	/* Compute event record size */
		{% set er_common_ctx_params = macros.ft_call_params(root_ft_prefixes.ERCC, dst.event_record_common_context_field_type, true) %}
		{% set spec_ctx_params = macros.ft_call_params(root_ft_prefixes.ERSC, ert.specific_context_field_type, true) %}
		{% set payload_params = macros.ft_call_params(root_ft_prefixes.ERP, ert.payload_field_type, true) %}
		{% set params %}{{ er_common_ctx_params }}{{ spec_ctx_params }}{{ payload_params }}{% endset %}
	er_size = _er_size_{{ dst.name }}_{{ ert.name }}(_TO_VOID_PTR(ctx){{ params }});

	/* Is there enough space to serialize? */
	if (!_reserve_er_space(_TO_VOID_PTR(ctx), er_size)) {
		/* no: forget this */
		ctx->in_tracing_section = 0;
		goto end;
	}

	/* Serialize event record */
		{% set er_common_ctx_params = macros.ft_call_params(root_ft_prefixes.ERCC, dst.event_record_common_context_field_type) %}
		{% set spec_ctx_params = macros.ft_call_params(root_ft_prefixes.ERSC, ert.specific_context_field_type) %}
		{% set payload_params = macros.ft_call_params(root_ft_prefixes.ERP, ert.payload_field_type) %}
		{% set params %}{{ er_common_ctx_params }}{{ spec_ctx_params }}{{ payload_params }}{% endset %}
	_serialize_er_{{ dst.name }}_{{ ert.name }}(_TO_VOID_PTR(ctx){{ params }});

	/* Commit event record */
	_commit_er(_TO_VOID_PTR(ctx));

	/* Not tracing anymore */
	ctx->in_tracing_section = 0;

end:
	return;
}
		{% if not loop.last %}{{ '\n' }}{% endif %}
	{% endfor %}
{% endfor %}
