///////////////////////////////////////////////////////////////////////////////
//	FILENAME:	eTDocInfo.m
//	SUMMARY:	Implementation of eTNavinfo objects; stringTable I/O.
//	SUPERCLASS:	Object:HashTable:NXStringTable:eTDocInfo
//	PROTOCOLS:	None
//	AUTHOR:		Rohit Khare
//	COPYRIGHT:	(c) 1994 California Institure of Technology, eText Project
///////////////////////////////////////////////////////////////////////////////
//	IMPLEMENTATION
//		eTDocInfo is built atop NXStringTable, but implementation should be
//	completely private and abstracted away. 
///////////////////////////////////////////////////////////////////////////////
//	HISTORY
//	07/20/94:	Added LaTeX support.
//	07/10/94:	Added support for HTML header/trailer writing
//	01/10/94:	Created. See Actors/eTNavinfo
///////////////////////////////////////////////////////////////////////////////

#import "eTDocInfo.h"
#import "Navigator.h"

@implementation eTDocInfo

// 	char	path[MAXPATHLEN];
// 	BOOL	readOnly;
// 	long	docID;

// Accessor Methods
- (long) docID
	{return docID;}
- (NXAtom) docIDStr
	{return [self valueForStringKey:DOCID];}
- (NXAtom) title
	{return [self valueForStringKey:TITLE];}
- (NXAtom) author
	{return [self valueForStringKey:AUTHOR];}
- (NXAtom) agent
	{return [self valueForStringKey:AGENT];}
- (NXAtom) parent
	{return [self valueForStringKey:PARENT];}
- (NXAtom) peers
	{return [self valueForStringKey:PEERS];}
- (NXAtom) keywords
	{return [self valueForStringKey:KEYWORDS];}
- (NXAtom) rtfComments
	{return [self valueForStringKey:COMMENTS];}
	// ASCII-coded RTF source of comment field
- (STR) comments
{	// ASCII contents of comment field
	static char comments[1024];
	NXStream *stream;
	id theText = [NXApp sharedText];

	stream = NXOpenMemory([self valueForStringKey:COMMENTS],
			 strlen([self valueForStringKey:COMMENTS]), NX_READONLY);
	[theText readRichText:stream];
	NXClose(stream);
	[theText getSubstring:comments start:0 length:1023];
	comments[1023]=0;
	return comments;
}
- (NXAtom) valueOf: (NXAtom) theQuery
{
	NXAtom tmp = [self valueForStringKey:theQuery];
	return (tmp ? tmp : NXUniqueString(""));
}

// Dynamic Bits
- (STR)	path
	{return path;}
- (BOOL) readOnly
	{return readOnly = (BOOL) access(path, W_OK);}

// Setter Methods
- setPath:(const char *) newPath
{
	if ([self title] == NXUniqueString("Unsaved Doc")) {
		char tmp[MAXPATHLEN];	// C Programmer's Disease
		strncpy(tmp, rindex(newPath, '/')+1, MAXPATHLEN);
		*rindex(tmp, '.') = 0; 
		[self setTitle:tmp];
	}
	strncpy(path, newPath, MAXPATHLEN); 
	// how do we tell eTNavinfoUI its info has been invalidated?
	return self;
}
- setTitle:(const char *) newTitle
	{[self insertKey:TITLE value:NXUniqueString(newTitle)]; return self;}
- setAuthor:(const char *) newAuthor
	{[self insertKey:AUTHOR value:NXUniqueString(newAuthor)]; return self;}
- setAgent:(const char *) newAgent
	{[self insertKey:AGENT value:NXUniqueString(newAgent)]; return self;}
- setParent:(const char *) newParent
	// ASCII-coded docID of parent etNavinfo
	{[self insertKey:PARENT value:NXUniqueString(newParent)]; return self;}
- setPeers:(const char *) newPeers
	// ASCII-coded, space between each docID
	{[self insertKey:PEERS value:NXUniqueString(newPeers)]; return self;}
- setKeywords:(const char *) newKeywords
	{[self insertKey:KEYWORDS value:NXUniqueString(newKeywords)]; return self;}
- setComments:(const char *) newComments
	// ASCII-coded RTF source of comment field
	{[self insertKey:COMMENTS value:NXUniqueString(newComments)]; return self;}
- bindKey:(const char *) newKey to: (const char *) newVal
	{[self insertKey:NXUniqueString(newKey) value:NXUniqueString(newVal)];
	 return self;}

// Searching Methods
- (BOOL) searchFor:(NXAtom) regex in:(NXAtom) field	// field == "*" means any
{	// Note that we search the RTF-coded comments field
	// -- not the ascii-filtered version
	BOOL found = NO;
	
	//special-case speed-up
	// REWRITE REST OF CODE TO USE 2 RE_COMP's RATHER THAN !recmp
	if (re_comp(regex))
		return NO;
	if ((field == ANYFIELD) &&
		(re_exec([self valueForStringKey:TITLE]) || 
		 re_exec([self valueForStringKey:PARENT]) ||
		 re_exec([self valueForStringKey:KEYWORDS]) ||
		 re_exec([self valueForStringKey:DOCID]) ||
		 re_exec([self valueForStringKey:AUTHOR]) ||
		 re_exec([self valueForStringKey:AGENT]) ||
		 re_exec([self valueForStringKey:PEERS]) ||
		 re_exec([self valueForStringKey:COMMENTS])))
		return YES;
	if (!recmp(field, DOCID))
		found |= !strcmp(regex, [self valueForStringKey:DOCID]);
	if (!found && !strcmp(field, PARENT))
		found |= !strcmp(regex, [self valueForStringKey:PARENT]);
	if (!found && !strcmp(field, TITLE))
		found |= !strcmp(regex, [self valueForStringKey:TITLE]);
	if (!found && !strcmp(field, AUTHOR))
		found |= !strcmp(regex, [self valueForStringKey:AUTHOR]);
	if (!found && !strcmp(field, AGENT))
		found |= !strcmp(regex, [self valueForStringKey:AGENT]);
	if (!found && !strcmp(field, PEERS))
		found |= !strcmp(regex, [self valueForStringKey:PEERS]);
	if (!found && !strcmp(field, KEYWORDS))
		found |= !strcmp(regex, [self valueForStringKey:KEYWORDS]);
	if (!found && !strcmp(field, COMMENTS))
		found |= !strcmp(regex, [self valueForStringKey:COMMENTS]);
	return found;
}

// Input/Output Methods
- init
{
	char	idStr[9];
	struct	passwd *p;
	[super init];
	path[0] = 0;
	docID = [NXApp uniqueID];
	sprintf(idStr,"%x", docID);
	[self insertKey:DOCID value:(char *)NXUniqueString(idStr)];
	[self insertKey:TITLE
			value:NXUniqueString("Unsaved Doc")];
	p = getpwuid(getuid());
	if (p->pw_name)
		[self insertKey:AUTHOR value:NXUniqueString(p->pw_gecos)];
	else [self insertKey:AUTHOR value:NXUniqueString("Author's Name")];
	[self insertKey:AGENT value:NXUniqueString("")];
	[self insertKey:PARENT value:NXUniqueString("")];
	[self insertKey:PEERS value:NXUniqueString("")];
	[self insertKey:KEYWORDS value:NXUniqueString("Index Terms")];
	[self insertKey:COMMENTS
		value:NXUniqueString("{\\rtf0\\ansi{\\fonttbl\\f1\\fnil Times-Roman;\\f2\\fswiss Helvetica;}\n\\margl40\n\\margr40\n\\pard\\tx520\\tx1060\\tx1600\\tx2120\\tx2660\\tx3200\\tx3720\\tx4260\\tx4800\\tx5320\\f1\\b0\\i\\ulnone\\fs24\\fc0\\cf0 Rich-Text \n\\b comments\n\\b0  about this document\n}\n")];
	readOnly = NO;
	[[NXApp navigator] registerNavinfo:self];
	return self;	
}

- free
{
	//sanity check
	char * foo = [self valueForStringKey:DOCID];
	if (foo) sscanf(foo, "%x", &docID);
	if (foo && [[NXApp navigator] navinfoFor:docID] != self)
		return [super free];
	return self;	
}
- initFromDoc:(const STR) pathname
{
	char	navinfoPath[MAXPATHLEN];
	
	[super init];
	strncpy(path, pathname, MAXPATHLEN);
	sprintf(navinfoPath, "%s/%s", path, NAVINFO);
	if (access(navinfoPath, R_OK | F_OK)) {
		// backward-compatibility with PR1 documents.
		sprintf(navinfoPath, "%s/etNavinfo", path);
	}
	if (access(navinfoPath, R_OK | F_OK)) {
		[NXApp delayedFree:self];
		return nil;
	}
	if (![self readFromFile:navinfoPath]) {
		[NXApp delayedFree:self];
		return nil;
	}
	sscanf([self valueForStringKey:DOCID], "%x", &docID);
	if ([[NXApp navigator] navinfoFor:docID]){
		[NXApp delayedFree:self];
		return [[NXApp navigator] navinfoFor:docID];
	}
	else [[NXApp navigator] registerNavinfo:self];
	return self;
}

- writeToDoc:(const STR) pathname	// Passing in null is ok; uses last path
{
	char	navinfoPath[MAXPATHLEN];
	
	sprintf(navinfoPath, "%s/%s", pathname ? pathname : path, NAVINFO);
	[self writeToFile:navinfoPath];
	return self;
}

- writeToDoc { return [self writeToDoc:NULL];}	// "Hidden" API

- writeHTMLHeader:(NXStream *)s
{
	char tmp[MAXPATHLEN];
	long dID;
	id	 dNI;
	id   NIlist;
	NXAtom peers;
	int	i;

	NXPrintf(s,"<HEAD>\n<TITLE>%v</TITLE>\n",[self title]);
	
	// does eTNavinfo have etfLink objects handy? -- no, it doesn't.
	strcpy(tmp, [self parent]);
	sscanf(tmp, "%x", &dID);
	dNI = [[NXApp navigator] navinfoFor:dID];
	if (dNI) {
		strcpy(tmp, [dNI path]);
		*rindex(tmp,'.')=0;
		NXPrintf(s,"<LINK HREF=\"../%s.htmd/TXT.html\" REL=\"Parent\" TITLE=\"%v\">\n", rindex(tmp,'/')+1,[dNI title]);
	}
	
	NIlist = [[NXApp navigator] query:[self docIDStr] field:PARENT];
	i = [NIlist count];
	for (;i;i--) {
		strcpy(tmp, [[NIlist objectAt:(i-1)] path]);
		*rindex(tmp,'.')=0;
		NXPrintf(s,"<LINK HREF=\"../%s.htmd/TXT.html\" REL=\"Child\" TITLE=\"%v\">\n", rindex(tmp,'/')+1,[[NIlist objectAt:(i-1)] title]);
	}

	// continue for "peer" documents?
	// for peers, we get a list of doc IDs that are space-separated
	i=0; peers = [self peers];
	while (peers[i] && (1 == sscanf(peers+i,"%x",&dID))) {
		dNI = [[NXApp navigator] navinfoFor:dID];
		if (dNI) {
			strcpy(tmp, [dNI path]);
			*rindex(tmp,'.')=0;
		NXPrintf(s,"<LINK HREF=\"../%s.htmd/TXT.html\" REL=\"Peer\" TITLE=\"%v\">\n", rindex(tmp,'/')+1,[dNI title]);
		}
		while(peers[i] && (peers[i] != ' ')) i++;  //skip ahead
	}
		
	NXPrintf(s,"</HEAD>\n");
	return self;
}

- writeHTMLTrailer:(NXStream *)s
{
	char tmp[MAXPATHLEN];
	long dID;
	id	 dNI;
	id   NIlist;
	NXAtom peers;
	int	i;

	// we should try to check the user's preferences here
	// we use a hole to tunnel through to a switch in the UI
	// if (trailerSw)
	// write HR, then parent, peers, then author, date, then
	NXPrintf(s,"\n\n<!-- TRAILER -->\n<HR>\n<DL COMPACT>\n");

	strcpy(tmp, [self parent]);
	sscanf(tmp, "%x", &dID);
	dNI = [[NXApp navigator] navinfoFor:dID];
	if (dNI) {
		strcpy(tmp, [dNI path]);
		*rindex(tmp,'.')=0;
		NXPrintf(s,"\n<DT>Parent:</DT>\n<DD><A HREF=\"../%s.htmd/TXT.html\">%v</A></DD>\n", rindex(tmp,'/')+1,[dNI title]);
	}
	
	NIlist = [[NXApp navigator] query:[self docIDStr] field:PARENT];
	i = [NIlist count];
	if ([NIlist count]) NXPrintf(s,"\n<DT>Children:</DT>\n");
	for (;i;i--) {
		strcpy(tmp, [[NIlist objectAt:(i-1)] path]);
		*rindex(tmp,'.')=0;
		NXPrintf(s,"\n<DD><A HREF=\"../%s.htmd/TXT.html\">%v</A></DD>\n", rindex(tmp,'/')+1,[[NIlist objectAt:(i-1)] title]);
	}
	
	// for peers, we get a list of doc IDs that are space-separated
	i=0; peers = [self peers];
	if(strlen(peers)) NXPrintf(s,"\n<DT>See Also:</DT>\n");;
	while (peers[i] && (1 == sscanf(peers+i,"%x",&dID))) {
		dNI = [[NXApp navigator] navinfoFor:dID];
		if (dNI) {
			strcpy(tmp, [dNI path]);
			*rindex(tmp,'.')=0;
		NXPrintf(s,"\n<DD><A HREF=\"../%s.htmd/TXT.html\">%v</A></DD>\n", rindex(tmp,'/')+1,[dNI title]);
		}
		while(peers[i] && (peers[i] != ' ')) i++;  //skip ahead
	}
	NXPrintf(s,"</DL>\n");
	
	NXPrintf(s,"<HR><A HREF=\"http://www.etext.caltech.edu/\">Created by the eText Engine, version %v</A><P>%v\n", [NXApp version], [NXApp date]);
	return self;
}

- writeLaTeXHeader:(NXStream *)s
{	
	NXPrintf(s, "\n\\author{%w}\n\\title{%w}\n\\maketitle\n",[self author], [self title]);
	return self;
}
@end