/* macstuff.c - macintosh interface routines for xlisp */

#ifndef __THINK__
  #include <qd.h>  
  #include <mem.h>
  #include <file.h>
  #include <font.h>
  #include <win.h>
  #include <menu.h>
  #include <dialog.h>
  #include <event.h>
  #include <pack.h>
#endif

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "xlisp.h" /* to get wrapup() prototype */

#define MACSTUFF
#include "macstuff.h"

#ifndef __THINK__
  #include <qdvars.h>	/* quickdraw globals */
#endif
MenuHandle myMenus[lastmenu];
SFReply loadfile;
#ifndef __THINK__
  OSType filetypes[] = { {'T','E','X','T'} };
#endif
#ifdef __THINK__
  OSType filetypes[] = {'T','E','X','T'};
#endif
Rect dragrect;
int x,y;

/* font information */
int tmargin,lmargin;
int xinc,yinc;

/* command window */
WindowRecord cwrecord;
WindowPtr cwindow;

/* graphics window */
WindowRecord gwrecord;
WindowPtr gwindow;

/* window mode */
int splitmode;

int cursortime,cursorstate;


/* screen buffer */
char screen[SCRH*SCRW],*topline,*curline;
int scrh,scrw;

char linebuf[LINEMAX+1],*lineptr;
int linepos[LINEMAX],linelen;

char charbuf[CHARBUFFMAX],*inptr,*outptr;
int charcnt;

void docommand(int themenu, int theitem);

extern GrafPtr thePort;

static void setupmenus(void);
static void scrclear(void);
static int linegetc(void);
static int lineputc(int ch);
static void TopMaintainCursor(void);
static void TopMaintainMenu(void);
static void CursorUpdate(void);
static void CursorOn(void);
static void CursorOff(void);
static void scrputc(int ch);
static void scrflush(void);
static void scrposition(int x, int y);
static void PtoC_StrCopy(register char *p1, register char *p2);
static void MenuAfterEditor(void);
static void doupdate(void);
static void nextline(char **pline);
static void scrollup(void);
static pascal Boolean filefilter(ParmBlkPtr pblock);
static pascal Boolean aboutfilter(DialogPtr theDialog, EventRecord *theEvent, short *itemHit);


void osinit(char *name)
{
    Rect screenrect;
    long cursp;
    char *p;
    
    /* initialize the toolbox */
    InitGraf(&thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(0L);
    InitCursor();

#ifdef OLDMACSTUFF
    /* reserve 32K more for the stack */
    asm { move.l A7,cursp(A6) }
    SetApplLimit(cursp - 32768L);
#endif    
    /* setup the menu bar */
    setupmenus();

    /* setup the bounds rectangle for dragging windows */
    SetRect(&dragrect,4,24,508,338);

    /* Setup stuff for editor */
    /* EditorSetUp(); */

    /* Create the graphics and control windows */
    gwindow = GetNewWindow(129,&gwrecord, (WindowPtr) -1L);
    cwindow = GetNewWindow(128,&cwrecord,(WindowPtr) -1L);
    

    /* establish the command window as the current port */
    SetPort(cwindow);
	ShowWindow(cwindow);
	SelectWindow(cwindow); 

    /* setup the font, size and writing mode for the command window */
    TextFont(monaco); TextSize(9); TextMode(srcCopy);
    
    /* setup command mode */
    splitmode = FALSE;
    scrh = SCRH;
    scrw = SCRW;
    scrclear();

    /* disable the Cursor */
    cursorstate = -1;

    /* setup the input ring buffer */
    inptr = outptr = charbuf;
    charcnt = 0;
    
    /* setup the Line editor */
    linelen = 0;

    /* lock the font in memory (need to write something first) */
    for (p = name; *p; )
		scrputc(*p++);
    scrputc('\r'); scrputc('\n');
    SetFontLock(TRUE);
}

void osfinish(void)
{
}

int osrand(int n)
{
    return (rand() % n);
}
#ifdef OLDMACSTUFF
int osgetc(FILE *fp)
{
    if (fp == stdin) return (linegetc());
    else return (getc(fp));
}

int osputc(int ch, FILE *fp)
{
    if (--xlsample <= 0) {	/* reduce oscheck overhead - RBD */
	    xlsample = SAMPLE;
	    oscheck();
    }
    if (fp == stdout) return (lineputc(ch));
    else return (putc(ch,fp));
}
#endif

int ostgetc(void)
{
    if (--xlsample <= 0) {	/* reduce oscheck overhead - RBD */
	    xlsample = SAMPLE;
	    oscheck();
    }
	return linegetc();
}


int ostputc(int ch)
{
    if (--xlsample <= 0) {	/* reduce oscheck overhead - RBD */
	    xlsample = SAMPLE;
	    oscheck();
    }
    return lineputc(ch);
}


int osaputc(int ch, FILE *fp)
{
    if (--xlsample <= 0) {	/* reduce oscheck overhead - RBD */
	    xlsample = SAMPLE;
	    oscheck();
    }
    return putc(ch, fp);
}


int osagetc(FILE *fp)
{
    if (--xlsample <= 0) {	/* reduce oscheck overhead - RBD */
	    xlsample = SAMPLE;
	    oscheck();
    }
    return getc(fp);
}

/* osbgetc - get a character from a binary file */
int osbgetc(FILE *fp) {return (getc(fp));}

/* osbputc - put a character to a binary file */
int osbputc(int ch, FILE *fp) {return (putc(ch,fp));}

/* osaopen - open an ascii file */
FILE *osaopen(char *name, char *mode)
{
    FILE *fp;
    fp = fopen(name,mode);
    return fp;
}

/* osbopen - open a binary file */
FILE *osbopen(char *name, char *mode)
{  char bmode[10];
    FILE *fp;
    strcpy(bmode,mode); strcat(bmode,"b");
    fp = fopen(name,bmode);
    return fp;
}

/* oserror - print an error message */
void oserror(char *msg) {printf("error: %s\n",msg);}

/* osclose - close a file */
int osclose(FILE *fp) {
    return (fclose(fp));}

/* osflush - flush the terminal input buffer */
void osflush(void) {}


static void setupmenus(void)
{
    int i;

    myMenus[applemenu]   = GetMenu(appleid); 
    AddResMenu(myMenus[applemenu],'DRVR');
    myMenus[filemenu]    = GetMenu(fileid);
    myMenus[editmenu]    = GetMenu(editid);
    myMenus[controlmenu] = GetMenu(controlid);
    /* myMenus[editormenu] = GetMenu(editorid); */
    for (i = 0; i < lastmenu; i++)
	   InsertMenu(myMenus[i],0);
    DrawMenuBar();
    MenuAfterEditor();
}

static int linegetc(void)
{
    int ch;

    if (linelen--) return (*lineptr++);
    linelen = 0;
    while ((ch = scrgetc()) != '\r')
	switch (ch) {
	case EOF:
	    return (linegetc());
	case '\010':
	    if (linelen > 0) {
		linelen--;
		while (x > linepos[linelen]) {
		    scrputc('\010'); scrputc(' '); scrputc('\010');
		}
	    }
	    break;
	default:
	    if (linelen < LINEMAX) { linebuf[linelen] = ch; linepos[linelen] = x; linelen++; }
	    scrputc(ch);
	    break;
	}
    scrputc('\r'); scrputc('\n');
    linebuf[linelen] = '\n';
    lineptr = linebuf;
    return (*lineptr++);
}

static int lineputc(int ch)
{
    if (ch == '\n') scrputc('\r');
    scrputc(ch);
    return (1);
}
 
static void scrclear(void)
{
    curline = screen;
    for (y = 0; y < SCRH; y++)
	for (x = 0; x < SCRW; x++)
	    *curline++ = ' ';
    topline = curline = screen;
    x = y = 0;
}

int scrgetc(void)
{
    CursorOn();
    while (charcnt == 0)
	oscheck();
    CursorOff();
    return (scrnextc());
}

int scrnextc(void)
{
    int ch;
    if (charcnt > 0) {
	ch = *outptr++; charcnt--;
	if (outptr >= &charbuf[CHARBUFFMAX])
	    outptr = charbuf;
    }
    else {
	charcnt = 0;
	ch = EOF;
    }
    return (ch);
}

void oscheck(void)
{
    WindowPtr whichwindow;
    EventRecord myevent;
    long sel;
    
    TopMaintainCursor();
    TopMaintainMenu();
    SystemTask();
    CursorUpdate();

    while (GetNextEvent(everyEvent,&myevent))
		switch (myevent.what) {
		    case mouseDown:
				switch (FindWindow(myevent.where,&whichwindow)) {
				    case inDesk:
							SysBeep(10);
						break;
				    case inMenuBar:
						if (sel = MenuSelect(myevent.where))
					    	docommand(sel >> 16, sel & 0xFFFF);
						break;
				    case inSysWindow:
						SystemClick(&myevent,whichwindow);
						break;
				    case inDrag:
						DragWindow(whichwindow,myevent.where,&dragrect);
						break;
				    case inGoAway:
				        if (TrackGoAway(whichwindow,myevent.where))
					   		wrapup();
						break;
				    case inGrow: 
				    case inContent:
				    	if (whichwindow != FrontWindow() /* && whichwindow != gwindow */)
					    	SelectWindow(whichwindow);
						break;
					default: ;
					}
				break;
		    case keyDown:
		    case autoKey:
				if (cwindow == FrontWindow()) {
				    if (myevent.modifiers & 0x100) {
						if (sel = MenuKey((char)myevent.message))
					   		docommand(sel >> 16, sel & 0xFFFF);
				    	}
				    else {
						if (charcnt < CHARBUFFMAX) {
				            *inptr++ = myevent.message & 0xFF; charcnt++;
					    	if (inptr >= &charbuf[CHARBUFFMAX])
							inptr = charbuf;
						}
				    }
				}
				break;
			case activateEvt:
				break;
		    case updateEvt:
				whichwindow = (WindowPtr) myevent.message;
				BeginUpdate(whichwindow);
				if (whichwindow == cwindow) 
				    doupdate();
				EndUpdate(whichwindow);
				break; 
/*				whichwindow = (WindowPtr) myevent.message; 
				if ((whichwindow == cwindow) || (whichwindow == gwindow)) {
						BeginUpdate(whichwindow);
						if (whichwindow == cwindow) 
						    doupdate();
						EndUpdate(whichwindow); 
					} 
				break; */
			}
}

static void TopMaintainCursor(void) { }

static void TopMaintainMenu(void)
{
/*************** how to maintain menus from Bugedit.c **************
	if ( !(*(WindowPeek)myWindow).visible || 
			!ours(FrontWindow()) ) {
		EnableItem( myMenus[fileM], fmNew );
		EnableItem( myMenus[fileM], fmOpen );
		DisableItem( myMenus[fileM], fmClose );
		DisableItem( myMenus[fileM], fmSave );
		DisableItem( myMenus[fileM], fmSaveAs );
		DisableItem( myMenus[fileM], fmRevert );
		DisableItem( myMenus[fileM], fmPrint );
		EnableItem( myMenus[editM], undoCommand );
		EnableItem( myMenus[editM], cutCommand );
		EnableItem( myMenus[editM], copyCommand );
		EnableItem( myMenus[editM], clearCommand );
*/
}


void CursorUpdate(void)
{
    if (cursorstate != -1)
	if (cursortime-- < 0) {
	    scrposition(x,y);
	    if (cursorstate) {
		DrawChar(' ');
		cursortime = TIMEOFF;
		cursorstate = 0;
	    }
	    else {
		DrawChar('_');
		cursortime = TIMEON;
		cursorstate = 1;
	    }
	}
}

static void CursorOn(void)
{
    cursorstate = cursortime = 0;
}

static void CursorOff(void)
{
    if (cursorstate == 1) {
	scrposition(x,y);
	DrawChar(' ');
    }
    cursorstate = -1;
}

static void scrputc(int ch)
{
    if (ch == '\r') x = 0;
    else if (ch == '\n') { nextline(&curline); if (y < scrh-1) y++; else scrollup(); }
    else if (ch == '\t') { scrputc(' '); while (x & 7) scrputc(' '); }
    else if (ch == '\010') { if (x) x--; }
    else if (ch >= 0x20 && ch < 0x7F) {
	scrposition(x,y);
	DrawChar(ch);
	curline[x] = ch;
	if (x < scrw-1) x++;
	else { x = 0; nextline(&curline); if (y < scrh-1) y++; else scrollup(); }
    }
}

static void scrflush(void)
{
    lineptr = linebuf; linebuf[0] = '\n'; linelen = 1;
    inptr = outptr = charbuf;
    charcnt = -1;
}

static void scrposition(int x, int y)
{
    MoveTo((x * 6) + 4,(y * 12) + 12);
}

#ifdef __THINK__
static pascal Boolean filefilter(ParmBlkPtr pblock)
{
    unsigned char *p; int len;
    p = pblock->fileParam.ioNamePtr; len = *p++ &0xFF;
/*    return (len >= 4 && strncmp(p+len-4,".lsp",4) == 0 ? 0 : 0x100); */
    /* return ((len >= 4 && strncmp(p+len-4,".lsp",4) == 0) ? 0 : -1); */
    if (len >= 4) {
        if (strncmp((char *) p+len-4, ".lsp", 4) == 0) {
    	    return 0;
    	} else {
    	    return -1;
    	}
    } else {
        return -1;
    }
}

static void PtoC_StrCopy(register char *p1, register char *p2 )
/* copies a pascal string from p1 to p2 */
{
	register int len;
	
	len = *p1++;
	while (--len>=0) *p2++=*p1++;
	*p2 = '\0';
}

#endif

#ifndef __THINK__
pascal filefilter(pblock)
parmblkptr pblock;
{
    char *p; int len;
    p = pblock->ioNamePtr; len = *p++ &0xFF;
    return (len >= 4 && strncmp(p+len-4,".lsp",4) == 0 ? 0 : 0x100);
}
#endif

static pascal Boolean aboutfilter(DialogPtr theDialog, EventRecord *theEvent, short *itemHit)
{
    return (theEvent->what == mouseDown ? 0x100 : 0);
}

extern WindowPtr myWindow;

void docommand(int themenu, int theitem)
{
    static Point p = {100,100};
    DialogRecord mydialog;
    Str255 name;
    GrafPtr gp, savePort;
    short n;
    int c;
    SFTypeList mytypes;
    
    mytypes[0] = 'TEXT';
    CursorOff();
    HiliteMenu(themenu);
	switch (themenu) {
	    case appleid:
			switch (theitem) {
				case 1:
				    GetNewDialog(129, &mydialog, (WindowPtr) -1L);
				    ModalDialog(aboutfilter, &n);
				    CloseDialog((DialogPtr) &mydialog);
				    break;
				default:
				    GetItem(myMenus[0], theitem, name);
				    GetPort(&gp); 
				    OpenDeskAcc(name);
				    SetPort(gp);
				    break;
				}
			break;
	    case fileid:
			switch (theitem) {
				case 1:	/* load */
				case 2:	/* load noisily */
			/*	    p.a.h = 100; p.a.v = 100; */
					/* The following couldn't open my files, so I changed it to
					 * match the call in my XLisp Release:Sources: directory
					 */
				    /* SFGetFile(p,"\p",filefilter,1,mytypes,0L,&loadfile); */
				    SFGetFile(p,"\P",filefilter,-1,mytypes,0L,&loadfile);
					if (loadfile.good) {
					HiliteMenu(0);
					SetVol(0L,loadfile.vRefNum);
#ifdef __THINK__					
				 	PtoC_StrCopy((char *) &loadfile.fName, (char *) name); 
					if (xlload((char *) name,1,(theitem == 1 ? false : true)))
					    scrflush();
#else
					if (xlload(loadfile.fName,1,(theitem == 1 ? 0 : 1)))
					    scrflush();
#endif
					else
					    xlabort("load error");
				    }
				 break;
			case 4:	/* quit */
			    wrapup();
				}
			break;
	    case editid:
			switch (theitem) {
				case 1:	/* undo */
				case 3:	/* cut */
				case 4:	/* copy */
				case 5:	/* paste */
				case 6:	/* clear */
				    SystemEdit(theitem-1);
				    break;
				}
			break;
	    case controlid:
			scrflush();
			HiliteMenu(0);
			switch (theitem) {
				case 1:	/* break */
				    xlbreak("user break", s_unbound);
				    break;
				case 2:	/* continue */
				    xlcontinue();
				    break;
				case 3:	/* clean-up error */
				    xlcleanup();
				    break;
				case 4:	/* Cancel input */
				    xlabort("input canceled");
				    break;
				case 5:	/* Top Level */
				    xltoplevel();
				    break;
				case 7:	/* split screen */
				    scrsplit(splitmode ? FALSE : TRUE);
				    break;
			}
			break;
#ifdef MINIEDITCODE
		case editorid:
			switch (theitem)
				case 1: /* fmTop */
  					HiliteMenu(0);
  					MenuBeforeEditor();
					GetPort(&savePort);
					SetPort(myWindow); 
					ShowWindow(myWindow);
					SelectWindow(myWindow);
					while(MainEditor());
					MenuAfterEditor();
					SetPort(savePort);
					ShowWindow(savePort);
					SelectWindow(savePort); 
					break;
#endif
				default: ;
			break;
	    }
    HiliteMenu(0);
    CursorOn();
}

#ifdef MINIEDITCODE
MenuBeforeEditor()
{
	SetItem(myMenus[editormenu], 1, "\pEditor To Bottom");
	
	DisableItem(myMenus[filemenu],ldLoad);
	DisableItem(myMenus[filemenu],ldLoadNosily);
	DisableItem(myMenus[filemenu],ldQuit);

	DisableItem(myMenus[controlmenu],clBreak);
	DisableItem(myMenus[controlmenu],clCont);
	DisableItem(myMenus[controlmenu],clClean);
	DisableItem(myMenus[controlmenu],clAbort);
	DisableItem(myMenus[controlmenu],clTop);
	DisableItem(myMenus[controlmenu],clSplit);

	EnableItem(myMenus[editmenu],undoCommand); 
	EnableItem(myMenus[editmenu],cutCommand); 
	EnableItem(myMenus[editmenu],copyCommand); 
	EnableItem(myMenus[editmenu],pasteCommand); 
	EnableItem(myMenus[editmenu],clearCommand);
	
/*	EnableItem(myMenus[editormenu],fmTop);  */
	EnableItem(myMenus[editormenu],fmNew);
	EnableItem(myMenus[editormenu],fmOpen);
	EnableItem(myMenus[editormenu],fmClose);
	EnableItem(myMenus[editormenu],fmSave);
	EnableItem(myMenus[editormenu],fmSaveAs);
	EnableItem(myMenus[editormenu],fmRevert);
	EnableItem(myMenus[editormenu],fmPageSetUp);
	EnableItem(myMenus[editormenu],fmPrint);
}
#endif

static void MenuAfterEditor(void)
{
	/* SetItem(myMenus[editormenu], 1, "\pEditor To Top"); */
	
	EnableItem(myMenus[filemenu],ldLoad);
	EnableItem(myMenus[filemenu],ldLoadNosily);
	EnableItem(myMenus[filemenu],ldQuit);

	EnableItem(myMenus[controlmenu],clBreak);
	EnableItem(myMenus[controlmenu],clCont);
	EnableItem(myMenus[controlmenu],clClean);
	EnableItem(myMenus[controlmenu],clAbort);
	EnableItem(myMenus[controlmenu],clTop);
	EnableItem(myMenus[controlmenu],clSplit);

	DisableItem(myMenus[editmenu],undoCommand);
	DisableItem(myMenus[editmenu],cutCommand); 
	DisableItem(myMenus[editmenu],copyCommand); 
	DisableItem(myMenus[editmenu],pasteCommand); 
	DisableItem(myMenus[editmenu],clearCommand);
	
/*	DisableItem(myMenus[editormenu],fmTop); */
#ifdef MINIEDITCODE
	DisableItem(myMenus[editormenu],fmNew);
	DisableItem(myMenus[editormenu],fmOpen);
	DisableItem(myMenus[editormenu],fmClose);
	DisableItem(myMenus[editormenu],fmSave);
	DisableItem(myMenus[editormenu],fmSaveAs);
	DisableItem(myMenus[editormenu],fmRevert);
	DisableItem(myMenus[editormenu],fmPageSetUp);
	DisableItem(myMenus[editormenu],fmPrint);
#endif
}

void scrsplit(int split)
{
    ShowHide(cwindow,0);
    if (split) {
	CheckItem(myMenus[controlmenu],7,1);
	scrh = SSCRH; scrw = SSCRW;
	scrclear();
	MoveWindow(cwindow,SHORIZONTAL,SVERTICAL,1);
	SizeWindow(cwindow,SWIDTH,SHEIGHT,1);
	EraseRect(&cwindow->portRect);
	ShowHide(gwindow,1);
    }
    else {
	CheckItem(myMenus[controlmenu],7,0);
	scrh = NSCRH; scrw = NSCRW;
	scrclear();
	MoveWindow(cwindow,NHORIZONTAL,NVERTICAL,1);
	SizeWindow(cwindow,NWIDTH,NHEIGHT,1);
	EraseRect(&cwindow->portRect);
	ShowHide(gwindow,0);
    }
    ShowHide(cwindow,1);
    splitmode = split;
}


void SetupScreen(void)
{
    FontInfo info;
    Rect *pRect;

    /* get font information */
    GetFontInfo(&info);

    /* compute the top and bottom margins */
    tmargin = TextMargin + info.ascent;
    lmargin = TextMargin;

    /* compute the x and y increments */
    xinc = info.widMax;
    yinc = info.ascent + info.descent + info.leading;

    /* compute the character dimensions of the screen */
    pRect = &cwindow->portRect;
    scrh = (pRect->bottom - (2 * TextMargin) - (SBarWidth - 1)) / yinc;
    if (scrh > SCRH) scrh = SCRH;
    scrw = (pRect->right - (2 * TextMargin) - (SBarWidth - 1)) / xinc;
    if (scrw > SCRW) scrw = SCRW;
    
    /* clear the screen */
    scrclear();
}


static void doupdate(void)
{
    char *Line; int y;
    Line = topline;
    for (y = 0; y < scrh; y++) {
	scrposition(0,y);
	DrawText(Line,0,scrw);
	nextline(&Line);
    }
}

static void nextline(char **pline)
{
    if ((*pline += SCRW) >= &screen[SCRH*SCRW]) *pline = screen;
}

static void scrollup(void)
{
    RgnHandle updateRgn;
    int x;
    updateRgn = NewRgn();
    ScrollRect(&cwindow->portRect,0,-12,updateRgn);
    DisposeRgn(updateRgn);
    for (x = 0; x < SCRW; x++)
	topline[x] = ' ';
    nextline(&topline);
}
