/*
  The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-)
  Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Aymeric MOIZARD jack@atosc.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
*/

#ifdef OSIP_MT

#include <stdio.h>

#include <osip2/internal.h>
#include <osip2/osip_mt.h>

#if defined(__rtems__)
#include <rtems.h>
#endif 

#if !defined(__rtems__)
#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(HAVE_PTHREAD_WIN32)
#  if defined _WIN32_WCE
#    define _beginthreadex	CreateThread
#    define	_endthreadex	ExitThread
#  elif defined WIN32
#    include <process.h>
#  endif
#endif

/* stack size is only needed on VxWorks. */

#ifndef __VXWORKS_OS__
#if defined(HAVE_PTHREAD) || defined(HAVE_PTH_PTHREAD_H) || defined(HAVE_PTHREAD_WIN32)

struct osip_thread *
osip_thread_create (int stacksize, void *(*func) (void *), void *arg)
{
  int i;
  osip_thread_t *thread = (osip_thread_t *) osip_malloc (sizeof (osip_thread_t));

  if (thread == NULL)
    return NULL;

  i = pthread_create (thread, NULL, func, (void *) arg);
  if (i != 0)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "Error while creating a new thread\n"));
      return NULL;
    }
  return (struct osip_thread *) thread;
}

int
osip_thread_set_priority (struct osip_thread *thread, int priority)
{
  return 0;
}

int
osip_thread_join (struct osip_thread *_thread)
{
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    return -1;
  return pthread_join (*thread, NULL);
}

void
osip_thread_exit ()
{
#if !defined(__arc__)
  pthread_exit (NULL);
#endif
}

#endif
#endif


#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(HAVE_PTHREAD_WIN32)

struct osip_thread *
osip_thread_create (int stacksize, void *(*func) (void *), void *arg)
{
  osip_thread_t *thread = (osip_thread_t *) osip_malloc (sizeof (osip_thread_t));

  if (thread == NULL)
    return NULL;
  thread->h = (HANDLE) _beginthreadex (NULL,    /* default security attr */
                                       0,       /* use default one */
                                       (unsigned (__stdcall *) (void *)) func,
                                       arg, 0, &(thread->id));
  if (thread->h == 0)
    {
      osip_free (thread);
      return NULL;
    }
  return (struct osip_thread *) thread;
}

int
osip_thread_join (struct osip_thread *_thread)
{
  int i;
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    return -1;
  i = WaitForSingleObject (thread->h, INFINITE);
  if (i == WAIT_OBJECT_0)
    {
      /* fprintf (stdout, "thread joined!\n"); */
  } else
    {
      /* fprintf (stdout, "ERROR!! thread joined ERROR!!\n"); */
      return -1;
    }
  CloseHandle (thread->h);
  return (0);
}

void
osip_thread_exit ()
{
  /* ExitThread(0); */
  _endthreadex (0);
}

int
osip_thread_set_priority (struct osip_thread *thread, int priority)
{
  return 0;
}


#endif

#ifndef __VXWORKS_OS__
#ifdef __PSOS__
struct osip_thread *
osip_thread_create (int stacksize, void *(*func) (void *), void *arg)
{
  osip_thread_t *thread = (osip_thread_t *) osip_malloc (sizeof (osip_thread_t));

  if (thread == NULL)
    return (NULL);
  if (t_create ("sip", 150, stacksize, 0, 0, &thread->tid) != 0)
    {
      osip_free (thread);
      return (NULL);
    }

  if (t_start (thread->tid, T_PREEMPT | T_ISR, func, 0) != 0)
    {
      osip_free (thread);
      return (NULL);
    }

  return (struct osip_thread *) thread;
}

int
osip_thread_set_priority (struct osip_thread *_thread, int priority)
{
  unsigned long oldprio;
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    return -1;
  t_set_pri (thread->tid, priority, &oldprio);
  return 0;
}

int
osip_thread_join (struct osip_thread *_thread)
{
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    return -1;
  t_delete (thread->tid);

  return (0);
}

void
osip_thread_exit ()
{
  t_delete (0);
}
#endif
#endif

#ifdef __VXWORKS_OS__
struct osip_thread *
osip_thread_create (int stacksize, void *(*func) (void *), void *arg)
{
  osip_thread_t *thread = (osip_thread_t *) osip_malloc (sizeof (osip_thread_t));

  if (thread == NULL)
    return NULL;
  thread->id = taskSpawn (NULL, 5, 0, stacksize, (FUNCPTR) func, (int) arg,
                          0, 0, 0, 0, 0, 0, 0, 0, 0);
  if (thread->id < 0)
    osip_free (thread);
  return (struct osip_thread *) thread;
}

int
osip_thread_set_priority (struct osip_thread *_thread, int priority)
{
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    return -1;
  taskPrioritySet (thread->id, 1);
  return 0;
}

int
osip_thread_join (struct osip_thread *_thread)
{
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    return -1;
  return taskDelete (thread->id);
}

void
osip_thread_exit ()
{
  /*?? */
}

#endif
#endif /* #ifndef __rtems__ */

#if defined(__rtems__)
struct osip_thread *osip_thread_create(int stacksize, void *(*func) (void *), void *arg)
{
  rtems_status_code status;
  osip_thread_t *thread = (osip_thread_t *) osip_malloc(sizeof(osip_thread_t));

  if (thread == NULL)
    return NULL;

  status = rtems_task_create(rtems_build_name('S','I','P','T'),
			     100 ,
			     stacksize,
			     RTEMS_DEFAULT_MODES, RTEMS_DEFAULT_ATTRIBUTES,
			     &thread->tid);

  if ( status == RTEMS_SUCCESSFUL )
    {
      status = rtems_task_start(thread->tid,
				(rtems_task_entry) func,
				( rtems_task_argument ) arg );
    }

  if ( status != RTEMS_SUCCESSFUL )
    {
      osip_free(thread);
      thread = NULL ;
    }

  return (struct osip_thread *) thread;
}

int osip_thread_set_priority(struct osip_thread *_thread, int priority)
{
  osip_thread_t *thread = (osip_thread_t *) _thread;

  rtems_task_priority old;
  if ( thread != NULL )
    {
      return rtems_task_set_priority( thread->tid,
				      priority,
				      &old);
    }
  return 0;
}


int osip_thread_join(struct osip_thread *_thread)
{
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    {
      return -1;
    }
  return rtems_task_delete( thread->tid);
}

void osip_thread_exit()
{
  rtems_task_delete( RTEMS_SELF);
}

#endif 

#if defined(PSP)

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

struct osip_thread *
osip_thread_create (int stacksize, void *(*func) (void *), void *arg)
{
  osip_thread_t *thread = (osip_thread_t *) osip_malloc (sizeof (osip_thread_t));

  if (thread == NULL)
    return NULL;
  thread->id = sceKernelCreateThread("osip2", (SceKernelThreadEntry)func, 0x18, stacksize, 0, NULL);
  thread->id = sceKernelStartThread(thread->id, 0, 0);

  if (thread->id < 0)
    osip_free (thread);
  return (struct osip_thread *) thread;
}

int
osip_thread_set_priority (struct osip_thread *thread, int priority)
{
  return 0;
}

int
osip_thread_join (struct osip_thread *_thread)
{
  osip_thread_t *thread = (osip_thread_t *) _thread;

  if (thread == NULL)
    return -1;

  return sceKernelWaitThreadEnd(thread->id, NULL);
}

void
osip_thread_exit ()
{
  sceKernelExitThread(0);
}

#endif

#endif /* #ifdef OSIP_MT */
