/*  --------------------------------------------------------------------------------------------------------
 *  File:   custom_log.h 
 *
 *  Desc:   ChenZhen 偏好的, 对 Android log 机构的定制配置. 
 *
 *          -----------------------------------------------------------------------------------
 *          < 习语 和 缩略语 > : 
 *
 *          -----------------------------------------------------------------------------------
 *
 *  Usage:	调用本 log 机构的 .c or .h 文件, 若要使能这里的 log 机制, 
 *          则必须在 inclue 本文件之前, "#define ENABLE_DEBUG_LOG" 先. 
 *          
 *          同 log.h 一样, client 文件在 inclue 本文件之前, "最好" #define LOG_TAG <module_tag>
 *          "module_tag" 是当前待调试的 模块的标识. 
 *          
 *          另外, 可以使用 V() 来输出 verbose 的 log, 但必须先 "#define ENABLE_VERBOSE_LOG". 
 *
 *          若用户要 dump 大量 mem 数据, 此时有大量快速的 log 输出, 可能导致 host 端接收来不及, 
 *          此时用户可以定义宏 MASSIVE_LOG, 通过在特定 log 接口中延时, 保证 host 端接收到完整 log. 
 *
 *  Note:
 *
 *  Author: ChenZhen
 *
 *  --------------------------------------------------------------------------------------------------------
 *  Version:
 *          v1.0
 *  --------------------------------------------------------------------------------------------------------
 *  Log:
	----Tue Mar 02 21:30:33 2010            v.10
 *        
 *  --------------------------------------------------------------------------------------------------------
 */


#ifndef __CUSTOM_LOG_H__
#define __CUSTOM_LOG_H__

#ifdef __cplusplus
extern "C" {
#endif

/* ---------------------------------------------------------------------------------------------------------
 *  Include Files
 * ---------------------------------------------------------------------------------------------------------
 */

#include <log/log.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
    
/* ---------------------------------------------------------------------------------------------------------
 *  Macros Definition 
 * ---------------------------------------------------------------------------------------------------------
 */

/** 
 * 若下列 macro 有被定义, dump 内存块的 log 接口每次执行之后将 sleep 若干时间. 
 */
// #define MASSIVE_LOG 

/**
 * 若定义有 MASSIVE_LOG, dump 内存块的 log 接口每次 sleep 的时间 us 为单位. 
 */
#define RESPITE_TIME_FOR_MASSIVE_LOG_IN_US      (1 * 10)

/** 若下列 macro 有被定义, 才 使能 log 输出 : 参见 "Usage". */ 
// #define ENABLE_DEBUG_LOG

/** .! : 若需要全局地关闭 D log, 可以使能下面的代码. */
/*
#undef ENABLE_DEBUG_LOG
#warning "debug log is disabled globally!"
*/

/** 是否 log 源文件 路径信息. */
#define LOG_FILE_PATH

/*-----------------------------------*/

#ifndef LOGV
#define LOGV ALOGV
#endif

#ifndef LOGD
#define LOGD ALOGD
#endif

#ifndef LOGI
#define LOGI ALOGI
#endif

#ifndef LOGW
#define LOGW ALOGW
#endif

#ifndef LOGE
#define LOGE ALOGE
#endif

/*-----------------------------------*/
#ifdef ENABLE_VERBOSE_LOG

#undef ENABLE_DEBUG_LOG
#define ENABLE_DEBUG_LOG
    
// #error
#ifdef LOG_FILE_PATH
#define V(fmt, args...) \
    { D(fmt, ## args); }
    // { LOGV("[File] : %s; [Line] : %d; [Func] : %s; " fmt , __FILE__, __LINE__, __FUNCTION__, ## args); }

#else
#define V(fmt, args...) \
    { D(fmt, ## args); }
    // { LOGV("[Line] : %d; [Func] : %s; " fmt , __LINE__, __FUNCTION__, ## args); }
#endif

#else
#define V(...)  ((void)0)
#endif

/*-----------------------------------*/
#ifdef ENABLE_DEBUG_LOG

#ifdef LOG_FILE_PATH
#define D(fmt, args...) \
    { LOGD("[File] : %s; [Line] : %d; [Func] : %s;\n" fmt , __FILE__, __LINE__, __FUNCTION__, ## args); }

#else
#define D(fmt, args...) \
    { LOGD("[Line] : %d; [Func] : %s; " fmt , __LINE__, __FUNCTION__, ## args); }
#endif

#else
#define D(...)  ((void)0)
#endif


/*-----------------------------------*/
// #ifdef ENABLE_DEBUG_LOG

#ifdef LOG_FILE_PATH
#define I(fmt, args...) \
    { LOGI("[File] : %s; [Line] : %d; [Func] : %s;\n" fmt , __FILE__, __LINE__, __FUNCTION__, ## args); }
#else
#define I(fmt, args...) \
    { LOGI("[Line] : %d; [Func] : %s; " fmt , __LINE__, __FUNCTION__, ## args); }
#endif

/*
#else       // #ifdef ENABLE_DEBUG_LOG
    
#define I(fmt, args...) \
    { LOGI(fmt, ## args); }

#endif
*/

/*-----------------------------------*/
#ifdef LOG_FILE_PATH
#define W(fmt, args...) \
    { LOGW("[File] : %s; [Line] : %d; [Func] : %s;\n" fmt , __FILE__, __LINE__, __FUNCTION__, ## args); }
#else
#define W(fmt, args...) \
    { LOGW("[Line] : %d; [Func] : %s; " fmt , __LINE__, __FUNCTION__, ## args); }
#endif

/*-----------------------------------*/
#ifdef LOG_FILE_PATH
#define E(fmt, args...) \
    { LOGE("[File] : %s; [Line] : %d; [Func] : %s;\n" fmt , __FILE__, __LINE__, __FUNCTION__, ## args); }
#else
#define E(fmt, args...) \
    { LOGE("[Line] : %d; [Func] : %s; " fmt , __LINE__, __FUNCTION__, ## args); }
#endif

/*-----------------------------------*/
/** 
 * 若程序重复运行到当前位置的次数等于 threshold 或者第一次到达, 则打印指定的 log 信息. 
 */
#ifdef ENABLE_DEBUG_LOG
#define D_WHEN_REPEAT(threshold, fmt, args...) \
    do { \
        static int count = 0; \
        if ( 0 == count || (count++) == threshold ) { \
            D(fmt, ##args); \
            count = 1; \
        } \
    } while (0)
#else
#define D_WHEN_REPEAT(...)  ((void)0)
#endif
    
/*-------------------------------------------------------*/
/** 使用 D(), 以十进制的形式打印变量 'var' 的 value. */
#define D_DEC(var) \
{ \
    long long num = (var); \
    D(#var " = %lld.", num); \
}

/** 使用 D(), 以十六进制的形式打印变量 'var' 的 value. */
#define D_HEX(var) \
{ \
    long long num = (var); \
    D(#var " = 0x%llx.", num); \
}

#define D_FLOAT(var) \
{ \
    float num = (var); \
    D(#var " = %f.", num); \
}

/** 使用 D(), 以十六进制的形式 打印指针类型变量 'ptr' 的 value. */
#define D_PTR(ptr)  D(#ptr " = %p.", ptr);

/** 使用 D(), 打印 char 字串. */
#define D_STR(pStr) \
{\
    if ( NULL == pStr )\
    {\
        D(#pStr" = NULL.");\
    }\
    else\
    {\
        D(#pStr" = '%s'.", pStr);\
    }\
}

/*---------------------------------------------------------------------------*/

/** 使用 V(), 以十进制的形式打印变量 'var' 的 value. */
#define V_DEC(var)  V(#var " = %d.", var);

/** 使用 V(), 以十六进制的形式打印变量 'var' 的 value. */
#define V_HEX(var)  V(#var " = 0x%x.", var);

/** 使用 V(), 以十六进制的形式打印 类型是 unsigned long long 变量 'var' 的 value. */
#define V_HEX_ULL(var)  V(#var " = 0x%016llx.", var);

/** 使用 V(), 以十六进制的形式 打印指针类型变量 'ptr' 的 value. */
#define V_PTR(ptr)  V(#ptr " = %p.", ptr);

/** 使用 V(), 打印 char 字串. */
#define V_STR(pStr) \
{\
    if ( NULL == pStr )\
    {\
        V(#pStr" = NULL.");\
    }\
    else\
    {\
        V(#pStr" = '%s'.", pStr);\
    }\
}

/*-------------------------------------------------------*/

/**
 * 调用函数, 并检查返回值, 根据返回值决定是否跳转到指定的错误处理代码. 
 * @param functionCall
 *          对特定函数的调用, 该函数的返回值必须是 表征 成功 or err 的 整型数. 
 *          这里, 被调用函数 "必须" 是被定义为 "返回 0 表示操作成功". 
 * @param result
 *		    用于记录函数返回的 error code 的 整型变量, 通常是 "ret" or "result" 等.
 * @param label
 *		    若函数返回错误, 程序将要跳转到的 错误处理处的 标号, 通常就是 "EXIT". 
 */
#define CHECK_FUNC_CALL(functionCall, result, label) \
{\
	if ( 0 != ( (result) = (functionCall) ) )\
	{\
		E("Function call returned error : " #result " = %d.", result);\
		goto label;\
	}\
}

/**
 * 分配 heap 空间, 检查返回地址, 将分配得到的 heap 空间全部清 0.
 * 若失败, 则将指定的 error code 赋值给指定的变量, 之后跳转到指定的错误处理代码. 
 * @param pDest
 *		    用于保存分配得到的 heap block 地址的目标指针变量.
 * @param type
 *          待分配的空间中, 数据单元 的类型. 
 * @param size
 *          待分配空间中包含 "数据单元"(对应类型 'type') 的具体个数. 
 *          所以, 待分配空间的字节大小 是 "sizeof(type) * size". 
 * @param retVar
 *		    用于记录函数返回的 error code 的 整型变量, 通常是 "ret" or "result" 等.
 * @param errCode
 *          用来表征 "内存不足" 的 error code, 不同模块中可能使用不同的常数标识. 
 *          标准 linux 下, 通常是 ENOMEM.
 * @param label
 *		    若空间分配失败, 程序将要跳转到的 错误处理代码的 标号, 通常就是 "EXIT". 
 */
#define CHECK_MALLOC(pDest, type, size, retVar, errCode, label) \
{\
	unsigned int bug_len = sizeof(type) * (size);\
	if ( NULL == ( (pDest) = (type*)malloc(bug_len)))\
	{\
        retVar = errCode;\
        E("Failed to malloc %u bytes.", bug_len);\
		goto label;\
	}\
	memset( (void*)(pDest), 0, sizeof(bug_len));\
}

/**
 * 在特定条件下, 判定 error 发生, 对变量 'retVar' 设置 'errCode', 
 * Log 输出对应的 Error Caution, 然后跳转 'label' 指定的代码处执行. 
 * @param msg
 *          纯字串形式的提示信息. 
 * @param retVar
 *		    标识函数执行状态或者结果的变量, 将被设置具体的 Error Code. 
 *		    通常是 'ret' or 'result'. 
 * @param errCode
 *          表征特定 error 的常数标识, 通常是 宏的形态. 
 * @param label
 *          程序将要跳转到的错误处理代码的标号, 通常就是 'EXIT'. 
 * @param args...
 *          对应 'msgFmt' 实参中 '%s', '%d', ... 等 转换说明符 的具体可变长实参. 
 */
#define SET_ERROR_AND_JUMP(msgFmt, retVar, errCode, label, args...) \
{\
    E("To set '" #retVar "' to %d('" #errCode "') : " msgFmt, (errCode), ## args);\
	(retVar) = (errCode);\
	goto label;\
}

#define EXIT_FOR_DEBUG \
{\
    E("To exit for debug.");\
    return 1;\
}

/*---------------------------------------------------------------------------*/

/**
 * 返回指定数组中的元素个数. 
 * @param array
 *      有效的数组实例标识符. 
 */
#define ELEMENT_NUM(array)      ( sizeof(array) /  sizeof(array[0]) )

/*---------------------------------------------------------------------------*/

/**
 * 断言.
 * 期望 'expect' 的 实参表达式 "成立", 即 非 0, 否则直接 abort 当前进程. 
 * @param msgFmt
 *          字串形式的提示信息, 可以包含 '%s', '%d' 等 转换说明符. 
 * @param args... 
 *          对应 'msgFmt' 实参中 '%s', '%d', ... 等 转换说明符 的具体可变长实参. 
 */
#define ASSERT(expect, msgFmt, args...) \
    do { \
        if ( !(expect) ) \
        { \
            E("assert('" #expect "') FAILED, to ABORT. " msgFmt, ## args); \
            abort(); \
        } \
    } while ( 0 )

/* ---------------------------------------------------------------------------------------------------------
 *  Types and Structures Definition
 * ---------------------------------------------------------------------------------------------------------
 */


/* ---------------------------------------------------------------------------------------------------------
 *  Global Functions' Prototype
 * ---------------------------------------------------------------------------------------------------------
 */


/* ---------------------------------------------------------------------------------------------------------
 *  Inline Functions Implementation 
 * ---------------------------------------------------------------------------------------------------------
 */
 
/*-------------------------------------------------------*/
/**
 * dump 从 地址 'pStart' 开始, 长度为 'len' 字节的 memory block 的内容(16 进制数的形式). 
 */
#ifdef ENABLE_DEBUG_LOG
#define D_MEM(pStart, len) \
    { \
        D("dump memory from addr of '" #pStart"' : "); \
        dumpMemory( (pStart), (len) ); \
    }
inline static void dumpMemory(const void* pStart, uint32_t len)
{
    char* pBuf = NULL;          /* dump buffer. 用来以文本的形式("0xaa, 0xbb, ...") 表示 一行, 最多 16 字节的 十六进制数. */
    size_t bufLen = 0;    /* *pBuf 的字节长度, 包括末尾的 '\0'. */

    unsigned char* pSource = (unsigned char*)pStart;          /* 单次 sprintf 操作的源地址. */
    char* pTarget = NULL;                                     /* 目标地址. */

    size_t i, j;

#define BYTES_PER_LINE (16)                         /* dump 输出每行打印的 source bytes 的数目. */
    size_t BUF_SIZE_PER_SRC_BYTE  = strlen("0xXX, ");  // const. 每个待 dump 的 字节在 *pBuf 中需要的空间大小.
                                                    // memory 中的每个字节被表示为 "0xXX, " 或者 "0xXX\n " 的形式. 
    
    size_t fullLines = len / BYTES_PER_LINE;  // 满 16 字节的行数.
    size_t leftBytes = len % BYTES_PER_LINE;  // 最后可能的不满行的 字节的个数.

    if ( NULL == pStart || 0 == len )
    {
        return;
    }
    
    bufLen = (BYTES_PER_LINE * BUF_SIZE_PER_SRC_BYTE) + 1;  
    pBuf = (char*)malloc(bufLen); 
    if ( NULL == pBuf )
    {
        E("no enough memory.");
        return;
    }

    LOGD("from %p; length %d : ", pStart, len);

    /* 处理所有的 full line, ... */
    for ( j = 0; j < fullLines; j++ )
    {
        pTarget = pBuf;
        /* 遍历每个待 log 的 字节, ... */
        for ( i = 0; i < BYTES_PER_LINE; i++ )
        {
            /* 打印到 *pTarget. */
            sprintf(pTarget, "0x%02x, ", *pSource);

            pTarget += BUF_SIZE_PER_SRC_BYTE;
            pSource++;
        }
        
        *(++pTarget) = '\0';
        /* log out 当前行. */
        LOGD("\t%s", pBuf);
    }
    
    /* 处理最后的 不满的行. */
    leftBytes = len % BYTES_PER_LINE;
    if ( 0 != leftBytes )
    {
        pTarget = pBuf;

        for ( i = 0; i < leftBytes; i++ )
        {
            sprintf(pTarget, "0x%02x, ", *pSource);
            pTarget += BUF_SIZE_PER_SRC_BYTE;
            pSource++;
        }

        *(++pTarget) = '\0';
        LOGD("\t%s", pBuf);
    }
    
    free(pBuf);
    
#ifdef MASSIVE_LOG
    usleep(RESPITE_TIME_FOR_MASSIVE_LOG_IN_US);
#endif
    return;
}

#else       // #ifdef ENABLE_DEBUG_LOG
#define D_MEM(...)  ((void)0)
#endif

/*-------------------------------------------------------*/

#ifdef ENABLE_VERBOSE_LOG
#define V_MEM(pStart, len) \
    { \
        D("dump memory from addr of '" #pStart"' : "); \
        dumpMemory( (pStart), (len) ); \
    }
#else       // #ifdef ENABLE_VERBOSE_LOG
#define V_MEM(...)  ((void)0)
#endif

/*---------------------------------------------------------------------------*/

#define DUMP_SIZE_PER_SRC_BYTE       (sizeof("0xXX, ") - 1)             // "1" : 字串最后的 '\0'.

/** 
 * 将 'pStart' 和 'len' 指定的一块内存数据, 以  hex ASCII 的形态 dump 'pBuf' 指定的 buf 中. 
 * 函数返回之后, "*pBuf' 尾部有 '\0' 字符. 
 * .! : 调用者 "必须" 保证 'pDest' 指向的 buf 的不小于 'len * DUMP_SIZE_PER_SRC_BYTE'. 
 */
inline static void dumpMemInHexAsciiToMem(const void* pStart, unsigned int len, char* pBuf) 
{
    unsigned int i = 0;
    unsigned char* pSource = (unsigned char*)pStart;
    char* pTarget = pBuf;       // 特定写入操作的目标地址. 

    if ( NULL == pStart || 0 == len || NULL == pBuf )
    {
        return;
    }
    
    for ( i = 0; i < len; i++ )
    {
        if ( i < len - 1 )
        {
            sprintf(pTarget, "0x%02x, ", *pSource);
        }
        else
        {
            sprintf(pTarget, "0x%02x", *pSource);
        }
        pSource++;
        pTarget += DUMP_SIZE_PER_SRC_BYTE;
    }
    
    pBuf[len * DUMP_SIZE_PER_SRC_BYTE - 1] = '\0';
}

/*---------------------------------------------------------------------------*/

/**
 * 以 hex ASCII 格式 log dump 从 'pStart' 开始, 长度为 'len' 字节的 mem 的内容, 16 字节为一行. 
 * 前导有 'indentNum' 个 '\t' 缩进. 
 * 支持的最大缩进不超过 15. 
 */
inline static void dumpMemoryWithIndents(const void* pStart, unsigned int len, unsigned int indentNum)
{
    char indents[16];

    const unsigned int SRC_BYTES_NUM_PER_LINE = 16;     // 输出中每行显示的源字节的个数. 

    unsigned int bufLen = SRC_BYTES_NUM_PER_LINE * DUMP_SIZE_PER_SRC_BYTE;
    char* pBuf = (char*)malloc(bufLen);

    unsigned int linesNum = 0;        // 输出的行数.

    unsigned char* pSrcStartInLine = NULL;      // 当前行输出对应的源字节序列的起始地址. 
    unsigned int srcBytesNumInLine = 0; // 当前行要输出端的源字节的个数. 
    
    unsigned int i = 0;
    
    /* 若原数据可以被若干个 满行 正好打印, 则... */
    if ( 0 == (len % SRC_BYTES_NUM_PER_LINE) )
    {
        linesNum = len / SRC_BYTES_NUM_PER_LINE;
    }
    /* 否则, ... */
    else
    {
        /* 增加最后一行的 不满行. */
        linesNum = (len / SRC_BYTES_NUM_PER_LINE) + 1;
    }

    /*-------------------------------------------------------*/

    indents[0] = '\0';
    // for ( i = 0; i < indentNum - 1; i++ )       // "-1" : 对输出内容的地址标识, 可以替代一个缩进. 
    for ( i = 0; i < indentNum; i++ )       // "-1" : 对输出内容的地址标识, 可以替代一个缩进. 
    {
        strcat(indents , "\t");
    }
    *(indents + indentNum) = '\0';
    
    /*-------------------------------------------------------*/

    pBuf[0] = '\0';
    pSrcStartInLine = (unsigned char*)pStart;

    /* 逐行输出. */
    for ( i = 0; i < linesNum; i++ )
    {
        srcBytesNumInLine = (len - (i * SRC_BYTES_NUM_PER_LINE) ) % SRC_BYTES_NUM_PER_LINE;

        /* 若当前行 "不是" 最后一行(第 (linesNum - 1) 行 , 则... */
        if ( i < (linesNum - 1) )
        {
            srcBytesNumInLine = SRC_BYTES_NUM_PER_LINE;
        }
        /* 否则, 即当前行 "是" 最后一行", 则... */
        else
        {
            /* 若是满行, 则... */
            if ( 0 == (len % SRC_BYTES_NUM_PER_LINE ) )
            {
                srcBytesNumInLine = SRC_BYTES_NUM_PER_LINE;
            }
            else 
            {
                srcBytesNumInLine = len % SRC_BYTES_NUM_PER_LINE;
            }
        }
        
        dumpMemInHexAsciiToMem(pSrcStartInLine, srcBytesNumInLine, pBuf);
        LOGD("%s" " [0x%04x] : " "%s", indents, SRC_BYTES_NUM_PER_LINE * i, pBuf);
        
        pSrcStartInLine += srcBytesNumInLine;

#ifdef MASSIVE_LOG
    usleep(RESPITE_TIME_FOR_MASSIVE_LOG_IN_US);
#endif
    }

    /*-------------------------------------------------------*/

    free(pBuf);

}

/**
 * 在 'pIndentsBuf' 指向的 buf 中, 从头插入 'indentNum' 个 '\t' 字符(缩进), 并跟上一个 '\0'. 
 * 通常 pIndentsBuf 指向一个 16 字节的 buffer. 
 */
inline static void setIndents(char* pIndentsBuf, unsigned char indentNum)
{
    unsigned char i = 0; 

    const unsigned char MAX_NUM_OF_INDENT = 16 - sizeof('\0');
    if ( indentNum > MAX_NUM_OF_INDENT )
    {
        indentNum = MAX_NUM_OF_INDENT;
    }

    *pIndentsBuf = '\0';
    for ( i = 0; i < indentNum; i++ )
    {
        strcat(pIndentsBuf, "\t");
    }
    *(pIndentsBuf + indentNum) = '\0';
}

inline static int dumpCharArray(const char* pStart, unsigned int len, unsigned char indentNum)
{
    int ret = 0;
    //unsigned long ret1 = -1;
    char indents[16];
    setIndents(indents, indentNum);

    char* pBuf = NULL;

    /*-------------------------------------------------------*/
    
    if ( NULL == pStart )
    {
        LOGD("%s %s", indents, "null");
        goto EXIT;
    }
    
    CHECK_MALLOC( pBuf, char, len + 1, ret, -1, EXIT);
    
    memcpy(pBuf, pStart, len);
    pBuf[len] = '\0';
    // pBuf[len] = 0;
    LOGD("%s %s", indents, pBuf);

EXIT:
    if ( NULL != pBuf )
    {
        free(pBuf);
    }

    return ret;
}

inline static int dumpStr(const char* pStr, unsigned char indentNum)
{
    int ret = 0;

    char indents[16];
    setIndents(indents, indentNum);
    /*-------------------------------------------------------*/

    if ( NULL == pStr )
    {
        LOGD("%s %s", indents, "null");
    }
    else
    {
        LOGD("%s %s", indents, pStr);
    }
    
    return ret;
}

/* ############################################################################################# */
/* < 单独针对 Android 平台的调试宏. > */

/**
 * 打印 sp<T> 实例 "sp" 指向的实例对象的当前 强被引用计数. 
 * "D_SC" : Debug log Strong Count.
 */
#define D_SC(sp)    D("strong count of '" #sp "' is '%d'.", (sp)->getStrongCount() )


#ifdef __cplusplus
}
#endif

#endif /* __CUSTOM_LOG_H__ */