#import "DWCachedString.h"
#import "DWSimpleTextField.h"
#import "DWText.h"
#import "NSStringExtensions.h"
#import "Profile.h"


@implementation DWCachedString

/* Window */

    /* 
        We create our own offscreen window to draw in.  This avoid the tremendous cost of creating a new offscreen window everytime you call -[NSImage lockFocus].
    */

    #define kDWCachedStringImageWidth 600
    #define kDWCachedStringImageHeight 16

    static NSWindow* window = nil;
    static DWSimpleTextField* textField = nil;
    
    - (void) createWindow; 
    {
        if (!window) 
        {
            NSRect r = { 
                {0,0},
                {kDWCachedStringImageWidth,kDWCachedStringImageHeight}
            };

            textField = [[DWSimpleTextField alloc] initWithFrame: r];
            window = [[NSWindow alloc] initWithContentRect: r styleMask:  NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO];

            [window setContentView: textField];
            
            #if 0
            [window makeKeyAndOrderFront: self];
            #endif
        }
    }


#pragma mark -
/* Initialization */

    + (id) cachedImageWithString: (NSString*) theString; {
        return [self cachedImageWithString: theString attributes: [DWText smallTextAttributes]];
    }

    + (id) cachedImageWithString: (NSString*) theString attributes: (id) theAttributes; {
        return [[[DWCachedString alloc] initWithString: theString attributes: theAttributes] autorelease];
    }

    - (id) initWithString: (NSString*) theString; {
        return [self initWithString: theString attributes: [DWText smallTextAttributes]];
    }
    
    - (id) initWithString: (NSString*) theString attributes: (id) theAttributes; 
    {
        #if 0
        if ([theString isEqual: @""])
            Debugger();
        #endif
            
    
        #if 0
        
        /* routine initialization */
        
            self = [super init];
        
        #else
        
        /* cached initialization */
        
            static id cache = nil;
            if (!cache) cache = [[NSMutableDictionary alloc] init];
            
            id img;
            if ((img = [cache objectForKey: ((theString) ? theString : @"")])) 
            {
                //NSLog(@"+ (%@)", theString);
                    
                [self release];			/* todo: restructure to avoid release */
                return [img retain];
            }
            
        /* purge cache */
        
            if ([cache count] > 300) {
                [cache removeAllObjects];
            }
            
        /* proceed to create & cache */
                
            //NSLog(@"- (%i)", [cache count]);
        
            self = [super init];
            [cache setObject: self forKey: ((theString) ? theString : @"")];
            
        #endif
        
        /* common */
        
            [self setString: theString attributes: theAttributes];
            [self createWindow];
            usesWhiteSilhouetteWhenAppropriate = YES;

            return self;
    }

    - (void) dealloc; 
    {
        //NSLog(@"DWCachedString: dealloc (%@)", string);
        
        int i;
        for (i=0; i<kDWColorTotalIndices; i++) {
            [cachedImages[i] release];
        }
        [string release];
        [attributes release];
        [super dealloc];
    }
    
    - (id) copyWithZone: (id) theZone; {
        return [self retain];
    }


#pragma mark -
/* set... */

    - (void) setUsesWhiteSilhouetteWhenAppropriate: (BOOL) value; {
        usesWhiteSilhouetteWhenAppropriate = value;
    }

    - (void) setString: (NSString*) theString attributes: (id) theAttributes; 
    {
        if ([theString isEqual: @""]) {
            theString = nil;
            theAttributes = nil;
        }

        [string release];
        string = [theString retain];
        
        [attributes release];
        attributes = [theAttributes retain];
    }
    

#pragma mark -
/* get... */

    - (NSString*) string; {
        return string;
    }

    - (id) attributes; {
        return attributes;
    }

      
#pragma mark -
/* Compare */

    - (NSComparisonResult) compare: (DWCachedString*) theImage; {
        return [string compare: [theImage string]];
    }


#pragma mark -
/* Render */

    id DWCachedStringWithBackgroundColorIndex(id theImage, id theColor) {
        return nil;
    }
    
    - (id) imageWithBackgroundColorIndex: (int) dwColorIndex; 
    {
        id opaqueImage, imageRep;

        if (!string) return nil;

        /* lookup */

            if ((opaqueImage = cachedImages[dwColorIndex])) {
                return opaqueImage;
            }

        /* else draw & cache */
        
            #ifdef PROFILE
            [Profile startWithString: string];
            #endif
        
            NSSize theSize = [string sizeWithAttributes: attributes];

        /* the following correction is necessary, otherwise there are fractional pixels (common when text is rendered) that result in a visual imperfection.  also, we wish to prevent writing text larger than the cache window. */
        
            theSize.width =  MIN(kDWCachedStringImageWidth, ceil(theSize.width));
            theSize.height = MIN(kDWCachedStringImageHeight,ceil(theSize.height));
    
            NSRect rect = {
                {0.0f,0.0f},
                theSize
            };
        
        /* use white text if necessary */
            
            id copiedAttributes = nil;
    
            if (usesWhiteSilhouetteWhenAppropriate && (dwColorIndex == kDWColorAlternateIndex)) 
            {
                copiedAttributes = [[NSMutableDictionary alloc] initWithDictionary: attributes copyItems: NO];
                [copiedAttributes setObject: [NSColor whiteColor] forKey: NSForegroundColorAttributeName];
            }

        /* set up the TextField */
        
            textField->backgroundColor = [DWColor colorForIndex: dwColorIndex];
            textField->theString       = string;
            textField->theAttributes   = (copiedAttributes) ? copiedAttributes : attributes;
        
        /* draw */
        
            [textField lockFocus];
            [textField drawRect: rect];
            imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: rect];
            [textField unlockFocus];
                    
        /* release copied attributes */
        
            if (copiedAttributes) {
                [copiedAttributes release];
            }
        
        /* generate NSImage */
        
            [imageRep setAlpha: NO];
            [imageRep setOpaque: YES];
            opaqueImage = [[NSImage alloc] initWithSize: theSize];
                [opaqueImage setFlipped: YES];
                [opaqueImage addRepresentation: imageRep];

            [imageRep release];
            
        /* cache */

            cachedImages[dwColorIndex] = opaqueImage;

            #ifdef PROFILE
            [Profile end];
            #endif

        return opaqueImage;
    }


#pragma mark -
/* Drawing */
 
    - (void) drawAtPoint: (NSPoint) thePoint withBackgroundColorIndex: (int) dwColorIndex; {
        id image = [self imageWithBackgroundColorIndex: dwColorIndex];
        [image drawAtPoint: thePoint fromRect: NSZeroRect operation: NSCompositeCopy fraction: 1.0];
    }

@end