// GSUtil.cp
//
// Copyright 1996 Diehl Graphsoft Inc.

#include "StdHeaders.h"	// DO NOT MOVE THIS LINE FROM BEING FIRST NON-COMMENT LINE.  [DMB, 10/30/95]

#include "GSCore.h"
#include <StdArg.h>
#include <time.h>
#if _WINDOWS
#include <process.h>
#else
#include <unistd.h>
#endif

#ifdef _MINICAD_
	#include "File.X.h"
	#include "ApplicationFolders.h"

	// from Builtins.h/.cpp
	double GetSecondsSinceLaunch(void);


	double gLogTime = 0.0;

#elif _WINDOWS
	#include <OSUtils.h>
#endif

#if _WINDOWS
#include "..\..\ShellWin\Resource.h"
#include "..\..\Include\URegKey.h"
#endif

#if _WINDOWS
#include "..\..\ShellWin\Resource.h"
#include "..\..\Include\URegKey.h"
#endif

#include "MiniCadHookIntf.h"

#include "../Include/Interfaces/VectorWorks/Debug/IDebugSupport.h"


// This must follow all header files.
#undef THIS_FILE
DECLARE_THIS_FILE


Boolean _setmember(int c, ...)
/*
	Tests if c == any of the following parameters.  The parameter
	list is zero-terminated, so c == 0 must be tested separately.
*/
{
	va_list	ap;
	int		i;

	va_start(ap, c);

	while ((i = va_arg(ap, int)) != 0) {
		if (c == i) { va_end(ap); return true; }
	}
	va_end(ap);
	return false;
}

IntegerSet::IntegerSet()
{
	for (short i = numLongs-1; i >= 0; i--) this->bits[i] = 0;
}

IntegerSet::IntegerSet(short i0, short i1, short i2, short i3,
							short i4, short i5, short i6, short i7,
							short i8, short i9)
{
	for (short i = numLongs-1; i >= 0; i--) this->bits[i] = 0;
	if (i0 >= 0) *this += i0;
	if (i1 >= 0) *this += i1;
	if (i2 >= 0) *this += i2;
	if (i3 >= 0) *this += i3;
	if (i4 >= 0) *this += i4;
	if (i5 >= 0) *this += i5;
	if (i6 >= 0) *this += i6;
	if (i7 >= 0) *this += i7;
	if (i8 >= 0) *this += i8;
	if (i9 >= 0) *this += i9;
}

IntegerSet::IntegerSet(const IntegerSet &s)
{
	for (short i = numLongs-1; i >= 0; i--) this->bits[i] = s.bits[i];
}

void IntegerSet::operator +=(short i)
{
	if (i < 0 || i > maxElement) {
		#if 0
		DebugStr("\p### Set element out of range.");
		#endif
		return;
	}
	this->bits[i / 32] |= 1L << (i % 32);
}

Boolean IntegerSet::contains(short i) const
{
	if (i < 0 || i > maxElement) {
		#if 0
		DebugStr("\p### Set element out of range.");
		#endif
		return false;
	}
	return ((this->bits[i / 32] & (1L<<(i % 32))) != 0);
}

void IntegerSet::operator -=(short i)
{
	if (i < 0 || i > maxElement) {
		#if 0
		DebugStr("\p### Set element out of range.");
		#endif
		return;
	}
	this->bits[i / 32] &= ~(1L << (i % 32));
}

void IntegerSet::operator +=(const IntegerSet &s)
{
	for (short i = numLongs-1; i >= 0; i--)
		this->bits[i] |= s.bits[i];
}

void IntegerSet::operator -=(const IntegerSet &s)
{
	for (short i = numLongs-1; i >= 0; i--)
		this->bits[i] &= ~s.bits[i];
}

#ifdef _MINICAD_

Boolean OSIsMetric()
//
// Description:
//   This function determines if the operating system is set to use metric units.
//
// Returns:
//   Boolean - whether it is set to metric.
//
// Created by Bruce Ferguson on 10/29/02.  Moved to this file on 10/24/05.
//
{
	Boolean isMetric;

#if GS_WIN
	const short kCharBufSize = 2;	// 1-character string plus 1 for null char
	char buffer[kCharBufSize];

	GetRegistryValue(HKEY_CURRENT_USER, IDS_REGKEY_INTERNATIONAL, IDS_REGVAL_MEASURE, &buffer, kCharBufSize);
	isMetric = (buffer[0] == '0');
#else
	isMetric = IsMetric();
#endif
	return isMetric;
}
#endif

/////////////////////////
// Cross-Platform byte swapping functions
/////////////////////////

// ByteSwapDWORD swaps 4 consecutive bytes (msb to lsb)

#if 0
void GS_API ByteSwapLONG(Sint32 *pl)
{
	char t;
	char* p1 = (char*)pl;
	char* p2 = p1 + 3;

	t = *p1;
	*p1 = *p2;
	*p2 = t;

	++p1; --p2;

	t = *p1;
	*p1 = *p2;
	*p2 = t;
}
#endif


#ifdef _MINICAD_
//----------------------------------------------
//	OutputLog
//
//	Writes a local log file.
//	JAK Feb 2005 for VW 12.
//
class OutputLog {
	public:
		OutputLog() : theFile(NULL)
		{
			TFolderIdentifier logFolder;
			if ( GetAppDataFolder(logFolder) ) {
				TFileIdentifier logFile(logFolder, "VW User Log.txt");
				#if GS_MAC
				theFile = fopen(_ccp logFile.GetPosixFilePath(), "a+");
				#else
				theFile = fopen(_ccp logFile.GetFilePath(), "a+");
				#endif
			}
		}

		~OutputLog()
		{
			if (theFile)
				fclose(theFile);
		}

		void SendString(const char* s, bool nocr = false)
		{
			if (theFile)
				if (nocr)
					fprintf(theFile, "%s", s);
				else
					fprintf(theFile, "%s\n", s);
		}

	private:
		FILE* theFile;
};


//----------------------------------------------
#if GS_LOCALLOGFLAG

extern bool gBufferLogMessages;
extern bool gRemoteLogInitialized;



typedef std::vector<std::pair<Uint16, TXString> > TLogBufferList;

TLogBufferList gLogBuffer;


void LLLocalLogStrCore(Uint16 level, bool doDebugWindow, const char *description)
//
//
//
{
	// use this to avoid the overhead of checking the buffer for each message
	// after the buffer is initially flushed. This logic allows the buffer
	// to be used only once at the beginning of execution - PCP

	static bool processedLogBuffer = false;



	// we do not want to respond to log requests at early init time
	if (gRemoteLogInitialized) {

		// levelzero is defined to output nothing to the log

		// 1-3 should happen on a session frequency
		// 4-6 can happen on a per-user-event frequency
		// 7-10 can happen up to once every few seconds on average
		// > 10 are special purpose and should not be used without
		// prior arrangement - Paul

		ASSERTN(kPaul, level > 0);

		if (!processedLogBuffer && !gBufferLogMessages) {
			// get time accurate to within microseconds
			double accurateTime = ::GetSecondsSinceLaunch();

			if (gLogBuffer.size() > 0) {
				OutputLog out;

				for(Uint32 i=0; i < gLogBuffer.size(); i++)
				{
					if (gLogBuffer[i].first <= gRemoteLogLevel) {
						out.SendString((const char*)gLogBuffer[i].second);
					}
				}
				gLogBuffer.clear();
			}	

			// get time accurate to within microseconds
			double accurateTime2 = ::GetSecondsSinceLaunch();
		
			// add up the approximate overhead of keeping these logs for each session - PCP
			gLogTime += accurateTime2 - accurateTime;

			processedLogBuffer = true;

		}

		if (gBufferLogMessages || level <= gRemoteLogLevel) {

			// get time accurate to within microseconds
			double accurateTime = ::GetSecondsSinceLaunch();

			OutputLog out;

			//using namespace std;
			time_t theTime;

			#if _WINDOWS
			int 	theProcess = _getpid();
			#else
			pid_t 	theProcess = getpid();
			#endif

			char datebuffer[255];
			char infoBuffer[255];


			time(&theTime);
			tm* tp = localtime(&theTime);
			strftime(datebuffer, 255, "%m/%d/%Y %H:%M:%S", tp);

			sprintf(infoBuffer, "[%s %ld %f %d] ", datebuffer, (long)theProcess, accurateTime, level);

			if (gBufferLogMessages) {
				TXString bufString = infoBuffer;
				bufString << description;

				gLogBuffer.push_back(std::make_pair(level, bufString));
			}
			else {
				out.SendString(infoBuffer, true);
				out.SendString(description);
			}

			if (doDebugWindow) {
				// send to the debug window - in future need to pass time and process also
				DMSG((kEveryone, "%s\n", description));
			}

			// get time accurate to within microseconds
			double accurateTime2 = ::GetSecondsSinceLaunch();

			// add up the approximate overhead of keeping these logs for each session - PCP
			gLogTime += accurateTime2 - accurateTime;
		}
	}
}
#endif

#endif

#if GS_LOCALLOGFLAG

static void LLLocalLogStr(Uint16 level, bool doDebugWindow, const char *description)
{

#ifdef _MINICAD_
		// the VCOM interface is not yet registered
		// use default in-place implementation

		LLLocalLogStrCore(level, doDebugWindow, description);

#else
	using namespace VectorWorks::Debug;
	::GS_InitializeVCOM( gCBP );
	VCOMPtr<IDebugOutputLog> pOutInst( IID_DebugOutputLog );

	if ( pOutInst ) {
		pOutInst->LocalLogStr(level, doDebugWindow, description);
	}
	else {
		// remove log being called before VCOM initialization
		ASSERTN( kVStanev, false );
	}
#endif

}




void LocalLogStr(Uint16 level, const char *description)
{
	LLLocalLogStr(level, true, description);
}

void LocalLogStrTight(Uint16 level, const char *description)
{
	LLLocalLogStr(level, false, description);
}

int LLLocalLog(Uint16 level, bool doDebugWindow, const char *format, va_list ap)
//
//	Log to local file.  Accept variable arguments.
//	This is similar to the function _Log( ) above.
//	JAK 1/31/05
//
{
	const size_t kBuf_size = 1024;

	static int r = 0;
	char buf[kBuf_size];
	r = vsprintf(buf, format, ap);

	LLLocalLogStr(level, doDebugWindow, buf);
	return r;
}

int LocalLog(Uint16 level, const char *format, ...)
{
	va_list	ap;
	va_start(ap, format);
	int result = LLLocalLog(level, true, format, ap);
	va_end(ap);

	return result;
}

int LocalLogTight(Uint16 level, const char *format, ...)
{
	va_list	ap;
	va_start(ap, format);
	int result = LLLocalLog(level, false, format, ap);
	va_end(ap);

	return result;
}

int LocalLogTiming(Uint16 level, double timing, const char *format, ...)
{
	const size_t kBuf_size = 1124;

	va_list	ap;
	va_start(ap, format);

	char modifiedFormat[kBuf_size];
	sprintf(modifiedFormat, "TIMER: %f sec | %s", timing, format);

	int result = LLLocalLog(level, false, modifiedFormat, ap);
	va_end(ap);

	return result;
}


#endif
