//
//  ICeCoffEEKeyEquivalents.m
//  ICeCoffEE APE
//
//  Created by Nicholas Riley on Mon Jun 9 2003.
//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
//

// Original implementations of these functions are in NSString-NJRExtensions
// and NSFont-NJRExtensions from Pester.

#import <Carbon/Carbon.h>
#import <AppKit/AppKit.h>

static NSFont *themeFont(ThemeFontID fontID) {
    NSFont *themeFont = nil;
    Str255 pstrFontName;
    SInt16 fontSize = 0;
    OSStatus status;
    // can't simulate algorithmic styles in Cocoa in any case, so here's hoping nothing will be passed back - XXX guess this should at least accommodate the bold system font, but we don't need it
    status = GetThemeFont(fontID, smSystemScript, pstrFontName, &fontSize, NULL);

    if (status == noErr) {
        NSString *fontName = (NSString *)CFStringCreateWithPascalString(NULL, pstrFontName, CFStringGetSystemEncoding());
        themeFont = [NSFont fontWithName: fontName size: fontSize];
        [fontName release];
    }
    if (themeFont == nil) {
        themeFont = [NSFont systemFontOfSize: fontSize == 0 ? [NSFont systemFontSize] : fontSize];
    }
    return themeFont;
}

static NSString *stringWithCharacter(unichar character) {
    return [NSString stringWithCharacters: &character length: 1];
}

static unichar combiningHelpChar[] = {0x003F, 0x20DD};

static NSFont *menuItemCmdKeyFont = nil;
static NSFont *menuItemFont = nil;
static NSParagraphStyle *keyEquivParaStyle;

static inline void initialize() {
    if (menuItemCmdKeyFont != nil) return;

    menuItemCmdKeyFont = [themeFont(kThemeMenuItemCmdKeyFont) retain];
    menuItemFont = [themeFont(kThemeMenuItemFont) retain];

    NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
    [paraStyle setTabStops: [NSArray arrayWithObjects:
        [[[NSTextTab alloc] initWithType: NSRightTabStopType location: 30] autorelease],
        [[[NSTextTab alloc] initWithType: NSLeftTabStopType location: 30.01] autorelease],
        nil]];
    [paraStyle setLineBreakMode: NSLineBreakByClipping];
    keyEquivParaStyle = [paraStyle copy];
    [paraStyle release];
}

// set tabs here, for ICeCoffEE (not in Pester)
static NSAttributedString *attributedStringWithFont(NSString *self, NSFont *font) {
    return [[[NSAttributedString alloc] initWithString: self attributes: [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, keyEquivParaStyle, NSParagraphStyleAttributeName, nil]] autorelease];
}

static NSString *keyEquivalentString(NSString *self) {
    if ([self length] != 0) {
        const char *str = [self UTF8String];
        unichar keyChar;
        if (str[1] != '\0') {
            keyChar = [self characterAtIndex: 0];
            switch (keyChar) {
                case NSUpArrowFunctionKey: keyChar = 0x21E1; break;
                case NSDownArrowFunctionKey: keyChar = 0x21E3; break;
                case NSLeftArrowFunctionKey: keyChar = 0x21E0; break;
                case NSRightArrowFunctionKey: keyChar = 0x21E2; break;
                case NSInsertFunctionKey:
                    return [NSString stringWithCharacters: combiningHelpChar length: 2];
                case NSDeleteFunctionKey: keyChar = 0x2326; break;
                case NSHomeFunctionKey: keyChar = 0x2196; break;
                case NSEndFunctionKey: keyChar = 0x2198; break;
                case NSPageUpFunctionKey: keyChar = 0x21DE; break;
                case NSPageDownFunctionKey: keyChar = 0x21DF; break;
                case NSClearLineFunctionKey: keyChar = 0x2327; break;
                default:
                    if (keyChar >= NSF1FunctionKey && keyChar <= NSF35FunctionKey) {
                        return [NSString stringWithFormat: @"F%u", keyChar - NSF1FunctionKey + 1];
                    }
                    return [NSString stringWithFormat: @"[unknown %lX]", keyChar];
            }
        } else if (str[0] >= 'A' && str[0] <= 'Z') {
            return self;
        } else if (str[0] >= 'a' && str[0] <= 'z') return [self uppercaseString];
        else switch (str[0]) {
            case '\t': keyChar = 0x21e5; break;
            case '\r': keyChar = 0x21a9; break;
            case '\e': keyChar = 0x238b; break;
            case ' ': keyChar = 0x2423; break;
            case 0x7f: keyChar = 0x232b; break; // delete
            case 0x03: keyChar = 0x2324; break; // enter
            case 0x19: keyChar = 0x21e4; break; // backtab
            case 0: return @"";
                // case '': keyChar = 0x; break;
            default: return self; // return [NSString stringWithFormat: @"[huh? %x]", (int)str[0]];
        }
            return stringWithCharacter(keyChar);
    }
    return self;
}

NSAttributedString *ICCF_KeyEquivalentAttributedStringWithModifierFlags(NSString *self, unsigned int modifierFlags) {
    initialize();
    NSString *keyEquivalentStringNoMask = keyEquivalentString(self);
    // use tabs here, for ICeCoffEE (not in Pester, at least without better subclassing)
    NSAttributedString *keyEquivalentAttributedString =
        attributedStringWithFont(
          [NSString stringWithFormat: @"\t%@%@%@%@\t%@",
            (modifierFlags & NSControlKeyMask) ? stringWithCharacter(kControlUnicode) : @"",
            (modifierFlags & NSAlternateKeyMask) ? stringWithCharacter(kOptionUnicode) : @"",
            (modifierFlags & NSShiftKeyMask) ? stringWithCharacter(kShiftUnicode) : @"",
            (modifierFlags & NSCommandKeyMask) ? stringWithCharacter(kCommandUnicode) : @"",
            keyEquivalentStringNoMask], menuItemCmdKeyFont);
    unsigned noMaskLength = [keyEquivalentStringNoMask length];
    if (noMaskLength > 3 || // Fxx
        (noMaskLength == 1 && [keyEquivalentStringNoMask characterAtIndex: 0] <= 0x7F)) {
        NSMutableAttributedString *astr = [keyEquivalentAttributedString mutableCopy];
        [astr setAttributes: [NSDictionary dictionaryWithObject: menuItemFont forKey: NSFontAttributeName] range: NSMakeRange([astr length] - noMaskLength, noMaskLength)];
        keyEquivalentAttributedString = [[astr copy] autorelease];
        [astr release];
    }
    return keyEquivalentAttributedString;
}