// 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 "OAFontCache.h"

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

RCS_ID("$Header: /Network/Developer/Source/CVS/OmniGroup/OmniAppKit/OAFontCache.m,v 1.6 1999/03/30 21:25:53 kc Exp $")

typedef struct _OAFontCacheKey {
    NSString *familyName;
    OAFontAttributes attributes;
} OAFontCacheKey;

static unsigned OAFontCacheKeyHash(NSMapTable *table, const void *key)
{
    const OAFontCacheKey *fontCacheKey = (const OAFontCacheKey *)key;

    return [fontCacheKey->familyName hash] +
           ((unsigned int)fontCacheKey->attributes.size << 2) +
           (fontCacheKey->attributes.bold << 1) +
           fontCacheKey->attributes.italic;
}

static BOOL OAFontCacheKeyIsEqual(NSMapTable *table, const void *key1, const void *key2)
{
    const OAFontCacheKey *fontCacheKey1 = (const OAFontCacheKey *)key1;
    const OAFontCacheKey *fontCacheKey2 = (const OAFontCacheKey *)key2;

    if (![fontCacheKey1->familyName isEqualToString:fontCacheKey2->familyName])
        return NO;
    return fontCacheKey1->attributes.size == fontCacheKey2->attributes.size &&
           fontCacheKey1->attributes.bold == fontCacheKey2->attributes.bold &&
           fontCacheKey1->attributes.italic == fontCacheKey2->attributes.italic;
}

static NSMapTable *fontMapTable;
static NSFontManager *fontManager;

@implementation OAFontCache

+ (void)initialize;
{
    static BOOL initialized = NO;
    NSMapTableKeyCallBacks keyCallBacks;
    
    [super initialize];
    if (initialized)
        return;
    initialized = YES;

    keyCallBacks.hash = OAFontCacheKeyHash;
    keyCallBacks.isEqual = OAFontCacheKeyIsEqual;
    keyCallBacks.retain = NULL;
    keyCallBacks.release = NULL;
    keyCallBacks.describe = NULL;
    fontMapTable = NSCreateMapTable(keyCallBacks, NSObjectMapValueCallBacks, 32);
    
    fontManager = [[NSFontManager sharedFontManager] retain];
}

+ (NSFont *)fontWithFamily:(NSString *)aFamily attributes:(OAFontAttributes)someAttributes;
{
    NSFont *font;
    OAFontCacheKey pseudo, *key;

    pseudo.familyName = aFamily;
    pseudo.attributes = someAttributes;

    if (!(font = NSMapGet(fontMapTable, &pseudo))) {
        //
        // Perform the thread-unsafe NSFontManager operation
        //

        [NSThread lockMainThread]; // Remove this when NSFont is thread-safe

        if (!(font = [NSFont fontWithName:aFamily size:someAttributes.size]))
            font = [NSFont userFontOfSize:someAttributes.size];

        // Only attempt to convert fonts if we actually have a font
        if (font && someAttributes.bold)
            font = [fontManager convertFont:font toHaveTrait:NSBoldFontMask];
        if (font && someAttributes.italic)
            font = [fontManager convertFont:font toHaveTrait:NSItalicFontMask];

        if (!font)
            font = [OFNull nullObject];

        [font retain];

        [NSThread unlockMainThread];

        //
        // Done being thread-unsafe.  Store the results to avoid future lookups
        //

        // Create a new entry
        key = NSZoneMalloc(NULL, sizeof(OAFontCacheKey));
        key->familyName = [aFamily copy];
        key->attributes = someAttributes;

        NSMapInsert(fontMapTable, key, font);
    }
                
    return [font isNull] ? nil : font;
}

+ (NSFont *)fontWithFamily:(NSString *)aFamily size:(float)size bold:(BOOL)bold italic:(BOOL)italic;
{
    OAFontAttributes attributes;

    attributes.size = size;
    attributes.bold = bold;
    attributes.italic = italic;
    return [self fontWithFamily:aFamily attributes:attributes];
}

+ (NSFont *)fontWithFamily:(NSString *)aFamily size:(float)size;
{
    return [self fontWithFamily:aFamily size:size bold:NO italic:NO];
}

@end
