#import "DWConfinedString.h"
#import "NSAttributedStringExtensions.h"
#import "Profile.h"


@implementation DWConfinedString

/* Initialization */

    + (id) stringWithAttributedString: (NSAttributedString*) theString; {
        return [[[DWConfinedString alloc] initWithAttributedString: theString] autorelease];
    }
    
    - (id) initWithAttributedString: (NSAttributedString*) theString; {
        self = [self init];
        [self setAttributedString: theString];
        return self;
    }

    - (id) initWithString: (NSString*) theString attributes: (id) theAttributes; {
        return [self initWithAttributedString: [[[NSAttributedString alloc] initWithString: theString attributes: theAttributes] autorelease]];
    }
    
    - (id) init; {
        self = [super init];
        style = DWCollapseRight;
        return self;
    }
    
    - (void) dealloc; 
    {
        //NSLog(@"DWConfinedString: dealloc: %@", string);
    
        [string release];
        [textStorage release];
        [layoutManager release];
        [textContainer release];
        if (metrics) free(metrics);
        [super dealloc];
    }
    
    - (id) copyWithZone: (id) theZone; {
        return [self retain];
    }


/* set... */

    - (void) setAttributedString: (NSAttributedString*) theString; {
        [string release];
        string = [theString retain];
        hasCachedMetrics = NO;
    }

    - (void) setStyle: (DWCollapsingStyle) theStyle; {
        style = theStyle;
    }

    - (void) setString: (NSString*) theString; {
    }


/* get... */

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

    - (NSAttributedString*) attributedString; {
        return string;
    }
    

/* Caching */

    - (void) cacheMetrics;
    {
        hasCachedMetrics = YES;

        if (!textStorage) {
            textStorage   = [[NSTextStorage alloc] initWithString: [[string string] stringByAppendingString: @"-"] attributes: [string attributesAtIndex: 0 effectiveRange: 0]];
            layoutManager = [[NSLayoutManager alloc] init];
            textContainer = [[NSTextContainer alloc] initWithContainerSize: (NSSize) {1.0e7, 1.0e7}];
    
            [textStorage addLayoutManager: layoutManager];
            [textContainer setLineFragmentPadding: 0.0];
            [layoutManager addTextContainer: textContainer];
        }

        nglyphs = [layoutManager numberOfGlyphs];

        if (metrics) free(metrics);
        metrics = malloc(nglyphs*sizeof(float));
        
        //NSLog(@"%i glyphs, %i characters", nglyphs, [[textStorage mutableString] length]);

        /* normal glyphs */

            int i;
            float previousX = 0;
            for (i=0; i<nglyphs; i++) 
            {
                NSPoint location = [layoutManager locationForGlyphAtIndex: i];
                if (i>0) {
                    metrics[i-1] = location.x - previousX;
                    //NSLog(@"%f\t%c\t%f\t%@", metrics[i-1], [[textStorage mutableString] characterAtIndex: i-1], location.x,  NSStringFromRect([layoutManager boundingRectForGlyphRange: (NSRange){i-1, 1} inTextContainer: textContainer]));
                }
                
                previousX = location.x;
            }
        
        /* ellipsis glyph */

            NSRect line = [layoutManager lineFragmentUsedRectForGlyphAtIndex: 0 effectiveRange: nil];
            metrics[nglyphs-1] = line.size.width - previousX;
            totalWidth = line.size.width - metrics[nglyphs-1];
        
            //NSLog(@"%f\t\t%c", metrics[nglyphs-1], [[textStorage mutableString] characterAtIndex: nglyphs-1]);
    }


    - (float) totalWidth; {
        if (!hasCachedMetrics) [self cacheMetrics];
        return totalWidth;
    }


/* API */

    - (void) drawInRect: (NSRect) theRect; {
        [self drawInRect: theRect centered: NO];
    }
    
    - (void) drawInRect: (NSRect) theRect centered: (BOOL) isCentered;
    {
        float theWidth = theRect.size.width;
        NSRange glyphRange;
        
        /* get glyph metrics */
        
            if (!hasCachedMetrics) [self cacheMetrics];
            
        /* simple case, no confinement */
        
            if (style == DWCollapseNone || totalWidth < theWidth) {
                glyphRange = (NSRange){0, nglyphs-1};
                if (isCentered) {
                    theRect.origin.x += (theWidth - totalWidth)/2;
                }
                [layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: theRect.origin];
                return;
            }
        
        /* collapsing cases */

            if (style == DWCollapseRight) {
            /* right */
                
                float ellipsisExtent = metrics[nglyphs-1];
                float stringExtent = totalWidth;
                int lastGlyphIndex = nglyphs-2;
                float availableExtent = theWidth - ellipsisExtent;
                
                //NSLog(@"A stringExten: %f", stringExtent);
                
                while (stringExtent > availableExtent && lastGlyphIndex >= 0) 
                {
                    stringExtent -= metrics[lastGlyphIndex];
                    lastGlyphIndex--;
                }

                lastGlyphIndex++;
                
                if (lastGlyphIndex > -1) {
                    glyphRange = (NSRange){0, lastGlyphIndex};
                    [layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: theRect.origin];
                }

                /* ellipsis glyph */
                
                if (theWidth > ellipsisExtent) {
                    glyphRange = (NSRange){nglyphs-1, 1};
                    theRect.origin.x = theRect.origin.x - (totalWidth - stringExtent); 
                    //theRect.origin.x = theRect.origin.x - (totalWidth - a.size.width); 
                    [layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: theRect.origin];
                }
            
            } else {				
            /* center */
            
                float ellipsisExtent = metrics[nglyphs-1];
                float stringExtent = totalWidth;
                int lastGlyphIndex = nglyphs-2;
            
                NSPoint originA, originB;
                float theWidthA, theWidthB;

                theWidthA = (theWidth + ellipsisExtent)/2;
                theWidthB = (theWidth - ellipsisExtent)/2;
                
                /* A */

                    while (stringExtent > (theWidthA - ellipsisExtent) && lastGlyphIndex >= 0) 
                    {
                        stringExtent -= metrics[lastGlyphIndex];
                        lastGlyphIndex--;
                    }
    
                    lastGlyphIndex++;
                    
                    glyphRange = (NSRange){0, lastGlyphIndex};
                    [layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: theRect.origin];
    
                    glyphRange = (NSRange){nglyphs-1, 1};
                    originA = theRect.origin;
                    originA.x = originA.x - (totalWidth - stringExtent); 
                    [layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: originA];
                    
                    float stringExtentA = stringExtent;

                /* B */
                
                    stringExtent = totalWidth;
                    int firstGlyphIndex = 0;
    
                    while (stringExtent > theWidthB && firstGlyphIndex <= nglyphs - 2)
                    {
                        stringExtent -= metrics[firstGlyphIndex];
                        firstGlyphIndex++;
                    }
    
                    glyphRange = (NSRange){firstGlyphIndex, nglyphs - firstGlyphIndex - 1};
                    originB = theRect.origin;
                    originB.x = originB.x - (totalWidth - stringExtentA - stringExtent - ellipsisExtent); 
                    [layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: originB];

            }
    }

@end