/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2004 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Sam Lantinga
    slouken@libsdl.org
*/

#ifdef SAVE_RCSID
static char rcsid =
 "@(#) $Id: SDL_main.c,v 1.5 2004/01/04 16:49:18 slouken Exp $";
#endif

/* This file takes care of command line argument parsing, and stdio redirection
   in the MacOS environment.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>	
#if TARGET_API_MAC_CARBON
#include <Carbon.h>
#else
#include <Dialogs.h>
#include <Fonts.h>
#include <Events.h>
#include <Resources.h>
#include <Folders.h>
#endif

/* Include the SDL main definition header */
#include "SDL.h"
#include "SDL_main.h"
#ifdef main
#undef main
#endif

/* The standard output files */
#define STDOUT_FILE	"stdout.txt"
#define STDERR_FILE	"stderr.txt"

#if !defined(__MWERKS__) && !TARGET_API_MAC_CARBON
	/* In MPW, the qd global has been removed from the libraries */
	QDGlobals qd;
#endif

/* Structure for keeping prefs in 1 variable */
typedef struct {
    Str255  command_line;
    Str255  video_driver_name;
    Boolean output_to_file;
}  PrefsRecord;

/* See if the command key is held down at startup */
static Boolean CommandKeyIsDown(void)
{
	KeyMap  theKeyMap;

	GetKeys(theKeyMap);

	if (((unsigned char *) theKeyMap)[6] & 0x80) {
		return(true);
	}
	return(false);
}

/* Parse a command line buffer into arguments */
static int ParseCommandLine(char *cmdline, char **argv)
{
	char *bufp;
	int argc;

	argc = 0;
	for ( bufp = cmdline; *bufp; ) {
		/* Skip leading whitespace */
		while ( isspace(*bufp) ) {
			++bufp;
		}
		/* Skip over argument */
		if ( *bufp == '"' ) {
			++bufp;
			if ( *bufp ) {
				if ( argv ) {
					argv[argc] = bufp;
				}
				++argc;
			}
			/* Skip over word */
			while ( *bufp && (*bufp != '"') ) {
				++bufp;
			}
		} else {
			if ( *bufp ) {
				if ( argv ) {
					argv[argc] = bufp;
				}
				++argc;
			}
			/* Skip over word */
			while ( *bufp && ! isspace(*bufp) ) {
				++bufp;
			}
		}
		if ( *bufp ) {
			if ( argv ) {
				*bufp = '\0';
			}
			++bufp;
		}
	}
	if ( argv ) {
		argv[argc] = NULL;
	}
	return(argc);
}

/* Remove the output files if there was no output written */
static void cleanup_output(void)
{
	FILE *file;
	int empty;

	/* Flush the output in case anything is queued */
	fclose(stdout);
	fclose(stderr);

	/* See if the files have any output in them */
	file = fopen(STDOUT_FILE, "rb");
	if ( file ) {
		empty = (fgetc(file) == EOF) ? 1 : 0;
		fclose(file);
		if ( empty ) {
			remove(STDOUT_FILE);
		}
	}
	file = fopen(STDERR_FILE, "rb");
	if ( file ) {
		empty = (fgetc(file) == EOF) ? 1 : 0;
		fclose(file);
		if ( empty ) {
			remove(STDERR_FILE);
		}
	}
}

static int getCurrentAppName (StrFileName name) {
	
    ProcessSerialNumber process;
    ProcessInfoRec      process_info;
    FSSpec              process_fsp;
    
    process.highLongOfPSN = 0;
    process.lowLongOfPSN  = kCurrentProcess;
    process_info.processInfoLength = sizeof (process_info);
    process_info.processName    = NULL;
    process_info.processAppSpec = &process_fsp;
    
    if ( noErr != GetProcessInformation (&process, &process_info) )
       return 0;
    
    memcpy (name, process_fsp.name, process_fsp.name[0] + 1);
    return 1;
}

static int getPrefsFile (FSSpec *prefs_fsp, int create) {

    /* The prefs file name is the application name, possibly truncated, */
    /* plus " Preferences */
    
    #define  SUFFIX   " Preferences"
    #define  MAX_NAME 19             /* 31 - strlen (SUFFIX) */
    
    short  volume_ref_number;
    long   directory_id;
    StrFileName  prefs_name;
    StrFileName  app_name;
    
    /* Get Preferences folder - works with Multiple Users */
    if ( noErr != FindFolder ( kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder,
                               &volume_ref_number, &directory_id) )
        exit (-1);
    
    if ( ! getCurrentAppName (app_name) )
        exit (-1);
    
    /* Truncate if name is too long */
    if (app_name[0] > MAX_NAME )
        app_name[0] = MAX_NAME;
        
    memcpy (prefs_name + 1, app_name + 1, app_name[0]);    
    memcpy (prefs_name + app_name[0] + 1, SUFFIX, strlen (SUFFIX));
    prefs_name[0] = app_name[0] + strlen (SUFFIX);
   
    /* Make the file spec for prefs file */
    if ( noErr != FSMakeFSSpec (volume_ref_number, directory_id, prefs_name, prefs_fsp) )
        if ( !create )
            return 0;
        else {
            /* Create the prefs file */
            memcpy (prefs_fsp->name, prefs_name, prefs_name[0] + 1);
            prefs_fsp->parID   = directory_id;
            prefs_fsp->vRefNum = volume_ref_number;
                
            FSpCreateResFile (prefs_fsp, '????', 'pref', 0);
            
            if ( noErr != ResError () )
                return 0;
        }
      
    return 1;
}

static int readPrefsResource (PrefsRecord *prefs) {
    
    Handle prefs_handle;
    
    prefs_handle = Get1Resource( 'CLne', 128 );

	if (prefs_handle != NULL) {
		int offset = 0;
		int j      = 0;
		
		HLock(prefs_handle);
		
		/* Get command line string */	
		memcpy (prefs->command_line, *prefs_handle, (*prefs_handle)[0]+1);

		/* Get video driver name */
		offset += (*prefs_handle)[0] + 1;	
		memcpy (prefs->video_driver_name, *prefs_handle + offset, (*prefs_handle)[offset] + 1);		
		
		/* Get save-to-file option (1 or 0) */
		offset += (*prefs_handle)[offset] + 1;
		prefs->output_to_file = (*prefs_handle)[offset];
		
		ReleaseResource( prefs_handle );
    
        return ResError() == noErr;
    }

    return 0;
}

static int writePrefsResource (PrefsRecord *prefs, short resource_file) {

    Handle prefs_handle;
    
    UseResFile (resource_file);
    
    prefs_handle = Get1Resource ( 'CLne', 128 );
    if (prefs_handle != NULL)
        RemoveResource (prefs_handle);
    
    prefs_handle = NewHandle ( prefs->command_line[0] + prefs->video_driver_name[0] + 4 );
    if (prefs_handle != NULL) {
    
        int offset;
        
        HLock (prefs_handle);
        
        /* Command line text */
        offset = 0;
        memcpy (*prefs_handle, prefs->command_line, prefs->command_line[0] + 1);
        
        /* Video driver name */
        offset += prefs->command_line[0] + 1;
        memcpy (*prefs_handle + offset, prefs->video_driver_name, prefs->video_driver_name[0] + 1);
        
        /* Output-to-file option */
        offset += prefs->video_driver_name[0] + 1;
        *( *((char**)prefs_handle) + offset)     = (char)prefs->output_to_file;
        *( *((char**)prefs_handle) + offset + 1) = 0;
              
        AddResource   (prefs_handle, 'CLne', 128, "\pCommand Line");
        WriteResource (prefs_handle);
        UpdateResFile (resource_file);
        DisposeHandle (prefs_handle);
        
        return ResError() == noErr;
    }
    
    return 0;
}

static int readPreferences (PrefsRecord *prefs) {

    int    no_error = 1;
    FSSpec prefs_fsp;

    /* Check for prefs file first */
    if ( getPrefsFile (&prefs_fsp, 0) ) {
    
        short  prefs_resource;
        
        prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdPerm);
        if ( prefs_resource == -1 ) /* this shouldn't happen, but... */
            return 0;
    
        UseResFile   (prefs_resource);
        no_error = readPrefsResource (prefs);     
        CloseResFile (prefs_resource);
    }
    
    /* Fall back to application's resource fork (reading only, so this is safe) */
    else {
    
          no_error = readPrefsResource (prefs);
     }

    return no_error;
}

static int writePreferences (PrefsRecord *prefs) {
    
    int    no_error = 1;
    FSSpec prefs_fsp;
    
    /* Get prefs file, create if it doesn't exist */
    if ( getPrefsFile (&prefs_fsp, 1) ) {
    
        short  prefs_resource;
        
        prefs_resource = FSpOpenResFile (&prefs_fsp, fsRdWrPerm);
        if (prefs_resource == -1)
            return 0;
        no_error = writePrefsResource (prefs, prefs_resource);
        CloseResFile (prefs_resource);
    }
    
    return no_error;
}

/* This is where execution begins */
int main(int argc, char *argv[])
{

#pragma unused(argc, argv)
	
#define DEFAULT_ARGS "\p"                /* pascal string for default args */
#define DEFAULT_VIDEO_DRIVER "\ptoolbox" /* pascal string for default video driver name */	
#define DEFAULT_OUTPUT_TO_FILE 1         /* 1 == output to file, 0 == no output */

#define VIDEO_ID_DRAWSPROCKET 1          /* these correspond to popup menu choices */
#define VIDEO_ID_TOOLBOX      2

    PrefsRecord prefs = { DEFAULT_ARGS, DEFAULT_VIDEO_DRIVER, DEFAULT_OUTPUT_TO_FILE }; 
	
	int     nargs;
	char   **args;
	char   *commandLine;
	
	StrFileName  appNameText;
	int     videodriver     = VIDEO_ID_TOOLBOX;
    int     settingsChanged = 0;
    
    long	i;

	/* Kyle's SDL command-line dialog code ... */
#if !TARGET_API_MAC_CARBON
	InitGraf    (&qd.thePort);
	InitFonts   ();
	InitWindows ();
	InitMenus   ();
	InitDialogs (nil);
#endif
	InitCursor ();
	FlushEvents(everyEvent,0);
#if !TARGET_API_MAC_CARBON
	MaxApplZone ();
#endif
	MoreMasters ();
	MoreMasters ();
#if 0
	/* Intialize SDL, and put up a dialog if we fail */
	if ( SDL_Init (0) < 0 ) {

#define kErr_OK		1
#define kErr_Text	2

        DialogPtr errorDialog;
        short	  dummyType;
    	Rect	  dummyRect;
	    Handle    dummyHandle;
	    short     itemHit;
	
		errorDialog = GetNewDialog (1001, nil, (WindowPtr)-1);
		DrawDialog (errorDialog);
		
		GetDialogItem (errorDialog, kErr_Text, &dummyType, &dummyHandle, &dummyRect);
		SetDialogItemText (dummyHandle, "\pError Initializing SDL");
		
		SetPort (errorDialog);
		do {
			ModalDialog (nil, &itemHit);
		} while (itemHit != kErr_OK);
		
		DisposeDialog (errorDialog);
		exit (-1);
	}
	atexit(cleanup_output);
	atexit(SDL_Quit);
#endif

/* Set up SDL's QuickDraw environment  */
#if !TARGET_API_MAC_CARBON
	SDL_InitQuickDraw(&qd);
#endif

	 if ( readPreferences (&prefs) ) {
		
        if (memcmp (prefs.video_driver_name+1, "DSp", 3) == 0)
            videodriver = 1;
        else if (memcmp (prefs.video_driver_name+1, "toolbox", 7) == 0)
            videodriver = 2;
	 }
	 	
	if ( CommandKeyIsDown() ) {

#define kCL_OK		1
#define kCL_Cancel	2
#define kCL_Text	3
#define kCL_File	4
#define kCL_Video   6
       
        DialogPtr commandDialog;
        short	  dummyType;
        Rect	  dummyRect;
        Handle    dummyHandle;
        short     itemHit;

        /* Assume that they will change settings, rather than do exhaustive check */
        settingsChanged = 1;
        
        /* Create dialog and display it */
        commandDialog = GetNewDialog (1000, nil, (DialogPtr)-1);
        SetPort (commandDialog);
            
        /* Setup controls */
        GetDialogItem   (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
        SetControlValue ((ControlHandle)dummyHandle, prefs.output_to_file );

        GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect);
        SetDialogItemText (dummyHandle, prefs.command_line);

        GetDialogItem   (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
        SetControlValue ((ControlRef)dummyHandle, videodriver);

        SetDialogDefaultItem (commandDialog, kCL_OK);
        SetDialogCancelItem  (commandDialog, kCL_Cancel);

        do {
        		
        	ModalDialog(nil, &itemHit); /* wait for user response */
            
            /* Toggle command-line output checkbox */	
        	if ( itemHit == kCL_File ) {
        		GetDialogItem(commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
        		SetControlValue((ControlHandle)dummyHandle, !GetControlValue((ControlHandle)dummyHandle) );
        	}

        } while (itemHit != kCL_OK && itemHit != kCL_Cancel);

        /* Get control values, even if they did not change */
        GetDialogItem     (commandDialog, kCL_Text, &dummyType, &dummyHandle, &dummyRect); /* MJS */
        GetDialogItemText (dummyHandle, prefs.command_line);

        GetDialogItem (commandDialog, kCL_File, &dummyType, &dummyHandle, &dummyRect); /* MJS */
        prefs.output_to_file = GetControlValue ((ControlHandle)dummyHandle);

        GetDialogItem (commandDialog, kCL_Video, &dummyType, &dummyHandle, &dummyRect);
        videodriver = GetControlValue ((ControlRef)dummyHandle);

        DisposeDialog (commandDialog);

        if (itemHit == kCL_Cancel ) {
        	exit (0);
        }
	}
    
    /* Set pseudo-environment variables for video driver, update prefs */
	switch ( videodriver ) {
	   case VIDEO_ID_DRAWSPROCKET: 
	      putenv ("SDL_VIDEODRIVER=DSp");
	      memcpy (prefs.video_driver_name, "\pDSp", 4);
	      break;
	   case VIDEO_ID_TOOLBOX:
	      putenv ("SDL_VIDEODRIVER=toolbox");
	      memcpy (prefs.video_driver_name, "\ptoolbox", 8);
	      break;
	}

    /* Redirect standard I/O to files */
	if ( prefs.output_to_file ) {
		freopen (STDOUT_FILE, "w", stdout);
		freopen (STDERR_FILE, "w", stderr);
	} else {
		fclose (stdout);
		fclose (stderr);
	}
   
    if (settingsChanged) {
        /* Save the prefs, even if they might not have changed (but probably did) */
        if ( ! writePreferences (&prefs) )
            fprintf (stderr, "WARNING: Could not save preferences!\n");
    }
   
    getCurrentAppName (appNameText); /* check for error here ? */

    commandLine = (char*) malloc (appNameText[0] + prefs.command_line[0] + 2);
    if ( commandLine == NULL ) {
       exit(-1);
    }

    /* Rather than rewrite ParseCommandLine method, let's replace  */
    /* any spaces in application name with underscores,            */
    /* so that the app name is only 1 argument                     */   
    for (i = 1; i < 1+appNameText[0]; i++)
        if ( appNameText[i] == ' ' ) appNameText[i] = '_';

    /* Copy app name & full command text to command-line C-string */      
    memcpy (commandLine, appNameText + 1, appNameText[0]);
    commandLine[appNameText[0]] = ' ';
    memcpy (commandLine + appNameText[0] + 1, prefs.command_line + 1, prefs.command_line[0]);
    commandLine[ appNameText[0] + 1 + prefs.command_line[0] ] = '\0';

    /* Parse C-string into argv and argc */
    nargs = ParseCommandLine (commandLine, NULL);
    args = (char **)malloc((nargs+1)*(sizeof *args));
    if ( args == NULL ) {
		exit(-1);
	}
	ParseCommandLine (commandLine, args);
        
	/* Run the main application code */
	SDL_main(nargs, args);
	free (args);
	free (commandLine);
   
   	/* Remove useless stdout.txt and stderr.txt */
   	cleanup_output ();
   	
	/* Exit cleanly, calling atexit() functions */
	exit (0);    

	/* Never reached, but keeps the compiler quiet */
	return (0);
}
