
#include "EgOSUtils.h"

#include "CEgFileSpec.h"
#include "CEgErr.h"
#include "UtilStr.h"

#include <stdlib.h>
#include <time.h>
#include <math.h>

#include <stdio.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/times.h>

#include <SDL/SDL_mouse.h>
// #include <gtk/gtk.h>

int          EgOSUtils::sXdpi              = 72;
int          EgOSUtils::sYdpi              = 72;
long         EgOSUtils::sLastCursor        = -1;
long         EgOSUtils::sLastCursorChange  = -1;
CEgFileSpec  EgOSUtils::sAppSpec;
char         EgOSUtils::sInitialDir[ INITAL_DIR_STRLEN + 2 ];
XStrList     EgOSUtils::sFontList( cDuplicatesAllowed );

void EgOSUtils::ShowFileErr( const UtilStr* inName, char* inErrMsg, bool wasReading )
{
	UtilStr	s;

	if ( wasReading )
		s.Assign( "Error reading : " );
	else
		s.Assign( "Error writing : " );
	s.Append( inErrMsg );

	s.Insert( 14, "\"\"" );

	s.Insert( 15, inName );
	ShowMsg( s );
}

void EgOSUtils::ShowFileErr( const UtilStr* inName, CEgErr& inErr, bool wasReading ) {
	UtilStr	msg;

	inErr.getErrStr( msg );
	ShowFileErr( inName, msg.getCStr(), wasReading );
}

void EgOSUtils::ShowFileErr( const CEgFileSpec& inFileSpec, CEgErr& inErr, bool wasReading ) {
	UtilStr	msg, fileName;

	inFileSpec.GetFileName( fileName  );
	inErr.getErrStr( msg );
	ShowFileErr( &fileName, msg.getCStr(), wasReading );
}

void EgOSUtils::Initialize()
{
	srand( clock() );

        sXdpi = sYdpi = 75;
}

void EgOSUtils::Shutdown()
{
}

bool EgOSUtils::AskSaveAs( const char* inPrompt, const char* inDefaultName, CEgFileSpec& outSpec, long inFileType )
{
	UtilStr inName( inDefaultName );

	return AskSaveAs( inPrompt, inName, outSpec, inFileType );
}

#ifdef EG_ZINC
#include <z_fildlg.hpp>
#endif

bool EgOSUtils::AskSaveAs( const char* inPrompt, const UtilStr& inDefaultName, CEgFileSpec& outSpec, long inFileType ) {
	int	doSave = false;
	UtilStr prompt( inPrompt );

	#if EG_WIN && EG_ZINC
	UtilStr defName( inDefaultName );

	ZafFileDialog	fileDialog( 20, 5, 60, 14 );

	if ( inFileType )
		CEgFileSpec::TypeToExt( defName, inFileType );

	fileDialog.SetFile( defName.getCStr()  );

	if ( fileDialog.GetFile( ZAF_FILE_DIALOG_SAVEAS ) == S_DLG_OK ) {
		doSave = true;
		outSpec.Assign( fileDialog.File(), inFileType );
	}
	/*
	unsigned char	c;
	UtilStr		winFilter;
	OPENFILENAME 	paramBlk;
	char			pathName[ 601 ];

	inDefaultName.copyTo( pathName, 600 );

	// See page 519 of Vol 5 in Win32 Ref for descrip of the lpstrFilter scheme.
	winFilter.Append( "/0*" );

	// Append the ext mask...
	for ( int d = 0; d <= 24; d += 8 ) {			// Go thru each byte in ID num
		c = ((inFileType << d) >> 24 );
		winFilter.Append( (char) c );
	}
	winFilter.Append( (char) 0 );					// Windows exptects an extra NUL to end the filter

	memset( &paramBlk, 0, sizeof(OPENFILENAME) );
	paramBlk.lStructSize = sizeof(OPENFILENAME);
	paramBlk.lpstrFilter		= winFilter.getCStr();
	paramBlk.lpstrFile			= pathName;
	paramBlk.nMaxFile			= 600;
	paramBlk.lpstrTitle			= prompt.getCStr();
	if ( ::GetSaveFileName( &paramBlk ) ) {
		outSpec.Assign( pathName );
		doSave = true;
	}*/
	#endif

	return doSave;
}




bool EgOSUtils::AskOpen( const char* inPrompt, CEgFileSpec& outSpec, long inTypeMask ) {
	bool didOpen = false;
	UtilStr prompt( inPrompt );

	#ifdef EG_MAC
	StandardFileReply	macFileReply;
	SFTypeList			typeList;

	// clear the rec
	for ( int i = 0; i < sizeof( macFileReply ); i++ )
		( (char*) &macFileReply )[ i ] = 0;

	//UDesktop::Deactivate();
	typeList[0] = inTypeMask;
	::StandardGetFile( NULL, inTypeMask ? 1 : 0, typeList, &macFileReply );
	//UDesktop::Activate();

	if ( macFileReply.sfGood ) {
		outSpec.Assign( &macFileReply.sfFile );
		didOpen = true;
	}
	#endif

	#if EG_WIN && EG_ZINC
	ZafFileDialog	fileDialog( 5, 5, 60, 14 );
	UtilStr		str( "*" );

	if ( inTypeMask ) {
		CEgFileSpec::TypeToExt( str, inTypeMask );
		fileDialog.SetFilter( str.getCStr() );
	}

	if ( fileDialog.GetFile( ZAF_FILE_DIALOG_OPEN ) == S_DLG_OK ) {
		char path[ 401 ];
		didOpen = true;

		fileDialog.FullPath( path, 400 );
		outSpec.Assign( path );
	}
	/*
	unsigned char	c;
	UtilStr		winFilter;
	OPENFILENAME 	paramBlk;
	char			pathName[ 601 ];

	// See page 519 of Vol 5 in Win32 Ref for descrip of the lpstrFilter scheme.
	winFilter.Append( "/0*" );

	// Append the ext mask...
	for ( int d = 0; d <= 24; d += 8 ) {			// Go thru each byte in ID num
		c = ((inTypeMask << d) >> 24 );
		winFilter.Append( (char) c );
	}
	winFilter.Append( (char) 0 );					// Windows exptects an extra NUL to end the filter

	memset( &paramBlk, 0, sizeof(OPENFILENAME) );
	paramBlk.lStructSize = sizeof(OPENFILENAME);
	paramBlk.lpstrFilter		= winFilter.getCStr();
	paramBlk.lpstrFile			= pathName;
	paramBlk.nMaxFile			= 600;
	paramBlk.lpstrTitle			= prompt.getCStr();
	if ( ::GetOpenFileName( &paramBlk ) ) {
		outSpec.Assign( pathName );
		didOpen = true;
	}*/
	#endif

	return didOpen;
}



bool EgOSUtils::AreYouSure( const UtilStr& inMsg )
{
	#ifdef EG_MAC
	int ans;
	::ParamText( inMsg.getPasStr(), "\p", "\p", "\p");
	//UDesktop::Deactivate();
	ans = ::CautionAlert( 2000, NULL );
	//UDesktop::Activate();

	return ans == 1; //answer_Save;
	#endif

	#ifdef EG_WIN
	int ans;
	ans = ::MessageBox( NULL, inMsg.getCStr(), "Examgen Message", MB_ICONEXCLAMATION | MB_YESNO | MB_SETFOREGROUND | MB_TASKMODAL );
	return ans == IDYES;
	#endif
	
	return 1;
}


bool EgOSUtils::AreYouSure( const char* inMsg ) {
	UtilStr	msg( inMsg );

	return AreYouSure( msg );
}



int EgOSUtils::AskSaveChanges( const char* inName ) {
	UtilStr name( inName );
	return AskSaveChanges( name );
}



int EgOSUtils::AskSaveChanges( const UtilStr& inName ) {

	#ifdef EG_MAC
	int ans;
	::ParamText( inName.getPasStr(), "\p", "\p", "\p" );
	//UDesktop::Deactivate();
	ans = ::CautionAlert( 2001, NULL );
	//UDesktop::Activate();

	return 2 - ans;
	#endif

	#ifdef EG_WIN
	int ans;
	UtilStr	msg( "Save changes to \"" );
	msg.Append( inName );
	msg.Append( "\" before closing?" );
	ans = ::MessageBox( NULL, msg.getCStr(), "Examgen Message", MB_ICONEXCLAMATION | MB_YESNOCANCEL | MB_SETFOREGROUND | MB_TASKMODAL );
	if ( ans == IDYES )
		return 1;
	else if ( ans == IDNO )
		return -1;
	else
		return 0;
	#endif
	
	return 1;
}


void EgOSUtils::SpinCursor() {
	long time = clock();

	if ( sLastCursorChange == -1 )
		sLastCursorChange = time;
}



void EgOSUtils::ShowCursor()
{
	sLastCursor = -1;
	sLastCursorChange = -1;
}


void EgOSUtils::HideCursor()
{
	sLastCursor = -1;
	sLastCursorChange = -1;
}



bool EgOSUtils::GetNextFile( const CEgFileSpec& folderSpec, CEgFileSpec& outSpec, bool inStartOver, bool inFolders )
{
	bool ok;

	UtilStr name;
	bool    isDir, tryAgain;
	UtilStr fullname;

	static DIR *d = NULL;
	struct dirent *de;

	ok = true;
	do {
	  if ( inStartOver ) {
	    if (d != NULL) {
	      closedir(d);
	      d = NULL;
	    }
	    inStartOver = false;
	    name.Assign( (char*) folderSpec.OSSpec() );
	    if ( name.getChar( name.length() ) == '/' )
	      name.Trunc( 1 );
	    d = opendir(name.getCStr());
	    if (d == NULL) return 0;
	  }
	  de = readdir(d);
	  if (de == NULL) return 0;
	  name.Assign(de->d_name);
	  struct stat statdata;
	  fullname.Assign( (char*) folderSpec.OSSpec() );
	  if ( fullname.getChar( name.length() ) != '/' )
	      fullname.Append( '/' );
	  fullname.Append(de->d_name);
	  if (stat(fullname.getCStr(), &statdata) != 0)
	    return 0;
	  if (S_ISDIR(statdata.st_mode))
	    isDir = 1;
	  else
	    isDir = 0;

	  if ( isDir == inFolders ) {

	    tryAgain = name.compareTo( "." ) == 0 || name.compareTo( ".." ) == 0;
	    outSpec.Assign( folderSpec );
	    if ( isDir )
	      name.Append( "\\" );
	    outSpec.Rename( name ); }
	  else
	    tryAgain = true;
	} while ( ok && tryAgain );

	return ok;
}

void EgOSUtils::Beep() {

	#ifdef EG_MAC
	::SysBeep( 200 );
	#endif

	#ifdef EG_WIN
	MessageBeep(0);
	#endif
}


#ifdef UNIX_X

	#include <sys/times.h>
#endif
long EgOSUtils::CurTimeMS() {
	#if EG_WIN
	return ::timeGetTime();
	#elif EG_MAC
	return ::TickCount() * 16;
        #else
	struct timeval tv;
	struct timezone tz;
	tz.tz_minuteswest = 0;
	tz.tz_dsttime = 0;
	gettimeofday(&tv, &tz);
	return ((tv.tv_sec * 1000) & 0x7fffffff) + tv.tv_usec / 1000;
        #endif
}


void EgOSUtils::GetMouse( Point& outPt )
{
	int x, y;
	SDL_GetMouseState(&x, &y);
	outPt.h = x;
	outPt.v = y;
}


void EgOSUtils::ShowMsg( const UtilStr& inMsg ) {

	#ifdef EG_MAC
	//UDesktop::Deactivate();
	::ParamText( inMsg.getPasStr(), "\p", "\p", "\p");
	::StopAlert( 2002, NULL );
	//#pragma rem back in!
	//UDesktop::Activate();
	#endif

	#ifdef WIN32
	::MessageBox( NULL, inMsg.getCStr(), "Examgen Message", MB_ICONEXCLAMATION | MB_OK | MB_SETFOREGROUND | MB_APPLMODAL );
	//ZafMessageWindow* w = new ZafMessageWindow( "Message", ZAF_EXCLAMATION_ICON, ZAF_DIALOG_OK, ZAF_DIALOG_OK, inMsg.getCStr() );
	//zafWindowManager -> Add( w );
	//w -> Control();
	#endif

}



void EgOSUtils::ShowMsg( const char* inMsg ) {
	UtilStr msg( inMsg );

	ShowMsg( msg );
}



long EgOSUtils::Rnd( long min, long max )
{
	long maxRnd 	= RAND_MAX;
	long retNum 	= rand() * ( max - min + 1 ) / maxRnd + min;

	if ( retNum >= max )
		return max;
	else
		return retNum;
}

unsigned long EgOSUtils::RevBytes( unsigned long inNum )
{
	return ( inNum << 24 ) |
		( ( inNum & 0xFF00 ) << 8 ) |
		( ( inNum & 0xFF0000 ) >> 8 ) |
		( inNum >> 24 );
}



#define __SET_RGB( R, G, B ) 	\
	if ( R < 0 )				\
		outRGB.red = 0;			\
	else if ( R <= 0xFFFF )		\
		outRGB.red = R;			\
	else						\
		outRGB.red = 0xFFFF;	\
	if ( G < 0 )				\
		outRGB.green = 0;		\
	else if ( G <= 0xFFFF )		\
		outRGB.green = G;		\
	else						\
		outRGB.green = 0xFFFF;	\
	if ( B < 0 )				\
		outRGB.blue = 0;		\
	else if ( B <= 0xFFFF )		\
		outRGB.blue = B;		\
	else						\
		outRGB.blue = 0xFFFF;	\
	break;





void EgOSUtils::HSV2RGB( float H, float S, float V, RGBColor& outRGB ) {


	// H is given on [0, 1] or WRAPPED. S and V are given on [0, 1].
	// RGB are each returned on [0, 1].
	long hexQuadrant, m, n, v;
	H = ( H - floor( H ) ) * 6;  // Wrap the Hue angle around 1.0, then find quadrant

	hexQuadrant = (long) H;
	float f = H - hexQuadrant;

	// Check sat bounds
	if ( S < 0 )
		S = 0;
	if ( S > 1 )
		S = 1;

	// Check val bounds
	if ( V < 0 )
		V = 0;
	if ( V > 1 )
		V = 1;

	if ( ! ( hexQuadrant & 1 ) )
		f = 1 - f; // hexQuadrant i is even

	V *= 65535.0;
	v = (long) V;
	m = (long) (V * (1 - S));
	n = (long) (V * (1 - S * f));

	switch ( hexQuadrant ) {
		case 1: __SET_RGB( n, v, m );
		case 2: __SET_RGB( m, v, n );
		case 3: __SET_RGB( m, n, v );
		case 4: __SET_RGB( n, m, v );
		case 5: __SET_RGB( v, m, n );
		default:
			__SET_RGB( v, n, m );
	}
}



/*
#define RETURN_HSV(h, w, b) {HSV.H = h; HSV.S = s; HSV.V = v; return HSV;}

#define RETURN_RGB(r, g, b) {RGB.R = r; RGB.G = g; RGB.B = b; return RGB;}

#define UNDEFINED -1

// Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms. Pure

// red always maps to 6 in this implementation. Therefore UNDEFINED can be

// defined as 0 in situations where only unsigned numbers are desired.

typedef struct {float R, G, B;} RGBType;

typedef struct {float H, S, V;} HSVType;

HSVType

RGB_to_HSV( RGBType RGB ) {

            // RGB are each on [0, 1]. S and V are returned on [0, 1] and H is
            // returned on [0, 6]. Exception: H is returned UNDEFINED if S==0.
            float R = RGB.R, G = RGB.G, B = RGB.B, v, x, f;
            int i;
            HSVType HSV;
            x = min(R, G, B);
            v = max(R, G, B);
            if(v == x) RETURN_HSV(UNDEFINED, 0, v);
            f = (R == x) ? G - B : ((G == x) ? B - R : R - G);
            i = (R == x) ? 3 : ((G == x) ? 5 : 1);
            RETURN_HSV(i - f /(v - x), (v - x)/v, v);


}


RGBType

HSV_to_RGB( HSVType HSV ) {

           // H is given on [0, 6] or UNDEFINED. S and V are given on [0, 1].
           // RGB are each returned on [0, 1].
           float h = HSV.H, s = HSV.S, v = HSV.V, m, n, f;
           int i;
           RGBType RGB;
           if(h == UNDEFINED) RETURN_RGB(v, v, v);
           i = floor(h);
           f = h - i;
           if(!(i & 1)) f = 1 - f; // if i is even
           m = v * (1 - s);
           n = v * (1 - s * f);
           switch (i) {
                     case 6:
                     case 0: RETURN_RGB(v, n, m);
                     case 1: RETURN_RGB(n, v, m);
                     case 2: RETURN_RGB(m, v, n)
                     case 3: RETURN_RGB(m, n, v);
                     case 4: RETURN_RGB(n, m, v);
                     case 5: RETURN_RGB(v, m, n);
           }


}
*/

