/*	XGPreferences.cpp
 *
 *		Preferences data class. This encapsulates the interface to the
 *	preferences information that I store away.
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or 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 MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library 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.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <stdlib.h>
#include <string.h>
#include <XError.h>
#include <XDataUtil.h>
#include <XPreferences.h>

#if OPT_XWIN
#include <stdio.h>
#endif

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

// @G"Construction/Destruction"

#if OPT_MACOS

/*	@F"XGPreferences::XGPreferences" @I
 *
 *		Construct me. This is the Macintosh version, which takes the
 *	name of the file to store in the Preferences Folder, and the
 *	creator type
 */

XGPreferences::XGPreferences(char *file, long type)
{
	fDirty = false;
	fFile = file;
	fType = type;
	ReadData();
}

#endif

#if OPT_XWIN

/*	@F"XGPreferences::XGPreferences" @I
 *
 *		Construct me. This is the Unix version, which takes the
 *	name of the file to store in the user's home directory
 */

XGPreferences::XGPreferences(char *file)
{
	fDirty = false;
	fFile = file;
	ReadData();
}

#endif

#if OPT_WINOS

/*	@F"XGPreferences::XGPreferences" @I
 *
 *		Construct me. This is the Windows version, which takes the
 *	name of the company, program and version information for storing
 *	this collection of definitions in the registry file.
 */

XGPreferences::XGPreferences(char *company, char *program, char *version)
{
	char buffer[256];
	DWORD disp;
	
	/*
	 *	Construct the key string:
	 *
	 *		Software\company\program\version
	 *
	 *	For example:
	 *
	 *		Software\PandaWave\WorldCal\1.0
	 */
	
	strcpy(buffer,"Software\\");
	strcat(buffer,company);
	strcat(buffer,"\\");
	strcat(buffer,program);
	strcat(buffer,"\\");
	strcat(buffer,version);
	
	fKey = (HKEY)INVALID_HANDLE_VALUE;
	if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
									  buffer,
									  0,
									  KEY_ALL_ACCESS,
									  &fKey)) {
		/*
		 *	Key doesn't exist. Create it
		 */
		
		if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE,
											buffer,
											0,
											"",
											REG_OPTION_NON_VOLATILE,
											KEY_ALL_ACCESS,
											NULL,
											&fKey,
											&disp)) {
			throw XGPostError(KXGPreferenceError);
		}
	}
}

#endif

/*	@F"XGPreferences::~XGPreferences" @I
 *
 *		Delete preferences data
 */

XGPreferences::~XGPreferences()
{
#if OPT_MACOS || OPT_XWIN
	if (fDirty) WriteData();
#endif

#if OPT_WINOS
	if (fKey != INVALID_HANDLE_VALUE) ::RegCloseKey(fKey);
#endif
}

/************************************************************************/
/*																		*/
/*	Get/Set Values														*/
/*																		*/
/************************************************************************/

// @G"Get/Set Values"

/*	@F"XGPreferences::GetBoolean" @I
 *
 *		Get boolean value
 */

bool XGPreferences::GetBoolean(char *key)
{
	char buffer[256];
	
	GetString(key,buffer);
	if (!strcmp(buffer,"true")) return true;
	else return false;
}

/*	@F"XGPreferences::GetInteger" @I
 *
 *		Get integer
 */

long XGPreferences::GetInteger(char *key)
{
	char buffer[256];
	char *c;
	long l;
	bool flag = false;
	
	GetString(key,buffer);
	l = 0;
	for (c = buffer; *c != 0; c++) {
		if (*c == '-') flag = true;
		else if ((*c >= '0') && (*c <= '9')) {
			l = (l * 10) + (*c - '0');
		}
	}
	if (flag) l = -l;
	return l;
}

/*	@F"XGPreferences::SetBoolean" @I
 *
 *		Set the boolean value
 */

void XGPreferences::SetBoolean(char *key, bool value)
{
	SetString(key,value ? "true" : "false");
}

/*	@F"XGPreferences::SetInteger" @I
 *
 *		Set integer value
 */

void XGPreferences::SetInteger(char *key, long value)
{
	char buffer[256];
	
	::yformat(buffer,"#L",value);
	SetString(key,buffer);
}

/*	@F"XGPreferences::GetString" @I
 *
 *		Get the string value
 */

void XGPreferences::GetString(char *key, char *string)
{
#if OPT_MACOS || OPT_XWIN
	long i,len;
	char buffer[256];
	
	len = fDataKey.GetNumStrings();
	for (i = 0; i < len; i++) {
		fDataKey.GetString(i,buffer);
		if (!strcmp(key,buffer)) {
			fDataValue.GetString(i,string);
			return;
		}
	}
	string[0] = 0;
#endif

#if OPT_WINOS
	unsigned long len;
	unsigned long type;
	long ret;
	
	string[0] = 0;
	len = 256;
	ret = ::RegQueryValueEx(fKey,
							key,
							NULL,
							&type,
							(unsigned char *)string,
							&len);
	if (ret) {
		string[0] = 0;
	}
#endif
}

/*	@F"XGPreferences::SetString" @I
 *
 *		Set the string value
 */

void XGPreferences::SetString(char *key, char *string)
{
#if OPT_MACOS || OPT_XWIN
	long i,len;
	char buffer[256];
		
	fDirty = true;
	len = fDataKey.GetNumStrings();
	for (i = 0; i < len; i++) {
		fDataKey.GetString(i,buffer);
		if (!strcmp(key,buffer)) {
			fDataValue.SetString(i,string);
			return;
		}
	}
	
	fDataKey.Append(key);
	fDataValue.Append(string);
#endif

#if OPT_WINOS
	::RegSetValueEx(fKey,key,0,REG_SZ,(unsigned char *)string,1+strlen(string));
#endif
}

/*	@F"XGPreferences::DeleteValue" @I
 *
 *		Delete this key
 */

void XGPreferences::DeleteValue(char *key)
{
#if OPT_MACOS || OPT_XWIN
	long i,len;
	char buffer[256];
	
	len = fDataKey.GetNumStrings();
	for (i = 0; i < len; i++) {
		fDataKey.GetString(i,buffer);
		if (!strcmp(key,buffer)) {
			fDirty = true;
			fDataKey.Delete(i);
			fDataValue.Delete(i);
			return;
		}
	}
#endif

#if OPT_WINOS
	::RegDeleteValue(fKey,key);
#endif
}

/************************************************************************/
/*																		*/
/*	Read/Write Data														*/
/*																		*/
/************************************************************************/

/*	XGPreferences::ReadData
 *
 *		Read the data from the preferences file. For Mac and Unix only
 */

#if OPT_XWIN

void XGPreferences::ReadData()
{
	FILE *f;
	char buffer[256];
	char *c,*d;
	
	c = getenv("HOME");						// home directory
	if (c == NULL) buffer[0] = 0;
	else strcpy(buffer,c);
	strcat(buffer,"/.");
	strcat(buffer,fFile.Get());
	f = fopen(buffer,"r");
	if (f == NULL) return;
	
	/*
	 *	Read the data
	 */
	
	fDataKey.DeleteAll();
	fDataValue.DeleteAll();
	while (NULL != fgets(buffer,255,f)) {
		d = NULL;
		for (c = buffer; *c != 0; c++) {
			if (*c == '=') {
				*c = 0;
				d = c+1;
			} else if (*c == '\n') {
				*c = 0;
				break;
			}
		}
		if (d) {
			fDataKey.Append(buffer);
			fDataValue.Append(d);
		}
	}
	
	/*
	 *	Done.
	 */
	
	fclose(f);
}

#endif

#if OPT_MACOS

void XGPreferences::ReadData()
{
	short refNum;
	short vRefNum;
	long dirID;
	OSErr err;
	Handle h;
	long len;
	long pos;
	unsigned char *data;
	char buf1[256];
	char buf2[256];
	
	/*
	 *	Get the preferences folder. this *will* work under system 6,
	 *	as the RTL provides this as glue
	 */
	
	FindFolder(kOnSystemDisk,
			   kPreferencesFolderType,
			   kDontCreateFolder,
			   &vRefNum,
			   &dirID);
	
	/*
	 *	Now open this preferences file. If it doesn't exist, bail
	 */
	
	err = HOpenDF(vRefNum,
				  dirID,
				  cvtc2p((unsigned char *)buf1,fFile.Get()),
				  fsRdPerm,
				  &refNum);
	if (err) return;
	
	/*
	 *	We have the file. Read the file into a handle for processing
	 */
	
	err = GetEOF(refNum,&len);
	if (err || (len == 0)) {
		FSClose(refNum);
		return;
	}
	h = NewHandle(len);
	if (h == NULL) {
		FSClose(refNum);
		return;
	}
	HLock(h);
	err = FSRead(refNum,&len,*h);
	FSClose(refNum);
	if (err) {
		DisposeHandle(h);
		return;
	}
	
	/*
	 *	And process the handle
	 */
	
	fDataKey.DeleteAll();
	fDataValue.DeleteAll();
	
	data = (unsigned char *)*h;
	pos = 0;
	while (pos < len) {
		cvtp2c(buf1,data+pos);
		pos += data[pos] + 1;
		if (pos >= len) break;			// EOF too soon?
		cvtp2c(buf2,data+pos);
		pos += data[pos] + 1;
		
		/*
		 *	Insert new token
		 */
		
		fDataKey.Append(buf1);
		fDataValue.Append(buf2);
	}
	
	/*
	 *	Done. Release data from handle
	 */
	
	HUnlock(h);
	DisposeHandle(h);
}

#endif

/*	XGPreferences::WriteData
 *
 *		Write the data to the preferences file. For Mac and Unix only
 */

#if OPT_XWIN

void XGPreferences::WriteData(void)
{
	FILE *f;
	char *c;
	char buffer[256];
	char bufval[256];
	long len,off;
	
	c = getenv("HOME");						// home directory
	if (c == NULL) buffer[0] = 0;
	else strcpy(buffer,c);
	strcat(buffer,"/.");
	strcat(buffer,fFile.Get());
	f = fopen(buffer,"w");
	if (f == NULL) return;
	
	/*
	 *	Write the data. This is relatively easy
	 */
	
	len = fDataKey.GetNumStrings();
	for (off = 0; off < len; off++) {
		fDataKey.GetString(off,buffer);
		fDataValue.GetString(off,bufval);
		
		fprintf(f,"%s=%s\n",buffer,bufval);
	}
	
	/*
	 *	Done.
	 */
	
	fclose(f);
}

#endif

#if OPT_MACOS

void XGPreferences::WriteData(void)
{
	short refNum;
	short vRefNum;
	long dirID;
	OSErr err;
	Handle h;
	long len;
	char buf1[256];
	char buf2[256];
	long off;
	unsigned char *p;
	
	/*
	 *	Build the data handle from my data
	 */
	
	h = NewHandle(0);
	len = fDataKey.GetNumStrings();
	for (off = 0; off < len; off++) {
		fDataKey.GetString(off,buf2);
		p = cvtc2p((unsigned char *)buf1,buf2);
		PtrAndHand(p,h,1+*p);
		fDataValue.GetString(off,buf2);
		p = cvtc2p((unsigned char *)buf1,buf2);
		PtrAndHand(p,h,1+*p);
	}
	
	/*
	 *	Get the preferences folder. this *will* work under system 6,
	 *	as the RTL provides this as glue
	 */
	
	FindFolder(kOnSystemDisk,
			   kPreferencesFolderType,
			   kDontCreateFolder,
			   &vRefNum,
			   &dirID);
	
	/*
	 *	Now open this preferences file. If it doesn't exist, create
	 */
	
	err = HOpenDF(vRefNum,
				  dirID,
				  cvtc2p((unsigned char *)buf1,fFile.Get()),
				  fsWrPerm,
				  &refNum);
	if (err) {
		if (err == fnfErr) {
			/*
			 *	File was not found. Create it instead.
			 */
			
			err = HCreate(vRefNum,
						  dirID,
						  (unsigned char *)buf1,
						  fType,
						  'pref');
			if (err) return;
			
			err = HOpenDF(vRefNum,
						  dirID,
						  (unsigned char *)buf1,
						  fsWrPerm,
						  &refNum);
			if (err) return;
		} else return;
	} else {
		/*
		 *	File found. Clear the file's contents by setting the
		 *	EOF to zero
		 */
		
		err = SetEOF(refNum,0L);
		if (err) return;
	}
	
	/*
	 *	We have the file. Write the handle and return
	 */
	
	len = GetHandleSize(h);
	HLock(h);
	err = FSWrite(refNum,&len,*h);
	FSClose(refNum);
	HUnlock(h);
	DisposeHandle(h);
}

#endif
