/*
 * IPRPC - Inter Process Remote Procedure Call
 *
 * Copyright (C) 2012-2013 by Hiroyuki KAJIURA. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *     1:Redistributions of source code must retain the above copyright notice, 
 *       this list of conditions and the following disclaimer.
 *     2:Redistributions in binary form must reproduce the above copyright notice, 
 *       this list of conditions and the following disclaimer in the documentation 
 *       and/or other materials provided with the distribution.
 *     3:Neither the name of copyright owner nor the names of its contributors 
 *       may be used to endorse or promote products derived from this software 
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#define	IPC_LOG_MAIN

#include	<stdio.h>
#include	<stdarg.h>

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include	"ipcInfo.h"
#include	"ipcType.h"
#include	"ipcSyscall.h"
#include	"ipcLog.h"
#ifdef	USE_IDC
#include	"idcLog.h"
#endif	/* USE_IDC */

typedef struct {
	RpcLogModule_t	module;
	RpcLogLevel_t	level;
} RpcLogLevelTable_t;

static RpcLogLevelTable_t RpcLogLevel[RPC_LOG_MODULE_MAX];

static RpcMutexID_t RpcLogMutexID;
static RpcMutexAttribute_t RpcLogMutexAttr;

static char *RPC_logConvertLevel(RpcLogLevel_t level);
static char *RPC_logConvertModule(RpcLogModule_t module);

/* ----- LOG APIs - Global Functions ----- */

extern RpcResult_t RPC_LogInit(void) {
	int32_t i;

	if(RPC_MutexAttrInit(&RpcLogMutexAttr) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
#ifdef	_LINUX_
	if(RPC_MutexAttrSetType(&RpcLogMutexAttr, PTHREAD_MUTEX_ADAPTIVE_NP) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
#else	/* _LINUX_ */
	if(RPC_MutexAttrSetType(&RpcLogMutexAttr, PTHREAD_MUTEX_RECURSIVE) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
#endif	/* _LINUX_ */
	if(RPC_MutexAttrSetPshared(&RpcLogMutexAttr, PTHREAD_PROCESS_PRIVATE) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
	if(RPC_InitMutex(&RpcLogMutexID,&RpcLogMutexAttr) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
	for(i = 0;i < RPC_LOG_MODULE_MAX;i++) {
		RpcLogLevel[i].module = RPC_LOG_MODULE_UNKNOWN;
		RpcLogLevel[i].level = RPC_LOG_LEVEL_FATAL;
	}
	return RPC_SUCCESS;
}

extern RpcResult_t RPC_LogSetAll(RpcLogLevel_t level) {
	int i;RpcResult_t ret;

	for(i = (int)RPC_LOG_MODULE_UNKNOWN;i < (int)RPC_LOG_MODULE_MAX;i++) {
		if((ret = RPC_LogSetLevel((RpcLogModule_t)i,level)) != RPC_SUCCESS) {
			fprintf(stderr,"RPC-LOG:ERROR IN RPC_LogSetAll LEVEL:%s;\n",RPC_logConvertLevel(level));
		}
	}
	return RPC_SUCCESS;
}

extern RpcResult_t RPC_LogSetLevel(RpcLogModule_t module, RpcLogLevel_t level) {
#if	0
	int i; int noUse = -1;
#endif	/* 0 */

	if(RPC_MutexLock(&RpcLogMutexID) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
#if	0
	for(i = 0;i < RPC_MAX_LOG_LEVEL;i++) {
		if(RpcLogLevel[i].module == module) {
			RpcLogLevel[i].level = level;
			(void)RPC_MutexUnlock(&RpcLogMutexID);
			return RPC_SUCCESS;
		}
		else if(RpcLogLevel[i].module == RPC_LOG_MODULE_UNKNOWN) {
			noUse = i;
		}
	}
	if(noUse >= 0) {
		RpcLogLevel[noUse].module = module;
		RpcLogLevel[noUse].level = level;
		(void)RPC_MutexUnlock(&RpcLogMutexID);
		return RPC_SUCCESS;
	}
#else	/* 0 */
	if((RPC_LOG_MODULE_UNKNOWN<= module)&&(module < RPC_LOG_MODULE_MAX)) {
		RpcLogLevel[(int)module].module = module;
		RpcLogLevel[(int)module].level = level;
		(void)RPC_MutexUnlock(&RpcLogMutexID);
		return RPC_SUCCESS;
	}
#endif	/* 0 */
	(void)RPC_MutexUnlock(&RpcLogMutexID);
	return RPC_PARAM_ERROR;
}

extern RpcResult_t RPC_LogPrint(RpcLogModule_t module, RpcLogLevel_t level, char *fmt, ...) {
	va_list argp;

	if((RPC_LOG_MODULE_UNKNOWN > module)||(module >= RPC_LOG_MODULE_MAX)) {
		return RPC_PARAM_ERROR;
	}
	if(RPC_MutexLock(&RpcLogMutexID) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
	if(RpcLogLevel[(int)module].level > level) {
		(void)RPC_MutexUnlock(&RpcLogMutexID);
		return RPC_SUCCESS;
	}
	va_start(argp, fmt);
	fprintf(stdout,"%s:%s:",RPC_logConvertModule(module),RPC_logConvertLevel(level));
	vfprintf(stdout,fmt, argp);
	fprintf(stdout,"\n");
	(void)RPC_MutexUnlock(&RpcLogMutexID);
	return RPC_SUCCESS;
}

extern RpcResult_t RPC_LogPrintNomark(RpcLogModule_t module, RpcLogLevel_t level, char *fmt, ...) {
	va_list argp;

	if((RPC_LOG_MODULE_UNKNOWN > module)||(module >= RPC_LOG_MODULE_MAX)) {
		return RPC_PARAM_ERROR;
	}
	if(RPC_MutexLock(&RpcLogMutexID) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
	if(RpcLogLevel[(int)module].level > level) {
		(void)RPC_MutexUnlock(&RpcLogMutexID);
		return RPC_SUCCESS;
	}
	va_start(argp, fmt);
	vfprintf(stdout,fmt, argp);
	(void)RPC_MutexUnlock(&RpcLogMutexID);
	return RPC_SUCCESS;
}

extern RpcResult_t RPC_LogPrintInfo(void) {
	if(RPC_MutexLock(&RpcLogMutexID) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
	fprintf(stdout,"##### IPRPC VERSION:%s; COPYRIGHT:%s; #####\n",iprpcVersion,iprpcCopyright);
	(void)RPC_MutexUnlock(&RpcLogMutexID);
	return RPC_SUCCESS;
}

extern char *RPC_LogConvertResultCode(RpcResult_t ret) {
	switch(ret) {
	case RPC_ERROR_UNKNOWN:		return "UNKNOWN";
	case RPC_SUCCESS:		return "SUCCESS";
	case RPC_FATAL_ERROR:		return "FATAL";
	case RPC_TIMEOUT_ERROR:		return "TIMEOUT";
	case RPC_NO_MORE_RESOURCE:	return "NO-MORE-RESOURCE";
	case RPC_PARAM_ERROR:		return "PARAM-ERROR";
	case RPC_CAN_NOT_EXEC:		return "CAN-NOT-EXEC";
	case RPC_BUSY_ERROR:		return "BUSY";
	case RPC_INTERUPTTED:		return "INTERRUPTTED";
	case RPC_LOCKING:		return "LOCKING";
	case RPC_NOT_READY:		return "NOT-READY";
	case RPC_SYSCALL_ERROR:		return "SYSCALL";
	default:			return "UNKNOWN";
	}
}

extern char *RPC_LogConvertErrCode(uint32_t errCode) {
	switch(errCode) {
	case RPC_NO_ERROR:		return "NO ERROR";
	case RPC_ERR_EACCES:		return "EACCES";
	case RPC_ERR_EEXIST:		return "EEXIST";
	case RPC_ERR_EINVAL:		return "EINVAL";
	case RPC_ERR_ENOENT:		return "ENOENT";
	case RPC_ERR_ENAMETOOLONG:	return "ENAMETOOLONG";
	case RPC_ERR_EFAULT:		return "EFAULT";
	case RPC_ERR_ELOOP:		return "ELOOP";
	case RPC_ERR_EISDIR:		return "EISDIR";
	case RPC_ERR_ENOTDIR:		return "ENOTDIR";
	case RPC_ERR_EPERM:		return "EPERM";
	case RPC_ERR_EROFS:		return "EROFS";
	case RPC_ERR_ETXTBSY:		return "ETXTBSY";
	case RPC_ERR_EBADF:		return "EBADF";
	case RPC_ERR_ENODEV:		return "ENODEV";
	case RPC_ERR_EOVERFLOW:		return "EOVERFLOW";
	case RPC_ERR_EMFILE:		return "EMFILE";
	case RPC_ERR_ENFILE:		return "ENFILE";
	case RPC_ERR_ENOMEM:		return "ENOMEM";
	case RPC_ERR_EINTR:		return "EINTR";
	case RPC_ERR_EIO:		return "EIO";
	case RPC_ERR_EAGAIN:		return "EAFAIN";
	case RPC_ERR_EFBIG:		return "EFBIG";
	case RPC_ERR_ENOSYS:		return "ENOSYS";
	case RPC_ERR_E2BIG:		return "E2BIG";
	case RPC_ERR_ELIBBAD:		return "ELIBBAD";
	case RPC_ERR_ENOEXEC:		return "ENOEXEC";
	case RPC_ERR_ECHILD:		return "ECHILD";
	case RPC_ERR_ETIMEDOUT:		return "ETIMEDOUT";
#ifdef	USE_IDC
	default:			return IDC_LogConvertErrCode(errCode);
#else	/* USE_IDC */
	default:			return "OTHER ERROR";
#endif	/* USE_IDC */
	}
}

extern void RPC_MemDump(uint8_t *data,uint32_t leng,uint8_t *label) {
#if	__WORDSIZE == 64
	uint64_t startAddr;
#else	/* __WORDSIZE */
	uint32_t startAddr;
#endif	/* __WORDSIZE */
	uint8_t uData[16],aData[16];uint8_t *p;uint32_t wrtLen = 0;int i;

	fprintf(stdout,"-- Addr:0x%x; Leng:%d; ## %s ## --\n",data,leng,label);
	p = data;
#if	__WORDSIZE == 64
	startAddr = (uint64_t)data;
#else	/* __WORDSIZE */
	startAddr = (uint32_t)data;
#endif	/* __WORDSIZE */
	while(leng > 0) {
		wrtLen = (leng > 16)?16:leng;
		for(i = 0;i < wrtLen;i++) {
			uData[i] = *p++;
			if((uData[i] > 0x20)&&(uData[i] < 0x7F))
				aData[i] = uData[i];
			else
				aData[i] = ' ';
		}
		for(;i < 16;i++) {
			uData[i] = 0; aData[i] = ' ';
		}
		fprintf(stdout,"%08x  %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x    %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
				  startAddr,uData[0],uData[1],uData[2],uData[3],uData[4],uData[5],uData[6],uData[7],uData[8],uData[9],uData[10],uData[11],uData[12],uData[13],uData[14],uData[15],
				            aData[0],aData[1],aData[2],aData[3],aData[4],aData[5],aData[6],aData[7],aData[8],aData[9],aData[10],aData[11],aData[12],aData[13],aData[14],aData[15]);
		leng -= wrtLen;
		startAddr += wrtLen;
	}
}

/* ----- Internal Functions ----- */

static char *RPC_logConvertLevel(RpcLogLevel_t level) {
	switch(level) {
	case RPC_LOG_LEVEL_INFO:		return "INFO  ";
	case RPC_LOG_LEVEL_DEBUG1:		return "DEBUG1";
	case RPC_LOG_LEVEL_DEBUG2:		return "DEBUG2";
	case RPC_LOG_LEVEL_DEBUG3:		return "DEBUG3";
	case RPC_LOG_LEVEL_WARNING:		return "WARNNG";
	case RPC_LOG_LEVEL_DEFAULT:		return "ERROR ";
	case RPC_LOG_LEVEL_ERROR:		return "ERROR ";
	case RPC_LOG_LEVEL_FATAL:		return "FATAL ";
	case RPC_LOG_LEVEL_URGENT:		return "URGENT";
	default:				return "UNKNWN";
	}
}

static char *RPC_logConvertModule(RpcLogModule_t module) {
	switch(module) {
	case RPC_LOG_MODULE_UNKNOWN:		return "UNKNWN";
	case RPC_LOG_MODULE_MAIN:		return "MAIN  ";
	case RPC_LOG_MODULE_SYSCALL:		return "SYSCLL";
	case RPC_LOG_MODULE_MSG_QUE:		return "MSGQUE";
	case RPC_LOG_MODULE_SHM:		return "SHM   ";
	case RPC_LOG_MODULE_SHM_DB:		return "SHM DB";
	case RPC_LOG_MODULE_LIB:		return "IPCLIB";
	case RPC_LOG_MODULE_MARSHAL:		return "MARSHL";
	case RPC_LOG_MODULE_STUB:		return "STUB  ";
	case RPC_LOG_MODULE_SKELTON:		return "SKELTN";
	case RPC_LOG_MODULE_RENDEZV:		return "RENDZV";
//	case RPC_LOG_MODULE_PRCSS_CNTL:		return "PRCSS ";
	case RPC_LOG_MODULE_MLTCST:		return "MLTCST";
	case RPC_LOG_MODULE_INIT:		return "IPCINT";
#ifdef	USE_IDC
	default:				return IDC_logConvertModule((IdcLogModule_t)module);
#else	/* USE_IDC */
	default:				return "UNKNWN";
#endif	/* USE_IDC */
	}
}
