
/******************************************************************************

  Copyright (C), 2011-2017 PeitaoShi @LRVS, All Rights Reserved.

  THIS SOFTWARE IS PROTECTED UNDER SOURCE CODE AGREEMENT OR NON-DISCLOSURE AGREEMENT. 

  PERMISSION IS GRANTED TO LICENSE TO USE THIS SOFTWARE FOR ANY PURPOSE, INCLUDING
  COMMERICLA APPLICATION AND TO ALTER IT BUT NOT REDISTRIBUTE IT UNDER SORCE CODE 
  AGREEMENT/NON-DISCLOSUE AGREEMENT. YOU CAN NOT CLAIM THAT YOU WROTE THE SOFTWARE
  AFTER ALTERING SOURCE CODE.

  THIS SOFTWARE IS PROVIDED 'AS-IS', WITHOUT ANY EXPRESS OR IMPLIED WARRANTY.
  IN NO EVENT WILL THE AUTHOR(S) BE HELD LIABLE FOR NAY DAMGES ARISING FROM THE
  THE USER OF THIS SOFTWARE.

  ABOVE NOTICE SHOULD BE KEPT IN THE SOURCE CODE.

 ******************************************************************************
  File Name     : log_api.c

  Version       : 1.0

  Author        : Peitao Shi

  Description   : Embedded Logging is a solution targeted to limited embedded 
                platform even system log is not available. The logging information(
                warning, error and debug) messages can be logged in one or mulitple locations. 
                All log configurations are intialially loaded from configuration files ,and 
                can be overriden later by enviroment variable at running time. 

                If you want to log messages via the system syslog, you not only need set 
                syslog_enable =1, you also should ensure that your syslogd daemon is running.
                If you want to redirect redirectsyslog message to a specific file,you need to 
                locate and edit the syslog.conf file on your system.

                To the syslog daemon is not available or you don't want to use syslog,setting
                different destinations can rediret message to different locations , such as 
                writing local files or send to remote serer syslog server etc. The Embedded 
                logging supports remote trace with UDP packages. The messages are also displayed
                 with different colors locally or remotely.

                The new log settings can be run-time changed without doing reinitialization.
                by adding or updating same enviroment variables followed with "USR1" or "USR2"
                signals, the user can enable/disale logging, change debug level or destintions 
                etc. 

                In addition, Embeddd logging  provides simple logging rotations by cheching 
                logging files and number of files, which is especially suitable for flash based 
                embedded system. 

                 A log configuration file that defines all logging behavior has to be created 
                and loaded when logging moudle is initialized. See the following template.

                 For detail information on how to use API in log_api.h,please refer to 
                 Embedded logging user menual.

******************************************************************************/



#include <errno.h> /* for definition of errnor */
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
#include <termios.h>

#include "log_api.h"
#include "alt_error_handler.h"

/** @internal
 * Holds the current configuration of the logging 
*/
s_config config = {LOGAPP_DEBUG, LOGFMT_RUNTIME };


/** @internal
 */

static struct timeval dbgStart;

/* Local function definitions [start]*/
void    LogLoc_SubtractTime( struct timeval * const tp1, 
            struct timeval * const tp2 );

int     LogLoc_LineHexDump(const char * pchInput, int inInputLen, char*pchOutput, 
                            int inMaxLen);
void    LogLoc_HexDump(char *pchData, int inDataLen, 
                        char * pchDbgString,int inDbgLen, int inOffset );
/* Local function definitions [end] */




/*****************************************************************************
    Function :      LogLoc_SubtractTime
    Parameters :    tp1 - current timestamp
                    tp2 - last loggingtimestap
    Returns :       TRUE
                    FALSE (-1)
    Descriptions:   Log format 0: logging with elapse time.
 *****************************************************************************/
void LogLoc_SubtractTime( struct timeval * const tp1, 
        struct timeval * const tp2 )
{
    tp1->tv_sec -= tp2->tv_sec;
    if ( tp1->tv_usec <= tp2->tv_usec )
    {
        tp1->tv_sec--;
        tp1->tv_usec = 1000000 - (tp2->tv_usec - tp1->tv_usec);
    }
    else
    {
        tp1->tv_usec = tp1->tv_usec - tp2->tv_usec;
    }
}

/*****************************************************************************
    Function :      LogLoc_LineHexDump
    Parameters :    pchInput - Data input
                    inInputLen - Data length
                    pchOutput - Hex string output, such as 
                    inMaxLen - Maximum puchOutput bufferlength
    Returns :       hex decoded data length             
    Descriptions:   Dump one line hex data  ( max 16 characters per one line)
 *****************************************************************************/
int LogLoc_LineHexDump(const char * pchInput, int inInputLen, char*pchOutput, int inMaxLen)
{
	short usLoop;
	char  ucLineLog[88 + 1];
	unsigned char  ucTemp;

	usLoop = 0;

	memset(ucLineLog, 0x20, sizeof(ucLineLog));
	while (usLoop < 16 && usLoop < inInputLen)
	{
		ucTemp = (unsigned char) pchInput[usLoop];
		ucLineLog[usLoop * 3] = ((ucTemp >> 4) > 9 ) ? (ucTemp >> 4) + 55 : (ucTemp >> 4) + '0';
		ucLineLog[usLoop * 3 + 1] = ((ucTemp & 15) > 9) ? (ucTemp & 15) + 55 : (ucTemp & 15) + '0';
		ucLineLog[48 + usLoop] = (ucTemp > 31) ? ((ucTemp < 128) ? ucTemp : ' ') : ' ';
		usLoop ++;
	}
	
    ucLineLog[47] = '|';
    ucLineLog[64] = '\n';
	ucLineLog[65] = 0;

    snprintf(pchOutput, inMaxLen, "%s", ucLineLog );
    return usLoop;
      
}

/*****************************************************************************
    Function :      LogLoc_HexDump
    Parameters :    pchData - Data input
                    inDataLen - Data length
                    pchDbgString - dump message buffer 
                    inbDgLen -  dump message buffer length
                    inOffset - Hex message log offset  
    Returns :       None                  
    Descriptions:   Dump Hex data message into multiple messages, such as:
            0.121 myproc.log[4066].DB7-main.c/60: myData - Length=5:
            0.121 myproc.log[4066].DB7-main.c/60: 31 32 33 34 35 |12345
 *****************************************************************************/
void LogLoc_HexDump(char *pchData, int inDataLen, 
                    char * pchDbgString,int inDbgLen, int inOffset )
{

    int iRemainLen = inDataLen;
    int iProcLen =0;
    char *pchLogStart = pchDbgString + inOffset;
    char *pchProcData = pchData;
    FILE *fd_out = stdout;

    if (pchData== NULL) 
    {
        fprintf(stderr,"Error: Invalid data input string\n");
        return;
    }

    while (iRemainLen > 0)
    {
        iProcLen = LogLoc_LineHexDump(pchProcData, iRemainLen, pchLogStart, 
                                inDbgLen-inOffset);
        pchProcData += iProcLen;
        iRemainLen -= iProcLen;    
        /* Print debug string first */
        fputs(pchDbgString ,fd_out);
        fflush(fd_out);
    }        
}


/*****************************************************************************
    Function :      LogApi_Output

    Parameters :    hex - 1: hex dump; 0 - format string
                    file - current file name
                    line - line number in current source file
                    level - logging level
                    fstring, - format string
     
    Returns :       TRUE
                    FALSE (-1)

    Descriptions:   Log format 0: logging with elapse time.
 *****************************************************************************/
void LogApi_Output( int hex, const char * const file, const int line, 
        const int level, const char *fstring, ... )
{
    char            classString[5];
    char            timeString[64];
    char            dbgString[8192];
    va_list            argPtr;               
    struct timeval    timeVal;
    INT8U error_code = 0;
    
    /* Debug level is not enough. */
    if ( level > config.log_level)
        return;

	OSSemPend(UniDebugSem, 0, &error_code);  
      
    switch ( level )
    {
        case LOGAPP_DEBUG:
            strncpy( classString, "DEBUG", sizeof(classString) );
            break;
        case LOGAPP_INFO:
            strncpy( classString, "INFO", sizeof(classString) );
            break;
        case LOGAPP_WARN:
            strncpy( classString, "WARN", sizeof(classString) );
            break;
        case LOGAPP_ERROR:
            strncpy( classString, "ERR", sizeof(classString) );
            break;
        case LOGAPP_FATAL:
            strncpy( classString, "FATAL", sizeof(classString) );
            break;
        default:
            fprintf(stderr, "Unknown Error Level %d", level );
            break;
    }

    gettimeofday( &timeVal, NULL );

    /* Get log format */
    char *dbgPtr = dbgString;
    char *timePtr = timeString;
    switch (config.log_format)
    {
       case LOGFMT_LOCALTIME:
 
            timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", 
                localtime(&timeVal.tv_sec) );
            snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), 
                ".%03ld", timeVal.tv_usec/1000 );
            dbgPtr += snprintf( dbgPtr, sizeof(dbgString), 
                    "%s .%s-%s/%d: ", 
                  timeString, classString, file, line);
            break;
       case LOGFMT_RUNTIME:
            LogLoc_SubtractTime( &timeVal, &dbgStart );

            snprintf( timeString, sizeof(timeString), 
                    "%ld.%03ld", 
                timeVal.tv_sec, timeVal.tv_usec/1000 );
            dbgPtr += snprintf( dbgPtr, sizeof(dbgString), 
                    "%s .%s-%s/%d: ", 
                  timeString, classString, file, line);
        break;
     
       default:
           fprintf(stderr, "Unknown log level %d\n", config.log_format); 
          break;
    } 
    char *dbg_log_start = dbgPtr;
    va_start( argPtr, fstring );
    char *title = NULL;
    unsigned char *data  = NULL;
    int len = 0;
    if ( hex )
    {
        title = va_arg(argPtr,char *);
        data = va_arg( argPtr, unsigned char * );
        len = va_arg( argPtr, int );
 
        dbgPtr += snprintf( dbgPtr, sizeof(dbgString)-(dbgPtr-dbgString),
                "%s - Length=%d", title, len );
    }
    else
    {
        dbgPtr += vsnprintf( dbgPtr, sizeof(dbgString)-(dbgPtr-dbgString),
                fstring, argPtr );
    }
    va_end(argPtr);
    /* Process the line feed*/
    strncpy( dbgPtr, "\n", sizeof(dbgString)-(dbgPtr-dbgString) );  
 
    /* Console output: stdout */
    FILE * fd = stdout;
    /* Console (stderr or stdout) output*/
    fprintf(fd, "%s", dbgString );
    fflush(fd);      
   
    if (hex)
    {   
        LogLoc_HexDump((char*) data, len, dbgString, sizeof(dbgString),
                        (dbg_log_start - dbgString) ) ;
    }
    
    error_code = OSSemPost(UniDebugSem);

}



/*****************************************************************************
    Function :      printf_error
    Parameters :    fmt - format string
    Returns :       N/A
    Descriptions:   printf wrapper with  error Color ouput ( thread_safe).
 *****************************************************************************/
void printf_error(const char *fmt, ...)
{
    FILE *fd = stderr;
    INT8U error_code = 0;
    
	OSSemPend(UniDebugSem, 0, &error_code);  
    
    va_list args;
    va_start(args, fmt);
    fprintf(fd, "%sError: ", ERROR_COLOR);
    vfprintf(fd, fmt, args);
    fprintf(fd, "%s\r\n", RESET_COLOR);
    va_end(args);
    
    error_code = OSSemPost(UniDebugSem);
    
}

/*****************************************************************************
    Function :      printf_warn
    Parameters :    fmt - format string
    Returns :       N/A
    Descriptions:   printf wrapper with  warning Color ouput ( thread_safe).
 *****************************************************************************/
void printf_warn(const char *fmt, ...)
{
    FILE *fd = stderr;
    INT8U error_code = 0;
    
	OSSemPend(UniDebugSem, 0, &error_code);
    
    va_list args;
    va_start(args, fmt);
    fprintf(fd, "%sWarning: ", WARNING_COLOR);
    vfprintf(fd, fmt, args);
    fprintf(fd,"%s\n", RESET_COLOR);
    va_end(args);
    
    error_code = OSSemPost(UniDebugSem);
}

/*****************************************************************************
    Function :      printf_debug
    Parameters :    fmt - format string
    Returns :       N/A
    Descriptions:   printf wrapper.
 *****************************************************************************/
void printf_debug(const char *fmt, ...)
{
    INT8U error_code;
    
    FILE *fd = stdout;
    if (config.log_level < LOGAPP_DEBUG) {
        return;
    }

	OSSemPend(UniDebugSem, 0, &error_code);  
    
    va_list args;
    va_start(args, fmt);
    fprintf(fd, "%sDebug: ", ANSI_COLOR_GREEN);
    vfprintf(fd, fmt, args);
    fprintf(fd, "%s\n", RESET_COLOR);
    va_end(args);
    
    error_code = OSSemPost(UniDebugSem);
}








