/*
 *	Controller.m - Simple application window management. 
 *
 *	$Id$
 *
 *  Basilisk II (C) 1997-2005 Christian Bauer
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#import "Controller.h"
#import "Emulator.h"

@implementation Controller

#import "sysdeps.h"				// Types used in Basilisk C++ code

#import <main.h>
#import <prefs.h>

#define DEBUG 0
#import <debug.h>

#import "misc_macosx.h"
#import "video_macosx.h"

//
// Standard NSApplication methods that we override
//

- (id) init
{
#ifdef ENABLE_MULTIPLE
	emulators  = [NSMutableArray new];
#endif
	return [super init];
}

- (void) dealloc
{
#ifdef ENABLE_MULTIPLE
	[emulators	dealloc];
#endif
	[super		dealloc];
}

- (void) awakeFromNib
{
#ifdef ENABLE_MULTIPLE
	[self NewEmulator: self];			// So the user gets something on screen
#endif
	[[NSApplication sharedApplication]
		setDelegate: self];				// Enable applicationShouldTerminate
}

- (void) sendEvent: (NSEvent *)event;
{
	if ( [self isAnyEmulatorDisplayingSheets] ||
			[[thePrefsEditor window] isVisible] || ! [self isAnyEmulatorRunning] )
		[super sendEvent: event];
	else
	{
		NSEventType	type = [event type];

		if ( type == NSKeyUp || type == NSKeyDown || type == NSFlagsChanged )
			[self dispatchKeyEvent: event
							  type: type];
		else
			[self dispatchEvent: event
						   type: type];
	}
}

// NSApplication methods which are invoked through delegation

- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *)app
{	return YES;  }

- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *)app
{
	short	count;
	char	*stillRunningMessage;

	if ( [thePrefsEditor hasEdited] )
		if ( ChoiceAlert("Preferences have been edited",
									"Save changes", "Quit") )
			SavePrefs();

//	if ( edited )
//	{
//		NSString *title = [NSString stringWithCString: getString(STR_WARNING_ALERT_TITLE)],
//				 *msg   = @"Preferences have been edited",
//				 *def   = @"Save changes",
//				 *alt   = @"Quit Application",
//				 *other = @"Continue";
//
//		switch ( NSRunAlertPanel(title, msg, def, alt, other, nil) )
//		{
//			case NSAlertDefault:	savePrefs();
//			case NSAlertAlternate:	return NSTerminateNow;
//			case NSAlertOther:		return NSTerminateCancel;
//		}
//	}


	if ( [[thePrefsEditor window] isVisible] )
		[[thePrefsEditor window] performClose: self];


	count = [self emulatorCreatedCount];

	if ( count > 0 )
	{
		if ( count > 1 )
			stillRunningMessage = "Emulators are still running\nExiting Basilisk may lose data";
		else
			stillRunningMessage = "Emulator is still running\nExiting Basilisk may lose data";
		if ( ! ChoiceAlert(stillRunningMessage, "Exit", "Continue") )
			return NSTerminateCancel;				// NSTerminateLater?
	}

	return NSTerminateNow;
}


// Event dispatching, called by sendEvent

- (void) dispatchKeyEvent: (NSEvent *)event
					 type: (NSEventType)type
{
	EmulatorView	*view;

#ifdef ENABLE_MULTIPLE
	// We need to work out what window's Emulator should receive these messages


	int	tmp;

	for ( tmp = 0; tmp < [emulators count], ++tmp )
	{
		theEmulator = [emulators objectAtIndex: tmp];
		view = [theEmulator screen];

		if ( [ theEmulator isRunning ] &&
				( [[theEmulator window] isKeyWindow] || [view isFullScreen] ) )
			break;
	}
	
	if ( tmp < [emulators count] )		// i.e. if we exited the for loop
#else
	view = [theEmulator screen];

	if ( [theEmulator isRunning] &&
				( [[theEmulator window] isKeyWindow] || [view isFullScreen] ) )
#endif
	{
		D(NSLog(@"Got a key event - %d\n", [event keyCode]));
		switch ( type )
		{
			case NSKeyUp:
				[view keyUp: event];
				break;
			case NSKeyDown:
				D(NSLog(@"%s - NSKeyDown - %@", __PRETTY_FUNCTION__, event));
				[view keyDown: event];
				break;
			case NSFlagsChanged:
				[view flagsChanged: event];
				break;
			default:
				NSLog(@"%s - Sent a non-key event (logic error)",
						__PRETTY_FUNCTION__);
				[super sendEvent: event];
		}
	}
	else				// No Basilisk window is key (maybe a panel or pane).
		[super sendEvent: event];				// Call NSApplication default

}

- (void) dispatchEvent: (NSEvent *)event
				  type: (NSEventType)type				
{
	EmulatorView	*view;
	BOOL			fullScreen;

#ifdef ENABLE_MULTIPLE
	// We need to work out what window's Emulator should receive these messages


	int	tmp;

	for ( tmp = 0; tmp < [emulators count], ++tmp )
	{
		theEmulator = [emulators objectAtIndex: tmp];
		view = [theEmulator screen];
		fullScreen = [view isFullScreen];

		if ( [theEmulator isRunning] &&
				( fullScreen || [[theEmulator window] isMainWindow] ) )
			break;
	}
	
	if ( tmp < [emulators count] )		// i.e. if we exited the for loop
#else
	view = [theEmulator screen];
	fullScreen = [view isFullScreen];

	if ( [theEmulator isRunning] &&
				( fullScreen || [[theEmulator window] isMainWindow] ) )
#endif
	{
		if ( fullScreen || [view mouseInView: event] )
		{
			switch ( type )
			{
				case NSLeftMouseDown:
					[view mouseDown: event];
					break;
				case NSLeftMouseUp:
					[view mouseUp: event];
					break;
				case NSLeftMouseDragged:
				case NSMouseMoved:
					if ( fullScreen )
						[view fullscreenMouseMove];
					else
						[view processMouseMove: event];
					break;
				default:
					[super sendEvent: event];		// NSApplication default
			}
			return;
		}
	}

	// Either the pointer is not in the Emulator's screen, no Basilisk window is running,
	// or no Basilisk window is main (e.g. there might be a panel or pane up).
	//
	// We should just be calling NSApp's default sendEvent, but then for some reason
	// mouseMoved events are still passed to our EmulatorView, so we filter them out.

	if ( type != NSMouseMoved )
		[super sendEvent: event];
}


// Methods to display documentation:

- (IBAction) HelpHowTo: (id)sender
{
	NSString	*path = [[NSBundle mainBundle] pathForResource: @"HowTo"
														ofType: @"html"];

	if ( ! path )
		InfoSheet(@"Cannot find HowTo.html", [theEmulator window]);
	else
		if ( ! [[NSWorkspace sharedWorkspace] openFile: path] )
			InfoSheet(@"Cannot open HowTo.html with default app", [theEmulator window]);
}

- (IBAction) HelpToDo: (id)sender
{
	NSString	*path = [[NSBundle mainBundle] pathForResource: @"ToDo"
														ofType: @"html"];

	if ( ! path )
		InfoSheet(@"Cannot find ToDo.html", [theEmulator window]);
	else
		if ( ! [[NSWorkspace sharedWorkspace] openFile: path
									   withApplication: @"TextEdit"] )
			InfoSheet(@"Cannot open ToDo.html with TextEdit", [theEmulator window]);
}

- (IBAction) HelpVersions: (id)sender
{
	NSString	*path = [[NSBundle mainBundle] pathForResource: @"Versions"
														ofType: @"html"];

	if ( ! path )
		InfoSheet(@"Cannot find Versions.html", [theEmulator window]);
	else
		if ( ! [[NSWorkspace sharedWorkspace] openFile: path
									   withApplication: @"TextEdit"] )
			InfoSheet(@"Cannot open Versions.html with TextEdit",
												[theEmulator window]);
}


// Menu items which for managing more than one window

#ifdef ENABLE_MULTIPLE

- (IBAction) NewEmulator: (id)sender
{
	NSString	*title;

	if ( ! [NSBundle loadNibNamed:@"Win512x342" owner:self] )
	{
		NSLog(@"%s - LoadNibNamed@Win512x342 failed", __PRETTY_FUNCTION__);
		return;
	}

	if ( theEmulator == nil)
	{
		NSLog(@"%s - Newly created emulator's NIB stuff not fully linked?", __PRETTY_FUNCTION__);
		return;
	}

	[emulators addObject: theEmulator];
	title = [NSString localizedStringWithFormat:@"BasiliskII Emulator %d", [emulators count]];
	[theEmulator -> win setTitle: title];
}

- (IBAction) PauseAll: (id)sender
{
	[emulators makeObjectsPerformSelector:@selector(Suspend:)
							   withObject:self];
}

- (IBAction) RunAll: (id)sender
{
	[emulators makeObjectsPerformSelector:@selector(Resume:)
							   withObject:self];
}

- (IBAction) TerminateAll: (id)sender
{
	[emulators makeObjectsPerformSelector:@selector(Terminate:)
							   withObject:self];
}

#endif

- (BOOL) isAnyEmulatorDisplayingSheets
{
#ifdef ENABLE_MULTIPLE
	int	tmp;

	for ( tmp = 0; tmp < [emulators count], ++tmp )
		if ( [[[emulators objectAtIndex: tmp] window] attachedSheet] )
			break;
	
	if ( tmp < [emulators count] )		// i.e. if we exited the for loop
#else
	if ( [[theEmulator window] attachedSheet] )
#endif
		return TRUE;

	return FALSE;
}

- (BOOL) isAnyEmulatorRunning
{
#ifdef ENABLE_MULTIPLE
	int	tmp;

	for ( tmp = 0; tmp < [emulators count], ++tmp )
		if ( [[emulators objectAtIndex: tmp] isRunning] )
			break;
	
	if ( tmp < [emulators count] )		// i.e. if we exited the for loop
#else
	if ( [theEmulator isRunning] )
#endif
		return TRUE;

	return FALSE;
}

- (short) emulatorCreatedCount
{
	short	count = 0;
#ifdef ENABLE_MULTIPLE
	int		tmp;

	for ( tmp = 0; tmp < [emulators count], ++tmp )
		if ( [[emulators objectAtIndex: tmp] uaeCreated] )
			++count;
#else
	if ( [theEmulator uaeCreated] )
		++count;
#endif

	return count;
}

@end
