/*
    SDLMain.m - main entry point for our Cocoa-ized SDL app
       Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
       Non-NIB-Code & other changes: Max Horn <max@quendi.de>
       Modifications for Hatari by Miguel Saro and Jerome Vernet

    Feel free to customize this file to suit your needs
*/

/* Use this flag to determine whether we use SDLMain.nib or not */
#define		SDL_USE_NIB_FILE	1

/* Use this flag to determine whether we use CPS (docking) or not */
#define		SDL_USE_CPS			1

#import "SDL.h"
#import "SDLMain.h"
#import <sys/param.h> // for MAXPATHLEN
#import <unistd.h>

// for Hatari
#import "dialog.h"
#import "floppy.h"
#import "reset.h"
#import "screenSnapShot.h"
#import "memorySnapShot.h"
#import "sound.h"
#import "screen.h"
#import "PrefsController.h"
#import "Shared.h"
#import "video.h"
#import "avi_record.h"
#import "debugui.h"
#import "clocks_timings.h"
#import "change.h"


#ifdef SDL_USE_CPS
// Portions of CPS.h
typedef struct CPSProcessSerNum
{
	UInt32		lo;
	UInt32		hi;
} CPSProcessSerNum;

extern OSErr	CPSGetCurrentProcess( CPSProcessSerNum *psn);
extern OSErr 	CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
extern OSErr	CPSSetFrontProcess( CPSProcessSerNum *psn);

#endif // SDL_USE_CPS

static int		gArgc;
static char		**gArgv;
static BOOL		gFinderLaunch = NO ;
static BOOL		gCalledAppMainline = NO ;


// The main class of the application, the application's delegate
//
@implementation HatariAppDelegate

char szPath[FILENAME_MAX] ;											// for general use

// Set the working directory to the .app's parent directory
- (void) setupWorkingDirectory:(BOOL)shouldChdir
{
	if (shouldChdir)
		chdir([[[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent] cStringUsingEncoding:NSASCIIStringEncoding]) ;
}


/*
 * Catch document open requests...this lets us notice files when the app
 *  was launched by double-clicking a document, or when a document was
 *  dragged/dropped on the app's icon. You need to have a
 *  CFBundleDocumentsType section in your Info.plist to get this message,
 *  apparently.
 *
 * Files are added to gArgv, so to the app, they'll look like command line
 *  arguments. Previously, apps launched from the finder had nothing but
 *  an argv[0].
 *
 * This message may be received multiple times to open several docs on launch.
 *
 * This message is ignored once the app's mainline has been called.
 */
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
	const char *temparg;
	size_t arglen;
	char *arg;
	char **newargv;

	if (!gFinderLaunch)			// MacOS is passing command line args.
		return FALSE;

	if (gCalledAppMainline)		// app has started, ignore this document.
		return FALSE;

	temparg = [filename UTF8String] ;
	arglen = SDL_strlen(temparg) + 1 ;
	arg = (char *) SDL_malloc(arglen) ;
	if (arg == NULL)
		return FALSE;

	newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)) ;
	if (newargv == NULL)
	{
		SDL_free(arg);
		return FALSE;
	}
	gArgv = newargv ;

	SDL_strlcpy(arg, temparg, arglen) ;
	gArgv[gArgc++] = arg ;
	gArgv[gArgc] = NULL ;
	return TRUE;
}


// Called when the internal event loop has just started running
//
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
	int status;

	// Set the working directory to the .app's parent directory
	[self setupWorkingDirectory:gFinderLaunch];

	//setenv ("SDL_ENABLEAPPEVENTS", "1", 1) ;

	// Hand off to main application code

	gCalledAppMainline = TRUE;
	status = SDL_main (gArgc, gArgv) ;

	// We're done, thank you for playing 
	exit(status) ;
}

// Hatari Stuff
- (IBAction)prefsMenu:(id)sender
{
	static int in_propdialog =  0;

	if (in_propdialog)
		return ;
	++in_propdialog ;
	Dialog_DoProperty() ;
	--in_propdialog;
}

/*- (IBAction) openPreferences:(id)sender
{
	[[PrefsController prefs] loadPrefs:sender];
}														// */


- (IBAction)debugUI:(id)sender
{
	DebugUI(REASON_USER);
}

- (IBAction)warmReset:(id)sender
{
	if ([NSApp myAlerte:NSInformationalAlertStyle Txt:localize(@"Warm reset!") firstB:localize(@"OK") alternateB:localize(@"Cancel")
									otherB:nil informativeTxt:localize(@"Really reset the emulator?")] == NSAlertDefaultReturn)
		Reset_Warm();
} 

- (IBAction)coldReset:(id)sender
{
	if ([NSApp myAlerte:NSInformationalAlertStyle Txt:localize(@"Cold reset") firstB:localize(@"OK") alternateB:localize(@"Cancel")
									otherB:nil informativeTxt:localize(@"Really reset the emulator?")] == NSAlertDefaultReturn)
		Reset_Cold();
}

- (IBAction)insertDiskA:(id)sender
{
	[self insertDisk:0] ;
}

- (IBAction)insertDiskB:(id)sender
{
	[self insertDisk:1] ;
}

- (void)insertDisk:(int)disque 
{
	NSString	*aDisk ;

	aDisk = [NSApp hopenfile:NO defoDir:nil defoFile:@"" types:[NSArray arrayWithObjects:allF,nil]] ;
	if ([aDisk length] == 0) return ;                 // user canceled

	[aDisk getCString:szPath maxLength:FILENAME_MAX-1 encoding:NSASCIIStringEncoding] ;
	Floppy_SetDiskFileName(disque, szPath, NULL) ;
	Floppy_InsertDiskIntoDrive(disque) ;
}


/*-----------------------------------------------------------------------*/
/*
 Controls the enabled state of the menu items
 */
- (BOOL)validateMenuItem:(NSMenuItem*)item
{
	if (item == beginCaptureAnim)
	{
		return !Avi_AreWeRecording() ;
	}
	if (item == endCaptureAnim)
	{
		return Avi_AreWeRecording() ;
	}
	if (item == beginCaptureSound)
	{
		return !Sound_AreWeRecording() ;
	}
	if (item == endCaptureSound)
	{
		return Sound_AreWeRecording() ;
	}

	return YES;
}

- (NSString*)displayFileSelection:(const char*)pathInParams preferredFileName:(NSString*)preferredFileName allowedExtensions:(NSArray*)allowedExtensions
{
	// BOOL test ;
	NSString *directoryToOpen;
	NSString *fileToPreselect;
	NSString *preferredPath;
	NSString *extensionText;
	NSString *selectFile;

	// Get the path from the user settings
	preferredPath = [[NSString stringWithCString:pathInParams encoding:NSASCIIStringEncoding] stringByAbbreviatingWithTildeInPath];

	if ((preferredPath != nil) && ([preferredPath length] > 0))					// Determine the directory and filename
	 {
		directoryToOpen = [preferredPath stringByDeletingLastPathComponent];	// Existing path: we use it
		fileToPreselect = [preferredPath lastPathComponent];
	 }
	else
	 {
		directoryToOpen = [@"~" stringByExpandingTildeInPath];					// No path: we use the user's directory
		fileToPreselect = preferredFileName;
	 }	;

	if(bInFullScreen)
		Screen_ReturnFromFullScreen();
	//  SavePanel for choosing what file to write
	extensionText = [NSString stringWithFormat:localize(@"Please specify a .%@ file"), [allowedExtensions componentsJoinedByString:localize(@" or a .")] ];

	selectFile = [NSApp hsavefile:YES defoDir:directoryToOpen defoFile:fileToPreselect types:allowedExtensions titre:extensionText ] ;
	if ([selectFile length] != 0 )
		return selectFile ;

	return nil;
}

- (IBAction)captureScreen:(id)sender
{
	GuiOsx_Pause();
	ScreenSnapShot_SaveScreen();
	GuiOsx_Resume();
}

- (IBAction)captureAnimation:(id)sender
{
	GuiOsx_Pause();
	if(!Avi_AreWeRecording()) {
		NSString* path = [self displayFileSelection:ConfigureParams.Video.AviRecordFile preferredFileName:@"hatari.avi"
									 allowedExtensions:[NSArray arrayWithObject:@"avi"]];

		if(path) {
			GuiOsx_ExportPathString(path, ConfigureParams.Video.AviRecordFile, sizeof(ConfigureParams.Video.AviRecordFile));
			Avi_StartRecording ( ConfigureParams.Video.AviRecordFile , ConfigureParams.Screen.bCrop ,
					ConfigureParams.Video.AviRecordFps == 0 ?
					ClocksTimings_GetVBLPerSec ( ConfigureParams.System.nMachineType , nScreenRefreshRate ) :
					(Uint32)ConfigureParams.Video.AviRecordFps << CLOCKS_TIMINGS_SHIFT_VBL ,
				1 << CLOCKS_TIMINGS_SHIFT_VBL ,
				ConfigureParams.Video.AviRecordVcodec );
		}
	} else {
		Avi_StopRecording();
	}
	GuiOsx_Resume();
}

- (IBAction)endCaptureAnimation:(id)sender
{
	GuiOsx_Pause();
	Avi_StopRecording();
	GuiOsx_Resume();
}

- (IBAction)captureSound:(id)sender
{
	GuiOsx_Pause();
	NSString* path = [self displayFileSelection:ConfigureParams.Sound.szYMCaptureFileName preferredFileName:@"hatari.wav"
								 allowedExtensions:[NSArray arrayWithObjects:@"ym", @"wav", nil]];
	if(path) {
		GuiOsx_ExportPathString(path, ConfigureParams.Sound.szYMCaptureFileName, sizeof(ConfigureParams.Sound.szYMCaptureFileName));
		Sound_BeginRecording(ConfigureParams.Sound.szYMCaptureFileName);
	}
	GuiOsx_Resume();
}

- (IBAction)endCaptureSound:(id)sender
{
	GuiOsx_Pause();
	Sound_EndRecording();
	GuiOsx_Resume();
}

- (IBAction)saveMemorySnap:(id)sender
{
	GuiOsx_Pause();

	NSString* path = [self displayFileSelection:ConfigureParams.Memory.szMemoryCaptureFileName preferredFileName:@"hatari.sav"
								 allowedExtensions:[NSArray arrayWithObject:@"sav"]];
	if(path) {
		GuiOsx_ExportPathString(path, ConfigureParams.Memory.szMemoryCaptureFileName, sizeof(ConfigureParams.Memory.szMemoryCaptureFileName));
		MemorySnapShot_Capture(ConfigureParams.Memory.szMemoryCaptureFileName, TRUE);
	}

	GuiOsx_Resume();
}

- (IBAction)restoreMemorySnap:(id)sender
{
	NSString *directoryToOpen;
	NSString *fileToPreselect;
	NSString *oldPath ;
	NSString *newPath ;

	GuiOsx_Pause();

	// Get the path from the user settings
	oldPath = [NSString stringWithCString:(ConfigureParams.Memory.szMemoryCaptureFileName) encoding:NSASCIIStringEncoding];

	if ((oldPath != nil) && ([oldPath length] > 0))						// Determine directory and filename
	 {	directoryToOpen = [oldPath stringByDeletingLastPathComponent];	// existing path: we use it.
		fileToPreselect = [oldPath lastPathComponent]; }
	else
	 {	directoryToOpen = [@"~" stringByExpandingTildeInPath];			// Currently no path: we use user's directory
		fileToPreselect = nil; } ;

	newPath = [NSApp hopenfile:NO defoDir:directoryToOpen defoFile:fileToPreselect types:[NSArray arrayWithObject:@"sav"] ] ;
	if ([newPath length] != 0)											// Perform the memory snapshot load
		MemorySnapShot_Restore([newPath cStringUsingEncoding:NSASCIIStringEncoding], TRUE);

	GuiOsx_Resume();
}

- (IBAction)doFullScreen:(id)sender
{
	// A call to Screen_EnterFullScreen() would be required, but this causes a crash when using
	// SDL runtime 1.2.11, probably due to conflicts between Cocoa and SDL.
	// Therefore we simulate the fullscreen key press instead

	SDL_KeyboardEvent event;
	memset(&event, 0, sizeof(event));
	event.type = SDL_KEYDOWN;
	event.state = SDL_PRESSED;
	event.keysym.sym = SDLK_F11;
	SDL_PushEvent((SDL_Event*)&event);	// Send the F11 key press
	event.type = SDL_KEYUP;
	event.state = SDL_RELEASED;
	SDL_PushEvent((SDL_Event*)&event);	// Send the F11 key release
}


- (IBAction)help:(id)sender
{
	NSString *the_help;

	the_help = [[NSBundle mainBundle] pathForResource:@"manual" ofType:@"html" inDirectory:@"HatariHelp"];

	if (![[NSWorkspace sharedWorkspace] openFile:the_help withApplication:@"HelpViewer"])
		if (![[NSWorkspace sharedWorkspace] openFile:the_help withApplication:@"Help Viewer"])
			[[NSWorkspace sharedWorkspace] openFile:the_help];
}

- (IBAction)compat:(id)sender
{
	NSString *C_aide ;

	C_aide = [[NSBundle mainBundle] pathForResource:@"compatibility" ofType:@"html" inDirectory:@"HatariHelp"] ;

	if (![[NSWorkspace sharedWorkspace] openFile:C_aide withApplication:@"HelpViewer"])
		if (![[NSWorkspace sharedWorkspace] openFile:C_aide withApplication:@"Help Viewer"])
			[[NSWorkspace sharedWorkspace] openFile:C_aide] ;
}

- (IBAction)openConfig:(id)sender
{
	BOOL		applyChanges ;
	NSString	*ConfigFile, *newCfg ;
	CNF_PARAMS	CurrentParams;

	applyChanges = true ;
	ConfigFile = [NSString stringWithCString:(sConfigFileName) encoding:NSASCIIStringEncoding];

	// Backup of configuration settings to CurrentParams (which we will only
	// commit back to the configuration settings if choosing user confirm)
	CurrentParams = ConfigureParams;

	GuiOsx_Pause();

	newCfg = [NSApp hopenfile:NO defoDir:nil defoFile:ConfigFile types:[NSArray arrayWithObject:@"cfg"] ] ;

	if ([newCfg length] != 0)
	{
		[newCfg getCString:szPath maxLength:FILENAME_MAX-1 encoding:NSASCIIStringEncoding] ;	// get Cstring  szPath
		Configuration_Load(szPath) ;															// Load the config into ConfigureParams
		strcpy(sConfigFileName,szPath) ;

		// Refresh all the controls to match ConfigureParams
		if (Change_DoNeedReset(&CurrentParams, &ConfigureParams))
		 	applyChanges = [NSApp myAlerte:NSInformationalAlertStyle Txt:localize(@"Reset the emulator") firstB:localize(@"Don't reset")
								alternateB:localize(@"Reset") otherB:nil informativeTxt:@"" ] == NSAlertAlternateReturn ;
		if (applyChanges)
			Change_CopyChangedParamsToConfiguration(&CurrentParams, &ConfigureParams, true); // Ok with Reset
		else
			ConfigureParams = CurrentParams;   //Restore previous Params.
	} ;

	GuiOsx_Resume();
}


- (IBAction)saveConfig:(id)sender {
}

@end

static int IsRootCwd()
{
	char buf[MAXPATHLEN];
	char *cwd = getcwd(buf, sizeof (buf));
	return (cwd && (strcmp(cwd, "/") == 0));
}

static int IsTenPointNineOrLater()
{
	/* Gestalt() is deprecated in 10.8 ... TODO: replace with better test? */
	SInt32 major, minor;
	Gestalt(gestaltSystemVersionMajor, &major);
	Gestalt(gestaltSystemVersionMinor, &minor);
	return ( ((major << 16) | minor) >= ((10 << 16) | 9) );
}

static int IsFinderLaunch(const int argc, char **argv)
{
	/* -psn_XXX is passed if we are launched from Finder in 10.8 and earlier */
	if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) {
		return 1;
	}
	if (IsTenPointNineOrLater() && argc == 1 && IsRootCwd()) {
		/* we might still be launched from the Finder; on 10.9+, you might not
		get the -psn command line anymore. Check version, if there's no
		command line, and if our current working directory is "/". */
		return 1;
	}
	return 0;  /* not a Finder launch. */
}

#ifdef main
#  undef main
#endif

// Main entry point to executable - should *not* be SDL_main!
int main (int argc, char **argv)
{
	// Copy the arguments into a global variable
	if (IsFinderLaunch(argc, argv)) {
		gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
		gArgv[0] = argv[0];
		gArgv[1] = NULL;
		gArgc = 1;
		gFinderLaunch = YES;
	} else {
		int i;
		gArgc = argc;
		gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
		for (i = 0; i <= argc; i++)
			gArgv[i] = argv[i];
		gFinderLaunch = NO;
	}

#if SDL_USE_NIB_FILE
	NSApplicationMain (argc, (const char**)argv);
#else
	CustomApplicationMain (argc, argv);
#endif
	return 0;
}
