// 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 "NSString-OAExtensions.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/OpenStepExtensions.subproj/NSString-OAExtensions.m,v 1.12 1999/03/30 21:26:02 kc Exp $")

@implementation NSString (OAExtensions)

+ (NSString *)possiblyAbbreviatedStringForBytes:(unsigned long long)bytes inTableView:(NSTableView *)tableView tableColumn:(NSTableColumn *)tableColumn;
{
    NSCell *dataCell;
    NSString *bytesString;

    bytesString = [NSString stringWithFormat:@"%@", [NSNumber numberWithUnsignedLongLong:bytes]];
    dataCell = [tableColumn dataCell];
    if ([[dataCell font] widthOfString:bytesString] + 5 <= [dataCell titleRectForBounds:NSMakeRect(0, 0, [tableColumn width], [tableView rowHeight])].size.width)
        return [bytesString stringByAppendingString:@" bytes"];
    else
        return [NSString abbreviatedStringForBytes:bytes];
}


// String drawing

- (void)drawWithFontAttributes:(NSDictionary *)attributes alignment:(int)alignment rectangle:(NSRect)rectangle;
{
    static NSTextStorage *showStringTextStorage = nil;
    static NSLayoutManager *showStringLayoutManager = nil;
    static NSTextContainer *showStringTextContainer = nil;

    NSAttributedString *attributedString;
    unsigned int originalGlyphCount;
    NSRange drawGlyphRange, drawGlyphRangeWithoutEllipsis;
    NSRect *rectArray;
    unsigned int rectCount;
    NSSize size;
    NSString *ellipsisString;
    NSSize ellipsisSize;
    BOOL drawEllipsisIfTruncated;

    OBPRECONDITION(attributes);

    if (!showStringTextStorage) {
        showStringTextStorage = [[NSTextStorage alloc] init];

        showStringLayoutManager = [[NSLayoutManager alloc] init];
        [showStringTextStorage addLayoutManager:showStringLayoutManager];

        showStringTextContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(1e7, 1e7)];
        [showStringTextContainer setLineFragmentPadding:0];
        [showStringLayoutManager addTextContainer:showStringTextContainer];
    }
    
    attributedString = [[NSAttributedString alloc] initWithString:self attributes:attributes];
    [showStringTextStorage setAttributedString:attributedString];
    [attributedString release];

    drawGlyphRange = [showStringLayoutManager glyphRangeForTextContainer:showStringTextContainer];
    if (drawGlyphRange.length == 0)
        return;
    drawGlyphRangeWithoutEllipsis = NSMakeRange(0, 0);
    originalGlyphCount = drawGlyphRange.length;

    ellipsisString = nil;
    ellipsisSize = NSMakeSize(0, 0);

    rectArray = [showStringLayoutManager rectArrayForGlyphRange:drawGlyphRange withinSelectedGlyphRange:NSMakeRange(NSNotFound, 0) inTextContainer:showStringTextContainer rectCount:&rectCount];
    if (rectCount < 1)
        return;

    size = rectArray[0].size;

    if (size.width > NSWidth(rectangle)) {
        NSSize testSize;
        unsigned int lowerCount, upperCount;

        lowerCount = 0;
        upperCount = originalGlyphCount;

        ellipsisString = [NSString horizontalEllipsisString];
        ellipsisSize = [ellipsisString sizeWithAttributes:attributes];

        while (lowerCount + 1 < upperCount) {
            unsigned int middleCount;

            middleCount = (upperCount + lowerCount) / 2;

#warning WJS: This is slow, I found out.  Use the same algorithm OHLine uses.
            rectArray = [showStringLayoutManager rectArrayForGlyphRange:NSMakeRange(0, middleCount) withinSelectedGlyphRange:NSMakeRange(NSNotFound, 0) inTextContainer:showStringTextContainer rectCount:&rectCount];
            if (rectCount < 1)
                return;

            testSize = rectArray[0].size;

            // DEBUGGING
//            rectArray[0].origin.x += rectangle.origin.x;
//            rectArray[0].origin.y += rectangle.origin.y;
//            NSDottedFrameRect(rectArray[0]);

            if (testSize.width <= NSWidth(rectangle) && drawGlyphRangeWithoutEllipsis.length < middleCount)
                drawGlyphRangeWithoutEllipsis = NSMakeRange(0, middleCount);
            testSize.width += ellipsisSize.width;

            if (testSize.width <= NSWidth(rectangle)) {
                lowerCount = middleCount;
                size = testSize;
            } else
                upperCount = middleCount;
        }
        
        drawGlyphRange.length = lowerCount;
    }

    if (drawGlyphRange.length)
        drawEllipsisIfTruncated = YES;
    else {
        // If we couldn't fit ANY characters with the ellipsis, try drawing some without it (better than drawing nothing)
        drawEllipsisIfTruncated = NO;
        drawGlyphRange = drawGlyphRangeWithoutEllipsis;
    }
    
    if (drawGlyphRange.length) {
        NSPoint drawPoint;

        // determine drawPoint based on alignment
        drawPoint.y = NSMinY(rectangle);
        switch (alignment) {
            default:
            case NSLeftTextAlignment:
                drawPoint.x = NSMinX(rectangle);
                break;
            case NSCenterTextAlignment:
                drawPoint.x = NSMidX(rectangle) - size.width / 2.0;
                break;
            case NSRightTextAlignment:
                drawPoint.x = NSMaxX(rectangle) - size.width;
                break;
        }

        [showStringLayoutManager drawGlyphsForGlyphRange:drawGlyphRange atPoint:drawPoint];
        if (drawGlyphRange.length < originalGlyphCount) {
            // draw only part of string, then maybe ellipsis if they fit
            [showStringLayoutManager drawGlyphsForGlyphRange:drawGlyphRange atPoint:drawPoint];
            
            if (drawEllipsisIfTruncated) {
                drawPoint.x += size.width - ellipsisSize.width;
                [ellipsisString drawAtPoint:drawPoint withAttributes:attributes];
            }
        }
    }
}

- (void)drawWithFont:(NSFont *)font color:(NSColor *)color alignment:(int)alignment rectangle:(NSRect)rectangle;
{
    NSDictionary *attributes;

    OBPRECONDITION(font);
    OBPRECONDITION(color);

    attributes = [[NSDictionary alloc] initWithObjectsAndKeys:font, NSFontAttributeName, color, NSForegroundColorAttributeName, nil];

    [self drawWithFontAttributes:attributes alignment:alignment rectangle:rectangle];
    
    [attributes release];
}

@end
