/*******************************************************************************
 * platformbase.cpp
 *
 * This file implements code that is needed (and linked to) the base code.
 *
 * from Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
 * Copyright 2006-2008 Persistence of Vision Raytracer Pty. Ltd.
 * ---------------------------------------------------------------------------
 * NOTICE: This source code file is provided so that users may experiment
 * with enhancements to POV-Ray and to port the software to platforms other
 * than those supported by the POV-Ray developers. There are strict rules
 * regarding how you are permitted to use this file. These rules are contained
 * in the distribution and derivative versions licenses which should have been
 * provided with this file.
 *
 * These licences may be found online, linked from the end-user license
 * agreement that is located at http://www.povray.org/povlegal.html
 * ---------------------------------------------------------------------------
 * POV-Ray is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 * ---------------------------------------------------------------------------
 * $File: //depot/povray/smp/vfe/unix/platformbase.cpp $
 * $Revision: #8 $
 * $Change: 4506 $
 * $DateTime: 2007/12/31 17:01:49 $
 * $Author: chrisc $
 *******************************************************************************/

// must come first
#include "syspovconfig.h"

# include <time.h>

# include <sys/time.h>

# include <sys/resource.h>

#include "vfe.h"

// this must be the last file included
#include "base/povdebug.h"

namespace pov_base
{
	////////////////////////////////////////////////////////////////////////////////////////
	//
	// needed if we define POV_DELAY_IMPLEMENTED in config.h
	//
	////////////////////////////////////////////////////////////////////////////////////////
	void Delay(unsigned int msec)
	{
#ifdef HAVE_NANOSLEEP
		timespec ts;
		ts.tv_sec = msec / 1000;
		ts.tv_nsec = (POV_ULONG) (1000000) * (msec % 1000);
		nanosleep(&ts, NULL);
#else
		// taken from source/base/timer.cpp
		boost::xtime t;
		boost::xtime_get(&t, boost::TIME_UTC);
		POV_ULONG ns = (POV_ULONG)(t.sec) * (POV_ULONG)(1000000000) + (POV_ULONG)(t.nsec) + (POV_ULONG)(msec) * (POV_ULONG)(1000000);
		t.sec = (boost::xtime::xtime_sec_t)(ns / (POV_ULONG)(1000000000));
		t.nsec = (boost::xtime::xtime_nsec_t)(ns % (POV_ULONG)(1000000000));
		boost::thread::sleep(t);
#endif
	}

	////////////////////////////////////////////////////////////////////////////////////////
	//
	// thread support
	//
	////////////////////////////////////////////////////////////////////////////////////////

	/////////////////////////////////////////////////////////////////////////
	// called by the base code each time a worker thread is created (the call
	// is made in the context of the new thread).
	void vfeSysThreadStartup(void)
	{
	}

	/////////////////////////////////////////////////////////////////////////
	// called by a worker thread just before it exits.
	void vfeSysThreadCleanup(void)
	{
	}

	////////////////////////////////////////////////////////////////////////////////////////
	//
	// class vfeTimer (OPTIONAL)
	//
	// if you don't want to supply this class, remove the definition for POV_TIMER from
	// config.h. see the base code for documentation on the implementation requirements.
	//
	////////////////////////////////////////////////////////////////////////////////////////

	vfeTimer::vfeTimer (bool CPUTimeIsThreadOnly)
	{
		m_ThreadTimeOnly = CPUTimeIsThreadOnly;
		Reset();
	}

	vfeTimer::~vfeTimer ()
	{
	}

	unsigned POV_LONG vfeTimer::GetWallTime (void) const
	{
	boost::xtime t;
	boost::xtime_get(&t, POV_TIME_UTC);
	POV_LONG tt = (POV_LONG)(t.sec) * (POV_LONG)(1000000000) + (POV_LONG)(t.nsec);
	POV_LONG st = (POV_LONG)(realTimeStart.sec) * (POV_LONG)(1000000000) + (POV_LONG)(realTimeStart.nsec);
	return ((tt - st) / (POV_LONG)(1000000));
		#ifdef HAVE_CLOCK_GETTIME
			struct timespec ts;
			if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
				return (unsigned POV_LONG) (1000)*ts.tv_sec + ts.tv_nsec/1000000;
		#endif
		#ifdef HAVE_GETTIMEOFDAY
			struct timeval tv;  // seconds + microseconds since the Epoch (1970-01-01)
			if (gettimeofday(&tv, NULL) == 0)
				return (unsigned POV_LONG) (1000)*tv.tv_sec + tv.tv_usec/1000;
		#endif
		return 0;  // FIXME: add fallback, using ftime(), or time() + a counter for ms
	}

	unsigned POV_LONG vfeTimer::GetCPUTime (void) const
	{
		boost::xtime t;
	boost::xtime_get(&t, POV_TIME_UTC);
	POV_LONG tt = (POV_LONG)(t.sec) * (POV_LONG)(1000000000) + (POV_LONG)(t.nsec);
	POV_LONG st = (POV_LONG)(cpuTimeStart.sec) * (POV_LONG)(1000000000) + (POV_LONG)(cpuTimeStart.nsec);
	return ((tt - st) / (POV_LONG)(1000000));

		#ifdef HAVE_CLOCK_GETTIME
			struct timespec ts;
			if (clock_gettime(m_ThreadTimeOnly ? CLOCK_THREAD_CPUTIME_ID : CLOCK_PROCESS_CPUTIME_ID, &ts) == 0)
				return (unsigned POV_LONG) (1000)*ts.tv_sec + ts.tv_nsec/1000000;
		#endif
		#ifdef HAVE_GETRUSAGE
			struct rusage ru;
			if (getrusage(RUSAGE_SELF, &ru) == 0)
				return (unsigned POV_LONG) (1000)*(ru.ru_utime.tv_sec + ru.ru_stime.tv_sec)
				+ (unsigned POV_LONG)(ru.ru_utime.tv_usec + ru.ru_stime.tv_usec)/1000;
		#endif
		return GetWallTime();
	}

	POV_LONG vfeTimer::ElapsedRealTime (void) const
	{
		return GetWallTime() - m_WallTimeStart;
	}

	POV_LONG vfeTimer::ElapsedCPUTime (void) const
	{
		return GetCPUTime() - m_CPUTimeStart;
	}

	void vfeTimer::Reset (void)
	{
	boost::xtime_get(&realTimeStart, POV_TIME_UTC);
	boost::xtime_get(&cpuTimeStart, POV_TIME_UTC);
		m_WallTimeStart = GetWallTime();
		m_CPUTimeStart = GetCPUTime();
	}

	bool vfeTimer::HasValidCPUTime() const
	{
	return true;
#ifdef HAVE_CLOCK_GETTIME
		struct timespec ts;
		if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0)
			return true;
#endif
		return false;
	}

	////////////////////////////////////////////////////////////////////////////////////////
	//
	// path parsing
	//
	////////////////////////////////////////////////////////////////////////////////////////

	////////////////////////////////////////////////////////////////////////////////////////
	// The first argument is the input, a UCS2 string.
	//
	// The second argument is the string you are supposed to return the "volume"
	// name with.  For DOS-style paths this implies i.e. "A:\" is the "volume".
	// Note that it is essential that the first "path" separator is also part of
	// the volume name.  If the path is relative, the "volume" name shall be empty.
	//
	// This trick is necessary so the code can account for the lack of volume names
	// in Unix-style paths: In Unix, the POV_PARSE_PATH_STRING function will have
	// to take a reading "/" or "~/" as "volume" name.  This makes it possible to
	// determine if a string is absolute or relative based on this 'virtual'
	// "volume" name rather than some flags.
	//
	// The third is a vector of strings you have to return, each has to contain the
	// folder name from left to right, without the path separator, of course.
	//
	// The fourth argument shall contain the filename, if any was given in the
	// source string. By definition if the source string does not contain a
	// trailing path separator, whatever comes after the last path separator
	// (or the start of the string if there is none) must be considered a filename,
	// even if it could be a directory (in other words, don't call a system function
	// to find out if it is a dir or not - see below).
	//
	// Please note that the function must not attempt to determine the validity of
	// a string by accessing the filesystem.  It has to parse anything that it is
	// given.  If the string provided cannot be parsed for some reason (that is if
	// you can determine that a given path cannot possibly be valid i.e. because it
	// contains invalid characters), the function has to return false.  It may not
	// throw exceptions.  The return value for success is true.
	////////////////////////////////////////////////////////////////////////////////////////

	bool vfeParsePathString (const UCS2String& path, UCS2String& volume, vector<UCS2String>& components, UCS2String& filename)
	{
		UCS2String q;

		if(path.empty() == true)
			return true;

		if(path[0] == '/')
			volume = '/';

		for(size_t i = 0; i < path.length(); ++i)
		{
			if(path[i] == '/')
			{
				if(q.empty() == false)
					components.push_back(q);
				q.clear();
			}
			else
				q += path[i];
		}

		filename = q;

		return true;
	}
}
