#ifndef SIEVE_RUNTIME_TRACE_H
#define SIEVE_RUNTIME_TRACE_H

#include "sieve-common.h"
#include "sieve-runtime.h"

/*
 * Runtime trace
 */

struct sieve_runtime_trace {
	struct sieve_trace_config config;
	struct sieve_trace_log *log;
	unsigned int indent;
};

/* Trace configuration */

static inline bool sieve_runtime_trace_active
(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level)
{
	return ( renv->trace != NULL && trace_level <= renv->trace->config.level );
}

static inline bool sieve_runtime_trace_hasflag
(const struct sieve_runtime_env *renv, unsigned int flag)
{
	return ( renv->trace != NULL && (renv->trace->config.flags & flag) != 0 );
}

/* Trace indent */

static inline void sieve_runtime_trace_descend
(const struct sieve_runtime_env *renv)
{
	if ( renv->trace != NULL ) renv->trace->indent++;
}

static inline void sieve_runtime_trace_ascend
(const struct sieve_runtime_env *renv)
{
	if ( renv->trace != NULL ) renv->trace->indent--;
}

static inline void sieve_runtime_trace_toplevel
(const struct sieve_runtime_env *renv)
{
	if ( renv->trace != NULL ) renv->trace->indent = 0;
}

/* Trace errors */

void _sieve_runtime_trace_error
	(const struct sieve_runtime_env *renv, const char *fmt, va_list args)
		ATTR_FORMAT(2, 0);

void _sieve_runtime_trace_operand_error
	(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
		const char *fmt, va_list args) ATTR_FORMAT(3, 0);

static inline void sieve_runtime_trace_error
	(const struct sieve_runtime_env *renv, const char *fmt, ...)
		ATTR_FORMAT(2, 3);

static inline void sieve_runtime_trace_operand_error
	(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
		const char *fmt, ...) ATTR_FORMAT(3, 4);

static inline void ATTR_FORMAT(2, 3) sieve_runtime_trace_error
	(const struct sieve_runtime_env *renv, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	if ( renv->trace != NULL )
		_sieve_runtime_trace_error(renv, fmt, args);
	va_end(args);
}

static inline void ATTR_FORMAT(3, 4) sieve_runtime_trace_operand_error
	(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd,
		const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	if ( renv->trace != NULL )
		_sieve_runtime_trace_operand_error(renv, oprnd, fmt, args);
	va_end(args);
}

/* Trace info */

void _sieve_runtime_trace
	(const struct sieve_runtime_env *renv, const char *fmt, va_list args)
		ATTR_FORMAT(2, 0);

static inline void sieve_runtime_trace
	(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
		const char *fmt, ...) ATTR_FORMAT(3, 4);

static inline void ATTR_FORMAT(3, 4) sieve_runtime_trace
(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
	const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);

	if ( renv->trace != NULL && trace_level <= renv->trace->config.level ) {
		_sieve_runtime_trace(renv, fmt, args);
	}

	va_end(args);
}

void _sieve_runtime_trace_address
	(const struct sieve_runtime_env *renv, sieve_size_t address,
		const char *fmt, va_list args) ATTR_FORMAT(3, 0);

static inline void sieve_runtime_trace_address
	(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
		sieve_size_t address, const char *fmt, ...) ATTR_FORMAT(4, 5);

static inline void ATTR_FORMAT(4, 5) sieve_runtime_trace_address
(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
	sieve_size_t address, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);

	if ( renv->trace != NULL && trace_level <= renv->trace->config.level ) {
		_sieve_runtime_trace_address(renv, address, fmt, args);
	}

	va_end(args);
}

static inline void ATTR_FORMAT(3, 4) sieve_runtime_trace_here
(const struct sieve_runtime_env *renv, sieve_trace_level_t trace_level,
	const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);

	if ( renv->trace != NULL && trace_level <= renv->trace->config.level ) {
		_sieve_runtime_trace_address(renv, renv->pc, fmt, args);
	}

	va_end(args);
}

/* Trace boundaries */

void _sieve_runtime_trace_begin(const struct sieve_runtime_env *renv);
void _sieve_runtime_trace_end(const struct sieve_runtime_env *renv);
void _sieve_runtime_trace_sep(const struct sieve_runtime_env *renv);

static inline void sieve_runtime_trace_begin
(const struct sieve_runtime_env *renv)
{
	if ( renv->trace != NULL )
		_sieve_runtime_trace_begin(renv);
}

static inline void sieve_runtime_trace_end
(const struct sieve_runtime_env *renv)
{
	if ( renv->trace != NULL )
		_sieve_runtime_trace_end(renv);
}

static inline void sieve_runtime_trace_sep
(const struct sieve_runtime_env *renv)
{
	if ( renv->trace != NULL )
		_sieve_runtime_trace_sep(renv);
}

#endif
