
/*
  The oRTP library is an RTP (Realtime Transport Protocol - rfc3550) stack.
  Copyright (C) 2001  Simon MORLAT simon.morlat@linphone.org

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


#include "ortp/port.h"
#include "ortp/ortp.h"
#include "utils.h"

#if	defined(_WIN32) && !defined(_WIN32_WCE)
#include <process.h>
#endif

void* ortp_malloc(size_t sz){
    return malloc(sz);
}

void* ortp_realloc(void *ptr, size_t sz){
    return realloc(ptr,sz);
}

void * ortp_malloc0(size_t size){
	void *ptr=malloc(size);
	memset(ptr,0,size);
	return ptr;
}

char * ortp_strdup(const char *tmp){
#if	defined(_WIN32) || defined(_WIN32_WCE)
    return _strdup(tmp);
#else
    return strdup(tmp);
#endif
}


void ortp_free(void* ptr){
	free(ptr);
}

/*
 * this method is an utility method that calls fnctl() on UNIX or
 * ioctlsocket on Win32.
 * int retrun the result of the system method
 */
int set_non_blocking_socket (ortp_socket_t sock)
{
	

#if	!defined(_WIN32) && !defined(_WIN32_WCE)
	return fcntl (sock, F_SETFL, O_NONBLOCK);
#else
	unsigned long nonBlock = 1;
	return ioctlsocket(sock, FIONBIO , &nonBlock);
#endif
}


/*
 * this method is an utility method that calls close() on UNIX or
 * closesocket on Win32.
 * int retrun the result of the system method
 */
int close_socket(ortp_socket_t sock){
#if	!defined(_WIN32) && !defined(_WIN32_WCE)
	return close (sock);
#else
	return closesocket(sock);
#endif
}



#if	!defined(_WIN32) && !defined(_WIN32_WCE)
	/* Use UNIX inet_aton method */
#else
	int inet_aton (const char * cp, struct in_addr * addr)
	{
		unsigned long retval;
		
		retval = inet_addr (cp);

		if (retval == INADDR_NONE) 
		{
			return -1;
		}
		else
		{
			addr->S_un.S_addr = retval;
			return 1;
		}
	}
#endif

char *ortp_strndup(const char *str,int n){
	int min=MIN((int)strlen(str),n)+1;
	char *ret=(char*)ortp_malloc(min);
	strncpy(ret,str,n);
	ret[min-1]='\0';
	return ret;
}

#if	!defined(_WIN32) && !defined(_WIN32_WCE) && !defined(PSP)
int __ortp_thread_join(ortp_thread_t thread, void **ptr){
	int err=pthread_join(thread,ptr);
	if (err!=0) {
		ortp_error("pthread_join error: %s",strerror(err));
	}
	return err;
}
#endif
#if	defined(_WIN32) || defined(_WIN32_WCE)

int WIN_mutex_init(ortp_mutex_t *mutex, void *attr)
{	
	*mutex=CreateMutex(NULL, FALSE, NULL);
	return 0;
}

int WIN_mutex_lock(ortp_mutex_t * hMutex)
{
	WaitForSingleObject(*hMutex, INFINITE); /* == WAIT_TIMEOUT; */
	return 0;
}

int WIN_mutex_unlock(ortp_mutex_t * hMutex)
{
	ReleaseMutex(*hMutex);
	return 0;
}

int WIN_mutex_destroy(ortp_mutex_t * hMutex)
{
	CloseHandle(*hMutex);
	return 0;
}

typedef struct thread_param{
	void * (*func)(void *);
	void * arg;
}thread_param_t;

static unsigned WINAPI thread_starter(void *data){
	thread_param_t *params=(thread_param_t*)data;
	void *ret=params->func(params->arg);
	ortp_free(data);
	return (DWORD)ret;
}

#if defined _WIN32_WCE
#    define _beginthreadex	CreateThread
#    define	_endthreadex	ExitThread
#endif

int WIN_thread_create(ortp_thread_t *th, void *attr, void * (*func)(void *), void *data)
{
    thread_param_t *params=ortp_new(thread_param_t,1);
    params->func=func;
    params->arg=data;
	*th=(HANDLE)_beginthreadex( NULL, 0, thread_starter, params, 0, NULL);
	return 0;
}

int WIN_thread_join(ortp_thread_t thread_h, void **unused)
{
	if (thread_h!=NULL)
	{
		WaitForSingleObject(thread_h, INFINITE);
		CloseHandle(thread_h);
	}
	return 0;
}

int WIN_cond_init(ortp_cond_t *cond, void *attr)
{
	*cond=CreateEvent(NULL, FALSE, FALSE, NULL);
    return 0;
}

int WIN_cond_wait(ortp_cond_t* hCond, ortp_mutex_t * hMutex)
{
	//gulp: this is not very atomic ! bug here ?
	WIN_mutex_unlock(hMutex);	
	WaitForSingleObject(*hCond, INFINITE);
	WIN_mutex_lock(hMutex);
    return 0;
}

int WIN_cond_signal(ortp_cond_t * hCond)
{
	SetEvent(*hCond);
	return 0;
}

int WIN_cond_broadcast(ortp_cond_t * hCond)
{
	WIN_cond_signal(hCond);
	return 0;
}

int WIN_cond_destroy(ortp_cond_t * hCond)
{
	CloseHandle(*hCond);
	return 0;
}


#if defined(_WIN32_WCE)
#include <time.h>

int
gettimeofday (struct timeval *tv, void *tz)
{
  DWORD timemillis = GetTickCount();
  tv->tv_sec  = timemillis/1000;
  tv->tv_usec = (timemillis - (tv->tv_sec*1000)) * 1000;
  return 0;
}

#else

int gettimeofday (struct timeval *tv, void* tz) 
{ 
	union 
	{ 
		__int64 ns100; /*time since 1 Jan 1601 in 100ns units */ 
		FILETIME fileTime; 
	} now; 

	GetSystemTimeAsFileTime (&now.fileTime); 
	tv->tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL); 
	tv->tv_sec = (long) ((now.ns100 - 116444736000000000LL) / 10000000LL); 
	return (0); 
} 

#endif

const char *getWinSocketError(int error)
{
	static char buf[80];

	switch (error)
	{
		case WSANOTINITIALISED: return "Windows sockets not initialized : call WSAStartup";
		case WSAEADDRINUSE:		return "Local Address already in use";
		case WSAEADDRNOTAVAIL:	return "The specified address is not a valid address for this machine";
		case WSAEINVAL:			return "The socket is already bound to an address.";
		case WSAENOBUFS:		return "Not enough buffers available, too many connections.";
		case WSAENOTSOCK:		return "The descriptor is not a socket.";
		case WSAECONNRESET:		return "Connection reset by peer";

		default :
			sprintf(buf, "Error code : %d", error);
			return buf;
		break;
	}

	return buf;
}

#ifdef _WORKAROUND_MINGW32_BUGS
char * WSAAPI gai_strerror(int errnum){
	 return (char*)getWinSocketError(errnum);
}
#endif

#endif

#ifdef PSP

#include <pspkernel.h>
#include <pspthreadman.h>

int psp_mutex_init(ortp_mutex_t *m, void *attr_unused)
{
	*m = sceKernelCreateSema("rtpM", 0, 1, 1, 0);
	return 0;
}

int psp_mutex_lock(ortp_mutex_t *mutex)
{
	sceKernelWaitSema(*mutex, 1, 0);
	return 0;
}

int psp_mutex_unlock(ortp_mutex_t *mutex)
{
	sceKernelSignalSema(*mutex, 1);
	return 0;
}

int psp_mutex_destroy(ortp_mutex_t *mutex)
{
	sceKernelDeleteSema(*mutex);
	return 0;
}

char temp[50];

#include <pspkernel.h>
#include <pspthreadman.h>

int psp_cond_init(ortp_cond_t *cond, void *attr)
{
	static int num=0;
	sprintf(temp, "rtpCond%d\n", num);
	*cond = sceKernelCreateEventFlag(temp, PSP_EVENT_WAITMULTIPLE, 0, 0);
    return 0;
}

int psp_cond_wait(ortp_cond_t* cond, ortp_mutex_t * mut)
{
	if(!cond || !mut)
		return -1;

	if(psp_mutex_unlock(mut) < 0)
		return -1;

	sceKernelWaitEventFlag(*cond, 1, PSP_EVENT_WAITCLEAR, NULL, 0);
	
	if(psp_mutex_unlock(mut) < 0)
		return -1;

    return 0;
}

int psp_cond_signal(ortp_cond_t * cond)
{
	if (!cond)
		return -1;

	sceKernelSetEventFlag(*cond, 1);

	return 0;
}

int psp_cond_broadcast(ortp_cond_t * cond)
{
	SceKernelEventFlagInfo info;
	int i;

	if (sceKernelReferEventFlagStatus(*cond, &info) >= 0)
	{
		for(i=0; i<info.numWaitThreads; i++)
		{
			psp_cond_signal(cond);
			sceKernelWaitEventFlag(*cond, 0, 0, NULL, 0);
		}
	}

	return 0;
}

int psp_cond_destroy(ortp_cond_t * cond)
{
	sceKernelDeleteEventFlag(*cond);

	return 0;
}

typedef struct thread_data
{
	void (*function)(void *arg);
	void *arg;
} thread_data;

int thread_func(SceSize args, void *argp);

int psp_thread_create(ortp_thread_t *t, void *attr_unused, void *(*func)(void*), void *arg, int size)
{
	thread_data data;
	data.function = (void*)func;
	data.arg = arg;

	*t = sceKernelCreateThread("rtp", thread_func, 0x18, 0x10000, 0, NULL);

	sceKernelStartThread(*t, sizeof(data), &data);
	return 0;
}

int thread_func(SceSize args, void *argp)
{	
	thread_data *pdata = (thread_data*)argp;
	
	(pdata->function)(pdata->arg);

	return 0;
}

int psp_thread_join(ortp_thread_t thread, void **unused)
{
	sceKernelWaitThreadEnd(thread, 0);
	sceKernelDeleteThread(thread);
	return 0;
}
#endif
