/*
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 *
 * This software is dual-licensed to you under the MIT License (MIT) and
 * the Universal Permissive License (UPL). See the LICENSE file in the root
 * directory for license terms. You may choose either license, or both.
 */

#include "log/log.h"


#ifndef IOTCS_LOG_H
#error "log.h must be included first"
#endif

#ifndef IOTCSP_MODULE_LOG_CHANNEL
#error "Module name must be specified in IOTCSP_MODULE_LOG_CHANNEL define"
#endif

#ifdef _LOG_TEMPLATE
#error "log_template.h must be included only once"
#endif

#if IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_DM
    #if defined IOTCS_LOG_LEVEL_CHANNEL_DM
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_DM
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_EXT
    #if defined IOTCS_LOG_LEVEL_CHANNEL_EXT
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_EXT
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_CORE
    #if defined IOTCS_LOG_LEVEL_CHANNEL_CORE
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_CORE
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_JSON
    #if defined IOTCS_LOG_LEVEL_CHANNEL_JSON
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_JSON
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_MSG
    #if defined IOTCS_LOG_LEVEL_CHANNEL_MSG
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_MSG
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PROTO
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PROTO
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PROTO
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_TAM
    #if defined IOTCS_LOG_LEVEL_CHANNEL_TAM
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_TAM
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_UTIL
    #if defined IOTCS_LOG_LEVEL_CHANNEL_UTIL
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_UTIL
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_LOG
    #if defined IOTCS_LOG_LEVEL_CHANNEL_LOG
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_LOG
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_TEST
    #if defined IOTCS_LOG_LEVEL_CHANNEL_TEST
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_TEST
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_QUEUE
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_QUEUE
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_QUEUE
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_CRYPTO
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_CRYPTO
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_CRYPTO
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_TAM
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_TAM
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_TAM
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_DIAG
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_DIAG
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_DIAG
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_MEM
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_MEM
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_MEM
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_MUTEX
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_MUTEX
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_MUTEX
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_SSL
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_SSL
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_SSL
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_SYSTEM
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_SYSTEM
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_SYSTEM
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_THREAD
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_THREAD
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_THREAD
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_PORT_MQTT
    #if defined IOTCS_LOG_LEVEL_CHANNEL_PORT_MQTT
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_LEVEL_CHANNEL_PORT_MQTT
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#elif IOTCSP_MODULE_LOG_CHANNEL == LOG_CHANNEL_CPP_WRAPPER
    #if defined IOTCS_LOG_CHANNEL_CPP_WRAPPER
        #define IOTCSP_MODULE_LOG_LEVEL IOTCS_LOG_CHANNEL_CPP_WRAPPER
    #else
        #define IOTCSP_MODULE_LOG_LEVEL  IOTCS_LOG_LEVEL
    #endif
#else
    #error "wrong IOTCSP_MODULE_LOG_CHANNEL value"
#endif

#if IOTCSP_MODULE_LOG_LEVEL >= LOG_LEVEL_CRIT
#define LOG_CRITS(_msg) LOG_WRP_CRITS((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg)
#define LOG_CRITSN(_msg, _len) LOG_WRP_CRITSN((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, _len)
#define LOG_CRIT(_msg, ...) LOG_WRP_CRIT((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, __VA_ARGS__)
#else
#define LOG_CRITS(_msg)
#define LOG_CRITSN(_msg, _len)
#define LOG_CRIT(_msg, ...)
#endif

#if IOTCSP_MODULE_LOG_LEVEL >= LOG_LEVEL_ERR
#define LOG_ERRS(_msg) LOG_WRP_ERRS((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg)
#define LOG_ERRSN(_msg, _len) LOG_WRP_ERRSN((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, _len)
#define LOG_ERR(_msg, ...) LOG_WRP_ERR((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, __VA_ARGS__)
#else
#define LOG_ERRS(_msg)
#define LOG_ERRSN(_msg, _len)
#define LOG_ERR(_msg, ...)
#endif

#if IOTCSP_MODULE_LOG_LEVEL >= LOG_LEVEL_WARN
#define LOG_WARNS(_msg) LOG_WRP_WARNS((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg)
#define LOG_WARNSN(_msg, _len) LOG_WRP_WARNSN((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, _len)
#define LOG_WARN(_msg, ...) LOG_WRP_WARN((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, __VA_ARGS__)
#else
#define LOG_WARNS(_msg)
#define LOG_WARNSN(_msg, _len)
#define LOG_WARN(_msg, ...)
#endif

#if IOTCSP_MODULE_LOG_LEVEL >= LOG_LEVEL_INFO
#define LOG_INFOS(_msg) LOG_WRP_INFOS((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg)
#define LOG_INFOSN(_msg, _len) LOG_WRP_INFOSN((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, _len)
#define LOG_INFO(_msg, ...) LOG_WRP_INFO((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, __VA_ARGS__)
#else
#define LOG_INFOS(_msg)
#define LOG_INFOSN(_msg, _len)
#define LOG_INFO(_msg, ...)
#endif

#if IOTCSP_MODULE_LOG_LEVEL >= LOG_LEVEL_DBG
#define LOG_DBGS(_msg) LOG_WRP_DBGS((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg)
#define LOG_DBGSN(_msg, _len) LOG_WRP_DBGSN((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, _len)
#define LOG_DBG(_msg, ...) LOG_WRP_DBG((iotcs_log_channel)IOTCSP_MODULE_LOG_CHANNEL, _msg, __VA_ARGS__)
#else
#define LOG_DBGS(_msg)
#define LOG_DBGSN(_msg, _len)
#define LOG_DBG(_msg, ...)
#endif

#define CHECK_NO_OK(call)                               \
    do {                                                \
        if ((call) != IOTCS_RESULT_OK) {                \
            LOG_ERRS("Operation failed");               \
            return IOTCS_RESULT_FAIL;                   \
        }                                               \
    } while(0)

#define CHECK_ERR(call)                                 \
        do {                                            \
                iotcs_result call_result = call;        \
                if (call_result != IOTCS_RESULT_OK) {   \
                        LOG_ERRS("Operation failed");   \
                        return call_result;             \
                }                                       \
        } while(0)

#define GOTO_ERR_OOM(call) GOTO_ERR_OOM_MSG(call, "Memory allocation failed")

#define GOTO_ERR_OOM_MSG(call, msg)                     \
    do {                                                \
        if ((call) == NULL) {                           \
            rv = IOTCS_RESULT_OUT_OF_MEMORY;            \
            LOG_CRITS(msg);                             \
            goto error;                                 \
        }                                               \
    } while(0)

#define CHECK_OOM(call) CHECK_OOM_MSG(call, "Memory allocation failed")

#define CHECK_OOM_MSG(call, msg)                        \
    do {                                                \
        if ((call) == NULL) {                           \
            LOG_CRITS(msg);                             \
            return IOTCS_RESULT_OUT_OF_MEMORY;          \
        }                                               \
    } while(0)

#define GOTO_ERR_OOM(rv, call)   GOTO_ERR_OOM_MSG(rv, call, "Allocation failed")

#define GOTO_ERR_OOM_MSG(rv, call, msg)                 \
    do {                                                \
        if ((call) == NULL) {                           \
            LOG_CRITS(msg);                             \
            rv = IOTCS_RESULT_OUT_OF_MEMORY;            \
            goto error;                                 \
        }                                               \
    } while(0)

#define GOTO_ERR_NO_OK(rv, expr)                        \
    do {                                                \
        if (IOTCS_RESULT_OK != (rv = expr)) {           \
            LOG_ERRS("Operation failed");               \
            goto error;                                 \
        }                                               \
    } while(0)

#define GOTO_ERR(condition)                             \
    do {                                                \
        if (condition) {                                \
            LOG_ERRS("Operation failed");               \
            goto error;                                 \
        }                                               \
    } while(0)

#define GOTO_ERR_CRIT(condition)                        \
    do {                                                \
        if (condition) {                                \
            LOG_CRITS("Operation failed");              \
            goto error;                                 \
        }                                               \
    } while(0)

#define GOTO_ERR_MSG(condition, msg)                \
    do {                                            \
        if (condition) {                            \
            LOG_ERRS(msg);                          \
            goto error;                             \
        }                                           \
    } while(0)  

#define GOTO_ERR_CRIT_MSG(condition, msg)           \
    do {                                            \
        if (condition) {                            \
            LOG_CRITS(msg);                         \
            goto error;                             \
        }                                           \
    } while(0)  
        
#define _LOG_TEMPLATE
