/*	SCCS Id: @(#)macmain.c	3.1	92/12/04	*/
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

/* main.c - Mac NetHack */

#include "hack.h"

#include <OSUtils.h>
#include <files.h>
#include <Types.h>
#ifdef MAC_MPW32
#include <String.h>
#include <Strings.h>
#endif
#ifdef MAC_THINKC5
#include <pascal.h>
#endif
#include <Dialogs.h>
#include <Packages.h>
#include <ToolUtils.h>
#include <Resources.h>
#include <SysEqu.h>
#include <Errors.h>

#ifndef O_RDONLY
#include <fcntl.h>
#endif

int NDECL(main);
void NDECL(ListGUnloads);

static void NDECL(process_options);
static void NDECL(whoami);

extern char * PtoCstr ( unsigned char * ) ;
extern unsigned char * CtoPstr ( char * ) ;
void SetFrameItem ( DialogPtr , short , short ) ;

extern void NDECL ( finder_file_request ) ;
// void NDECL( askname ) ;

extern void NDECL ( InitMac ) ;

int
main ( void )
{
	register int fd;

	windowprocs = mac_procs ;
	InitMac ( ) ;

	hname = "Mac Hack" ;
	hackpid = getpid();

	initoptions();
	init_nhwindows();
	whoami();

	/*
	 * It seems you really want to play.
	 */
	setrandom();
	u.uhp = 1;	/* prevent RIP on early quits */

	process_options ( ) ;	/* emulate command line options */
	finder_file_request ( ) ;

#ifdef WIZARD
	if (wizard)
		Strcpy(plname, "wizard");
	else
#endif
	if(!*plname || !strncmp(plname, "player", 4)
		    || !strncmp(plname, "games", 4))
		askname();
	plnamesuffix();		/* strip suffix from name; calls askname() */
				/* again if suffix was whole name */
				/* accepts any suffix */

	Sprintf ( lock , "%d%s" , getuid ( ) , plname ) ;
	getlock ( ) ;

	/*
	 * Initialisation of the boundaries of the mazes
	 * Both boundaries have to be even.
	 */

	x_maze_max = COLNO-1;
	if (x_maze_max % 2)
		x_maze_max--;
	y_maze_max = ROWNO-1;
	if (y_maze_max % 2)
		y_maze_max--;

	/*
	 *  Initialize the vision system.  This must be before mklev() on a
	 *  new game or before a level restore on a saved game.
	 */
	vision_init();

	display_gamewindows();

	set_savefile_name();
	uncompress(SAVEF);

	if((fd = open_savefile()) >= 0 &&
	   /* if not up-to-date, quietly delete file via false condition */
	   (uptodate(fd) || delete_savefile())) {
#ifdef WIZARD
		/* Since wizard is actually flags.debug, restoring might
		 * overwrite it.
		 */
		boolean remember_wiz_mode = wizard;
#endif
#ifdef NEWS
		if(flags.news) display_file(NEWS, FALSE);
#endif
		pline("Restoring save file...");
		mark_synch();	/* flush output */
		if(!dorecover(fd))
			goto not_recovered;
#ifdef WIZARD
		if(!wizard && remember_wiz_mode) wizard = TRUE;
#endif
		pline("Hello %s, welcome back to NetHack!", plname);
		check_special_room(FALSE);

#ifdef EXPLORE_MODE
		if (discover)
			You("are in non-scoring discovery mode.");
#endif
#if defined(EXPLORE_MODE) || defined(WIZARD)
		if (discover || wizard) {
			if(yn("Do you want to keep the save file?") == 'n')
			    (void) delete_savefile();
			else {
			    compress(SAVEF);
			}
		}
#endif
		flags.move = 0;
	} else {
not_recovered:
		player_selection();
		newgame();
		/* give welcome message before pickup messages */
		pline("Hello %s, welcome to NetHack!", plname);
#ifdef EXPLORE_MODE
		if (discover)
			You("are in non-scoring discovery mode.");
#endif
		flags.move = 0;
		set_wear();
		pickup(1);
	}

	flags.moonphase = phase_of_the_moon();
	if(flags.moonphase == FULL_MOON) {
		You("are lucky!  Full moon tonight.");
		change_luck(1);
	} else if(flags.moonphase == NEW_MOON) {
		pline("Be careful!  New moon tonight.");
	}
	if(flags.friday13 = friday_13th()) {
		pline("Watch out!  Bad things can happen on Friday the 13th.");
		change_luck(-1);
	}

	initrack();

	UndimMenuBar ( ) ; /* Yes, this is the place for it (!) */
	
	attemptingto("proceed");
#if defined(MAC_MPW32) && !defined(MODEL_FAR)
	UnloadAllSegments();						/* Do this before naming residents */
	IsResident( (Ptr) display_nhwindow );		/* Sample resident segments */
	IsResident( (Ptr) rhack );
	IsResident( (Ptr) engr_at );
	IsResident( (Ptr) movemon );
	IsResident( (Ptr) attacktype ) ;
	IsResident( (Ptr) mac_get_nh_event ) ;
#endif
	moveloop();
	/*NOTREACHED*/
	return 0;
}


/*
 * This filter handles the movable-modal dialog
 *
 */
static pascal Boolean
DragFilter ( DialogPtr dp , EventRecord * event , short * item )
{
	WindowPtr wp ;
	short code ;
	Rect r ;

/*
 *	Handle shortcut keys
 *	enter, return -> OK
 *	clear, escape, period -> Cancel
 *	all others are handled default
 *
 */

	if ( event -> what == keyDown ) {

		char c = event -> message & 0xff ;
		unsigned char b = ( event -> message >> 8 ) & 0xff ;

		switch ( c ) {

		case 3 :	/* 3 == Enter */
		case 10 :	/* Newline */
		case 13 :	/* Return */
			* item = 1 ;
			return 1 ;

		case '.' :	/* Cmd-period - we allow only period */
		case 27 :	/* Escape */
			* item = 2 ;
			return 1 ;
		}

		switch ( b ) {

		case 0x4c :	/* Enter */
		case 0x24 :	/* Return */
			* item = 1 ;
			return 1 ;

		case 0x35 :	/* Escape */
		case 0x47 :	/* Clear */
			* item = 2 ;
			return 1 ;
		}

		return 0 ;
	}

/*
 *	OK, don't handle others
 *
 */

	if ( event -> what != mouseDown ) {

		return 0 ;
	}
	code = FindWindow ( event -> where , & wp ) ;
	if ( wp != dp || code != inDrag ) {

		return 0 ;
	}
	r = ( * GetGrayRgn ( ) ) -> rgnBBox ;
	InsetRect ( & r , 3 , 3 ) ;

	DragWindow ( wp , event -> where , & r ) ;
	SaveWindowPos ( wp ) ;

	event -> what = nullEvent ;
	return 1 ;
}


/*
 * plname is filled either by an option (-u Player  or  -uPlayer) or
 * explicitly (by being the wizard) or by askname.
 * It may still contain a suffix denoting pl_character.
 */
void
mac_askname(void) /* Code taken from getlin */
{
	ControlHandle	ctrl ;
	DialogPtr		promptDialog ;
	short			itemHit , type ;
	Rect			box ;
	Str255			pasStr ;

	/*
	** Set the query line as parameter text.
	*/

	ParamText ( "\PWho are you?" , "\p" , "\p" , "\p" ) ;

	promptDialog = GetNewDialog ( 130 , ( Ptr ) NULL , ( WindowPtr ) -1 ) ;
	ShowWindow ( promptDialog ) ;

	InitCursor ( ) ;
	SetFrameItem ( promptDialog , 6 , 1 ) ;
	do {

		ModalDialog ( ( ModalFilterProcPtr ) DragFilter , & itemHit ) ;

	} while ( ( itemHit != 1 ) && ( itemHit != 2 ) ) ;

	if ( itemHit == 1 ) {

		/*
		** Get the text from the text edit item.
		*/

		GetDItem ( promptDialog , 4 , & type , ( Handle * ) & ctrl , & box ) ;
		GetIText ( ( Handle ) ctrl , pasStr ) ;

		/*
		** Convert it to a 'C' string and copy it into the return value.
		*/

		PtoCstr ( pasStr ) ;
		strcpy ( plname , ( char * ) pasStr ) ;

	/*
	 * Special check for debugging here
	 *
	 */
#ifdef WIZARD
		if ( ! strcmp ( plname , WIZARD ) ) {

			flags . debug = 1 ;
		}
#endif

	} else {

		/*
		** Okay, we didn't want to run
		*/

	/*	* ( short * ) DSErrCode = dsBadLaunch ; */
		ExitToShell ( ) ;
	}

	DisposDialog ( promptDialog ) ;
}


static void
process_options(void)
{
	int argc = 0 ;
	char * foo [ ] = { "Mac Hack" , NULL } ;
	char * * argv = foo ;
	/*
	 * Process options.
	 */
	while(argc > 1 && argv[1][0] == '-'){
		argv++;
		argc--;
		switch(argv[0][1]){
#if defined(WIZARD) || defined(EXPLORE_MODE)
# ifndef EXPLORE_MODE
		case 'X':
# endif
		case 'D':
# ifdef WIZARD
			wizard = TRUE ;
			break ;
# endif
# ifdef EXPLORE_MODE
		case 'X':
			discover = TRUE;
# endif
			break;
#endif
#ifdef NEWS
		case 'n':
			flags.news = FALSE;
			break;
#endif
		case 'u':
			if(argv[0][2])
			  (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
			else if(argc > 1) {
			  argc--;
			  argv++;
			  (void) strncpy(plname, argv[0], sizeof(plname)-1);
			} else
				raw_print("Player name expected after -u");
			break;
		case 'I':
		case 'i':
			if (!strncmpi(argv[0]+1, "IBM", 3))
				switch_graphics(IBM_GRAPHICS);
			break;
	    /*  case 'D': */
		case 'd':
			if (!strncmpi(argv[0]+1, "DEC", 3))
				switch_graphics(DEC_GRAPHICS);
			break;
		default:
			/* allow -T for Tourist, etc. */
			(void) strncpy(pl_character, argv[0]+1,
				sizeof(pl_character)-1);

			/* raw_print("Unknown option: %s", *argv); */
		}
	}
}


static void
whoami ( void )
{
	/*TODO*/
	donull ( ) ;
}


/*------------------- UnloadAllSegments and support stuff --------------------------*/
/* Derived from MacApp source */

typedef Handle **HandleListHandle;
typedef Boolean **BoolListHandle;
typedef short *ShortPtr, **ShortHandle;

short FDECL(GetSegNumber,(ShortPtr));
void FDECL(InitSegMgmt,(void *));
pascal long NDECL(GetA5);
pascal short NDECL(GetCurJTOffset);
void NDECL(UnloadAllSegments);
void FDECL(IsResident,(void *));
void FDECL(NotResident, (void *));

short 			 pMaxSegNum = 0,		/* Highest segment number */
	  			 gCodeRefNum;			/* rsrc refnum of the application */
HandleListHandle gCodeSegs;				/* List of code seg handles */
BoolListHandle   gIsResidentSeg;		/* Resident flags */

#define kLoaded   0x4EF9				/* if loaded then a JMP instruction */
#define	kUnLoaded 0x3F3C				/* if unloaded then a LoadSeg trap */
										/* Note: probably incorrect for -model far! */

/* #define TRACKSEGS /* Utility to print a trace of segment load frequencies. Add
   a call to ListGUnloads into terminate() in end.c to use it */

#ifdef TRACKSEGS

long	  gUnloads[120];
char	  gSegNames[120][16];

void ListGUnloads(void)
{
  int i;
  FILE *f;
  
  f = fopen("unloads","r+");
  fprintf(f,"%d calls to UnloadAllSegments\n\n",gUnloads[0]);
  for (i=1; i<=pMaxSegNum; i++) {
	 fprintf(f,"Unloaded %10s, segment %2d, %6d times\n",gSegNames[i],i,gUnloads[i]);
  }
  fclose(f);
}

#endif

short GetSegNumber(ShortPtr aProc)
/* Derives seg number from a procedure ptr */

{
	if (*aProc == kLoaded) 				/* loaded segment */
		return(*--aProc);
	else if (*aProc == kUnLoaded)  		/* unloaded segment */
		return(*++aProc);
	else {
		progerror("GetSegNumber was not passed an jump table address");
		return(1);
	}
}

void InitSegMgmt(void * mainSeg)
/* Initialise a list of handles to all the CODE segments and mark the mainseg as resident */
{
	short 	i,
			lastRsrc,
			rsrcID,
			oldResFile;
	Handle  seg;
	ResType rsrcType;
	Str255  rsrcName;
	 
	gCodeRefNum = HomeResFile(GetResource('CODE', 1));	
	oldResFile = CurResFile();
	UseResFile(gCodeRefNum);
	
	/* Discover the highest CODE rsrc ID: be ready for noncontiguous IDs */
	lastRsrc = Count1Resources('CODE');	
	SetResLoad(false);
	for (i=1; i<=lastRsrc; i++) 
		if (seg = Get1IndResource('CODE', i)) {
			GetResInfo(seg, &rsrcID, &rsrcType, rsrcName);
			if (rsrcID > pMaxSegNum) pMaxSegNum = rsrcID;
		}
		
	/* Make handles of appropriate size to keep flags/segment handles */
	SetResLoad(true);  /* In case we fail */
	gCodeSegs = (HandleListHandle) NewHandle((pMaxSegNum+1) * sizeof(Handle));	
	mustwork(MemError());
	gIsResidentSeg = (BoolListHandle) NewHandle((pMaxSegNum+1) * sizeof(Boolean));
	mustwork(MemError());
	SetResLoad(false);	

	#ifdef TRACKSEGS
	gUnloads[0]=0;
	#endif
	for (i=1; i<=pMaxSegNum; i++) {
	   (*gIsResidentSeg)[i] = false;
	   (*gCodeSegs)[i] = Get1Resource('CODE',i);   /* Will be NIL if it doesn't exist */
	   #ifdef TRACKSEGS
	   {  /* Go get the segment name and save it */
	      short id;
		  ResType rType;
		  Str255 name;
		  char *cptr;
		  
		  GetResInfo((*gCodeSegs)[i],&id,&rType,&name);
		  if (name[0]>15) name[0]=15;
		  cptr = p2cstr(&name);
		  cptr = strcpy(&gSegNames[i], &name);
		  gUnloads[i] = 0;
	   }
	   #endif
	}
	SetResLoad(true);	
	(*gIsResidentSeg)[GetSegNumber((ShortPtr)mainSeg)] = true;	
	UseResFile(oldResFile);
}

#ifdef MAC_MPW32
pascal long GetA5(void) = { 0x2E8D };					/* MOVE.L A5,(A7) */
pascal short GetCurJTOffset(void) = { 0x3EB8, 0x934 };   /* MOVE.W CurJTOffset,(SP) */
#endif
#ifdef MAC_THINKC5
pascal long GetA5(void) = { 0x2E8D };					/* MOVE.L A5,(A7) */
pascal short GetCurJTOffset(void) = { 0x3EB8, 0x934 };   /* MOVE.W CurJTOffset,(SP) */
#endif

void UnloadAllSegments(void)
{
  short	 i,
		 oldResFile;
  Handle seg;
  long	 jumpTablePtr;

  jumpTablePtr = GetA5() + GetCurJTOffset();
  oldResFile = CurResFile();
  UseResFile(gCodeRefNum);
#ifdef TRACKSEGS
  gUnloads[0]++;
#endif
  for (i=1; i<=pMaxSegNum; i++)
	  if (!(*gIsResidentSeg)[i]) {
		  seg = (*gCodeSegs)[i];
		  if ((seg != (Handle) nil) && (*seg != (Ptr) nil))  /* Check it exists and hasn't been purged */
			  if (HGetState(seg) & 0x80)  {   /* Is it locked? => loaded */
#ifdef TRACKSEGS
				 gUnloads[i]++;
#endif
				 UnloadSeg( (void *) (jumpTablePtr + **(ShortHandle)seg + 2) );
			  }
	  }

  UseResFile(oldResFile);
}

void IsResident( void * routineaddr )
/* We want to move this high up in the heap as it won't be shifted again, so... */
{
	int    segnum;
	Handle theseg;
	
	segnum = GetSegNumber((ShortPtr)routineaddr);
	theseg = (*gCodeSegs)[segnum];
	UnloadSeg( routineaddr );
	if (*theseg != nil) {
	   MoveHHi( theseg );  /* If it has been purged we can't do this */
	   HLock( theseg );
	}
	(*gIsResidentSeg)[segnum] = true;	
}

void NotResident( void * routineaddr )
{
	(*gIsResidentSeg)[GetSegNumber((ShortPtr)routineaddr)] = false;	
}

/*macmain.c*/
