/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * Copyright (c) Likewise Software.  All rights Reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewise.com
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *     rtllog.h
 *
 * Abstract:
 *
 *     RTL Logging
 *
 * Authors: Danilo Almeida (dalmeida@likewise.com)
 */

/**
 * @file lw/rtllog.h RTL logging API
 * @brief RTL logging API
 */

#ifndef __RTL_LOG_H__
#define __RTL_LOG_H__

#include <lw/types.h>
#include <lw/attrs.h>

/**
 * @defgroup logging Message logging
 * @brief Message Logging API
 *
 * This API provides a generic unstructured logging framework.  The actual
 * logging is performed by a user callback (#LW_RTL_LOG_CALLBACK) which must be
 * provided by the application via #LwRtlLogSetCallback().
 */

/*@{*/

LW_BEGIN_EXTERN_C

#ifndef LW_RTL_LOG_COMPONENT
/**
 * @brief Current component for logging
 *
 * The client application may override this macro prior to the inclusion of
 * the header to a string constant representing the current component in the
 * software stack.  This will be the default facility name used in log messages.
 * @hideinitializer
 */
#define LW_RTL_LOG_COMPONENT NULL
#endif

/**
 * @brief Log level
 *
 * A log level
 */
typedef LW_UCHAR LW_RTL_LOG_LEVEL, *PLW_RTL_LOG_LEVEL;

/**
 * @brief Undefined log level
 */
#define LW_RTL_LOG_LEVEL_UNDEFINED  0
/**
 * @brief Always log level
 *
 * The log level for messages which should always be logged.
 */
#define LW_RTL_LOG_LEVEL_ALWAYS     1
/**
 * @brief Error log level
 *
 * The log level for messages which indicate serious errors.
 */
#define LW_RTL_LOG_LEVEL_ERROR      2
/**
 * @brief Warning log level
 *
 * The log level for messages which indicate unusual or
 * undesirable but conditions.
 */
#define LW_RTL_LOG_LEVEL_WARNING    3
/**
 * @brief Info log level
 *
 * The log level for general informational messages.
 */
#define LW_RTL_LOG_LEVEL_INFO       4
/**
 * @brief Info log level
 *
 * The log level for developer-centric informational messages.
 */
#define LW_RTL_LOG_LEVEL_VERBOSE    5
/**
 * @brief Debug log level
 *
 * The log level for message assisting in debugging.
 */
#define LW_RTL_LOG_LEVEL_DEBUG      6
/**
 * @brief Trace log level
 *
 * The log level for messages tracing call execution in detail.
 */
#define LW_RTL_LOG_LEVEL_TRACE      7

/**
 * @brief A logging callback function
 *
 * A user-provided function responsible for logging a message.
 *
 * @param[in] Context The optional context pointer provided to #LwRtlLogSetCallback().
 * @param[in] Level The log level of the message.
 * @param[in] ComponentName The name of the component/facility (or NULL if undefined).
 * @param[in] FunctionName The name of the function logging the message.
 * @param[in] FileName The name of the source file logging the message.
 * @param[in] LineNumber The source code line number logging the message.
 * @param[in] Format The format string for the message.
 * @param[in] ... Arguments for the format string.
 */
typedef LW_VOID (*LW_RTL_LOG_CALLBACK)(
    LW_IN LW_OPTIONAL LW_PVOID Context,
    LW_IN LW_RTL_LOG_LEVEL Level,
    LW_IN LW_OPTIONAL LW_PCSTR ComponentName,
    LW_IN LW_PCSTR FunctionName,
    LW_IN LW_PCSTR FileName,
    LW_IN LW_ULONG LineNumber,
    LW_IN LW_PCSTR Format,
    LW_IN ...
    );

/**
 * @brief Get the current logging callback/context.
 *
 * Gets the currently-set logging callback and/or logging callback context pointer.
 *
 * @param[out] Callback If provided, set to the current logging callback.
 * @param[out] Context If provided, set to the current logging callback context pointer.
 */
LW_VOID
LwRtlLogGetCallback(
    LW_OUT LW_OPTIONAL LW_RTL_LOG_CALLBACK *Callback,
    LW_OUT LW_OPTIONAL LW_PVOID *Context
    );

/**
 * @brief Set the current logging callback/context.
 *
 * Sets the current logging callback and optional callback context pointer.
 *
 * @warning The result of calling this function concurrently with respect to messages
 * being logged is undefined.
 *
 * @param[in] Callback The new logging callback to set, or NULL to unset it
 * @param[in] Context An optional context pointer to pass to the logging callback
 */
LW_VOID
LwRtlLogSetCallback(
    LW_IN LW_OPTIONAL LW_RTL_LOG_CALLBACK Callback,
    LW_IN LW_OPTIONAL LW_PVOID Context
    );

/**
 * @brief Set the current maximum log level
 *
 * Sets the current maximum level at which logging will occur.
 * Messages at or below this level will be logged while all others
 * are filtered before reaching the current logging callback.
 *
 * @param[in] Level The new log level.
 */
LW_VOID
LwRtlLogSetLevel(
    LW_IN LW_RTL_LOG_LEVEL Level
    );

/**
 * @brief Get current maximum log level
 *
 * Returns the current maximum log level.
 * @hideinitializer
 */
#define LwRtlLogGetLevel() _LwRtlLogControl.Level

/**
 * @brief Log message at @a ALWAYS level
 *
 * Logs a message at the @a ALWAYS level.
 *
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_ALWAYS(Format, ...) \
    _LW_RTL_LOG_IF(LW_RTL_LOG_LEVEL_ALWAYS, Format, ## __VA_ARGS__)

/**
 * @brief Log message at @a ERROR level
 *
 * Logs a message at the @a ERROR level.
 *
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_ERROR(Format, ...) \
    _LW_RTL_LOG_IF(LW_RTL_LOG_LEVEL_ERROR, Format, ## __VA_ARGS__)

/**
 * @brief Log message at @a WARNING level
 *
 * Logs a message at the @a WARNING level.
 *
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_WARNING(Format, ...) \
    _LW_RTL_LOG_IF(LW_RTL_LOG_LEVEL_WARNING, Format, ## __VA_ARGS__)

/**
 * @brief Log message at @a INFO level
 *
 * Logs a message at the @a INFO level.
 *
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_INFO(Format, ...) \
    _LW_RTL_LOG_IF(LW_RTL_LOG_LEVEL_INFO, Format, ## __VA_ARGS__)

/**
 * @brief Log message at @a VERBOSE level
 *
 * Logs a message at the @a VERBOSE level.
 *
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_VERBOSE(Format, ...) \
    _LW_RTL_LOG_IF(LW_RTL_LOG_LEVEL_VERBOSE,  Format, ## __VA_ARGS__)

/**
 * @brief Log message at @a DEBUG level
 *
 * Logs a message at the @a DEBUG level.
 *
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_DEBUG(Format, ...) \
    _LW_RTL_LOG_IF(LW_RTL_LOG_LEVEL_DEBUG, Format, ## __VA_ARGS__)

/**
 * @brief Log message at @a TRACE level
 *
 * Logs a message at the @a TRACE level.
 *
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_TRACE(Format, ...) \
    _LW_RTL_LOG_IF(LW_RTL_LOG_LEVEL_TRACE, Format, ## __VA_ARGS__)

/**
 * @brief Log raw message
 *
 * Logs a message with all relevant metadata specified explicitly.
 * This is useful for funneling messages from a higher-level logging API.
 *
 * @param[in] LogLevel The log level of the message.
 * @param[in] Component The name of the component/facility.
 * @param[in] Function The name of the function that generated the message.
 * @param[in] File The name of source file that generated the message.
 * @param[in] Line The line of the source file that generated the message.
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_RAW(LogLevel, Component, Function, File, Line, Format, ...) \
    do { \
        if ((_LwRtlLogControl.Level >= (LogLevel)) && _LwRtlLogControl.Callback) \
        { \
            _LwRtlLogControl.Callback( \
                    _LwRtlLogControl.Context, \
                    LogLevel, \
                    Component, \
                    Function, \
                    File, \
                    Line, \
                    Format, \
                    ## __VA_ARGS__); \
        } \
    } while (0)

/**
 * @brief Log message with explicit component
 *
 * Logs a message with an explicitly-specified component/facility name.
 *
 * @param[in] LogLevel The log level of the message.
 * @param[in] Component The name of the component/facility.
 * @param[in] Format A @a printf() format string for the message.
 * @param[in] ... Parameters for the format string.
 * @hideinitializer
 */
#define LW_RTL_LOG_AT_LEVEL(LogLevel, Component, Format, ...) \
    LW_RTL_LOG_RAW(LogLevel, Component, __FUNCTION__, __FILE__, __LINE__, Format, ## __VA_ARGS__)

/**
 * @brief Ensure C string is log-safe
 *
 * Returns the given string verbatim if not @a NULL, or
 * @a "<null>" if it is.
 *
 * @param[in] x the string
 * @hideinitializer
 */
#define LW_RTL_LOG_SAFE_STRING(x) \
    ( (x) ? (x) : "<null>" )

/**
 * @brief Ensure wide character string is log-safe
 *
 * Returns the given string verbatim if not @a NULL, or
 * @a "<null>" if it is.
 *
 * @param[in] x the string
 * @hideinitializer
 */
#define LW_RTL_LOG_SAFE_WSTRING(x) \
    ( (x) ? (x) : _LwRtlLogGetEmptyWC16String() )

/*@}*/

//
// Do not use any of the below directly
//
#ifndef DOXYGEN

#define _LW_RTL_LOG_IF(LogLevel, Format, ...) \
    LW_RTL_LOG_AT_LEVEL(LogLevel, LW_RTL_LOG_COMPONENT, Format, ## __VA_ARGS__)

static
inline
LW_PCWSTR
_LwRtlLogGetEmptyWC16String(
    LW_VOID
    )
{
    static const WCHAR empty = 0;
    return &empty;
}

typedef struct _LW_RTL_LOG_CONTROL {
    LW_RTL_LOG_CALLBACK Callback;
    LW_PVOID Context;
    volatile LW_RTL_LOG_LEVEL Level;
} LW_RTL_LOG_CONTROL, *PLW_RTL_LOG_CONTROL;

extern LW_RTL_LOG_CONTROL _LwRtlLogControl;

#endif /* DOXYGEN */

LW_END_EXTERN_C

#endif /* __RTL_LOG_H__ */
