// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2019 Christian Hergert <christian@hergert.me>
// SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>

#pragma once

#if !defined (VALENT_INSIDE) && !defined (VALENT_COMPILATION)
# error "Only <valent.h> can be included directly."
#endif

#include <glib.h>

#include "valent-version.h"

G_BEGIN_DECLS

VALENT_AVAILABLE_IN_1_0
void   valent_debug_init  (void);
VALENT_AVAILABLE_IN_1_0
void   valent_debug_clear (void);


/**
 * VALENT_LOG_LEVEL_TRACE: (skip)
 *
 * The [flags@GLib.LogLevelFlags] log level for trace messages in Valent.
 *
 * Since: 1.0
 */
#ifndef VALENT_LOG_LEVEL_TRACE
# define VALENT_LOG_LEVEL_TRACE ((GLogLevelFlags)(1 << G_LOG_LEVEL_USER_SHIFT))
#endif


/**
 * VALENT_ENABLE_TRACE: (skip)
 *
 * Whether tracing is enabled.
 *
 * If %TRUE, the macros for tracing function entry, exit and jump labels will
 * write to the log and pass events to sysprof, if it's available. If %FALSE,
 * these macros will be compiled out.
 *
 * The macros include %VALENT_ENTRY, %VALENT_EXIT, %VALENT_RETURN, %VALENT_GOTO,
 * %VALENT_NOTE and %VALENT_PROBE.
 *
 * Since: 1.0
 */
#ifndef VALENT_ENABLE_TRACE
# define VALENT_ENABLE_TRACE 0
#endif
#if VALENT_ENABLE_TRACE != 1
# undef VALENT_ENABLE_TRACE
#endif


/**
 * VALENT_ENTRY: (skip)
 *
 * Traces the entry into a function. Place this at the beginning of functions,
 * above pre-condition checks.
 *
 * Since: 1.0
 */

/**
 * VALENT_EXIT: (skip)
 *
 * Traces the exit from a function. Use this instead of `return` to return
 * and log the function exiting.
 *
 * Since: 1.0
 */

/**
 * VALENT_RETURN: (skip)
 * @_r: the return value
 *
 * Traces the exit from a function. Use this instead of `return` to return @_r
 * and log the function exiting.
 *
 * Since: 1.0
 */

/**
 * VALENT_GOTO: (skip)
 * @_l: the label to jump to
 *
 * Traces the jump to a label in a function. Use this instead of `goto` to jump
 * to @_l and log the label.
 *
 * Since: 1.0
 */

/**
 * VALENT_NOTE: (skip)
 * @fmt: the message format (a la `printf()`)
 * @...: the message parameters
 *
 * Logs a formatted message at %VALENT_LOG_LEVEL_TRACE.
 *
 * Since: 1.0
 */

/**
 * VALENT_PROBE: (skip)
 *
 * Appends to the tracing log that a line of code was reached.
 *
 * Since: 1.0
 */


#ifdef VALENT_ENABLE_TRACE

_VALENT_EXTERN
void valent_trace_mark (const char *strfunc,
                        int64_t     begin_time_usec,
                        int64_t     end_time_usec);

# define VALENT_ENTRY                                                       \
   int64_t __trace_begin_time = g_get_monotonic_time ();                    \
   g_log(G_LOG_DOMAIN, VALENT_LOG_LEVEL_TRACE, "ENTRY: %s():%d",            \
         G_STRFUNC, __LINE__)
# define VALENT_EXIT                                                        \
   G_STMT_START {                                                           \
      valent_trace_mark (G_STRFUNC,                                         \
                         __trace_begin_time,                                \
                         g_get_monotonic_time ());                          \
      g_log(G_LOG_DOMAIN, VALENT_LOG_LEVEL_TRACE, " EXIT: %s():%d",         \
            G_STRFUNC, __LINE__);                                           \
      return;                                                               \
   } G_STMT_END
# define VALENT_RETURN(_r)                                                  \
   G_STMT_START {                                                           \
      valent_trace_mark (G_STRFUNC,                                         \
                         __trace_begin_time,                                \
                         g_get_monotonic_time ());                          \
      g_log(G_LOG_DOMAIN, VALENT_LOG_LEVEL_TRACE, " EXIT: %s():%d ",        \
            G_STRFUNC, __LINE__);                                           \
      return _r;                                                            \
   } G_STMT_END
# define VALENT_GOTO(_l)                                                    \
   G_STMT_START {                                                           \
      g_log(G_LOG_DOMAIN, VALENT_LOG_LEVEL_TRACE, " GOTO: %s():%d ("#_l")", \
            G_STRFUNC, __LINE__);                                           \
      goto _l;                                                              \
   } G_STMT_END
# define VALENT_NOTE(fmt, ...)                                              \
   g_log(G_LOG_DOMAIN, VALENT_LOG_LEVEL_TRACE, " NOTE: %s():%d: " fmt,      \
         G_STRFUNC, __LINE__, ##__VA_ARGS__)
# define VALENT_PROBE                                                       \
   g_log(G_LOG_DOMAIN, VALENT_LOG_LEVEL_TRACE, "PROBE: %s():%d",            \
         G_STRFUNC, __LINE__)
#else
# define VALENT_ENTRY               G_STMT_START {            } G_STMT_END
# define VALENT_EXIT                G_STMT_START { return;    } G_STMT_END
# define VALENT_RETURN(_r)          G_STMT_START { return _r; } G_STMT_END
# define VALENT_GOTO(_l)            G_STMT_START { goto _l;   } G_STMT_END
# define VALENT_NOTE(fmt, ...)      G_STMT_START {            } G_STMT_END
# define VALENT_PROBE               G_STMT_START {            } G_STMT_END
#endif


/**
 * VALENT_ENABLE_DEBUG: (skip)
 *
 * Whether debugging is enabled.
 *
 * If %TRUE, the macros for debugging will write messages to the log that are
 * only appropriate during development. If %FALSE, these macros will be compiled
 * out.
 *
 * The macros include %VALENT_JSON.
 */
#ifndef VALENT_ENABLE_DEBUG
# define VALENT_ENABLE_DEBUG 0
#endif
#if VALENT_ENABLE_DEBUG != 1
# undef VALENT_ENABLE_DEBUG
#endif

/**
 * VALENT_JSON: (skip)
 * @_node: a `JsonNode`
 * @_ctx: (type utf8): a prefix for context
 *
 * Logs @_node as pretty-printed JSON at %G_LOG_LEVEL_DEBUG, prefixed with
 * @_ctx for context.
 *
 * Since: 1.0
 */


#ifdef VALENT_ENABLE_DEBUG
# define VALENT_JSON(_node, _ctx)                                           \
   G_STMT_START {                                                           \
     char *__json_str = json_to_string (_node, TRUE);                       \
     g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, " JSON: %s():%d %s: %s",        \
           G_STRFUNC, __LINE__, _ctx, __json_str);                          \
     g_free(__json_str);                                                    \
   } G_STMT_END
#else
# define VALENT_JSON(_node, _ctx)      G_STMT_START {          } G_STMT_END
#endif

G_END_DECLS

