/*
 * PrompterController.m
 *
 * $Header$
 *
 * Copyright 2004 Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 * require a specific license from the United States Government.
 * It is the responsibility of any person or organization contemplating
 * export to obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */

#import "KerberosAgentController.h"
#import "PrompterController.h"
#import "AuthenticationController.h"
#import "ChangePasswordController.h"
#import "ErrorAlert.h"

#define ResponseSpacing 16
#define PromptSpacing   12

#pragma mark -

// ---------------------------------------------------------------------------

krb5_error_code GraphicalKerberosPrompter (krb5_context  context,
                                           void         *data,
                                           const char   *name,
                                           const char   *banner,
                                           int           num_prompts,
                                           krb5_prompt   prompts[])
{
    krb5_error_code err = 0;
    int i;
    NSMutableArray *promptsArray = NULL;
    PrompterController *controller = NULL;
    
    if (!err) {
        promptsArray = [[NSMutableArray alloc] init];
        if (promptsArray == NULL) { err = ENOMEM; }
    }
    
    if (!err) {
        for (i = 0; i < num_prompts; i++) {
            Prompt *prompt = [[Prompt alloc] initWithPrompt: &prompts[i]];
            if (prompt == NULL) { err = ENOMEM; break; }
            
            [promptsArray addObject: prompt];
            [prompt release];
        }
    }
    
    if (!err) {
        NSString *message     = (name   != NULL) ? [NSString stringWithUTF8String: name]   : NULL;
        NSString *description = (banner != NULL) ? [NSString stringWithUTF8String: banner] : NULL;;

        controller = [[PrompterController alloc] initWithTitle: message
                                                        banner: description
                                                       prompts: promptsArray];
        if (controller == NULL) { err = klFatalDialogErr; }
    }
    
    if (!err) {
        NSWindow *parentWindow = [[NSApp delegate] activeWindow];
        if (parentWindow != NULL) {
            err = [controller runSheetModalForWindow: parentWindow];
        } else {
            err = [controller runWindow];
        }
    }
    
    if (!err) {
        for (i = 0; i < num_prompts; i++) {
            Prompt *prompt = [promptsArray objectAtIndex: i];
            [prompt saveResponseInPrompt: &prompts[i]];
        }        
    }
    
    [promptsArray release];
    [controller release];
    
    return err;
}

#pragma mark -

@implementation Prompt

// ---------------------------------------------------------------------------

- (id) initWithPrompt: (krb5_prompt *) prompt
{
    if ((self = [super init])) {
        promptString = NULL;
        responseString = NULL;
        
        if (prompt == NULL) {
            [self release];
            return NULL;
        }
        
        promptString = [[NSString alloc] initWithCString: prompt->prompt];
        if (promptString == NULL) {
            [self release];
            return NULL;
        }
        
        secure = prompt->hidden;
        
        responseString = [[NSMutableString alloc] init];
        if (responseString == NULL) {
            [self release];
            return NULL;
        }
    }
    return self;
}

// ---------------------------------------------------------------------------

- (void) dealloc
{
    if (promptString   != NULL) { [promptString release]; }
    if (responseString != NULL) { [responseString release]; }
    
    [super dealloc];
}

// ---------------------------------------------------------------------------


- (NSString *) prompt
{
    return promptString;
}

// ---------------------------------------------------------------------------

- (BOOL) secure
{
    return secure;
}

// ---------------------------------------------------------------------------

- (void) setResponse: (NSString *) response
{
    [responseString setString: response];
}

// ---------------------------------------------------------------------------

- (void) saveResponseInPrompt: (krb5_prompt *) prompt
{
    if (prompt != NULL) {
        const char *response = [responseString UTF8String];
        
        unsigned int responseLength = strlen (response) + 1;
        unsigned int replyLength = (responseLength >= prompt->reply->length) ? prompt->reply->length : responseLength;

        memmove (prompt->reply->data, response, replyLength * sizeof (char));
        prompt->reply->data[replyLength - 1] = '\0';
        prompt->reply->length = strlen (prompt->reply->data);
    }
}

@end

#pragma mark -

@implementation PrompterController

// ---------------------------------------------------------------------------

- (id) initWithTitle: (NSString *) title
              banner: (NSString *) banner
             prompts: (NSArray *) prompts
{
    if ((self = [super initWithWindowNibName: @"PrompterController"])) {
        dprintf ("PrompterController initializing");
        
        result = klNoErr;
        titleString = NULL;
        bannerString = NULL;
        promptsArray = NULL;
        responseTextFieldArray = NULL;
        applicationNameString = NULL;
        applicationIconPathString = NULL;
        isSheet = NO;
        
        responseTextFieldArray = [[NSMutableArray alloc] init];
        if (responseTextFieldArray == NULL) {
            [self release];
            return NULL;
        }

        if (title   != NULL) { titleString  = [title retain]; }
        if (banner  != NULL) { bannerString = [banner retain]; }
        
        if (prompts == NULL) { 
            [self release];
            return NULL;
        }
        promptsArray = [prompts retain];
    }
    
    return self;
}

// ---------------------------------------------------------------------------

- (void) dealloc
{
    dprintf ("PrompterController deallocating");
    if (titleString               != NULL) { [titleString release]; }
    if (bannerString              != NULL) { [bannerString release]; }
    if (promptsArray              != NULL) { [promptsArray release]; }
    if (responseTextFieldArray    != NULL) { [responseTextFieldArray release]; }
    if (applicationNameString     != NULL) { [applicationNameString release]; }
    if (applicationIconPathString != NULL) { [applicationIconPathString release]; }
    [super dealloc];
}

#pragma mark -- Loading --

// ---------------------------------------------------------------------------

- (void) windowDidLoad
{
    dprintf ("Entering PrompterController windowDidLoad");

    // Set up some values used later to dynamically size the window
    NSRect singleResponseFrame = [responsesBox frame];
    float singlePromptHeight = [responsesBox frame].size.height;
    NSSize baseWindowSize = [[[self window] contentView] frame].size;
    baseWindowSize.height -= singlePromptHeight;
    int promptCount = [promptsArray count];
    int i;
        
    // Set up the dialog icon:
    NSImage *applicationIconImage = [NSImage imageNamed: @"NSApplicationIcon"];
    [kerberosIconImageView setImage: applicationIconImage];
    if (applicationIconPathString != NULL) {
        NSImage *badgeImage = [[NSWorkspace sharedWorkspace] iconForFile: applicationIconPathString];
        [kerberosIconImageView setBadgeImage: badgeImage];
    }
    
    // Title and Banner
    [[self window] setTitle: (titleString == NULL) ? @"" : titleString];
    [bannerTextField setStringValue: (bannerString == NULL) ? @"" : bannerString];

    if (promptCount > 0) {
        // Resize the prompts matrix
        NSSize spacingSize = { 0, ResponseSpacing };
        
        [promptsMatrix setIntercellSpacing: spacingSize];
        [promptsMatrix renewRows: promptCount columns: 1];
        [promptsMatrix sizeToCells];
    } else {
        // if there are no prompts, hide everything related to them
        [promptsMatrix setHidden: YES];
        [promptsMatrix setEnabled: FALSE];
        [responsesBox setHidden: YES];
        [cancelButton setHidden: YES];
        [cancelButton setEnabled: FALSE];
    }    
    
    // Resize the window to hold the new cells.
    // The matrix cells will automatically be moved down.
    NSSize windowSize = baseWindowSize;
    if (promptCount > 0) {
        windowSize.height += ((singlePromptHeight * [promptsArray count]) + 
                              (PromptSpacing * ([promptsArray count] - 1)));
    }
    [[self window] setContentSize: windowSize];
        
    // Prompt and Response Text Fields
    //
    // Note that we can't have an NSMatrix with NSSecureTextFields in it because
    // of a bug in NSMatrix (stringValue on the secure text field returns bullets).
    for (i = 0; i < promptCount; i++) {
        NSTextField *responseTextField = NULL;
        Prompt *prompt = [promptsArray objectAtIndex: i];
        
        // Set the prompt
        [[promptsMatrix cellAtRow: i column: 0] setObjectValue: [prompt prompt]];
        
        // Create the response text field:
        NSRect frameRect = singleResponseFrame;
        frameRect.origin.y += ((promptCount - 1) - i) * (PromptSpacing + frameRect.size.height);
        
        if ([prompt secure]) {
            NSSecureTextField *responseSecureTextField = [[NSSecureTextField alloc] initWithFrame: frameRect];
            
            [[responseSecureTextField cell] setEchosBullets: YES];
            responseTextField = responseSecureTextField;
        } else {
            responseTextField = [[NSTextField alloc] initWithFrame: frameRect];
        }
        
        [responseTextField setEnabled: YES];
        [responseTextField setEditable: YES];
        [responseTextField setBezeled: YES];
        [responseTextField setBezelStyle: NSTextFieldSquareBezel];
        [responseTextField setAlignment: NSLeftTextAlignment];
        [responseTextField setDrawsBackground: YES];
        [responseTextField setBackgroundColor: [NSColor whiteColor]];
        [responseTextField setFont: [NSFont systemFontOfSize: 13]];
        [responseTextField setStringValue: @""];
        
        [responseTextFieldArray addObject: responseTextField];
        [[[self window] contentView] addSubview: responseTextField];
        
        if (i == 0) {
            [[self window] setInitialFirstResponder: responseTextField];
        } else {
            // chain up the text field so we can tab between them
            [[responseTextFieldArray objectAtIndex: i - 1] setNextKeyView: responseTextField];
        }
        
        [responseTextField release];
    }
    
    // connect the last text field to the first one
    if (promptCount > 1) {
        [[responseTextFieldArray objectAtIndex: promptCount - 1] setNextKeyView: 
            [responseTextFieldArray objectAtIndex: 0]];
    }
}    

#pragma mark -- Actions --

// ---------------------------------------------------------------------------

- (IBAction) ok: (id) sender
{
    unsigned int i;
    
    for (i = 0; i < [promptsArray count]; i++) {
        NSTextField *responseTextField = [responseTextFieldArray objectAtIndex: i];
        Prompt *prompt = [promptsArray objectAtIndex: i];
        
        [prompt setResponse: [responseTextField stringValue]];
    }

    [self stopWithCode: klNoErr];
}

// ---------------------------------------------------------------------------

- (IBAction) cancel: (id) sender
{
    [self stopWithCode: klUserCanceledErr];
}

#pragma mark -- Miscellaneous --

// ---------------------------------------------------------------------------

- (int) runWindow
{
    isSheet = NO;
    
    [[NSApp delegate] addActiveWindow: [self window]];    
    [[self window] center];
    [self showWindow: self];
    [NSApp run];
    [self close];
    [[NSApp delegate] removeActiveWindow: [self window]];
    
    return result;
}


// ---------------------------------------------------------------------------

- (int) runSheetModalForWindow: (NSWindow *) parentWindow
{
    isSheet = YES;
    
    [NSApp beginSheet: [self window]
       modalForWindow: parentWindow 
        modalDelegate: self 
       didEndSelector: NULL
          contextInfo: NULL];
    
    int err = [NSApp runModalForWindow: [self window]];
    
    [self close];
    
    return err;
}


// ---------------------------------------------------------------------------

- (void) stopWithCode: (int) returnCode
{
    if (isSheet) {
        [NSApp endSheet: [self window] returnCode: returnCode];
        [NSApp stopModalWithCode: returnCode];
    } else {
        result = returnCode;
        [NSApp stop: self];
    }
}

// ---------------------------------------------------------------------------

- (BOOL) worksWhenModal
{
    return YES;
}

@end

