// Copyright 1997-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import "OAPreferenceController.h"

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>

#import "OAPreferenceClient.h"
#import "OAPreferenceClientRecord.h"

RCS_ID("$Header: /Network/Developer/Source/CVS/OmniGroup/OmniAppKit/Preferences.subproj/OAPreferenceController.m,v 1.12 1999/03/30 21:26:08 kc Exp $")

@interface OAPreferenceController (Private)
- (void)loadInterface;
- (void)resetWindowTitle;
- (void)registerClassName:(NSString *)className title:(NSString *)title iconName:(NSString *)iconName iconTitle:(NSString *)iconTitle nibName:(NSString *)nibName orderingString:(NSString *)orderingString defaultsDictionary:(NSDictionary *)defaultsDictionary defaultsArray:(NSArray *)defaultsArray;
- (void)setCurrentClientIndex:(int)clientIndex;
@end


@implementation OAPreferenceController

static NSString *windowFrameSaveName = @"Preferences";
static NSMutableDictionary *nameToControllerDictionary;


+ (void)initialize;
{
    static BOOL initialized = NO;

    [super initialize];
    if (initialized)
        return;
    initialized = YES;

    nameToControllerDictionary = [[NSMutableDictionary alloc] init];
}

// OFBundleRegistryTarget informal protocol

+ (void)registerItemName:(NSString *)itemName bundle:(NSBundle *)bundle description:(NSDictionary *)description;
{
    NSString *controllerCategoryName;
    OAPreferenceController *preferenceController;
    
    [OFBundledClass createBundledClassWithName:itemName bundle:bundle description:description];

    if (!(controllerCategoryName = [description objectForKey:@"category"]))
        controllerCategoryName = @"";

    preferenceController = [self sharedPreferenceControllerNamed:controllerCategoryName];
    [preferenceController registerClassName:itemName title:[description objectForKey:@"title"] iconName:[description objectForKey:@"icon"] iconTitle:[description objectForKey:@"iconTitle"] nibName:[description objectForKey:@"nib"] orderingString:[description objectForKey:@"ordering"] defaultsDictionary:[description objectForKey:@"defaultsDictionary"] defaultsArray:[description objectForKey:@"defaultsArray"]];
}


// Init and dealloc

+ (OAPreferenceController *)sharedPreferenceControllerNamed:(NSString *)controllerCategoryName;
{
    OAPreferenceController *sharedPreferenceController;
    
    sharedPreferenceController = [nameToControllerDictionary objectForKey:controllerCategoryName];
    if (!sharedPreferenceController) {
        sharedPreferenceController = [[self alloc] initWithCategoryName:controllerCategoryName];
        [nameToControllerDictionary setObject:sharedPreferenceController forKey:controllerCategoryName];
        [sharedPreferenceController release];
    }
    
    return sharedPreferenceController;
}

+ (OAPreferenceController *)sharedPreferenceController;
{
    return [self sharedPreferenceControllerNamed:@""];
}

- initWithCategoryName:(NSString *)controllerCategoryName;
{
    [super init];
	
    categoryName = [controllerCategoryName copy];
    clientRecordsArray = [[NSMutableArray alloc] initWithCapacity:16];
    clientClassNamesToClientRecordDictionary = [[NSMutableDictionary alloc] initWithCapacity:16];
    clientClassNamesToInstancesDictionary = [[NSMutableDictionary alloc] initWithCapacity:16];
    
    return self;
}

- init;
{
    return [self initWithCategoryName:@""];
}

- (void)dealloc;
{
    // NO!
}


// API

- (OAPreferenceClient *)clientNamed:(NSString *)clientName;
{
    OAPreferenceClient *client;
    OAPreferenceClientRecord *clientRecord;
    Class clientClass;
    NSString *nibFilename;
    NSArray *defaultsArray;

    client = [clientClassNamesToInstancesDictionary objectForKey:clientName];
    if (client)
        return client;

    clientRecord = [clientClassNamesToClientRecordDictionary objectForKey:clientName];

    [window setTitle:[NSString stringWithFormat:@"Loading %@...", [clientRecord title]]];

    clientClass = [OFBundledClass classNamed:clientName];
    defaultsArray = [clientRecord defaultsArray];
    if (!defaultsArray)
        defaultsArray = [[clientRecord defaultsDictionary] allKeys];
    client = [[clientClass alloc] initWithTitle:[clientRecord title] defaultsArray:defaultsArray];

    nibFilename = [clientRecord nibName];
    if (nibFilename)
        [NSBundle loadNibNamed:nibFilename owner:client];
    [clientClassNamesToInstancesDictionary setObject:client forKey:clientName];
    [client release];

    [self resetWindowTitle];
    
    return client;
}

- (void)close;
{
    if ([window isVisible])
        [window performClose:nil];
}


// Actions

- (IBAction)showPreferencesPanel:(id)sender;
{
    NSEnumerator *objectEnumerator;
    OAPreferenceController *controller;
    
    [self loadInterface];

    objectEnumerator = [nameToControllerDictionary objectEnumerator];
    while ((controller = [objectEnumerator nextObject]))
        if (controller != self)
            [controller close];

    [self resetWindowTitle];
    [window makeKeyAndOrderFront:sender];
}

- (IBAction)selectPreference:(NSTableView *)sender;
{
    [self setCurrentClientIndex:[sender clickedRow]];
}

- (IBAction)restoreDefaults:(id)sender;
{
    [currentClient restoreDefaults:sender];
}


// NSTableDataSource informal protocol

- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
{
    return [clientRecordsArray count];
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex;
{
    return [(OAPreferenceClientRecord *)[clientRecordsArray objectAtIndex:rowIndex] iconTitle];
}


// NSWindow delegate

- (void)windowWillClose:(NSNotification *)notification;
{
    [[notification object] makeFirstResponder:nil];
}

- (void)windowDidResignKey:(NSNotification *)notification;
{
    [[notification object] makeFirstResponder:nil];
}

@end


@implementation OAPreferenceController (Private)

- (void)loadInterface;
{
    if (window)
        return;

    [NSBundle loadNibNamed:@"OAPreferences.nib" owner:self];

    if (![window setFrameUsingName:windowFrameSaveName])
        [window center];
    [window setFrameAutosaveName:windowFrameSaveName];

    preferredBoxSize = [[box contentView] bounds].size;
    windowExtraSize = [window frame].size;
    windowExtraSize.width -= preferredBoxSize.width;
    windowExtraSize.height -= preferredBoxSize.height;

    [tableView setHeaderView:nil];
    [tableView setCornerView:nil];
    
    [tableView reloadData];
    if ([clientRecordsArray count] == 0)
        return;

    [tableView selectRow:0 byExtendingSelection:NO];
    [self setCurrentClientIndex:0];
}

- (void)resetWindowTitle;
{
    if ([categoryName length] > 0)
        [window setTitle:[categoryName stringByAppendingString:@" Preferences"]];
    else
        [window setTitle:@"Preferences"];
}

- (void)registerClassName:(NSString *)className title:(NSString *)title iconName:(NSString *)iconName iconTitle:(NSString *)iconTitle nibName:(NSString *)nibName orderingString:(NSString *)orderingString defaultsDictionary:(NSDictionary *)defaultsDictionary defaultsArray:(NSArray *)defaultsArray;
{
    OAPreferenceClientRecord *newRecord;
    unsigned int insertionIndex, preferenceCount;

    if (defaultsDictionary)
        [[OFUserDefaults sharedUserDefaults] registerDefaults:defaultsDictionary];
    else
        defaultsDictionary = [NSDictionary dictionary]; // placeholder

    if (!(className && title && iconName && nibName))
        return;

    newRecord = [[OAPreferenceClientRecord alloc] init];
    [newRecord setClassName:className];
    [newRecord setTitle:title];
    [newRecord setIconName:iconName];
    [newRecord setIconTitle:iconTitle ? iconTitle : title];
    [newRecord setNibName:nibName];
    [newRecord setDefaultsDictionary:defaultsDictionary];
    [newRecord setDefaultsArray:defaultsArray];

    if (orderingString)
        [newRecord setOrdering:[orderingString floatValue]];
    else
        [newRecord setOrdering:100.0];

    preferenceCount = [clientRecordsArray count];
    for (insertionIndex = 0; insertionIndex < preferenceCount; insertionIndex++) {
        OAPreferenceClientRecord *otherRecord;

        otherRecord = [clientRecordsArray objectAtIndex:insertionIndex];
        if ([newRecord ordering] < [otherRecord ordering])
            break;
    }

    [clientRecordsArray insertObject:newRecord atIndex:insertionIndex];
    [clientClassNamesToClientRecordDictionary setObject:newRecord forKey:[newRecord className]];
    
    [newRecord release];
}

// Setting the current preference client

- (void)setCurrentClientIndex:(int)clientIndex;
{
    OAPreferenceClientRecord *clientRecord;
    NSView *contentView, *controlBox;
    NSRect newBoxFrame, windowFrame;
    NSSize newWindowSize;

    clientRecord = [clientRecordsArray objectAtIndex:clientIndex];
    currentClient = [self clientNamed:[clientRecord className]];

    [tableView scrollRowToVisible:clientIndex];

    // Save changes in any editing text fields
    [window setInitialFirstResponder:nil];
    [window makeFirstResponder:nil];
    
    // Remove old client box
    contentView = [box contentView];
    [[[contentView subviews] lastObject] removeFromSuperview];
    OBASSERT([[contentView subviews] count] == 0);

    // Compute the new window size for the new client box
    controlBox = [currentClient controlBox];
    newBoxFrame.size = [controlBox frame].size;
    if (newBoxFrame.size.width < preferredBoxSize.width)
        newBoxFrame.size.width = preferredBoxSize.width;
    if (newBoxFrame.size.height < preferredBoxSize.height)
        newBoxFrame.size.height = preferredBoxSize.height;
    newWindowSize.width = newBoxFrame.size.width + windowExtraSize.width;
    newWindowSize.height = newBoxFrame.size.height + windowExtraSize.height;
    
    // Resize the window
    // We don't just tell the window to resize, because that tends to move the upper left corner (which will confuse the user)
    windowFrame = [window frame];
    windowFrame.origin.y += windowFrame.size.height - newWindowSize.height;
    windowFrame.size = newWindowSize;
    [window setFrame:windowFrame display:NO];
    
    // Add the new client box to the view hierarchy
    newBoxFrame.origin.x = 0;
    newBoxFrame.origin.y = 0;
    [controlBox setFrame:newBoxFrame];
    [contentView addSubview:controlBox];

    // Set title textField
    [titleTextField setStringValue:[clientRecord title]];
    
    // Set icon in imageView
    [imageView setImage:[clientRecord iconImage]];

    [currentClient updateUI];

    // Highlight the initial first responder, and also tell the window what it should be because I think there is some voodoo with nextKeyView not working unless the window has an initial first responder.
    [window setInitialFirstResponder:[currentClient initialFirstResponder]];
    [window makeFirstResponder:[currentClient initialFirstResponder]];
}

@end
