/*******************************************************************************
 * Copyright (c) 2005, 2009 Intel Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Hoang M Nguyen, Intel - TPTP OS Calls Linux specific Implementation
 * $Id: TPTPOSCalls_aix.c,v 1.1 2009/07/10 00:11:29 jwest Exp $
 *******************************************************************************/


#ifdef _AIX  // AIX-specific


#include <stdio.h>

#include "TPTPOSCalls.h"
#include <string.h>


#include <dlfcn.h>
#include <errno.h>


const char OS_SPECIFIC_NAME_SEPARATOR = '/' ;



/**
 *********************************************************
 *
 * @brief
 *    Return a global unique id string (GUID or UUID)
 *
 * @note
 *    This requires to compile with "libuuid.so" library
 *    or "-luuid" option.
 *
 *********************************************************/

const char INTEGER_TO_ASCIIHEX[16] = { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70 };

void formatUUIDString(char *inHexVals, char *outResultUUID) {
	char first[8+1];
	char second[4+1];
	char third[4+1];
	char fourth[4+1];
	char fifth[12+1];

	char * buffer = inHexVals;

	first[0] = 0; second[0] = 0; third[0] = 0; fourth[0] = 0; fifth[0] = 0;

	strncat(first, buffer, 8); buffer += 8;
	strncat(second, buffer, 4); buffer += 4;
	strncat(third, buffer, 4); buffer += 4;
	strncat(fourth, buffer, 4); buffer += 4;
	strncat(fifth, buffer, 12); buffer += 12;

	sprintf(outResultUUID, "%s-%s-%s-%s-%s", first, second, third, fourth, fifth);
}

char * getGlobalUniqueId()
{
	static int firstTime = 1;
	static unsigned short seed[3];
	struct timeval tv;
	unsigned long seconds, microseconds;
	char buffer[128];
	char *result;
	
	result = (char *)tptp_malloc(36+8);
	
	if(buffer) {
		/* Get a timestamp */
		gettimeofday(&tv, 0);
		seconds=tv.tv_sec;
		microseconds=tv.tv_usec;

		/* Seed our generator on the first pass */
		if(firstTime) {
			seed[0]=seconds;
			seed[1]=microseconds;
			seed[2]=0;
			seed48(seed);
			firstTime=0;
		}

		for(int x = 0; x < 32; x++) {
			buffer[x] = INTEGER_TO_ASCIIHEX[(int)(lrand48() % 16)];
		}
		
		// 4
		buffer[12] = '4';
		
		// 8, 9, A or B
		buffer[16] = INTEGER_TO_ASCIIHEX[(int)(lrand48() % 4)+8];
		
		buffer[32] = 0;

		formatUUIDString(buffer, result);		

		return result;

	}
	return NULL;
}

/**
 *********************************************************
 *
 * @brief
 *    convenient way to compare string ignoring case
 *    and is still platform independent.
 *
 *********************************************************/

int compareIgnoreCase(const char *pStr1, const char *pStr2)
{
	return ( strcasecmp(pStr1, pStr2) ) ;
}



/**
 *********************************************************
 *
 * @brief
 *    load the given module
 *
 *********************************************************/

void * loadTheModule(const char *pLibName)
{
	char buffer[1024] ;

	strcpy(buffer, "lib") ;
	strcat(buffer, pLibName) ;
	strcat(buffer, ".so") ;

	return ( dlopen( buffer, RTLD_LAZY ) );
}


/**
 *********************************************************
 *
 * @brief
 *    Start a new running thread and execute the given function
 *       in that running thread.
 *
 * @return
 *    0 - Success
 *    Nonzero - Error
 *
 *********************************************************/

int  startNewThread(RUN_FUNC_TYPE  pFunc, void * pParmBlock, TID *pThreadId, HANDLE *pThreadHandle)
{
	int    rc = 0 ;

	pthread_attr_t thread_attr;

	pthread_attr_init(&thread_attr);
	pthread_attr_setstacksize( &thread_attr, 4194304 );
	rc = pthread_create(pThreadId, &thread_attr, pFunc, pParmBlock);

	/* Clear pThreadHandle as it is only used for Windows version of this function. */
	*pThreadHandle = 0; /* Use 0 rather than NULL to stop compiler warning. */

	if (rc != 0)
		rc = 1 ;

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    Indicate that thread resources can be reclaimed when the thread terminates.
 *
 * @return
 *    0 - Success
 *    Nonzero - Error
 *
 *********************************************************/

int  detachThread(TID threadId, HANDLE threadHandle)
{
	return pthread_detach (threadId);
}

/**
 *********************************************************
 *
 * @brief
 *    Start a new running thread, execute the given function
 *       in that running thread, detach from the thread.
 *
 * @return
 *    0 - Success
 *    Nonzero - Error
 *
 *********************************************************/

int  tptpStartThread(RUN_FUNC_TYPE  pFunc, void * pParamBlock, TID *pThreadId, HANDLE *pThreadHandle)
{
	int    rc = startNewThread (pFunc, pParamBlock, pThreadId, pThreadHandle);

	if (!rc) detachThread (*pThreadId, *pThreadHandle);

	return ( rc ) ;
}


/**
 *********************************************************
 *
 * @brief
 *    Return the process id of the current running process
 *
 *********************************************************/

PID  getCurrentlyRunningProcessId()
{
	return ( getpid() ) ;
}

/**
 *********************************************************
 *
 * @brief
 *    Return the executable name of the current running process.
 *    Caller must free the allocated memory for the name string returned.
 *
 *********************************************************/

char *getExecutableName()
{
	char *exeName = NULL;
	char procFileName[128];
	char cmdLine[_MAX_PATH];
	FILE *hFile = NULL;
	int num = 0;

	/* Get the name of the executable for this process from the /proc
	* file structure.  The file /proc/<pid>/cmdline contains the cmdline
	* arguments as null terminated strings.  So reading the first element
	* of that file gives us the executable name (i.e., argv[0]).
	*/
	sprintf(procFileName, "/proc/%d/cmdline",getpid());
	hFile = fopen(procFileName,"r");
	if (hFile)
	{
		num = fread(cmdLine, _MAX_PATH, 1, hFile);
		if (!ferror(hFile))
		{
			/*Make sure a null is at the end of whatever we read*/
			cmdLine[_MAX_PATH-1] = '\0';
			exeName = (char *)tptp_malloc(strlen(cmdLine) +1);
			strcpy(exeName, cmdLine);
			return exeName;
		}
	}

	/* if error occurred trying to get the name, just return 'unknown' */
	exeName = (char *)tptp_malloc(strlen("unknown") +1);
	strcpy(exeName, "unknown");
	return exeName;
}

/**
 *********************************************************
 *
 * @brief
 *    Return the process id of the current running process
 *
 *********************************************************/

PID  getCurrentlyRunningThreadId()
{
	/* On Linux the Process ID is the thread ID */
	return ( getpid() ) ;
}

/**
 *********************************************************
 *
 * @brief
 *    Print out the current system error message
 *
 *********************************************************/
void printCurrentSysError()
{
	printf("errno(%d) error_message(%s) \n", errno, strerror(errno));
}


/**
 *********************************************************
 *
 * @brief
 *    Put the current thread in sleep mode
 *    for a given amount of time (in milliseconds)
 *
 *********************************************************/
void  tptpSleep(int milliseconds)
{
    long seconds   = milliseconds / 1000;
    long remainder_in_micro_seconds = (milliseconds % 1000) * 1000;

	if (seconds != 0)
		sleep(seconds);

	if (remainder_in_micro_seconds != 0)
		usleep(remainder_in_micro_seconds);
}


#endif   // end-of-Linux-specific



