/**************************************************************
**                                                           **
**      $VER: Utility.c started on 31.08.2001                **
**                                                           **
**      Review Buffer, preference file, date manipulation,   **
**      ASL and locale.library support                       **
**                                                           **
**      Free software written by T.Pierron                   **
**                                                           **
**************************************************************/

#include <intuition/intuition.h>
#include <workbench/startup.h>
#include <libraries/gadtools.h>
#include <libraries/asl.h>
#include <dos/dos.h>
#include <exec/memory.h>

#include "Execute.h"
#define  CATCOMP_STRINGS
#define  CATCOMP_NUMBERS
#include "ExecuteStrs.h"


extern struct AslBase *AslBase;
extern struct Window  *window;
extern STRPTR StrBuf;
struct FileRequester  *FR = NULL;

/** Simply remove ending spaces from a string **/
STRPTR Trim( STRPTR str )
{
	register STRPTR p = str + strlen(str) - 1;
	while(p >= str && (*p == ' ' || *p == '\t' || *p == '\n')) p --;
	p[1] = 0;
	return str;
}

/** Convert character **/
void ConvChar( STRPTR str, UWORD len, UBYTE src, UBYTE rep )
{
	register STRPTR p;
	register UBYTE  s = src;
	for(p = str; len--; p++) if(*p == s) *p = rep;
}

/*** Init a review buffer ***/
void InitReview( ReviewBuf *rb, STRPTR init )
{
	UWORD len = 0;
	if(init != NULL)
		ConvChar(init, len = strlen( init )+1, '\n', 0),
		CopyMem(init, rb->Buffer, len);
	else rb->Buffer[0] = 0;
	rb->Usage = rb->Review = len;
}

/*** Add a string to a review buffer ***/
void AddToBuffer(ReviewBuf *rb, STRPTR new)
{
	UWORD len = strlen(new)+1, i;

	/* Check if new string doesn't exists yet */
	for(i=0; i < rb->Usage; i += strlen(rb->Buffer+i)+1)
		if( Stricmp(rb->Buffer+i, new) == 0 ) {
			/* Yes, the string already exists, move it to the end */
			WORD j = i + len;
			rb->Review = rb->Usage;
			if( j < rb->Usage ) {
				CopyMem(rb->Buffer+j, rb->Buffer+i, rb->Usage-j);
				CopyMem(new, rb->Buffer+rb->Review-len, len);
			}
			return;
		}

	/* String does not exists yet: check first for free space */
	if(rb->Usage + len >= sizeof(rb->Buffer)) {
		/* Need to discard some strings */
		STRPTR p = rb->Buffer; i=0;
		do {
			while(i < rb->Usage && *p) i++, p++;
			if(*p == 0) i++, p++;
		} while( sizeof(rb->Buffer) - rb->Usage + i < len );
		/* Shift buffer */
		CopyMem(p, rb->Buffer, rb->Usage -= (p - rb->Buffer));
	}
	
	/* Now we can add the new string */
	CopyMem(new, rb->Buffer + rb->Usage, len);
	rb->Review = rb->Usage += len;
}

/** Get previous/next string in review buffer **/
UWORD GetPrevNextBuffer(ReviewBuf *rb, STRPTR dest, UWORD max, BYTE dir)
{
	STRPTR p   = rb->Buffer + rb->Review;
	UWORD  len = 0;
	if(dir < 0) {
		if(rb->Review > 1) {
			for(p -= 2; p > rb->Buffer && *p; p--);
			if(*p == 0) p++;
			len = rb->Buffer + rb->Review - p;
		}
		else rb->Review = -1;
	} else {
		if(rb->Review >= 0) while(*p++); else p++;
		if(p - rb->Buffer < rb->Usage)
			len = strlen(p)+1;
		else rb->Review = rb->Usage;
	}
	/* No dest str. given: just give starting index of buffer */
	if(dest == NULL) return (len > 0 ? rb->Review = p - rb->Buffer : 0);

	if(len > 0) {
		CopyMem(p, dest, len > max ? max : len);
		rb->Review = p - rb->Buffer; dest[max-1] = 0;
		return len - 1;
	}
	*dest = 0; return 0;
}

/*** Convert a geometry string, describing windows positions ***/
void Parse_geometry( WORD *Geometry, STRPTR p )
{
	WORD *nb;
	LONG  buf;

	for(nb=Geometry; ; p++)
	{
		if(*p == '-' || *p == '+' || ('0' <= *p && *p <= '9'))
			p += StrToLong(p, &buf), *nb = (WORD) buf;

		switch(*p)
		{
			case 0:
			case ',': if(*p && ++nb - Geometry<4) break;
			default:  return;
		}
	}
}

/*** Adjust position to what user wants ***/
WORD Adjust_pos(WORD pos, WORD max, WORD len)
{
	/* if 0x7fff center between 0 and max */
	if(pos==0x7fff) return (WORD)(max-len>>1);
	/* if neg, align pos to max */
	if(pos<0) pos+=max-len+1;
	if(pos<0) return 0;
	if(pos+len>max) return (WORD)(max-len);
	return pos;
}

/**********************************************************
***             ASL File requester support             ****
**********************************************************/
ULONG FileTags[] = {
	ASLFR_Window,         NULL,
	ASLFR_SleepWindow,    TRUE,
	ASLFR_TitleText,      (ULONG) MSG_ASLTITLE_STR,
	ASLFO_MaxHeight,      255,
	ASLFR_Flags1,         FRF_DOPATTERNS | FRF_DOMULTISELECT,
	ASLFR_InitialWidth,   320,
	ASLFR_InitialHeight,  256,
	ASLFR_InitialDrawer,  NULL,
	ASLFR_InitialFile,    NULL,
	ASLFR_InitialPattern, (ULONG)"#?",
	ASLFR_RejectIcons,    TRUE,
	TAG_DONE
};

struct EasyStruct Request = {
	sizeof(struct EasyStruct), 0, APPNAME, NULL, "Ok"
};

void AvertUser( STRPTR msg )
{
	Request.es_TextFormat = msg;
	EasyRequestArgs(window, &Request, NULL, NULL);
}

/** Get starting index of a path **/
UWORD StartPath( STRPTR str, UWORD pos, UBYTE FullPath )
{
	STRPTR p;
	if(pos == 0) return 0;
	for(p = str+pos-1; p > str && *p != ' ' && *p != '\"'; p--);
	if(p > str ) {
		/* Be sure there isn't any opened quote before */
		UWORD  nbquote = (*p == '\"' ? 1 : 0);
		STRPTR q = p, lastquote;
		for(lastquote = NULL; q >= str; q--)
			if(*q == '\"') {
				nbquote++; if(lastquote == NULL) lastquote = q;
			}
		if(nbquote & 1) p = lastquote;
		if(!FullPath || *p != '\"') p++;
	}
	return p - str;
}

/*** Extract path from a command line ***/
STRPTR GetPath( STRPTR str, UWORD pos )
{
	UWORD  len = pos - StartPath(str, pos, 0);
	STRPTR new;

	if(len > 0 && (new = (STRPTR) AllocVec(len+2, 0))) {
		CopyMem(str + pos - len, new, len); new[len] = 0;
		return new;
	}
	return NULL;
}

/*** MemMove: copy overlapping chunk of mem ***/
void MemMove(UBYTE *Src, UWORD Offset, LONG sz)
{
	register UBYTE *src, *dst;
	for(src=Src+sz-1, dst=src+Offset; sz>0; sz--, *dst-- = *src--);
}

/*** Split file part from path part ***/
STRPTR SplitPath( STRPTR path )
{
	STRPTR dir;

	if(path == NULL) return NULL;
	dir = (STRPTR) PathPart( path );

	if(*dir != '/') MemMove(dir, 1, strlen(dir)+1);
	*dir = 0;
	return dir + 1;
}

/*** Add a new path into a command line, removing incomplete one ***/
UBYTE WorkBuf[ MAX_COM+4 ], RemPartial;
UBYTE AddPath(struct StringInfo *sti, STRPTR path, STRPTR compl)
{
	UBYTE AddQuote = FALSE;
	/* Look crap, sorry... */
	if( (InfoFlags & CHECKED) == 0 && !Stricmp(compl + strlen(compl) - 5, ".info") )
		return TRUE;
	if( RemPartial )
	{
		UWORD   pos = sti->BufferPos;
		UWORD stpos = StartPath( StrBuf, pos, 1 );
		if(StrBuf[ stpos ] == '\"') AddQuote = TRUE;
		CopyMem(StrBuf+pos, StrBuf+stpos, sti->NumChars-pos+1);
		pos -= stpos; sti->NumChars -= pos; sti->BufferPos -= pos;
		RemPartial = FALSE;
	}
	strcpy(WorkBuf, "  ");
	/* Use an external buffer in case original one is too short */
	strcpy(WorkBuf+2, path); path = WorkBuf + 2;
	if(compl != NULL) AddPart(WorkBuf+2, compl, MAX_COM);
	compl = (STRPTR) strchr(WorkBuf+2, 0) - 1;
	if( *compl == '/' || *compl == ':' ) compl = NULL;
	if(AddQuote || strchr(WorkBuf+2, ' ')) {
		WorkBuf[1]='\"'; path--;
		/* Do not add quote if added part is a directory */
		if( compl != NULL ) strcpy(compl+1, "\"");
	}
	if(compl != NULL) strcat(compl, " ");
	{
		UWORD len = sti->BufferPos;
		if(!AddQuote && len > 0 && StrBuf[len-1] != ' ') path--;
		len = strlen( path );
		if(len + sti->NumChars <= sti->MaxChars) {
			MemMove(StrBuf + sti->BufferPos, len, sti->MaxChars-sti->BufferPos+1);
			CopyMem(path, StrBuf + sti->BufferPos, len);
			sti->BufferPos += len; sti->NumChars += len;
			return TRUE;
		} /* else not enough place .... */
	}
	DisplayBeep( NULL );
	return FALSE;
}

/*** Select a path using ASL file requester ***/
void choose_path( struct StringInfo *sti )
{
	/* Try to open ASL library for choosing a file name */
	if(NULL == AslBase && NULL == (AslBase = (void *) OpenLibrary("asl.library", 36)) ||
	   NULL == FR      && NULL == (FR = (void *) AllocAslRequest(ASL_FileRequest,NULL)))
	{
		AvertUser( ErrMsg(NO_ASLREQ) );
		return;
	}
	FileTags[1]  = (ULONG) window;
	FileTags[15] = (ULONG) GetPath(StrBuf, sti->BufferPos);
	FileTags[17] = (ULONG) SplitPath((STRPTR) FileTags[15]);
	FileTags[21] = (InfoFlags & CHECKED ? FALSE : TRUE);

	if( AslRequest( FR, FileTags ) )
	{
		RemPartial = TRUE;
		/* Has user multi-selected some files? */
		if (FR->rf_NumArgs)
		{
			struct WBArg * args = FR->fr_ArgList;
			UWORD          nb   = FR->rf_NumArgs;
			while( nb && AddPath(sti, FR->fr_Drawer, args->wa_Name) ) nb--, args++;
		}
		else /* No multi-select, just get what's in text gadgets */
		{
			AddPath(sti, FR->fr_Drawer, FR->fr_File);
		}
	}
	if( FileTags[15] ) FreeVec( FileTags[15] );
}

/**********************************************
**				Locale.library support 				**
**********************************************/

/* All strings affected */
extern struct NewMenu newmenu[];

APTR catalog = NULL;

/*** Translate all messages of the application ***/
void TranslateStrings( void )
{
	if(catalog = (void *) OpenCatalogA(NULL,APPNAME ".catalog",NULL))
	{
		WORD n;
		FileTags[5] = (ULONG) GetCatalogStr(catalog, MSG_ASLTITLE, FileTags[5]);

		{	STRPTR * msgs;
			for(n = 0, msgs = GadTxt; n < 14; n++, msgs++)
				*msgs = (STRPTR) GetCatalogStr(catalog, MSG_EXEC_TITLE + n, *msgs);
		}
		{	struct NewMenu * nm;
			for(n = 0, nm = newmenu; n < 9; nm++)
				if(nm->nm_Label != (STRPTR) NM_BARLABEL)
					nm->nm_Label  = (STRPTR) GetCatalogStr(catalog, MSG_MAINTITLE + n++, nm->nm_Label);
		}
	}
}

/**********************************************
**			Preference file management 			**
**********************************************/

STRPTR PrefsFile = DEF_CONFIG;
STRPTR PrefsBuf  = NULL;
UWORD  PrefsLen  = 0;

#define	rbs		((ReviewBuf *)PrefsBuf)

BOOL MemCmp(STRPTR src, STRPTR src2, UWORD len)
{
	for ( ; len && *src == *src2; len--, src++, src2++);
	return len != 0;
}

void save_prefs( ReviewBuf *rb )
{
	/* First, check if config has changed */
	if( PrefsBuf == NULL || PrefsLen != rb->Usage ||
	    MemCmp(PrefsBuf, rb->Buffer, PrefsLen) )
	{
		APTR fh;
		if(fh = (APTR) Open(PrefsFile, MODE_NEWFILE))
		{
			ConvChar(rb->Buffer, rb->Usage, 0, '\n');
			Write(fh,rb->Buffer, rb->Usage-1);
			ConvChar(rb->Buffer, rb->Usage, '\n', 0);
			Close(fh);
		}
		else Fault(IoErr(), PrefsFile, WorkBuf, sizeof(WorkBuf)),
		     AvertUser( WorkBuf );
	}
}


void load_prefs( ReviewBuf *rb, STRPTR file )
{
	struct FileInfoBlock *fib;
	APTR fh;

	if(file != NULL) PrefsFile = file;
	InitReview( rb, NULL );

	if(fib = (void *) AllocDosObject(DOS_FIB, NULL))
	{
		if(fh = (APTR) Open(PrefsFile, MODE_OLDFILE))
		{
			ExamineFH(fh, fib);
			if(PrefsBuf = (STRPTR) AllocVec(PrefsLen = fib->fib_Size+1, 0))
			{
				PrefsBuf[ Read(fh, PrefsBuf, fib->fib_Size) ] = 0;
				InitReview(rb, PrefsBuf);
			}
			Close(fh);
		}
		FreeDosObject(DOS_FIB, fib);
	}
}

void free_prefs( void )
{
	if(PrefsBuf != NULL) FreeVec(PrefsBuf);
}
