//
//  XTPrefsWindowController.m
//  TadsTerp
//
//  Created by Rune Berg on 14/10/14.
//  Copyright (c) 2014 Rune Berg. All rights reserved.
//

#import "XTPrefsWindowController.h"
#import "XTFontManager.h"
#import "XTParameterizedFont.h"
#import "XTUIUtils.h"
#import "XTNotifications.h"
#import "XTLogger.h"
#import "XTAllocDeallocCounter.h"


@interface XTPrefsWindowController ()

@property XTUIUtils *uiUtils;
@property XTFontManager *fontManager;

@property (weak) IBOutlet NSTabView *tabView;
@property IBOutlet NSTabViewItem *devModeTabViewItem;
	// this is shown/hidden dynamically, so keep a ref. to it

// Folder / Files tab:

@property NSArray *selectableDirectoryModesForGames;
@property NSArray *selectableDirectoryModes; // for saves, transcripts, and cmd scripts
@property NSArray *selectableFileNameModesForSaves;
@property NSArray *selectableFileNameModesForTranscripts;
@property NSArray *selectableFileNameModesForCommandScripts;

@property NSArray *selectableIOSafetyModesForRead;
@property NSArray *selectableIOSafetyModesForWrite;

// Fonts tab:

@property NSString *parameterizedFontBeingSelected;
	//TODO nil when...

@property (weak) IBOutlet NSTextField *defaultGameFontTextField;
@property (weak) IBOutlet NSButton *inputFontSameAsDefaultGameFontButton;
@property (weak) IBOutlet NSButton *statusLineFontSameAsDefaultGameFontButton;
@property (weak) IBOutlet NSTextField *minAllowedFontSize;
@property (weak) IBOutlet NSTextField *maxAllowedFontSize;

// Colors tab:

@property (weak) IBOutlet NSColorWell *statusLineTextColorWell;
@property (weak) IBOutlet NSColorWell *statusLineBackgroundColorWell;
@property (weak) IBOutlet NSColorWell *outputAreaTextColorWell;
@property (weak) IBOutlet NSColorWell *outputAreaInputColor;
@property (weak) IBOutlet NSColorWell *outputAreaBackgroundColor;

// Layout tab:

@property NSArray *selectableGameWindowStartModes;

// Misc. tab:

@property NSArray *selectableScrollbackBufferSizes;
@property NSArray *selectableTads2Encodings;

// "View" fields, just for the purpose of the Prefs dialog:

@property NSURL *visibleGamesDirectory;
@property NSURL *visibleSavesDirectory;
@property NSURL *visibleTranscriptsDirectory;
@property NSURL *visibleCommandScriptsDirectory;

@end


@implementation XTPrefsWindowController

#define KEY_MODE @"mode"
#define KEY_LABEL @"label"

static XTLogger* logger;

+ (void)initialize
{
	logger = [XTLogger loggerForClass:[XTPrefsWindowController class]];
}

OVERRIDE_ALLOC_FOR_COUNTER

OVERRIDE_DEALLOC_FOR_COUNTER

- (id)initWithWindowNibName:(NSString *)windowNibName
{
	self = [super initWithWindowNibName:windowNibName];
	if (self) {
		[self myCustomInit];
	}
	return self;
}

- (void)myCustomInit
{
	_uiUtils = [XTUIUtils new];
	_fontManager = [XTFontManager fontManager];
	
	_selectableDirectoryModesForGames = @[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_DIR_MODE_NONE],
		  KEY_LABEL: @"Whatever"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_DIR_MODE_LAST_SELECTED],
		  KEY_LABEL: @"Last folder used for this:"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_DIR_MODE_SPECIFIC],
		  KEY_LABEL: @"Specific folder selected below: "}
	];
	
	_selectableDirectoryModes =	@[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_DIR_MODE_NONE],
		  KEY_LABEL: @"Whatever"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_DIR_MODE_LAST_SELECTED],
		  KEY_LABEL: @"Last folder used for this:"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_DIR_MODE_CURRENT_GAMEFILE],
		  KEY_LABEL: @"Running game's folder"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_DIR_MODE_SPECIFIC],
		  KEY_LABEL: @"Specific folder selected below:"}
	];
	
	_selectableFileNameModesForSaves = @[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_FILENAME_MODE_GAMENAME_DATETIME],
		  KEY_LABEL: @"<game file>-<timestamp>.sav|t3v"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_FILENAME_MODE_UNTITLED],
		  KEY_LABEL: @"untitled.sav|t3v"}
	];
	
	_selectableFileNameModesForTranscripts = @[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_FILENAME_MODE_GAMENAME_DATETIME],
		  KEY_LABEL: @"<game file>-<timestamp>.txt"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_FILENAME_MODE_UNTITLED],
		  KEY_LABEL: @"untitled.txt"}
	 ];
	
	_selectableFileNameModesForCommandScripts = @[
	   @{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_FILENAME_MODE_GAMENAME_DATETIME],
		 KEY_LABEL: @"<game file>-<timestamp>.cmd"},
	   @{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_FILENAME_MODE_UNTITLED],
		 KEY_LABEL: @"untitled.cmd"}
	   ];
	
	_selectableIOSafetyModesForRead = @[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_IO_SAFETY_MODE_NO_ACCESS],
		  KEY_LABEL: @"No read access"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_IO_SAFETY_MODE_GAME_DIR_ONLY],
		  KEY_LABEL: @"Read only from running game's folder"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_IO_SAFETY_MODE_ANYWHERE],
		  KEY_LABEL: @"Read from anywhere"}
	];
	
	_selectableIOSafetyModesForWrite = @[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_IO_SAFETY_MODE_NO_ACCESS],
		  KEY_LABEL: @"No write access"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_IO_SAFETY_MODE_GAME_DIR_ONLY],
		  KEY_LABEL: @"Write only to running game's folder"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_IO_SAFETY_MODE_ANYWHERE],
		  KEY_LABEL: @"Write anywhere"}
	];
	
	_selectableGameWindowStartModes = @[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_GAME_WINDOW_SAME_AS_LAST],
		  KEY_LABEL: @"Same as last game window "},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_GAME_WINDOW_NICE_IN_MIDDLE],
		  KEY_LABEL: @"Nicely in the middle of the screen "},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_GAME_WINDOW_WHATEVER],
		  KEY_LABEL: @"Whatever "}
	];
	
	_selectableScrollbackBufferSizes = @[
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_SCROLLBACK_BUFFER_SIZE_50KB],
		  KEY_LABEL: @"50 KB"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_SCROLLBACK_BUFFER_SIZE_100KB],
		  KEY_LABEL: @"100 KB"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_SCROLLBACK_BUFFER_SIZE_200KB],
		  KEY_LABEL: @"200 KB"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_SCROLLBACK_BUFFER_SIZE_500KB],
		  KEY_LABEL: @"500 KB"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_SCROLLBACK_BUFFER_SIZE_1000KB],
		  KEY_LABEL: @"1 MB"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_SCROLLBACK_BUFFER_SIZE_2000KB],
		  KEY_LABEL: @"2 MB"},
		@{KEY_MODE: [NSNumber numberWithInteger:XTPREFS_SCROLLBACK_BUFFER_SIZE_5000KB],
		  KEY_LABEL: @"5 MB"}
	];
	
	// Looks hairy, eh? Remember, we're building an array of dicts, each dict with two entries.
	NSMutableArray *tempSelectableTads2Encodings = [NSMutableArray array];
	const NSStringEncoding *encodingsZeroTermd = [NSString availableStringEncodings];
	for (const NSStringEncoding *enc = encodingsZeroTermd; *enc != 0; enc++) {
		NSNumber *encKey = [NSNumber numberWithUnsignedInteger:*enc];
		NSString *encName = [NSString localizedNameOfStringEncoding:*enc];
		if ([encName localizedCaseInsensitiveContainsString:@"UTF"] ||
			[encName localizedCaseInsensitiveContainsString:@"Unicode"]) {
			// Multi-byte encodings aren't relevant for T2
			continue;
		}
		NSMutableDictionary *tempEncEntry = [NSMutableDictionary dictionary];
		[tempEncEntry setObject:encKey forKey:KEY_MODE];
		[tempEncEntry setObject:encName forKey:KEY_LABEL];
		NSDictionary *encEntry = [NSDictionary dictionaryWithDictionary:tempEncEntry]; // so it's r/o
		[tempSelectableTads2Encodings addObject:encEntry];
	}
	_selectableTads2Encodings = tempSelectableTads2Encodings;
	
	_parameterizedFontBeingSelected = nil;
}

+ (XTPrefsWindowController *)controllerWithPrefs:(XTPrefs *)prefs
{
	XT_TRACE_ENTRY;
	
	XTPrefsWindowController *wc = [[XTPrefsWindowController alloc] initWithWindowNibName:@"XTPrefsWindowController"];
	wc.prefs = prefs;
	[wc.prefs startObservingChangesToAll:wc];
		//TODO where's the stop... for this?

	return wc;
}

- (void)windowDidLoad
{
	// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
	
	XT_TRACE_ENTRY;

	[super windowDidLoad];
    
	[self syncVisibleDirectoryFields];
	[self showOrHideDevModeTabViewItem];
}

- (void)windowWillClose:(NSNotification *)notification
{
	XT_TRACE_ENTRY;
	
	[XTNotifications notifyPrefsWindowClosed:self];
}

//------- UI event handlers -------

- (IBAction)selectGamesDirectory:(id)sender
{
	NSURL *dirUrl = [self.uiUtils runModalOpenDirectorySheetForWindow:self.window
												  defaultDirectoryUrl:self.prefs.gamesDirectoryWhenSpecific];
	if (dirUrl != nil) {
		self.prefs.gamesDirectoryWhenSpecific = dirUrl;
	}
}

- (IBAction)selectSavesDirectory:(id)sender
{
	NSURL *dirUrl = [self.uiUtils runModalOpenDirectorySheetForWindow:self.window
												  defaultDirectoryUrl:self.prefs.savesDirectoryWhenSpecific];
	if (dirUrl != nil) {
		self.prefs.savesDirectoryWhenSpecific = dirUrl;
	}
}

- (IBAction)selectTranscriptsDirectory:(id)sender
{
	NSURL *dirUrl = [self.uiUtils runModalOpenDirectorySheetForWindow:self.window
												  defaultDirectoryUrl:self.prefs.transcriptsDirectoryWhenSpecific];
	if (dirUrl != nil) {
		self.prefs.transcriptsDirectoryWhenSpecific = dirUrl;
	}
}

- (IBAction)selectCommandScriptsDirectory:(id)sender
{
	NSURL *dirUrl = [self.uiUtils runModalOpenDirectorySheetForWindow:self.window
												  defaultDirectoryUrl:self.prefs.commandScriptsDirectoryWhenSpecific];
	if (dirUrl != nil) {
		self.prefs.commandScriptsDirectoryWhenSpecific = dirUrl;
	}
}

- (IBAction)selectDefaultGameFont:(id)sender
{
	[self handleSelectFont:[self.fontManager xtadsDefaultParameterizedFontName] sender:sender];
}

- (IBAction)selectFixedWidthFont:(id)sender
{
	[self handleSelectFont:[self.fontManager xtadsFixedWidthParameterizedFontName] sender:sender];
}

- (IBAction)selectSerifedFont:(id)sender
{
	[self handleSelectFont:[self.fontManager tadsSerifParameterizedFontName] sender:sender];
}

- (IBAction)selectSansSerifFont:(id)sender
{
	[self handleSelectFont:[self.fontManager tadsSansParameterizedFontName] sender:sender];
}

- (IBAction)selectScriptFont:(id)sender
{
	[self handleSelectFont:[self.fontManager tadsScriptParameterizedFontName] sender:sender];
}

- (IBAction)selectTypeWriterFont:(id)sender
{
	[self handleSelectFont:[self.fontManager tadsTypewriterParameterizedFontName] sender:sender];
}

- (IBAction)toggleInputSameAsDefaultGameFont:(id)sender
{
	NSInteger buttonState = [sender state];
	
	if (buttonState == NSOnState) {
		[self.prefs updateInputFontWithName:self.prefs.defaultFontName
									   size:self.prefs.defaultFontSize.floatValue];
		self.prefs.inputFontUsedEvenIfNotRequestedByGame = [NSNumber numberWithBool:YES];
	} else {
		// leave input font as it is
	}
}

- (IBAction)selectInputFont:(id)sender
{
	[self handleSelectFont:[self.fontManager tadsInputParameterizedFontName] sender:sender];
}

- (IBAction)resetDefaultDirsAndFiles:(id)sender
{
	[self.prefs resetDefaultDirsAndFiles];
}

- (IBAction)resetDefaultFonts:(id)sender
{
	[self.prefs resetDefaultFonts];
}

- (IBAction)resetDefaultColors:(id)sender
{
	[self.prefs resetDefaultColors];
}

- (IBAction)resetDefaultLayout:(id)sender
{
	[self.prefs resetDefaultLayout];
}

- (IBAction)resetDefaultIOSafetyModes:(id)sender {
	[self.prefs resetDefaultIOSafetyModes];
}

- (IBAction)resetDefaultMisc:(id)sender {
	[self.prefs resetDefaultMisc];
	[self showOrHideDevModeTabViewItem];
}

- (IBAction)resetDefaultDevMode:(id)sender {
	[self.prefs resetDefaultDevMode];
}

// If user left field blank, fill in default value
- (IBAction)selectMinAllowedFontSize:(id)sender {

	NSString *s = [self.minAllowedFontSize stringValue];
	if (s == nil || s.length == 0) {
		NSInteger i = self.prefs.minMinAllowedFontSize.integerValue;
		[self.minAllowedFontSize setIntegerValue:i];
	}
}

// If user left field blank, fill in default value
- (IBAction)selectMaxAllowedFontSize:(id)sender {
	
	NSString *s = [self.maxAllowedFontSize stringValue];
	if (s == nil || s.length == 0) {
		NSInteger i = self.prefs.maxMaxAllowedFontSize.integerValue;
		[self.maxAllowedFontSize setIntegerValue:i];
	}
}

- (IBAction)setEnableDevModeFeatures:(id)sender {
	
	[self showOrHideDevModeTabViewItem];
}

- (void)showOrHideDevModeTabViewItem
{
	BOOL isShowingDevModeItem = (self.tabView.tabViewItems.count == 7);
		//TODO retest
	
	if (self.prefs.enableDevelopmentModeFeatures.boolValue) {
		if (! isShowingDevModeItem) {
			[self.tabView addTabViewItem:self.devModeTabViewItem];
		}
	} else {
		if (isShowingDevModeItem) {
			[self.tabView removeTabViewItem:self.devModeTabViewItem];
			[self.prefs resetDefaultDevMode];
		}
	}
}

- (void)handleSelectFont:(NSString *)parameterizedFontName sender:(id)sender
{
	XT_DEF_SELNAME;
	XT_TRACE_1(@"parameterizedFontName=\"%@\"", parameterizedFontName);

	// Remove focus from the text fields - otherwise our changeFont: isn't called.
	[[self.minAllowedFontSize window] makeFirstResponder:nil];
	
	//TODO modal issues?
	
	//TODO check that pFN exists
	
	self.parameterizedFontBeingSelected = parameterizedFontName;
	
	NSFontManager *fontManager = [NSFontManager sharedFontManager];
	//[fontManager setDelegate:self];
	NSFont *oldFont = [self.fontManager getFontWithParameterizedName:parameterizedFontName];
	//TODO make method in xtads f.m. class
	
	XT_TRACE_1(@"oldFont=\"%@\"", oldFont.description);
	
	[fontManager setSelectedFont:oldFont isMultiple:NO];
	
	NSFontPanel *fontPanel = [NSFontPanel sharedFontPanel];
	[fontPanel makeKeyAndOrderFront:sender];
	
	// [fontManager setTarget:self]; ???
}

- (void)changeFont:(id)sender
{
	XT_DEF_SELNAME;

	NSString *paramFontName = self.parameterizedFontBeingSelected;

	NSFont *oldFont = [self.fontManager getFontWithParameterizedName:paramFontName];
		//TODO make method in xtads f.m. class
		//TODO ...or mv to f.m.
	
	XT_TRACE_1(@"oldFont=\"%@\"", oldFont.description);
	
	NSFont *newFont = [sender convertFont:oldFont];
	
	[self.fontManager setFont:newFont forParameterizedName:paramFontName];

	[self toggleInputSameAsDefaultGameFont:self.inputFontSameAsDefaultGameFontButton];
}

- (void)syncVisibleDirectoryFields {
	
	NSURL *tempVisibleGamesDirectory = nil;
	if (self.prefs.gamesDirectoryMode == XTPREFS_DIR_MODE_SPECIFIC) {
		tempVisibleGamesDirectory = self.prefs.gamesDirectoryWhenSpecific;
	} else if (self.prefs.gamesDirectoryMode == XTPREFS_DIR_MODE_LAST_SELECTED) {
		tempVisibleGamesDirectory = self.prefs.gamesDirectoryLastUsed;
	}
	self.visibleGamesDirectory = tempVisibleGamesDirectory;
	
	NSURL *tempVisibleSavesDirectory = nil;
	if (self.prefs.savesDirectoryMode == XTPREFS_DIR_MODE_SPECIFIC) {
		tempVisibleSavesDirectory = self.prefs.savesDirectoryWhenSpecific;
	} else if (self.prefs.savesDirectoryMode == XTPREFS_DIR_MODE_LAST_SELECTED) {
		tempVisibleSavesDirectory = self.prefs.savesDirectoryLastUsed;
	}
	self.visibleSavesDirectory = tempVisibleSavesDirectory;
	
	NSURL *tempVisibleTranscriptsDirectory = nil;
	if (self.prefs.transcriptsDirectoryMode == XTPREFS_DIR_MODE_SPECIFIC) {
		tempVisibleTranscriptsDirectory = self.prefs.transcriptsDirectoryWhenSpecific;
	} else if (self.prefs.transcriptsDirectoryMode == XTPREFS_DIR_MODE_LAST_SELECTED) {
		tempVisibleTranscriptsDirectory = self.prefs.transcriptsDirectoryLastUsed;
	}
	self.visibleTranscriptsDirectory = tempVisibleTranscriptsDirectory;
	
	NSURL *tempVisibleCommandScriptsDirectory = nil;
	if (self.prefs.commandScriptsDirectoryMode == XTPREFS_DIR_MODE_SPECIFIC) {
		tempVisibleCommandScriptsDirectory = self.prefs.commandScriptsDirectoryWhenSpecific;
	} else if (self.prefs.commandScriptsDirectoryMode == XTPREFS_DIR_MODE_LAST_SELECTED) {
		tempVisibleCommandScriptsDirectory = self.prefs.commandScriptsDirectoryLastUsed;
	}
	self.visibleCommandScriptsDirectory = tempVisibleCommandScriptsDirectory;
}

//------- KVO on Prefs object -------

- (void)observeValueForKeyPath:(NSString *)keyPath
					  ofObject:(id)object
						change:(NSDictionary *)change
					   context:(void *)context
{
	XT_DEF_SELNAME;
	XT_TRACE_1(@"keyPath=\"%@\"", keyPath);
	
	if (object == self.prefs) {
		[self.prefs persist];
	}
	
	// Refresh members that are bound to the dialog window:
	[self syncVisibleDirectoryFields];
}

@end
