/* File: Text.m - (Interactive) Unix shell version of Text
 *
 * By: Gerben Wierda (gerben@rna.indiv.nluug.nl)
 * As inspired by Christopher Lane (lane@sumex-aim.stanford.edu)
 *
 * Date: 2 may 1993
 *
 * Modified:
 *	[17-Feb-96 TRH] Fix for Application option munching (like -NXHost).
 *	[12-Mar-96 TRH] add -V -H -A options; add EditMenu keyEquiv support.
 *	[24-Mar-96 TRH] add -m option; handle RTF files.
 *	    (BUG: cannot yet detect RTF piped in from stdin.)
 *
 * Copyright: 1993,1996 by R&A
 *
 * This program may be distributed without restriction.
 */

// A very long line to see if everyhting is displayed OK. A very long line to see if everyhting is displayed OK. A very long line to see if everyhting is displayed OK.

#define KEY_EQUIVALENTS \
	"terminate:",	'q',

#define USES_APPICON
#define USES_KEYEQUIV
#import "ShellPanel.h"

#define TIMEOUT 30

#define DEFAULTWIDTH	400
#define DEFAULTHEIGHT	400
#define DEFAULTX	150
#define DEFAULTY	750

#define USAGE "\
usage: %s [-F] [-m] [-t title] [-x xpos -y ypos] [-w width -h height] [-T seconds] [-V] [-H] file...\n"

#define HELP "\
options:\n\
 -F           order front\n\
 -m           use monospaced (fixed-pitch) font\n\
 -t title     set title (default: name of file)\n\
 -x xpos\n\
 -y ypos      set origin of window, in screen coordinates (default "STRINGIZE(DEFAULTX)"x"STRINGIZE(DEFAULTY)")\n\
 -w width\n\
 -h height    set size of window, in pixels (default "STRINGIZE(DEFAULTWIDTH)"x"STRINGIZE(DEFAULTHEIGHT)")\n\
 -T seconds   timeout in seconds, 0 means wait indefinitely (default "STRINGIZE(TIMEOUT)")\n\
 -V           print version string and exit\n\
 -H           this help\n\
"


@interface TextApp : Application
{
    int windowcount;
}

- initWindow;
- appDidInit:sender;

@end

@implementation TextApp : Application

- initWindow
{
    Window *window;
    NXRect windowrect;

    NXSetRect( &windowrect, DEFAULTX, DEFAULTY,
	       DEFAULTWIDTH, DEFAULTHEIGHT);
    window = [[Window alloc]
	      initContent:  &windowrect
	      style:	    NX_RESIZEBARSTYLE
	      backing:	    NX_BUFFERED
	      buttonMask:   NX_CLOSEBUTTONMASK
	      defer:	    NO];
    [window setContentView:[[ScrollView alloc] initFrame:&windowrect]];
    [[window contentView] setDocView:[[Text alloc] initFrame:&windowrect]];

    ++windowcount;

    return window;
}

- appDidInit:sender
{
    [[self appIcon] orderOut:self];

    return self;
}

@end

void timer(DPSTimedEntry teNumber, double now, int status) { exit(status); }

BOOL isRichTextStream(NXStream *stream)
{
    static const char magic[] = "{\\rtf0";
    char buf[sizeof magic -1];
    long n;

    /* This isn't really kosher because it knows about NXStream internals,
       but we'll use it anyway since it is mentioned in the documentation
       (GeneralRef/03_Common/Functions/CommonFunctions.rtf - NXSeek()). */
    if (!(stream->flags & NX_CANSEEK)) return NO;
    // XXX This also means that we cannot detect piped-in RTF.  Sigh...

    n = NXRead(stream, buf, sizeof buf);
    NXSeek(stream, -n, NX_FROMCURRENT);

    return (n == sizeof buf && strncmp(buf, magic, sizeof buf) == 0);
}


void main(int argc, char *argv[])
{
    BOOL orderFront = NO;
    const char *title = NULL;
    double timeout = TIMEOUT;
    BOOL monoFont = NO;
    const char *iconFile = NULL;
    int option, status = EXIT_SUCCESS;
    NXPoint topleft = { DEFAULTX, DEFAULTY };
    NXSize size = { DEFAULTWIDTH, DEFAULTHEIGHT };

    NXApp = [TextApp new];
    argc = NXArgc, argv = NXArgv;	// Application may munch options.

    while ((option = getopt(argc, argv, "Fc:t:x:y:w:h:X:Y:T:i:mVH")) != EOF)
    {
	switch (option)
	{
	// XXX What's `c' supposed to be doing?
	case 'F': orderFront = YES; break;
	case 't': title = optarg; break;
	case 'x': topleft.x = atof(optarg); break;
	case 'y': topleft.y = atof(optarg); break;
	case 'X': // backward compatibility.
	case 'w': size.width = atof(optarg); break;
	case 'Y': // backward compatibility.
	case 'h': size.height = atof(optarg); break;
	case 'T': timeout = atol(optarg); break;
	case 'm': monoFont = YES; break;
	case 'i': iconFile = optarg; break;
	case 'V': status = EXIT_VERSION; break;
	case 'H': status = EXIT_HELP; break;
	default : status = EXIT_USAGE;
	}
    }
    if (optind == argc) status = EXIT_USAGE;

    HandleUsageHelpVersion(status, USAGE, HELP);

    SetAppIconToFile(iconFile);
    ProvideKeyEquivalents();

    {
	DPSTimedEntry teNumber = NULL;
	NXStream *stream = NULL;
	id previousWindow = nil;
	float xdisplacement = 0.0;
	float ydisplacement = 0.0;

	if (monoFont)
	{
	    // Use default fixed-pitch font of default size.
	    [Text setDefaultFont:
	     [Font userFixedPitchFontOfSize:0 matrix:NX_FLIPPEDMATRIX]];
	}

	for (; optind < argc; optind++)
	{
	    BOOL isstdin = (strcmp( argv[optind], "-") == 0);

	    if (stream = (!isstdin ?
			  NXMapFile( argv[optind], NX_READWRITE) :
			  NXOpenFile(fileno( stdin), NX_READONLY)))
	    {
		NXRect windowrect, textrect;
		id window;
		id scrollview;
		id text;

		// XXX Most of this stuff should be moved to -initWindow...
		window = [NXApp initWindow];
		[window setDelegate:NXApp];
		scrollview = [window contentView];
		text = [scrollview docView];

		if (title == NULL && !isstdin)
		    [window setTitleAsFilename:argv[optind]];
		else
		    [window setTitle:(title? title : isstdin ? "<<stdin>>" : "")];

		[window displayBorder];
		[window setBackgroundGray:NX_LTGRAY];

		[window sizeWindow:size.width :size.height];
		[window moveTopLeftTo:topleft.x :topleft.y];

		if (xdisplacement != 0.0 ||
		    ydisplacement != 0.0)
		{
		    [window getFrame:&windowrect];
		    windowrect.origin.x += xdisplacement;
		    windowrect.origin.y += ydisplacement;
		    [window moveTo:windowrect.origin.x :windowrect.origin.y];
		}
		xdisplacement += 18.0;
		ydisplacement -= 18.0;

		[scrollview setVertScrollerRequired:YES];
		[scrollview setHorizScrollerRequired:NO];
		[scrollview getContentSize:&textrect.size];

		[text setHorizResizable:NO];
		[text setVertResizable:YES];
		[text setMinSize:&textrect.size];
		[text sizeTo:textrect.size.width :textrect.size.height];
		[text setOpaque:YES];
		[text setEditable:NO];
		if (isRichTextStream(stream))
		{
		    [text setMonoFont:NO];
		    [text readRichText:stream];
		}
		else
		{
		    [text readText:stream];
		    [text sizeToFit];
		}

		if (previousWindow)
		{
		    [window orderWindow:NX_BELOW
		     relativeTo:[previousWindow windowNum]];
		}
		else if (orderFront)
		{
		    [window orderFrontRegardless];
		}
		else
		{
		    [window orderFront:nil];
		}
		[window display];

		if (!isstdin)
		    NXCloseMemory( stream, NX_FREEBUFFER);
		else
		    NXClose( stream);

		previousWindow = window;
	    }
	}
	if (timeout)
	{
	    teNumber = DPSAddTimedEntry( timeout,
					 (DPSTimedEntryProc) &timer,
					 (void *) EXIT_FAILURE,
					 NX_BASETHRESHOLD);
	}

	[NXApp run];

	if(timeout) DPSRemoveTimedEntry(teNumber);
    }
    [NXApp free];

    exit(status);
}

@interface TextApp(WindowDelegate)
- windowWillClose:sender;
- windowDidResize:sender;
@end

@implementation TextApp(WindowDelegate)
- windowWillClose:sender
{
    if (--windowcount <= 0)
    {
	[NXApp terminate:nil];
    }
    return self;
}
- windowDidResize:sender
{
    NXRect textrect;

    [[sender contentView] getContentSize:&textrect.size];

    [[[sender contentView] docView] setMinSize:&textrect.size];
    [[[sender contentView] docView]
     sizeTo:textrect.size.width :textrect.size.height];
    [[[sender contentView] docView] sizeToFit];

//    [sender display];
    return self;
}
@end

@interface TextApp(TextDelegate)
@end

@implementation TextApp(TextDelegate)
@end
