
/*
 *  MAIN.C
 */

#include "defs.h"

#define MSTLEN	64	/*  # of forward referenced articles	*/

char	User[32];
NGroup	*GBase;
NGroup	*CurGroup;
void	*GrpDisp;
void	*HelpDisp;
void	*DataDisp;
void	*ScanDisp;	/*  subject scan display    */
char	*GroupFile = "T:DNews_Groups";
char	*HelpFile = "T:DNews_Help";
char	TmpBuf[TMPBSIZE];

long	MStack[MSTLEN];
short	Mi;
short	Mx;
short	ShowAllGroups = 0;

#ifndef LATTICE
void *GfxBase;
void *IntuitionBase;
#endif

Prototype char *FileForArticle(NGroup *, int);
Prototype void SetGroup(NGroup *);
Prototype void SetCurNo(NGroup *, int);
Prototype int NumUnreadArticles(NGroup *);

Local NGroup *FindGroupByIndex(int);
Local void HandleKey(int);
Local void UpdateGroupList(char *);
Local void CreateHelpFile();

Local char *TitleForArticle(NGroup *);

IDENT(".03");

int
brk()
{
    return(0);
}

void
closlib()
{
    if (GfxBase) {
	CloseLibrary(GfxBase);
	GfxBase = NULL;
    }
    if (IntuitionBase) {
	CloseLibrary(IntuitionBase);
	IntuitionBase = NULL;
    }
}
main(ac, av)
char **av;
{
    onbreak(brk);
    atexit(closlib);

    IntuitionBase = OpenLibrary("intuition.library", 0);
    GfxBase = OpenLibrary("graphics.library", 0);
    if (IntuitionBase == NULL || GfxBase == NULL) {
	puts("unable to open libraries");
	exit(20);
    }

    FindUser(User, sizeof(User));   /*  who am I ?  */
    LoadGroups(User);               /*  load newsrc for user    */
    LoadKillFile(User);
    if (GBase == NULL) {
	CreateNewsrc(User);
	LoadGroups(User);
	if (GBase == NULL) {
	    printf("Unable to find any news!\n");
	    exit(1);
	}
    }

    LoadDisplayConfig();

    UpdateGroupList(GroupFile);
    GrpDisp = OpenBrowseDisplay(GroupFile, "h-help", 0);
    if (GrpDisp == NULL) {
	puts("unable to open window");
	exit(1);
    }

    /*
     *	Handle browse events
     */

    {
	void *disp;
	int row;
	int col;
	NGroup *grp;

	while (WaitBrowseEvent(&disp, &row, &col)) {
	    if (disp == GrpDisp) {      /*  handle main group list  */
		if (row == -1) {        /*  close window    */
		    if (col == -1)
			break;
		    HandleKey(col);
		    continue;
		}
		if (grp = FindGroupByIndex(row))
		    SetGroup(grp);
		continue;
	    }
	    if (disp == HelpDisp) {
		if (row == -1) {
		    if (col == -1) {
			CloseBrowseDisplay(HelpDisp);
			HelpDisp = NULL;
		    }
		}
		continue;
	    }
	    if (disp == DataDisp) {
		if (row == -1) {
		    if (col == -1) {
			CloseBrowseDisplay(DataDisp);
			DataDisp = NULL;
			SetGroup(NULL);
			continue;
		    }
		    HandleKey(col);
		    continue;
		}
	    }
	}
    }

    if (GrpDisp)
	CloseBrowseDisplay(GrpDisp);
    if (HelpDisp)
	CloseBrowseDisplay(HelpDisp);
    if (DataDisp)
	CloseBrowseDisplay(DataDisp);
    if (ScanDisp)
	CloseBrowseDisplay(ScanDisp);
    SaveKillFile(User);
    UnloadGroups(User);
    SaveDisplayConfig();
    remove(GroupFile);
    return(0);
}

static void
UpdateGroupList(file)
char *file;
{
    FILE *fi;
    NGroup *grp;

    fi = fopen(file, "w");
    if (fi == NULL)
	return;

    for (grp = GBase; grp; grp = grp->Next) {
	long unread;

	if (grp->Enabled == 0) {
	    grp->Unread = 0;
	    continue;
	}
	grp->Unread = NumUnreadArticles(grp);
	if (grp->Unread > 0 || ShowAllGroups)
	    fprintf(fi, "%-3d %-20s\n", NumUnreadArticles(grp), grp->Name);
    }
    fclose(fi);
}

static void
HandleKey(key)
{
    NGroup *grp = CurGroup;
    int n;

    for (;;) {
	if (key != 'm' && key != 'M') {
	    Mi = 0;
	    Mx = 0;
	}

	switch(key) {
	case 'A':
	    ShowAllGroups = ~ShowAllGroups;
	    SetGroup(NULL);
	    break;
	case 'h':
	    if (HelpDisp == NULL) {
		CreateHelpFile();
		HelpDisp = OpenBrowseDisplay(HelpFile, "HELP", 2);
	    }
	    break;
	case 'v':       /*  first unread article    */
	    if (grp) {
		n = NextNotInRange(grp->RList, 0);
		if (n >= grp->MaxNo + 1)
		    n = grp->MaxNo;
		grp->CurNo = n - 1;
		/* SetCurNo(grp, n - 1); */
		key = 's';
		continue;
	    }
	    break;
	case 'M':       /*  next reference auto-rewind          */
	case 'm':       /*  next reference                      */
	    /*
	     *	Find an article that references this one.
	     */
	    if (grp->CurNo < grp->MaxNo)
		RangeAdd(&grp->RList, grp->CurNo, grp->CurNo);

	    if (grp) {
		char *ref;
		short alloced = 0;

		if (Mi || Mx == 0) {
		    ref = NewsMessageIdOf(grp, grp->CurNo);
		} else {
		    short i;
		    char *tmp;

		    ref = NULL;
		    if (tmp = ReferenceLineOf(grp, grp->CurNo)) {
			for (i = Mx; i > 0; --i) {
			    ref = NextRef(&tmp);
			    if (ref == NULL)
				break;
			}
		    }
		    if (ref) {
			ref = strdup(ref);
			alloced = 1;
		    }
		}
		if (ref) {
		    for (n = NextNotInRange(grp->RList, grp->CurNo); n < grp->MaxNo; n = NextNotInRange(grp->RList, n)) {
			if (FindArticleReferencing(grp, ref, n))
			    break;
		    }
		    if (alloced)
			free(ref);
		    if (n >= grp->MaxNo) {      /*  no more references  */
			if (Mi) {               /*  try refs to previous articles on the same line */
			    n = MStack[--Mi];
			    grp->CurNo = n;
			    /* SetCurNo(grp, n); */
			    continue;		/*  w/ same key */
			}
			++Mx;			/*  try References: fields of original	*/
			continue;		/*  w/ same key */
		    }
		    if (Mi != MSTLEN)
			MStack[Mi++] = n;
		    SetCurNo(grp, n);
		} else {
		    Mx = 0;
		    Mi = 0;
		    if (key == 'M') {
			key = 'v';
			continue;
		    }
		}
	    }
	    break;
	case 'g':
	    if (grp) {
		FILE *fi;
		long artno = -1;
		if (fi = fopen("CON:0/0/200/50/Article?", "w+")) {
		    fprintf(fi, ": ");
		    fflush(fi);
		    if (fgets(TmpBuf, sizeof(TmpBuf), fi))
			artno = atoi(TmpBuf);
		    fclose(fi);
		}
		if (artno < 0)
		    artno = 0;
		if (artno > grp->MaxNo)
		    artno = grp->MaxNo;
		SetCurNo(grp, artno);
		RangeDel(&grp->RList, artno, artno);
	    }
	    break;
	case 'n':       /*  mark and go to next article         */
	case '.':
	case '>':
	    if (grp->CurNo < grp->MaxNo)
		RangeAdd(&grp->RList, grp->CurNo, grp->CurNo);
	case 's':       /*  do not mark and go to next article  */
	    grp->CurNo = NextNotInRange(grp->RList, grp->CurNo);
	    /* SetCurNo(grp, NextNotInRange(grp->RList, grp->CurNo)); */
	    if (grp->CurNo >= grp->MaxNo + 1) {
		grp->CurNo = grp->MaxNo;
	    } else {
		/*
		 *  skip past killed or non-existant articles, marking them as
		 *  we go
		 */

		if (ArticleRefKilled(grp, grp->CurNo)) {
		    RangeAdd(&grp->RList, grp->CurNo, grp->CurNo);
		    key = 's';
		    continue;
		}
	    }
	    SetCurNo(grp, grp->CurNo);
	    break;
	case 'b':
	case '<':
	    if (grp) {
		if (grp->CurNo == grp->PrevNo[0]) {
		    movmem(grp->PrevNo + 1, grp->PrevNo, sizeof(grp->PrevNo) - sizeof(grp->PrevNo[0]));
		    grp->PrevNo[arysize(grp->PrevNo)-1] = 0;
		}
		if (grp->PrevNo[0]) {
		    SetCurNo(grp, -grp->PrevNo[0]);
		    movmem(grp->PrevNo + 1, grp->PrevNo, sizeof(grp->PrevNo) - sizeof(grp->PrevNo[0]));
		    grp->PrevNo[arysize(grp->PrevNo)-1] = 0;
		} else if (grp->CurNo > 1) {
		    SetCurNo(grp, -(grp->CurNo - 1));
		}
		if (grp->CurNo)
		    RangeDel(&grp->RList, grp->CurNo, grp->CurNo);
	    }
	    break;
	case 'k':       /*  kill followups to this article and go to next article */
	    if (grp->CurNo < grp->MaxNo)
		RangeAdd(&grp->RList, grp->CurNo, grp->CurNo);
	    if (grp) {
		char *ref;
		if (ref = NewsMessageIdOf(grp, grp->CurNo))
		    AddKillFile(ref, 0, 0);
		if (ref = ReferenceLineOf(grp, grp->CurNo))
		    AddKillFile(ref, 0, 0);
		if (ref = SubjectOf(grp, grp->CurNo, 1))
		    AddKillFile(ref, 0, 1);
	    }
	    key = 'n';
	    continue;
	case 'K':       /*  kill followups for 30 days  */
	    if (grp->CurNo < grp->MaxNo)
		RangeAdd(&grp->RList, grp->CurNo, grp->CurNo);
	    if (grp) {
		char *ref;
		if (ref = NewsMessageIdOf(grp, grp->CurNo))
		    AddKillFile(ref, 30, 0);
		if (ref = ReferenceLineOf(grp, grp->CurNo))
		    AddKillFile(ref, 30, 0);
		if (ref = SubjectOf(grp, grp->CurNo, 1))
		    AddKillFile(ref, 30, 1);
	    }
	    key = 'n';
	    continue;
	case 'U':       /*  unkill followups            */
	    if (grp) {
		char *ref;
		if (ref = NewsMessageIdOf(grp, grp->CurNo))
		    RemKillFile(ref);
		if (ref = ReferenceLineOf(grp, grp->CurNo))
		    RemKillFile(ref);
	    }
	    break;
	case CTRL('g'): /*  goto specific article and mark as unread    */
	    break;
	case '=':       /*  bring up subject scan window                */
	    break;
	case '/':       /*  search in article               */
	    break;
	case CTRL('/'): /*  search in all remaining articles*/
	    break;
	case 'w':       /*  write article to file   */
	    if (grp) {
		char *fn;

		if (fn = GetFileName(0)) {
		    FILE *fi = fopen(FileForArticle(grp, grp->CurNo), "r");
		    FILE *fo = fopen(fn, "w");
		    long n;

		    if (fi && fo) {
			while ((n = fread(TmpBuf, 1, sizeof(TmpBuf), fi)) > 0)
			    fwrite(TmpBuf, 1, n, fo);
		    }
		    if (fi)
			fclose(fi);
		    if (fo)
			fclose(fo);
		}
	    }
	    break;
	case 'r':       /*  reply to sender         */
	case 'R':       /*  w/ text of current art  */
	    if (grp)
		ReplyArticle(key, grp);
	    break;
	case 'f':       /*  followup to newsgroup   */
	case 'F':       /*  w/ text of current art  */
	    if (grp)
		FollowUpArticle(key, grp);
	    break;
	case 'p':       /*  post article (asynch)   */
	    PostArticle(grp);       /*  grp can be NULL!    */
	    break;
	case 'N':
	    {
		if (grp)
		    grp = grp->Next;

		while (grp && NumUnreadArticles(grp) == 0)
		    grp = grp->Next;

		if (grp == NULL) {
		    grp = GBase;
		    while (grp && NumUnreadArticles(grp) == 0)
			grp = grp->Next;
		}
		if (grp == NULL)    /*  set to NULL?    */
		    grp = GBase;

		SetGroup(grp);
	    }
	    break;
	case 'P':
	    {
		if (grp)
		    grp = PrevGroup(grp);

		while (grp && NumUnreadArticles(grp) == 0)
		    grp = PrevGroup(grp);

		if (grp == NULL) {
		    grp = LastGroup();
		    while (grp && NumUnreadArticles(grp) == 0)
			grp = PrevGroup(grp);
		}
		if (grp == NULL)    /*  set to NULL?    */
		    grp = LastGroup();

		SetGroup(grp);
	    }
	    break;
	default:
	    break;
	}
	break;
    }
    if (DataDisp && grp && (grp->Flags & GROUPF_REDISPLAY)) {
	grp->Flags &= ~GROUPF_REDISPLAY;
	ChangeBrowseDisplay(DataDisp, FileForArticle(grp, grp->CurNo), TitleForArticle(grp));
    }
}

static NGroup *
FindGroupByIndex(i)
{
    NGroup *grp;


    for (grp = GBase; grp && i > 0; grp = grp->Next) {
	if (grp->Enabled == 0)
	    continue;
	if (grp->Unread > 0 || ShowAllGroups)
	    --i;
    }
    return(grp);
}


char *
FileForArticle(grp, artno)
NGroup *grp;
{
    static char buf[256];

    sprintf(buf, "%s/%d", grp->Name, artno);

    return(MakeConfigPath(UUNEWS, buf));
}

void
CreateHelpFile()
{
    FILE *fi;
    if (fi = fopen(HelpFile, "w")) {
	fputs(
	    "use mouse buttons to select newsgroup\n"
	    "\n"
	    "h  -help\n"
	    "v  -first unread article (usually after m's)\n"
	    "m  -mark article read and follow references, stop at end\n"
	    "M  -mark article read and follow references, 'v' at end\n"
	    "n  -mark article read and goto next\n"
	    "s  -do not mark and goto next unread\n"
	    "b  -backup to previously read article\n"
	    "k  -kill refs to article (this session only)\n"
	    "K  -kill refs to article for 30 days\n"
	    "U  -unkill refs to article\n"
	    "w  -write article to file\n"
	    "r  -reply to sender of article\n"
	    "R  -reply to sender, include article contents\n"
	    "f  -followup to newsgroup\n"
	    "F  -followup to newsgroup, include article contents\n"
	    "p  -post a new article\n"
	    "N  -NEXT GROUP\n"
	    "P  -PREV GROUP\n"

	    "4  -left half of article\n"
	    "6  -right half of article\n"
	    "8  -page up\n"
	    "2  -page down (SPACE also pages down)\n"
	    "3  -bottom of article\n"
	    "9  -top of article\n"

	    "   -(space) page down\n"
	    "CR -(return) two lines down\n"

	    "\n", fi
	);
	fclose(fi);
    }
}

char *
TitleForArticle(grp)
NGroup *grp;
{
    char *fname = FileForArticle(grp, grp->CurNo);
    char *ptr;

    ptr = fname + strlen(fname);

    while (ptr > fname && *ptr != '/')
	--ptr;
    --ptr;
    while (ptr >= fname && *ptr != '/')
	--ptr;
    ++ptr;

    sprintf(TmpBuf, "%s (%d)", ptr, grp->MaxNo - 1);
    return(TmpBuf);
}


void
SetGroup(grp)
NGroup *grp;
{
    if (CurGroup)
	ClearGroupCache(CurGroup);
    if (grp) {
	CurGroup = grp;
	Mi = 0;
	Mx = 0;
	if (DataDisp)
	    ChangeBrowseDisplay(DataDisp, FileForArticle(grp, grp->CurNo), TitleForArticle(grp));
	else
	    DataDisp = OpenBrowseDisplay(FileForArticle(grp, grp->CurNo), TitleForArticle(grp), 1);
    }
    /*
     *	ChangeBrowseDisplay() closes file so we can update it, only openning
     *	it again when actually scanned on a display refresh.
     */
    ChangeBrowseDisplay(GrpDisp,GroupFile,"h-help");
    UpdateGroupList(GroupFile);
    RefreshBrowseDisplay(GrpDisp);
}

void
SetCurNo(grp, n)
NGroup *grp;
int n;
{
    short addbkup = 1;
    int oldno = grp->CurNo;

    if (n < 0) {
	n = -n;
	addbkup = 0;
    }

    grp->Flags |= GROUPF_REDISPLAY;
    grp->CurNo = n;

    if (addbkup && oldno && grp->PrevNo[0] != oldno) {
	movmem(grp->PrevNo, grp->PrevNo + 1, sizeof(grp->PrevNo) - sizeof(grp->PrevNo[0]));
	grp->PrevNo[0] = oldno;
    }
}

int
NumUnreadArticles(grp)
NGroup *grp;
{
    int n = grp->MaxNo - 1;

    if (grp) {
	Range *ran;

	for (ran = grp->RList; ran; ran = ran->Next)
	    n -= ran->ENo - ran->SNo + 1;
    }
    if (n < 0)
	n = 0;
    return(n);
}

