//
//  osxtads_input_dialog.m
//  XTads
//
//  Created by Rune Berg on 12/07/2020.
//  Copyright © 2020 Rune Berg. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "osxtads_support.h"
#import "XTConstants.h"


/*
 *   Ask for input through a dialog.
 *
 *   'prompt' is a text string to display as a prompting message.  For
 *   graphical systems, this message should be displayed in the dialog;
 *   for text systems, this should be displayed on the terminal after a
 *   newline.
 *
 *   'standard_button_set' is one of the OS_INDLG_xxx values defined
 *   below, or zero.  If this value is zero, no standard button set is to
 *   be used; the custom set of buttons defined in 'buttons' is to be used
 *   instead.  If this value is non-zero, the appropriate set of standard
 *   buttons, with labels translated to the local language if possible, is
 *   to be used.
 *     // OK
 *     #define OS_INDLG_OK            1
 *     // OK, Cancel
 *     #define OS_INDLG_OKCANCEL      2
 *     // Yes, No
 *     #define OS_INDLG_YESNO         3
 *     // Yes, No, Cancel
 *     #define OS_INDLG_YESNOCANCEL   4
 *
 *   'buttons' is an array of strings to use as button labels.
 *   'button_count' gives the number of entries in the 'buttons' array.
 *   'buttons' and 'button_count' are ignored if 'standard_button_set' is
 *   non-zero, since a standard set of buttons is used instead.  If
 *   'buttons' and 'button_count' are to be used, each entry contains the
 *   label of a button to show.
 */
/*
 *   An ampersand ('&') character in a label string indicates that the
 *   next character after the '&' is to be used as the short-cut key for
 *   the button, if supported.  The '&' should NOT be displayed in the
 *   string; instead, the character should be highlighted according to
 *   local system conventions.  On Windows, for example, the short-cut
 *   character should be shown underlined; on a text display, the response
 *   might be shown with the short-cut character enclosed in parentheses.
 *   If there is no local convention for displaying a short-cut character,
 *   then the '&' should simply be removed from the displayed text.
 *
 *   The short-cut key specified by each '&' character should be used in
 *   processing responses.  If the user presses the key corresponding to a
 *   button's short-cut, the effect should be the same as if the user
 *   clicked the button with the mouse.  If local system conventions don't
 *   allow for short-cut keys, any short-cut keys can be ignored.
 *
 *   'default_index' is the 1-based index of the button to use as the
 *   default.  If this value is zero, there is no default response.  If
 *   the user performs the appropriate system-specific action to select
 *   the default response for the dialog, this is the response that is to
 *   be selected.  On Windows, for example, pressing the "Return" key
 *   should select this item.
 *
 *   'cancel_index' is the 1-based index of the button to use as the
 *   cancel response.  If this value is zero, there is no cancel response.
 *   This is the response to be used if the user cancels the dialog using
 *   the appropriate system-specific action.  On Windows, for example,
 *   pressing the "Escape" key should select this item.
 */
/*
 *   icon_id is one of the OS_INDLG_ICON_xxx values defined below.  If
 *   possible, an appropriate icon should be displayed in the dialog.
 *   This can be ignored in text mode, and also in GUI mode if there is no
 *   appropriate system icon.
 *      // no icon
 *      // #define OS_INDLG_ICON_NONE     0
 *      // warning
 *      // #define OS_INDLG_ICON_WARNING  1
 *      // information
 *      // #define OS_INDLG_ICON_INFO     2
 *      // question
 *      // #define OS_INDLG_ICON_QUESTION 3
 *      // error
 *      // #define OS_INDLG_ICON_ERROR    4
 *
 *   The return value is the 1-based index of the response selected.  If
 *   an error occurs, return 0.
 */
// Drool.gam, inkeyext (test game) call this
int
os_input_dialog( int icon_id, const char* prompt, int standard_button_set, const char** buttons,
				int button_count, int default_index, int cancel_index )
{
	XTOSIFC_DEF_SELNAME(@"os_input_dialog");
	XTOSIFC_TRACE_6(@"icon_id=%d prompt=%s standard_button_set=%d button_count=%d default_index=%d cancel_index=%d",
					icon_id, prompt, standard_button_set, button_count, default_index, cancel_index);
	
	NSString *title = [getGameRunner() makeString:prompt];
	NSUInteger standardButtonSetId = 0;
		//TODO enum?
	NSMutableArray *customButtomSpecs = [NSMutableArray array];
	
	if (standard_button_set != 0) {
		standardButtonSetId = standard_button_set;
		//TODO? enum
	} else {
		//customButtomSpecs = [NSMutableArray array];
		for (NSUInteger i = 0; i < button_count; i++) {
			NSString *buttonString = [getGameRunner() makeString:buttons[i]];
			[customButtomSpecs addObject:buttonString];
		}
	}
	
	XTadsInputDialogIconId iconId = XTadsInputDialogIconIdNone;
	switch (icon_id) {
		case OS_INDLG_ICON_NONE:
			iconId = XTadsInputDialogIconIdNone;
			break;
		case OS_INDLG_ICON_WARNING:
			iconId = XTadsInputDialogIconIdWarning;
			break;
		case OS_INDLG_ICON_INFO:
			iconId = XTadsInputDialogIconIdInfo;
			break;
		case OS_INDLG_ICON_QUESTION:
			iconId = XTadsInputDialogIconIdQuestion;
			break;
		case OS_INDLG_ICON_ERROR:
			iconId = XTadsInputDialogIconIdError;
			break;
		default:
			XTOSIFC_WARN_1(@"got unexpected icon_id %d", icon_id);
			break;
	}
	
	NSUInteger resIndex = [getGameRunner() inputDialogWithTitle:title
											standardButtonSetId:standardButtonSetId
											  customButtomSpecs:customButtomSpecs
												   defaultIndex:default_index
													cancelIndex:cancel_index
														 iconId:iconId];
	
	XTOSIFC_TRACE_1(@"-> %d", (int)resIndex);
	
	return (int)resIndex;
}

/*
 *   Ask the user for a filename, using a system-dependent dialog or other
 *   mechanism.  Returns one of the OS_AFE_xxx status codes (see below).
 *
 *   prompt_type is the type of prompt to provide -- this is one of the
 *   OS_AFP_xxx codes (see below).  The OS implementation doesn't need to
 *   pay any attention to this parameter, but it can be used if desired to
 *   determine the type of dialog to present if the system provides
 *   different types of dialogs for different types of operations.
 *
 *   file_type is one of the OSFTxxx codes for system file type.  The OS
 *   implementation is free to ignore this information, but can use it to
 *   filter the list of files displayed if desired; this can also be used
 *   to apply a default suffix on systems that use suffixes to indicate
 *   file type.  If OSFTUNK is specified, it means that no filtering
 *   should be performed, and no default suffix should be applied.
 */
int
os_askfile( const char* prompt, char* fname_buf, int fname_buf_len, int prompt_type, os_filetype_t file_type )
{
	XTOSIFC_DEF_SELNAME(@"os_askfile");
	XTOSIFC_TRACE_2(@"prompt_type=%d file_type=%d", prompt_type, file_type);
	
	//TODO consider moving much of this logic to gameRunner impl
	
	NSString *dialogTitle = @"Select File";
	if (prompt != nil) {
		//dialogTitle = XTADS_FILESYSTEM_C_STRING_TO_NSSTRING(prompt);
		dialogTitle = [getGameRunner() makeString:prompt];
	}
	
	NSString *fileTypeDescription = @"(fileTypeDesription not specified)";
	NSArray *allowedExtension = @[];
	
	switch (file_type) {
		case OSFTGAME:
			fileTypeDescription = @"TADS 2 games";
			allowedExtension = @[XT_FILE_EXTENSION_TADS2_GAME];
			break;
		case OSFTSAVE:
			fileTypeDescription = @"TADS 2 saved sames";
			allowedExtension = @[XT_FILE_EXTENSION_TADS2_SAVE];
			break;
		case OSFTLOG:
			fileTypeDescription = @"Game transcripts";
			allowedExtension = @[XT_FILE_EXTENSION_TRANSCRIPT];
			break;
		case OSFTT3IMG:
			fileTypeDescription = @"TADS 3 games";
			allowedExtension = @[XT_FILE_EXTENSION_TADS3_GAME];
			break;
		case OSFTT3SAV:
			fileTypeDescription = @"TADS 3 saved games";
			allowedExtension = @[XT_FILE_EXTENSION_TADS3_SAVE];
			break;
		case OSFTCMD:
			fileTypeDescription = @"Command log files";
			allowedExtension = @[XT_FILE_EXTENSION_COMMAND_LOG];
			break;
		case OSFTUNK:
			// note "save" in cquest.gam, firebird.gam sends this value, don't know why
			fileTypeDescription = nil;
			allowedExtension = nil;
			break;
		default:
			XTOSIFC_ERROR_1(@"failed due to unknown file_type=%d", file_type);
			return OS_AFE_FAILURE;
	}
	
	BOOL existingFile = (prompt_type == OS_AFP_OPEN);
	
	NSURL *url = nil;
	
	switch (file_type) {
		case OSFTSAVE:
		case OSFTT3SAV:
			// Saved T2/T3 game
			url = [getGameRunner() getFileNameForFor:XTadsFileNameDialogFileTypeSavedGame
										 dialogTitle:dialogTitle
								 fileTypeDescription:fileTypeDescription
								   allowedExtensions:allowedExtension
										existingFile:existingFile];
			break;
		case OSFTLOG:
			// Transcript
			url = [getGameRunner() getFileNameForFor:XTadsFileNameDialogFileTypeTranscript
										 dialogTitle:dialogTitle
								 fileTypeDescription:fileTypeDescription
								   allowedExtensions:allowedExtension
										existingFile:existingFile];
			break;
		case OSFTCMD:
			// Command script
			url = [getGameRunner() getFileNameForFor:XTadsFileNameDialogFileTypeCommandScript
										 dialogTitle:dialogTitle
								 fileTypeDescription:fileTypeDescription
								   allowedExtensions:allowedExtension
										existingFile:existingFile];
			break;
		case OSFTUNK:
			// Unknown
			url = [getGameRunner() getFileNameForFor:XTadsFileNameDialogFileTypeGeneral
										 dialogTitle:dialogTitle
								 fileTypeDescription:fileTypeDescription
								   allowedExtensions:allowedExtension
										existingFile:existingFile];
			break;
		case OSFTGAME:
		case OSFTT3IMG:
			// Game files
			XTOSIFC_WARN_1(@"unhandled file_type %d", file_type);
			break;
		default:
			XTOSIFC_WARN_1(@"unknown file_type %d", file_type);
			break;
	}

	if (url == nil) {
		return OS_AFE_CANCEL;
	}
	
	BOOL okUrlString = [url getFileSystemRepresentation:fname_buf maxLength:(NSUInteger)fname_buf_len];
	if (okUrlString) {
		return OS_AFE_SUCCESS;
	} else {
		XTOSIFC_ERROR_1(@"couldn't convert file url \"%@\"to string", url);
		return OS_AFE_FAILURE;
	}
}

/*
 *   Open a popup menu window.  The new window should be opened at the given
 *   x,y coordinates (given in pixels, relative to the top left of the main
 *   game window), and should be drawn in a style consistent with local
 *   system conventions for context menus.  If 'default_pos' is true, ignore
 *   the x,y coordinates and place the menu at a suitable default position,
 *   according to local conventions; for example, on Windows, the default
 *   position for a context menu is at the current mouse position.  The given
 *   HTML text is to be used as the contents of the new window.  The window
 *   should be sized to fit the contents.
 *
 *   The window should have essentially the same lifetime as a context menu
 *   would have.  That is, if the user clicks the mouse outside of the popup,
 *   the popup should be automatically closed.  If the user clicks on a
 *   hyperlink in the popup, the hyperlink should fire its event as normal,
 *   and the popup window should be closed.
 *
 *   If we successfully show the menu, and the user clicks on a hyperlink
 *   item from the menu, we'll return OSPOP_HREF, and we'll fill in *evt with
 *   the event information for the hyperlink selection.  If we successfully
 *   show the menu, but the user clicks outside the menu or otherwise cancels
 *   the menu without making a selection, we'll return OSPOP_CANCEL.  If we
 *   can't show the menu, we'll return OSPOP_FAIL.  If the application is
 *   shut down while we're showing the menu (because the user closes the app,
 *   for example, or because the system is shutting down) we'll return
 *   OSPOP_EOF.
 */
int
os_show_popup_menu( int default_pos, int x, int y, const char* txt,
				   size_t txtlen, union os_event_info_t* evt )
{
	/*TODO? impl
	 if (qFrame->gameRunning()) {
	 return OSPOP_FAIL;
	 }
	 return OSPOP_EOF;
	 */
	LOG_CALL_TO_UNIMPLEMENTED_FUNCTION(@"os_show_popup_menu");
	return 0; // == OSPOP_FAIL
}
