/*
 * Apple // emulator for *ix 
 *
 * This software package is subject to the GNU General Public License
 * version 3 or later (your choice) as published by the Free Software
 * Foundation.
 *
 * Copyright 2013-2015 Aaron Culliney
 *
 */

#import "EmulatorPrefsController.h"
#import "EmulatorDiskController.h"
#import "EmulatorJoystickCalibrationView.h"
#import "EmulatorWindowController.h"
#import "common.h"

#define kApple2SavedPrefs @"kApple2SavedPrefs"
#define kApple2CPUSpeed @"kApple2CPUSpeed"
#define kApple2CPUSpeedIsMax @"kApple2CPUSpeedIsMax"
#define kApple2AltSpeed @"kApple2AltSpeed"
#define kApple2AltSpeedIsMax @"kApple2AltSpeedIsMax"
#define kApple2SoundcardConfig @"kApple2SoundcardConfig"
#define kApple2ColorConfig @"kApple2ColorConfig"
#define kApple2JoystickConfig @"kApple2JoystickConfig"
#define kApple2JoystickAutoRecenter @"kApple2JoystickAutoRecenter"
#define kApple2JoystickClipToRadius @"kApple2JoystickClipToRadius"
#define kApple2JoystickStep @"kApple2JoystickStep"

@interface EmulatorPrefsController ()

@property (assign) IBOutlet NSSlider *cpuSlider;
@property (assign) IBOutlet NSSlider *altSlider;
@property (assign) IBOutlet NSTextField *cpuSliderLabel;
@property (assign) IBOutlet NSTextField *altSliderLabel;
@property (assign) IBOutlet NSButton *cpuMaxChoice;
@property (assign) IBOutlet NSButton *altMaxChoice;

@property (assign) IBOutlet NSMatrix *soundCardChoice;

@property (assign) IBOutlet NSPopUpButton *colorChoice;

@property (assign) IBOutlet NSPopUpButton *joystickChoice;
@property (assign) IBOutlet NSButton *joystickRecenter;
@property (assign) IBOutlet NSButton *joystickClipToRadius;
@property (assign) IBOutlet NSTextField *joystickStepLabel;
@property (assign) IBOutlet NSStepper *joystickStepper;
@property (assign) IBOutlet NSTextField *joystickStepperLabel;
@property (assign) IBOutlet NSTextField *joystickKPadNotes;
@property (assign) IBOutlet NSTextField *joystickDeviceNotes;

@property (assign) IBOutlet NSTextField *button0Pressed;
@property (assign) IBOutlet NSTextField *button1Pressed;
@property (assign) IBOutlet EmulatorJoystickCalibrationView *joystickCalibrationView;

@end

@implementation EmulatorPrefsController

- (void)awakeFromNib
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    BOOL firstTime = ![defaults boolForKey:kApple2SavedPrefs];
    if (firstTime)
    {
        [defaults setBool:YES forKey:kApple2SavedPrefs];
        [defaults setDouble:1.0 forKey:kApple2CPUSpeed];
        [defaults setDouble:CPU_SCALE_SLOWEST forKey:kApple2AltSpeed];
        [defaults setBool:NO forKey:kApple2CPUSpeedIsMax];
        [defaults setBool:NO forKey:kApple2AltSpeedIsMax];
        [defaults setInteger:COLOR_INTERP forKey:kApple2ColorConfig];
        [defaults setInteger:JOY_KPAD forKey:kApple2JoystickConfig];
        [defaults setBool:YES forKey:kApple2JoystickAutoRecenter];
        [defaults removeObjectForKey:kApple2PrefStartupDiskA];
        [defaults removeObjectForKey:kApple2PrefStartupDiskB];
    }
    
    cpu_scale_factor = [defaults doubleForKey:kApple2CPUSpeed];
    [self.cpuSlider setDoubleValue:cpu_scale_factor];
    [self.cpuSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", cpu_scale_factor*100]];
    if ([defaults boolForKey:kApple2CPUSpeedIsMax])
    {
        cpu_scale_factor = CPU_SCALE_FASTEST;
        [self.cpuMaxChoice setState:NSOnState];
        [self.cpuSlider setEnabled:NO];
    }
    else
    {
        [self.cpuMaxChoice setState:NSOffState];
        [self.cpuSlider setEnabled:YES];
    }
    
    cpu_altscale_factor = [defaults doubleForKey:kApple2AltSpeed];
    [self.altSlider setDoubleValue:cpu_altscale_factor];
    [self.altSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", cpu_altscale_factor*100]];
    if ([defaults boolForKey:kApple2AltSpeedIsMax])
    {
        cpu_altscale_factor = CPU_SCALE_FASTEST;
        [self.altMaxChoice setState:NSOnState];
        [self.altSlider setEnabled:NO];
    }
    else
    {
        [self.altMaxChoice setState:NSOffState];
        [self.altSlider setEnabled:YES];
    }
    
#warning TODO : actually implement sound card choices
    [self.soundCardChoice deselectAllCells];
    [self.soundCardChoice selectCellAtRow:1 column:0];
    
    NSInteger mode = [defaults integerForKey:kApple2ColorConfig];
    if (! ((mode >= COLOR_NONE) && (mode < NUM_COLOROPTS)) )
    {
        mode = COLOR_NONE;
    }
    [self.colorChoice selectItemAtIndex:mode];
    prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, (color_mode_t)mode);
    prefs_sync(PREF_DOMAIN_VIDEO);

    mode = [defaults integerForKey:kApple2JoystickConfig];
    if (! ((mode >= JOY_PCJOY) && (mode < NUM_JOYOPTS)) )
    {
        mode = JOY_PCJOY;
    }
    joy_mode = (joystick_mode_t)mode;
    [self.joystickChoice selectItemAtIndex:mode];
    
#ifdef KEYPAD_JOYSTICK
    bool autoRecenter = [defaults integerForKey:kApple2JoystickAutoRecenter];
    [self.joystickRecenter setState:autoRecenter ? NSOnState : NSOffState];
    prefs_setBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, autoRecenter);
    
    long joyStep = [defaults integerForKey:kApple2JoystickStep];
    if (!joyStep)
    {
        joyStep = 1;
    }
    [self.joystickStepLabel setIntegerValue:joyStep];
    [self.joystickStepper setIntegerValue:joyStep];
    prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_STEP, joyStep);
    
    prefs_sync(PREF_DOMAIN_JOYSTICK);
#endif
    
    joy_clip_to_radius = [defaults boolForKey:kApple2JoystickClipToRadius];
    [self.joystickClipToRadius setState:joy_clip_to_radius ? NSOnState : NSOffState];
    
    [self _setupJoystickUI];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(drawJoystickCalibration:) name:(NSString *)kDrawTimerNotification object:nil];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}

- (void)_savePrefs
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setBool:YES forKey:kApple2SavedPrefs];
    [defaults setDouble:cpu_scale_factor forKey:kApple2CPUSpeed];
    [defaults setDouble:cpu_altscale_factor forKey:kApple2AltSpeed];
    [defaults setBool:([self.cpuMaxChoice state] == NSOnState) forKey:kApple2CPUSpeedIsMax];
    [defaults setBool:([self.altMaxChoice state] == NSOnState) forKey:kApple2AltSpeedIsMax];
    
    long lVal = 0;
    color_mode_t mode = prefs_parseLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, &lVal, /*base:*/10) ? (color_mode_t)lVal : COLOR_INTERP;
    [defaults setInteger:mode forKey:kApple2ColorConfig];
    [defaults setInteger:joy_mode forKey:kApple2JoystickConfig];
    
    long joyStep = prefs_parseLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_STEP, &lVal, /*base:*/10) ? lVal : 1;
    [defaults setInteger:joyStep forKey:kApple2JoystickStep];
    
    bool bVal = false;
    bool autoRecenter = prefs_parseBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, &bVal) ? bVal : true;
    [defaults setBool:autoRecenter forKey:kApple2JoystickAutoRecenter];
    [defaults setBool:joy_clip_to_radius forKey:kApple2JoystickClipToRadius];
    
    prefs_sync(PREF_DOMAIN_JOYSTICK);
}

- (IBAction)sliderDidMove:(id)sender
{
    NSSlider *slider = (NSSlider *)sender;
    double value = [slider doubleValue];
    if (slider == self.cpuSlider)
    {
        cpu_scale_factor = value;
        [self.cpuSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", value*100]];
    }
    else
    {
        cpu_altscale_factor = value;
        [self.altSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", value*100]];
    }
    
    timing_initialize();
    
    [self _savePrefs];
}

- (IBAction)peggedChoiceChanged:(id)sender
{
    NSButton *maxButton = (NSButton *)sender;
    if (maxButton == self.cpuMaxChoice)
    {
        [self.cpuSlider setEnabled:([maxButton state] != NSOnState)];
        cpu_scale_factor = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.cpuSlider doubleValue];
    }
    else
    {
        [self.altSlider setEnabled:([maxButton state] != NSOnState)];
        cpu_altscale_factor = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.altSlider doubleValue];
    }
    
    timing_initialize();

    [self _savePrefs];
}

- (IBAction)colorChoiceChanged:(id)sender
{
    NSInteger mode = [self.colorChoice indexOfSelectedItem];
    if (! ((mode >= COLOR_NONE) && (mode < NUM_COLOROPTS)) )
    {
        mode = COLOR_NONE;
    }
    prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, mode);
    prefs_sync(PREF_DOMAIN_VIDEO);
    [self _savePrefs];
    
#warning HACK TODO FIXME need to refactor video resetting procedure
    video_reset();
}

- (IBAction)soundCardChoiceChanged:(id)sender
{
#warning TODO : make soundcard configurable at runtime
}

#pragma mark -
#pragma mark Joystick preferences

- (void)_setupJoystickUI
{
    [self.joystickRecenter setHidden:(joy_mode == JOY_PCJOY)];
    [self.joystickClipToRadius setHidden:(joy_mode != JOY_PCJOY)];
    [self.joystickStepLabel setHidden:(joy_mode == JOY_PCJOY)];
    [self.joystickStepper setHidden:(joy_mode == JOY_PCJOY)];
    [self.joystickStepperLabel setHidden:(joy_mode == JOY_PCJOY)];
    [self.joystickKPadNotes setHidden:(joy_mode == JOY_PCJOY)];
    [self.joystickDeviceNotes setHidden:(joy_mode != JOY_PCJOY)];
}

- (IBAction)joystickChoiceChanged:(id)sender
{
    NSInteger mode = [self.joystickChoice indexOfSelectedItem];
    if (! ((mode >= JOY_PCJOY) && (mode < NUM_JOYOPTS)) )
    {
        mode = JOY_PCJOY;
    }
    joy_mode = (joystick_mode_t)mode;
    [self _setupJoystickUI];
    [self _savePrefs];
}

- (IBAction)autoRecenterChoiceChanged:(id)sender
{
    bool autoRecenter = ([self.joystickRecenter state] == NSOnState);
    prefs_setBoolValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, autoRecenter);
    [self _savePrefs];
}

- (IBAction)clipToRadiusChoiceChanged:(id)sender
{
    joy_clip_to_radius = ([self.joystickClipToRadius state] == NSOnState);
    [self _savePrefs];
}

- (IBAction)stepValueChanged:(id)sender
{
    long joyStep = [self.joystickStepper intValue];
    [self.joystickStepLabel setIntegerValue:joyStep];
    prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_KPAD_AUTO_RECENTER, joyStep);
    [self _savePrefs];
}

#pragma mark -
#pragma Joystick calibration

- (void)drawJoystickCalibration:(NSNotification *)notification
{
    if (![self.joystickCalibrationView isHidden])
    {
        [self.joystickCalibrationView setNeedsDisplay:YES];
        [self.button0Pressed setHidden:!(joy_button0)];
        [self.button1Pressed setHidden:!(joy_button1)];
    }
}

- (void)flagsChanged:(NSEvent*)event
{
    // NOTE : yay, awesome! checking for ([event modifierFlags] & NSAlternateKeyMask) does not work properly if both were pressed and then one ALT key is unpressed ... kudoes to Apple for an excellent key-handling API /sarc
    static BOOL leftAltEngaged = NO;
    static BOOL rightAltEngaged = NO;
    switch ([event keyCode])
    {
        case ALT_LT:
            leftAltEngaged = !leftAltEngaged;
            c_keys_handle_input(SCODE_L_ALT, /*pressed:*/leftAltEngaged, /*cooked:*/0);
            break;
        case ALT_RT:
            rightAltEngaged = !rightAltEngaged;
            c_keys_handle_input(SCODE_R_ALT, /*pressed:*/rightAltEngaged, /*cooked:*/0);
            break;
        default:
            break;
    }
    if (!([event modifierFlags] & NSAlternateKeyMask))
    {
        // But we can trust the system state if no alt modifier exists ... this resets b0rken edge cases
        leftAltEngaged = NO;
        rightAltEngaged = NO;
    }
    [self.button0Pressed setHidden:!(joy_button0)];
    [self.button1Pressed setHidden:!(joy_button1)];
}

- (void)_handleKeyEvent:(NSEvent *)event pressed:(BOOL)pressed
{
    unichar c = [[event charactersIgnoringModifiers] characterAtIndex:0];
    int scode = (int)c;
    switch (scode)
    {
        case NSUpArrowFunctionKey:
            c_keys_handle_input(SCODE_U, pressed, /*cooked:*/0);
            break;
        case NSDownArrowFunctionKey:
            c_keys_handle_input(SCODE_D, pressed, /*cooked:*/0);
            break;
        case NSLeftArrowFunctionKey:
            c_keys_handle_input(SCODE_L, pressed, /*cooked:*/0);
            break;
        case NSRightArrowFunctionKey:
            c_keys_handle_input(SCODE_R, pressed, /*cooked:*/0);
            break;
        default:
            break;
    }
}

- (void)keyUp:(NSEvent *)event
{
    [self _handleKeyEvent:event pressed:NO];
    
    // Allow other character to be handled (or not and beep)
    //[super keyDown:event];
}

- (void)keyDown:(NSEvent *)event
{
    [self _handleKeyEvent:event pressed:YES];
    
    // Allow other character to be handled (or not and beep)
    //[super keyDown:event];
}

@end
