// Utilities.c
// R.H. Pugmire
// My general utilitiy routines  -  version for use with DNews

#include <Types.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <Events.h>
#include <string.h>
#include <ctype.h>
#include <Processes.h>
#include <Memory.h>
#include <stdarg.h>
#include "utilities.h"
#include <Resources.h>


#define haveAUX() 0


// get version number stuff  just short number string for now
unsigned char *GetVers(void){
	static Str32	Vers = "\p";

	if (!Vers[0]){
		Handle	rHandle;
		char	*cp, firstByte;
		unsigned char lngth;
	
		rHandle = GetResource( 'vers', 1 );
		if ( rHandle == nil ) { 
				SysBeep(1);
				pstrcpy(Vers,"\pNo version resource");
				 return Vers; 
		}
		HLock( rHandle );			/* always lock dereferenced handle */
		cp = *rHandle;				/* cp now points to the data */
		firstByte = *cp;	  		/* use (*cp) or (**rHandle) to refer to first byte of data */
		cp += 6; 					// 6 bytes before short version number as a pstring
		lngth = *cp++;
		memcpy(&Vers[1],cp,lngth);		/*... now OK to access data from the 'danR' resource data ... */
		Vers[0] = lngth;
		
		HUnlock( rHandle );			/* let it float again */
		ReleaseResource( rHandle );	/* free up memory when ALL done */
	}
		return Vers;
}

char *CGetVers(void){
	static Str32	Vers;

	if (!Vers[0]){
		Handle	rHandle;
		char	*cp, firstByte;
		unsigned char lngth;
	
		rHandle = GetResource( 'vers', 1 );
		if ( rHandle == nil ) { 
				SysBeep(1);
				pstrcpy(Vers,"\pNo version resource");
				 return (char *) Vers; 
		}
		HLock( rHandle );			/* always lock dereferenced handle */
		cp = *rHandle;				/* cp now points to the data */
		firstByte = *cp;	  		/* use (*cp) or (**rHandle) to refer to first byte of data */
		cp += 6; 					// 6 bytes before short version number as a pstring
		lngth = *cp++;
		memcpy(&Vers[1],cp,lngth);		/*... now OK to access data from the 'danR' resource data ... */
		Vers[0] = lngth;
		
		HUnlock( rHandle );			/* let it float again */
		ReleaseResource( rHandle );	/* free up memory when ALL done */
		PtoCstr(Vers);
	}
	return (char *)Vers;
}

Boolean GetFileToOpen(OSType filetype, FSSpec *target){
	SFTypeList	mytypes;
	StandardFileReply sfReply;
	
	mytypes[0] = filetype;
	StandardGetFile(nil,1,mytypes,&sfReply);
	BlockMove(&sfReply.sfFile,target,sizeof(FSSpec));
	
	return sfReply.sfGood;
}

OSErr OpenInputFile(OSType filetype, FSSpecPtr infile, Str63 filename, short *inrefNum){

	StandardFileReply sfReply;
	Point 		where = {0,0};
	OSErr		err;
	SFTypeList	mytypes;
	
	mytypes[0] = filetype;
	pstrcpy(infile->name,filename);
	StandardGetFile(nil,1,mytypes,&sfReply);
	if (!sfReply.sfGood) return sfReply.sfGood;
	BlockMove(&sfReply.sfFile,infile,sizeof(FSSpec));

	err = FSOpen (infile->name, infile->vRefNum, inrefNum);
	if (err != noErr) { demsg("Error %hd on open %s", err, PtoCstr(infile->name)); return err;}
	return noErr;
}


// Safe DisposPtr() which can be called twice and for unallocated ptrs provided they are set to NULL
void myDisposePtr(Ptr p){
	if (p!=NULL){
		DisposPtr(p);
		p = NULL;
	}
	return;
}

//Dislay an error message with a call like printf but in an alert dialogue
void demsg(char * arg_list, ...){
        va_list arg_ptr;
        char *format;
        int len;
        char output[256];
        
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        va_end(arg_ptr);
		ParamText(CtoPstr(output),"\pError Message","\p","\p");
		Alert(ErrAlrtID,nil);
}

//Dislay adebug message if option key is down with a call like printf but in an alert dialogue
void dbgmsg(char * arg_list, ...){
        va_list arg_ptr;
        char *format;
        int len;
        char output[256];
        
        if (!isPressed(OptionKey))
        	return;
        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsprintf(output, format, arg_ptr);
        va_end(arg_ptr);
		ParamText(CtoPstr(output),"\pDebugMessage","\p","\p");
		Alert(ErrAlrtID,nil);
}


// Pascal string utilities
 
// pstrcpy - copies string 'src' to string 'dst'
void pstrcpy(StringPtr dst, StringPtr src){
	// copy string in 
	BlockMove(src + 1, dst + 1, *src);
	// adjust length byte 
	*dst = *src;
}

// pstrcat - add string 'src' to end of string 'dst'
void pstrcat(StringPtr dst, StringPtr src){
	// copy string in 
	BlockMove(src + 1, dst + *dst + 1, *src);
	// adjust length byte 
	*dst += *src;
}

// pstrinsert - insert string 'src' at beginning of string 'dst'
void pstrinsert(StringPtr dst, StringPtr src){
	// make room for new string 
	BlockMove(dst + 1, dst + *src + 1, *dst);
	// copy new string in 
	BlockMove(src + 1, dst + 1, *src);
	// adjust length byte 
	*dst += *src;
}

void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName){
	DirInfo	block;
	Str255	directoryName;
	OSErr	err;

	fullPathName[0] = 0;

	block.ioDrParID = dirID;
	block.ioNamePtr = directoryName;
	do {
			block.ioVRefNum = vRefNum;
			block.ioFDirIndex = -1;
			block.ioDrDirID = block.ioDrParID;
			err = PBGetCatInfo((CInfoPBPtr)&block, FALSE);
			if (haveAUX()) {
					if (directoryName[1] != '/')
							/* If this isn't root (i.e. "/"), append a slash ('/') */
							pstrcat(directoryName, (StringPtr)"\p/");
			}
			else
					pstrcat(directoryName, (StringPtr)"\p:");
			pstrinsert(fullPathName, directoryName);
	} while (block.ioDrDirID != 2);
}

/*
PathNameFromWD:
Given an HFS working directory, this routine returns the full pathname that
corresponds to it. It does this by calling PBGetWDInfo to get the VRefNum and
DirID of the real directory. It then calls PathNameFromDirID, and returns its
result.
*/
void PathNameFromWD(long vRefNum, StringPtr pathName){
	WDPBRec	myBlock;
	OSErr	err;
	/*
 	PBGetWDInfo has a bug under A/UX 1.1.  If vRefNum is a real
	 vRefNum and not a wdRefNum, then it returns garbage.
 	Since A/UX has only 1 volume (in the Macintosh sense) and
 	only 1 root directory, this can occur only when a file has been
 	selected in the root directory (/).
 	So we look for this and hardcode the DirID and vRefNum.
	*/
	if ((haveAUX()) && (vRefNum == -1))
			PathNameFromDirID(2, -1, pathName);
	else {
			myBlock.ioNamePtr = nil;
			myBlock.ioVRefNum = vRefNum;
			myBlock.ioWDIndex = 0;
			myBlock.ioWDProcID = 0;
	/*
	 Change the Working Directory number in vRefnum into a real
	vRefnum and DirID. The real vRefnum is returned in ioVRefnum,
	 and the real DirID is returned in ioWDDirID.
	*/
			err = PBGetWDInfo(&myBlock, FALSE);
			if (err != noErr)
					return;
			PathNameFromDirID(myBlock.ioWDDirID, myBlock.ioWDVRefNum,
					pathName);
	}
}

// Outlines the OK button in a dialog box
pascal void OutlineOK (DialogPtr myDialogPtr, int item){
	short		OKType;
	Rect		OKBox;
	Handle		OKItem;
	
	GetDItem (myDialogPtr, OK, &OKType, &OKItem, &OKBox);
	
	PenNormal ();
	PenSize (3,3);
	
	InsetRect (&OKBox, -4, -4);
	FrameRoundRect (&OKBox, 16, 16);
	
	PenNormal ();
	
	return;
}

// Centers a dialog template 1/3 of the way down on the main screen. 
void CenterDialog (DialogTHndl myDialogTHndl){
	int							width, height;
	Rect						myRect;
	GDHandle					myGDHandle;
	
	myGDHandle = GetMainDevice ();
	
	width  = (**myGDHandle).gdRect.right - (**myGDHandle).gdRect.left;
	height = (**myGDHandle).gdRect.bottom - (**myGDHandle).gdRect.top;
	
	myRect = (**myDialogTHndl).boundsRect;
	
	OffsetRect (&myRect, -myRect.left, -myRect.top);
	OffsetRect (&myRect, (width - myRect.right) / 3, (height - myRect.bottom - 20) / 3 + 20);
	
	 (**myDialogTHndl).boundsRect = myRect;
	
	return;
}


// Searching for a specific process by signature
Boolean FindAProcess (
	OSType 				signature, 
	ProcessSerialNumber *process,
	ProcessInfoRec 		*infoRec, 
	FSSpecPtr			aFSSpecPtr){
	
	process->highLongOfPSN = 0;
	process->lowLongOfPSN = kNoProcess; 	// start from the beginning

	infoRec->processInfoLength = sizeof(ProcessInfoRec);
	infoRec->processName = (StringPtr) NewPtr(32);
	infoRec->processAppSpec = aFSSpecPtr;

	while (!GetNextProcess(process)) {

		if ( !GetProcessInformation(process, infoRec) ) {
			if ( (infoRec->processType == (long) 'APPL') &&
				 (infoRec->processSignature == signature) )
				return TRUE;		// found the process

		}
	}

	return FALSE;
}

//Set temp string to next one in list or first if off the end
void Cycle(char *temp,char *list){
	char 	*l, *m;
	Boolean match;
	
	match=true;
	for (m=temp, l=list; *l; l++,m++){
		if (*l == ','){
			l++;
			if (match==true && *m==0){
			 	 break;
			} else {
				match = true;
				m = temp;
			}
		}else if (toupper(*m) != toupper(*l)) match=false;
	}
	if (!*l) l = list;
	for(m=temp; *l; l++,m++){
		if (*l == ',') break;
		*m = *l;
	}
	*m = 0;	
}

short isPressed(unsigned short k ){
// k =  any keyboard scan code, 0-127
	union {
		KeyMap	kmap;
		unsigned char km[16];
	} u;

	GetKeys( u.kmap);
	return ( ( u.km[k>>3] >> (k & 7) ) & 1);
}

//check string for true false yes no
Boolean istrue(char *s, Boolean dflt){
	if (!strncmp(s, "true",4) || !strncmp(s, "TRUE",4)) return true;
	if (!strncmp(s, "false",5) || !strncmp(s, "FALSE",5)) return false;
	if (!strncmp(s, "yes",3) || !strncmp(s, "YES",3)) return true;
	if (!strncmp(s, "no",2) || !strncmp(s, "NO",2)) return false;
	return dflt;
}

OSErr getline(short refNum,char *s,int maxl){ 
// get line of chars <100 nchar = # we got
	OSErr	err;
	int 	i;
	long	count;
	for(i=0; i<maxl; i++){
			count = 1;
			err = FSRead(refNum,&count,&s[i]);
			if (err) {FSClose(refNum);return err;}
			if (s[i] == 13) {
				if (s[0] == '!'){
					i=-1;
				}else{
					s[i] = 0;
					return noErr;
				}
			}
	}
	return 1;  //line too long error
}

OSErr putline(short refNum,char *s){ 
	OSErr	err;
	long	count;
			count = strlen(s);
			err = FSWrite(refNum,&count,s);
			if (err) return err;
			count = 1;
			err = FSWrite(refNum,&count,"\r");
			if (err) return err;
			
	return noErr;
}


OSErr dimimgr(Pimgdr m, int y, int x){
	int i;
	m->y = y;
	m->x = x;
	m->oneblock = false;
	if (!(m->e = (ppsdouble) NewPtr(sizeof(psdouble) * (long)y))) return memFullErr;
	for (i=0; i<y; i++){
		if (!(m->e[i] = (psdouble)NewPtr(sizeof(short double) * (long)x))){
			m->y = i;
			freeimgr(m);
			return memFullErr;
		}
	}
	return noErr;	
}

// Difference from dimimgr is we try and allocate it all in one block (or two anyway)
// and we try for system temporary memory first
OSErr dimimgr1(Pimgdr m, int y, int x){
	int i;
	m->oneblock = true;
	m->y = y;
	m->x = x;
	if (!(m->e = (ppsdouble) NewPtr(sizeof(psdouble) * (long)y))) return memFullErr;
	if (!(m->e[0] = (psdouble)NewPtrSys((long)x * sizeof(short double) * y)))
	 	((m->e[0] = (psdouble)NewPtr((long)x * sizeof(short double) * y)));	
	if (m->e[0]){
		for (i=1; i<y; i++)
			m->e[i] = (psdouble) (m->e[i-1] + x);
		return noErr;	
	}else{
		DisposPtr((Ptr)m->e);
		return memFullErr;
	}
}


OSErr dimimg(Pimgd m, int y, int x){
	int i;
	m->y = y;
	m->x = x;
	m->oneblock = false;
	if (!(m->e = (unsigned char **) NewPtr( (sizeof(unsigned char *) * (long)y)))) return memFullErr;
		for (i=0; i<y; i++){
			if (!(m->e[i] = (unsigned char *)NewPtr((long)x))){
				m->y = i;
				freeimg(m);
				return memFullErr;
			}
		}
	return noErr;	
}

// Difference from dimimg is we try and allocate it all in one block (or two anyway)
// and we try for system temporary memory first
OSErr dimimg1(Pimgd m, int y, int x){
	int i;
	m->oneblock = true;
	m->y = y;
	m->x = x;
	if (!(m->e = (unsigned char **) NewPtr( (sizeof(unsigned char *) * (long)y)))) return memFullErr;
	if (!(m->e[0] = (unsigned char *)NewPtrSys((long)x * y)))
		((m->e[0] = (unsigned char *)NewPtr((long)x * y)));
/*	{ Str255 s;
		  sprintf((char*)s,"dimimg: System memory get ok"); 
		  CtoPstr((char *)s);ParamText(s,"\p", "\p ","\p");Alert(300, nil);
	}*/
	if (m->e[0] ){
		for (i=1; i<y; i++)
			m->e[i] = (unsigned char *) m->e[i-1] + x;
		return noErr;	
	}else{
		DisposPtr((Ptr)m->e);
		return memFullErr;
	}
}

void freeimgr(Pimgdr m){
	int y,i;
	
	y = m->y;
	if (y){
		if (m->oneblock)
			DisposPtr((Ptr)m->e[0]);
		else
			for (i=0; i<y; i++)
				DisposPtr((Ptr)m->e[i]);
		DisposPtr((Ptr)m->e);
		m->y = 0;
	}
}
void freeimg(Pimgd m){
	int y,i;
	
	y = m->y;
	if (y){
		if (m->oneblock)
			DisposPtr((Ptr)m->e[0]);
		else
			for (i=0; i<y; i++)
				DisposPtr((Ptr)m->e[i]);
		DisposPtr((Ptr)m->e);
		m->y = 0;
	}
}

int myGetDialogItem(DialogPtr TheDialog, int item){
	short ItemType;
	Rect ItemBox; 
  	Handle ItemHdl; 
	
	GetDItem(TheDialog, item, &ItemType, &ItemHdl, &ItemBox);
	return GetCtlValue((ControlHandle)ItemHdl);
}

void GetItemRect(DialogPtr TheDialog, int item, RectPtr ItemBox){
	short ItemType;
  	Handle ItemHdl; 
	
	GetDItem(TheDialog, item, &ItemType, &ItemHdl, ItemBox);
}

void mySetDialogItem(DialogPtr TheDialog, int item, int n){
    short ItemType;
    Rect ItemBox; 
    Handle ItemHdl; 
  
    GetDItem(TheDialog, item, &ItemType, &ItemHdl, &ItemBox);
    SetCtlValue((ControlHandle)ItemHdl, n);
}

void DimDialogItem(DialogPtr TheDialog, int item, int n){
    short ItemType;
    Rect ItemBox; 
    Handle ItemHdl; 
  
    GetDItem(TheDialog, item, &ItemType, &ItemHdl, &ItemBox);
    HiliteControl((ControlHandle)ItemHdl, n*255);
}

void SetDNum(DialogPtr TheDialog, int item, int n){
    short ItemType;
    Rect ItemBox; 
    Handle ItemHdl; 
    Str255 str;
  
  	GetDItem(TheDialog, item, &ItemType, &ItemHdl, &ItemBox);
    NumToString(n,str);
    SetIText(ItemHdl, str);
}

void SetDReal(DialogPtr TheDialog, int item, double n, int fwidth){
  	short ItemType;
  	Rect ItemBox; 
  	Handle ItemHdl; 
  	Str255 str;

  	GetDItem(TheDialog, item, &ItemType, &ItemHdl, &ItemBox);
 	Real2String(n,1, fwidth, str);
 	SetIText(ItemHdl, str);
}

double GetDReal(DialogPtr TheDialog, int item){
  	Str255 str;
	Rect r;
	short itemType;
	Handle Text;
	
	GetDItem (TheDialog, item, &itemType, &Text, &r);
	GetIText (Text, str);
  	return String2Real(str);
}

int GetDNum(DialogPtr TheDialog, int item){
  	long n;
  	Str255 str;
	Rect r;
	short itemType;
	Handle Text;
	
	GetDItem (TheDialog, item, &itemType, &Text, &r);
	GetIText (Text, str);
  	StringToNum(str,&n);
  	return n;
}

void SetDString(DialogPtr TheDialog, int item, Str255 str){
  	short ItemType;
  	Rect ItemBox; 
  	Handle ItemHdl; 

  	GetDItem(TheDialog, item, &ItemType, &ItemHdl, &ItemBox);
  	if (ItemType== ctrlItem)
  		SetCTitle((ControlHandle)ItemHdl, str);
  	else
  		SetIText(ItemHdl, str);
}

void GetDString(DialogPtr TheDialog, int item, Str255 str){
	Rect r;
	short ItemType;
  	Handle ItemHdl; 
	
	GetDItem (TheDialog, item, &ItemType, &ItemHdl, &r);
  	if (ItemType== ctrlItem)
  		GetCTitle((ControlHandle)ItemHdl, str);
  	else
		GetIText (ItemHdl, str);
}

double String2Real(Str255 str){
	PtoCstr(str);
  	return atof((char *)str);
}


 void Real2String(double val, int width, int fwidth, Str255 str)/* ignoring width for now */
{
  	char fs[] = "%6.2f";
  	/* fwidth =-1 => make up a sensible value for fwidth  0 2 or 4 */
  	if ( fwidth < 0 ) {
	    if ( val < 1.0 )
	     	fwidth = 4;
	    else if ( (int)val == val )
	     	fwidth = 0;
	    else
	     	fwidth = 2;
  	}
  	fs[1] = '0'+width;
  	fs[3] = '0'+fwidth;
  	sprintf((char *)str,fs,val);
  	CtoPstr((char *)str);
 }
