#include "ostask.h"
#include "ostime.h"
#include "osutils.h"
#include <stdarg.h>

#ifdef _VXWORKS_
// VxWorks headers fail to define this
extern "C" { 
int sysClkRateGet();
int kernelTimeSlice(int tcks);
};
#endif

extern "C" void * 
taskAdapter (void *args)
{
  TaskAdapter *targs = (TaskAdapter *) args;

  // Invoke the user-supplied function with the args.
  void *status = targs->invoke ();

  return status;
}
THR_C_FUNC
TaskAdapter::entryPoint()
{
	return _entryPoint;
}
TaskAdapter::~TaskAdapter()
{
}
// The thread entry point.  This must be an extern "C" to 
// run on Visual C++ compiler ver. 5.0
TaskAdapter::TaskAdapter (THR_FUNC userfunc,
			  void *arg,
			  THR_C_FUNC entrypoint)
	:_userFunc (userfunc),
	 _arg (arg),
	_entryPoint (entrypoint)
{
}
void *
TaskAdapter::invoke (void)
{
  // Extract the arguments.
  THR_FUNC func = this->_userFunc;
  void *arg = this->_arg;

  // Delete ourselves since we don't need <this> anymore.  Make sure
  // not to access <this> anywhere below this point.
  delete this;
  
  //try{
  // Call thread entry point.
  void *status = (void*) (*func) (arg);
  //}

#ifdef _WIN32_
  ::AfxEndThread ((DWORD)status);
#endif

  return status; 
}
TaskWrapper::TaskWrapper()
{
}
TaskWrapper::~TaskWrapper()
{
}
int TaskWrapper::setprio (TaskHandle *h, int prio)
{
  // Set the thread priority on the current thread.
#ifdef _WIN32_
  return ::SetThreadPriority(h, prio);
#else
  return ::taskPrioritySet(*h, prio);
#endif
}
// Return the exit code
int TaskWrapper::exitCode(TaskHandle *h)
{
# if defined _WIN32_
	unsigned long exitCode = 0;
	::GetExitCodeThread(*h, &exitCode);
	switch(exitCode){
	case(STILL_ACTIVE):
		return TASK_READY;
	break;
	case(WAIT_TIMEOUT):
		return TASK_PEND;
	break;
	case(WAIT_IO_COMPLETION):
		return TASK_PEND;
	break;
	default:
		return -1;
	break;
	};
# else
	WIND_TCB *tasktcb;
	if (*h  != 0) //hasn't been initialized yet
	{
	tasktcb = taskTcb(*h);
	if (tasktcb != 0)
	return tasktcb->exitCode;
	}
# endif	
	return -1;
}
// Exit the current thread and return "status".
void
TaskWrapper::exit (TaskId tid, void *status)
{
# if defined _WIN32_
// Exit the thread.  Allow CWinThread-destructor to be
// invoked from AfxEndThread.  _endthreadex will be called
// from AfxEndThread so don't exit the thread now if we are
// running an MFC thread.
   ::AfxEndThread ((DWORD)status);
#endif
#ifdef _VXWORKS_
 /*   *((int *) status) = ::taskDelete (tid); */
#endif

}  

int
TaskWrapper::create(char *name,
			 THR_FUNC func,
			 void *args,
			 int flags,
			 TaskHandle *thrhandle,
			 TaskId *thrid,
			 int priority,
			 size_t stacksize )
{
#ifdef _VXWORKS_
static int init;

if(init == 0){
	kernelTimeSlice(sysClkRateGet() / 2);
	init++;
}
#endif

TaskAdapter *task = new TaskAdapter(func, 
				args,
				(THR_C_FUNC) taskAdapter);

if(taskAdapter == 0)
	return -1; 

#if defined (_WIN32_)
	CWinThread *cwinthread =
        ::AfxBeginThread ((AFX_THREADPROC) &task->entryPoint (),
                          task, priority, stacksize,
                          flags | THR_SUSPENDED, 0);
      // Have to duplicate the handle because
      // CWinThread::~CWinThread() closes the original handle.
      (void) ::DuplicateHandle (::GetCurrentProcess (),
                                cwinthread->m_hThread,
                                ::GetCurrentProcess (),
                                thrhandle,
                                0,
                                TRUE,
                                DUPLICATE_SAME_ACCESS);

      *thrid = cwinthread->m_nThreadID;

      cwinthread->ResumeThread ();
      // cwinthread will be deleted in AfxThreadExit()
#else
  // The hard-coded values below are what ::sp () would use.  (::sp ()
  // hardcodes priority to 100, flags to VXFPTASK, and stacksize to
  // 20,000.)  stacksize should be an even integer.  If a stack is not
  // specified, ::taskSpawn () is used so that we can set the
  // priority, flags, and stacksize.  If a stack is specified,
  // ::taskInit ()/::taskActivate() are used.

  flags = DEFAULT_TASK_OPTIONS;
  if (stacksize == 0) stacksize = DEFAULT_STACK_SIZE;

  // The call below to ::taskSpawn () causes _VXWORKS_ to assign a
  // unique task name of the form: "t" + an integer, because the
  // first argument is 0.
  *thrhandle = ::taskSpawn (name,
                         priority,
                         (int) flags,
                         (int) stacksize,
                         task->entryPoint(),
                         (int) task,
                         0,0,0,0,0,0,0,0,0); 

  if ( (flags & THR_SUSPENDED))
	suspend(thrhandle);

  if (*thrhandle == ERROR)
	  return -1;	  
#endif
    return 0;
}
long int TaskWrapper::suspend(TaskHandle *h)
{
#ifdef _WIN32_
	return ::SuspendThread(*h);
#endif
#ifdef _VXWORKS_
	return ::taskSuspend(*h);
#endif
}


long int TaskWrapper::resume(TaskHandle *h)
{
#ifdef _WIN32_
	return ::ResumeThread(*h);
#endif
#ifdef _VXWORKS_
	return ::taskResume(* h);	
#endif
	return 0;
}

TaskId TaskWrapper::id(void)
{
#ifdef _WIN32_
	CWinThread *t = ::AfxGetThread();
	return t->m_nThreadID;	
#endif
#ifdef _VXWORKS_
	return ::taskIdSelf();
#endif
	return 0;
}

TaskBase::TaskBase () 
: _thrCount (0),
_flags(0),
_thrhandle(0)
{
 
}
int TaskBase::exitCode()
{
	return TaskWrapper::exitCode(&_thrhandle);
}
int TaskBase::suspendTask()
{
	return TaskWrapper::suspend(&_thrhandle);
}

int TaskBase::resumeTask()
{
	return TaskWrapper::resume(&_thrhandle);
}

TaskId TaskBase::id()
{
	return _thrid;
}
int
TaskBase::wait(int milliSeconds)
{							   
	int ntcks = milliSeconds*Clock::getSystemClkTck()/1000 + Clock::getSystemClock();
	while(TaskBase::exitCode() == TASK_READY && ntcks > Clock::getSystemClock())
		Utility::sleep(1);
	
	if(ntcks > Clock::getSystemClock())
		return 0;

	return -1;
}
void 
TaskBase::kill()
{
#if defined _WIN32_
   void *status = 0;
// Exit the thread.  Allow CWinThread-destructor to be
// invoked from AfxEndThread.  _endthreadex will be called
// from AfxEndThread so don't exit the thread now if we are
// running an MFC thread.
   ::AfxEndThread ((DWORD)status);
#endif
#ifdef _VXWORKS_
	::taskDelete (this->_thrhandle);
#endif

}

void
TaskBase::cleanup (void *object)
{
  void *status = 0;
  TaskBase *t = (TaskBase *) object;  
  if(t != NULL) {
  t->close();
  t->_thrCount--;  
  TaskWrapper::exit (t->id(),status);
  }
}
int TaskBase::close()
{
	return -1;
}

void *
TaskBase::svc (void *args)
{
  TaskBase *t = (TaskBase *) args;

  // Call the Task's run() hook method.
  void * status = (void *) t->run ();

  // This calls the Task->close () hook.
  t->cleanup (t);

  return status;
}
int TaskBase::open(void *args)
{
	return -1;
}
void *
TaskBase::run(void)
{
	return 0;
}
TaskBase::~TaskBase()
{

}
int
TaskBase::activate( char *name, 
		   int priority, 
		   int flags, 
		   int stacksize,
		   TaskBase *task)
{   
  Guard<ThreadMutex> guard(_lock);

  if(guard.locked() == -1 )
	  return -1;

  // If the task passed in is zero, we will use <this>
  if (task == 0)
    task = this;

  if (_thrCount > 0)
    return 1; // Already active.
  else
    _thrCount++;

  if(TaskWrapper::create(name, THR_FUNC (&TaskBase::svc), 
						(void *) this,
						flags,
						&_thrhandle,
						&_thrid,
						priority,
						stacksize) == -1)
    {
      _thrCount--;
      return -1;
    }
  else
    return 0;
}
