/* Controller.m -- Controller object for MailHelper application.
   Copyright 1991, Izumi Ohzawa and Manuel Alberto Ricart, All rights reserved.
   3-17-91  V1.0 (IO)
	* Starting project.
	* Some codes are borrowed from AtYourService NeXT demo application.
   4-6-91   V1.0 (IO)
	* Bug fix: fclose(fpin); was missing in method insertSignature.
   9-18-91  V1.1 (MAR)
	* Preferences panel and use of default database to store them.
	    Joint venture with Izumi? Perhaps.	Izumi sent me his code
	    for MailHelper.  I told him I implement some basic prefs for him.
   9-22-91  V1.1 (IO)
	* Defaults now written out as soon as OK button in Pref panel is pressed.
	* Signature insertion now adds a separator '---' within the code to
	    remove requirement of having it in signature file itself (to make MailHelper
	    conform with other programs which use .signature).
	* RTF signature insertion added.
	
  10-9-91  V1.2 (Mitsuhiro Kishimoto)
	* Japanese re-format routine: needed to handle mixed ASCII and 2-byte EUC code.
	    Original "Kin-soku" (forbidden line break) code was written
	    by Masatoshi Kurihara.
	    
  9-10-93 V1.3 Mike O'Neill (emo@mitre.org) added .rtfd support, grayed rtf selection, and 3.0 colorized icon and compiled FAT

   To implement:
   User selectable command keys for various functions.
*/


#import "Controller.h"
#import <appkit/appkit.h>


@implementation Controller

/* Modified Izumi's code to load the defaults at startup.
 * It also sets up the default values on the prefs panel.
*/
- appDidInit:sender
{
	static NXDefaultsVector MailHelperDefaults = {
		{WIDTH,"62"},
		{QUOTE,"> "},
		{SIGFILE,SIGFILENAME},
		{RTFSIGFILE,RTFSIGFILENAME},
		{RTFDSIGFILE,RTFDSIGFILENAME},
		{NULL}
	};
	myListener = [NXApp appListener];
	[myListener setServicesDelegate: self];
	
	NXRegisterDefaults(APPNAME, MailHelperDefaults);
	wrapWidth = atoi(NXGetDefaultValue(APPNAME, WIDTH));
	[gWrapWidth setIntValue:wrapWidth];
	[gWrapScroller setIntValue:wrapWidth];
	quoteString =(char *) NXGetDefaultValue(APPNAME, QUOTE);
	[gQuoteString setStringValue:quoteString];
	sigFile =(char *) NXGetDefaultValue(APPNAME, SIGFILE);
	[gSigFileName setStringValue:sigFile];
	rtfSigFile =(char *) NXGetDefaultValue(APPNAME, RTFSIGFILE);
	[gRtfSigFileName setStringValue:rtfSigFile];	
	rtfdSigFile =(char *) NXGetDefaultValue(APPNAME, RTFDSIGFILE);
	[gRtfdSigFileName setStringValue:rtfdSigFile];
//	[gRtfdText setGraphicsImportEnabled:YES];
	return self;
}

	

/* Brings up the Info panel.   Not done on startup because it's in a separate
 * interface file. Saves startup time for the user if we do this when they ask
 * for it, and not before.
 * "infoPanel" and "helpPanel" are defined in "Info.nib".
 */
- infoPanel:sender
{
    if( ! infoPanel ){
	[NXApp loadNibSection:"Info.nib" owner:self];
    }
    [infoPanel makeKeyAndOrderFront:sender];
    return self;    
}

// Records the prefsPanel values into the global variables
/* [izumi] Moved from delegate method -appWillTerminate:, because it is
 * not apparently called if user is logging out from Workspace --
 * this is how MailHelper gets terminated (MailHelper is almost never
 * exited via Quit from menu.).
*/
- prefsOK:sender
{
   char buf[20];
	wrapWidth = (int)[gWrapWidth intValue];
	quoteString = (char *)[gQuoteString stringValue];
	sigFile = (char *)[gSigFileName stringValue];	
	rtfSigFile = (char *)[gRtfSigFileName stringValue];	

	sprintf(buf, "%d", wrapWidth);
	NXWriteDefault(APPNAME, WIDTH, buf);
	NXWriteDefault(APPNAME, QUOTE, quoteString);
	NXWriteDefault(APPNAME, SIGFILE, sigFile);
	NXWriteDefault(APPNAME, RTFSIGFILE, rtfSigFile);
	return self;	
}

- prefsCancel:sender
{ 
	[gWrapWidth setIntValue:wrapWidth];
	[gWrapWidth display];
	[gWrapScroller setIntValue:wrapWidth];
	[gWrapScroller display];
	[gQuoteString setStringValue:quoteString];
	[gQuoteString display];
	[gSigFileName setStringValue:sigFile];
	[gSigFileName display];
	[gRtfSigFileName setStringValue:rtfSigFile];
	[gRtfSigFileName display];
	
	return self;
}
/*
 * Brings up the Help panel (as above)
 */
- helpPanel:sender
{
    if( ! helpPanel ){
	[NXApp loadNibSection:"Info.nib" owner:self];
    }
    [helpPanel makeKeyAndOrderFront:sender];
    return self;    

}



/* Reformats selected text so that it fits into a specified column width.
 * Alberto's Modifications:
 * initialized colLimit to wrapWidth global variable.
*/

- reformatSelection:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
char *data; 
int   length;
const char *const *types;
int   hasAscii, i;

	types = [pbid types];		/* get a list of pasteboard types */
	hasAscii = 0;
	for(i=0; !hasAscii && types[i]; i++)
	    if(!strcmp(types[i], NXAsciiPboardType)) hasAscii = 1;
	if(hasAscii)
	{
	    [pbid readType:NXAsciiPboardType data:&data length:&length];
	    if(data && length)
	    {
		char *returnData, *dataptr, *endptr;
		char *retptr;
		int col;
		unsigned char c1, c2;

		col = 0;
		returnData=malloc(length+10);
		dataptr = data;
		endptr = dataptr+length;
		retptr = returnData;

		while (dataptr < endptr) {
			c1 = *dataptr++;
			c2 = (iskanji (c1) ? *dataptr++ :  *dataptr);

			switch (c1) {
				case '\n':
					if ( c2 == '\n')
						col = 0;
					else if (iskanji (c2))
						goto skip_char;
					else
						c1 = ' ';
					break;
				case '\t':
					col = (col + 8) &~ 7;
					break;
				case '\b':
					col = col ? col - 1 : 0;
					break;
				default:
					col = col + (iskanji (c1) ? 2 : 1);
			}
			if (col > wrapWidth) {
			    if ( ! iskanji (c1)) {
				for (i = 1; 0 < col - i; i++) {
					c2 = *(retptr - i);
					if ( iskanji (c2) ) {
						dataptr -= i;
						retptr -= (i - 1);
						*retptr++ = '\n';
						col = 0;
						goto skip_char;
					} else if ( isspace (c2) ) {
						dataptr -= i;
						retptr -= i;
						*retptr++ = '\n';
						col = 0;
						goto skip_char;
					}
				}
				*retptr++ = '\n';
				col = 1;
			    } else if (iskanji (c1) &&  ! KINSOKU (c1, c2)) {
				*retptr++ = '\n';
				col = 2;
			    }				
			}
			*retptr++= c1;
			if (iskanji (c1))
				*retptr++= c2;
			else if (c1 == '\n' &&  c2 == '\n') {
				*retptr++= c2;
				dataptr++;
			}
	skip_char:
		;
		}
		*retptr++= '\n';
		*retptr = '\0';
		[pbid declareTypes:&NXAsciiPboardType num:1 owner:self];
		[pbid writeType:NXAsciiPboardType data:returnData length:strlen(returnData)];
		free(returnData);
	    } /* end if(data && length) */
	    else
		*errmsg = "Selection is empty.";
	}  /* end if(hasAscii) */
	else
	    *errmsg = "No ASCII text found in your selection.";
	return self;
}



/* Adds quotation marks "> " to the head of each line of selected text.
 * Alberto's Modification:
 * Only changed the definition of quotestr to point to the quoteString global.
*/

- quoteSelection:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
char *data; 
int   length;
const char *const *types;
char *quotestr = quoteString;
int   hasAscii, i, quotelen;

	quotelen = strlen(quotestr);
	types = [pbid types];		/* get a list of pasteboard types */
	hasAscii = 0;
	for(i=0; !hasAscii && types[i]; i++)
	    if(!strcmp(types[i], NXAsciiPboardType)) hasAscii = 1;
	if(hasAscii)
	{
	    [pbid readType:NXAsciiPboardType data:&data length:&length];
	    if(data && length)
	    {
	int numlines;
		char ch;
	char *returnData, *dataptr, *endptr, *outptr;

		numlines = 1;
		dataptr = data;		  /* ptr to original data */
		endptr = data+length;	  /* ptr to end of data */
		while(dataptr < endptr)	  /* count the number of lines */
		    if(*dataptr++ == '\n')
			numlines++;
		returnData=malloc(length+numlines*quotelen+10);
		dataptr = data;		  /* reinitialize ptr to orig data */
		outptr = returnData;
		for(i=0;i<quotelen;i++)
		   *outptr++ = quotestr[i];
		while(dataptr < endptr)
		{
		    if((ch = *dataptr++) == '\n')
		    {
			*outptr++ = ch;
			for(i=0;i<quotelen;i++)
			   *outptr++ = quotestr[i];
		    }
		    else
			*outptr++ = ch;
		}
		*outptr = '\n';
		*(outptr+1) = '\0';
		[pbid declareTypes:&NXAsciiPboardType num:1 owner:self];
		[pbid writeType:NXAsciiPboardType data:returnData length:strlen(returnData)];
		free(returnData);
	    } /* end if(data && length) */
	    else
		*errmsg = "Selection is empty.";
	}  /* end if(hasAscii) */
	else
	    *errmsg = "No ASCII text found in your selection.";
	return self;
}


// Adds the content of ~/.signature at the current cursor location.

- insertSignature:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
char *data, *dataptr;
char *signaturefile = sigFile;
char signaturepath[256];
int length, i;
struct stat filestat;
FILE *fpin;
    sprintf(signaturepath, "%s/%s", NXHomeDirectory(), signaturefile);
    if(stat(signaturepath, &filestat))
    {
	data = "";
	length = 0;
	*errmsg = "No signature file found in your home directory.";
    }
    else
    {
	length = filestat.st_size;
	if((fpin = fopen(signaturepath, "r")) != NULL)
	{
	    dataptr = data = malloc(length+NUMSIGDASH+10);  /* data remains unchanged */
	    for(i=0; i<NUMSIGDASH; i++)		/* Add '---' signature separator here */
		*dataptr++ = '-';
	    *dataptr++ = '\n';
	    fread((void *)dataptr, 1, length, fpin);
	    fclose(fpin);		/* This was missing in V1.0 release */
	    *(dataptr+length) = '\0';
	    [pbid declareTypes:&NXAsciiPboardType num:1 owner:self];
	    [pbid writeType:NXAsciiPboardType data:data length:strlen(data)];
	    free(data);
	}
	else
	    *errmsg = "Cannot open signature file.";
    }
    return self;
}

// Adds the content of ~/.signature.rtf at the current cursor location.
// RTF signature insertion method.
- insertRtfSignature:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
char *data;
char *signaturefile = rtfSigFile;
char signaturepath[256];
int length;
struct stat filestat;
FILE *fpin;
    sprintf(signaturepath, "%s/%s", NXHomeDirectory(), signaturefile);
    if(stat(signaturepath, &filestat))
    {
	data = "";
	length = 0;
	*errmsg = "No RTF signature file found in your home directory.";
    }
    else
    {
	length = filestat.st_size;
	if((fpin = fopen(signaturepath, "r")) != NULL)
	{
	    data = malloc(length+10);
	    fread((void *)data, 1, length, fpin);
	    fclose(fpin);
	    *(data+length) = '\0';
	    [pbid declareTypes:&NXRTFPboardType num:1 owner:self];
	    [pbid writeType:NXRTFPboardType data:data length:strlen(data)];
	    free(data);
	}
	else
	    *errmsg = "Cannot open RTF signature file.";
    }
    return self;
}


// Adds the content of ~/.signature.rtfd at the current cursor location.
// RTFD signature insertion method.
- insertRtfdSignature:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
char *data;
char *signaturefile = rtfdSigFile;
char signaturepath[256];
int length;
struct stat filestat;
    sprintf(signaturepath, "%s/%s", NXHomeDirectory(), signaturefile);
    if(stat(signaturepath, &filestat))
    {
	data = "";
	length = 0;
	*errmsg = "No RTFD signature found in your home directory.";
    }
    else
    {
    
	if(![gRtfdText openRTFDFrom:signaturepath])
	{
	    [tempText selectAll:self];	// This bit of code is necessary because
	    [tempText paste:self];	// while Edit, Mail and Text all support RTFD
	    [gRtfdText selectAll:self];	// there is as yet no proper NXAtom for the
	    [gRtfdText copy:self];	// pasteboard type. So I use the copy and
            [gRtfdText writeSelectionToPasteboard:pbid types:(NXAtom *)[[Pasteboard newName:NXGeneralPboard] types]];
	    [tempText selectAll:self];	// to put the text on the general pboard,
	    [tempText copy:self];	//from whence I then get the types!
	}
        else
	    *errmsg = "Cannot open RTFD signature.";
    }
    return self;
}

// quote an RTF selection by graying it out
- grayRTFSelection:(id)pbid userData:(const char *)udata error:(char **)errmsg
{
    NXAtom pbtype[] = {NXRTFPboardType, 0};
    [tempText selectAll:self];
    [tempText readSelectionFromPasteboard:pbid];
    [tempText setSelGray:NX_DKGRAY];
    [tempText writeSelectionToPasteboard:pbid types:pbtype];
    return self;
}


@end
