// Copyright 1998-2000 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 "OHLay.h"

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

#import "OHBasicCell.h"
#import "OHHTMLView.h"
#import "OHParagraph.h"
#import "OHWord.h"

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniHTML/View.subproj/OHLay.m,v 1.11 2000/01/20 00:28:05 kc Exp $")

@interface OHLay (Private)
- (BOOL)calculatesWidthIndependentlyOfTextStorageInHTMLView:(OHHTMLView *)htmlView;
@end

@implementation OHLay

- (void)drawInLineBounds:(NSRect)lineBounds maxAscent:(float)maxAscent paragraph:(OHParagraph *)paragraph selectedGlyphRange:(NSRange)selectedGlyphRange inHTMLView:(OHHTMLView *)htmlView;
{
    OHWord *word;
    OHBasicCell *attachmentCell;
    NSLayoutManager *layoutManager;
    NSRange glyphRange;


    word = [[htmlView words] objectAtIndex:wordRange.location];

    switch (word->type) {
        case OHWordTypeTab:
            // Tabs are in their own lays, and they're whitespace, so nothing needs to be drawn
            break;
        case OHWordTypeAttachment:
            attachmentCell = [word attachmentCellInHTMLView:htmlView];
            [htmlView setAntialias:NO];
            [attachmentCell drawRect:lineBounds inHTMLView:htmlView];
        default:
            [htmlView setAntialias:YES];
            layoutManager = [htmlView layoutManager];
            glyphRange = [htmlView glyphRangeForWordRange:wordRange];

            // Draw the glyphs
            [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:NSMakePoint(NSMinX(lineBounds) + x - firstWordXLocationInLayoutManager, NSMinY(lineBounds) + maxAscent - (paragraph->bounds.origin.y + paragraph->maxAscent))];
    }
}

// Help calculate the selection rect
- (void)getMinX:(float *)minX maxX:(float *)maxX forSelectedGlyphRange:(NSRange)selectedGlyphRange lineBounds:(NSRect)lineBounds inHTMLView:(OHHTMLView *)htmlView;
{
    NSLayoutManager *layoutManager;
    NSRange glyphRange;
    NSRange laySelectedGlyphRange;
    const NSRect *selectedRectArray;
    unsigned int selectedRectCount;
    float layMinX;

    layoutManager = [htmlView layoutManager];
    glyphRange = [htmlView glyphRangeForWordRange:wordRange];

    laySelectedGlyphRange = NSIntersectionRange(selectedGlyphRange, glyphRange);
    if (laySelectedGlyphRange.length == 0)
        return;

    if ([self calculatesWidthIndependentlyOfTextStorageInHTMLView:htmlView]) {
        layMinX = NSMinX(lineBounds) + x;
        *minX = MIN(*minX, layMinX);
        *maxX = MAX(*maxX, layMinX + width);
        return;
    }
    
    selectedRectArray = [layoutManager rectArrayForGlyphRange:laySelectedGlyphRange withinSelectedGlyphRange:NSMakeRange(NSNotFound, 0) /* saves computation over passing "laySelectedGlyphRange" */ inTextContainer:[htmlView textContainer] rectCount:&selectedRectCount];
    if (selectedRectCount == 0)
        return;

    OBASSERT(selectedRectCount == 1);
    layMinX = NSMinX(selectedRectArray[0]) + NSMinX(lineBounds) + x - firstWordXLocationInLayoutManager;
    *minX = MIN(*minX, layMinX);
    *maxX = MAX(*maxX, layMinX + NSWidth(selectedRectArray[0]));
}

- (float)xPositionOfCharacterAtIndex:(unsigned int)characterIndex inHTMLView:(OHHTMLView *)htmlView;
{
    NSRange characterRange;

    characterRange = [self characterRangeInHTMLView:htmlView];
    if (characterIndex == characterRange.location)
        return x;
    else if (characterIndex == NSMaxRange(characterRange))
        return x + width;
    else {
        const NSRect *rectArray;
        unsigned int rectCount;

        rectArray = [[htmlView layoutManager] rectArrayForCharacterRange:NSMakeRange(characterIndex, 1) withinSelectedCharacterRange:NSMakeRange(NSNotFound, 0) inTextContainer:[htmlView textContainer] rectCount:&rectCount];

        OBASSERT(rectCount > 0);
        OBASSERT(rectCount == 1);
        return NSMinX(rectArray[0]) - firstWordXLocationInLayoutManager + x;
    }
}

- (NSComparisonResult)compareToPoint:(NSPoint)point glyphIndex:(unsigned int *)glyphIndex fractionOfDistanceThroughGlyph:(float *)fraction lineBounds:(NSRect)lineBounds paragraph:(OHParagraph *)paragraph inHTMLView:(OHHTMLView *)htmlView;
{
    float layMinimumX;

    layMinimumX = NSMinX(lineBounds) + x;
    if (point.x < layMinimumX) {
        if (fraction)
            *fraction = 0.0;
        *glyphIndex = [htmlView glyphRangeForWordRange:wordRange].location;
        return NSOrderedDescending;
    }

    if (point.x >= layMinimumX + width) {
        if (fraction)
            *fraction = 1.0;
        *glyphIndex = NSMaxRange([htmlView glyphRangeForWordRange:wordRange])-1;
        return NSOrderedAscending;
    }

    // Check if the lay just contains a cell, because if so we need to calculate hits ourselves, since cells in the textStorage can be radically different sizes from the way we render them (cellSize vs. contentFrame).
    if ([self calculatesWidthIndependentlyOfTextStorageInHTMLView:htmlView]) {
        if (fraction)
            *fraction = (point.x - layMinimumX) / width;
        *glyphIndex = NSMaxRange([htmlView glyphRangeForWordRange:wordRange]) - 1;
        return NSOrderedSame;
    }
    
    // Hackage: For some reason, if you have two blank lines and a paragraph that gets wrapped into several lines, and you click at the exact top of one of the lines, NSLayoutManager will say you clicked on the line above, even though the y we pass in is the y it gave us.  So, instead we just pass in our y + 1, since the y offset really doesn't matter once we've figured out what line we're on.
    *glyphIndex = [[htmlView layoutManager] glyphIndexForPoint:NSMakePoint(point.x - NSMinX(lineBounds) - x + firstWordXLocationInLayoutManager, paragraph->bounds.origin.y + 1.0) inTextContainer:[htmlView textContainer] fractionOfDistanceThroughGlyph:fraction];
    return NSOrderedSame;
    
    // The original code, that should have worked:
    //    return [[htmlView layoutManager] glyphIndexForPoint: NSMakePoint(point.x - NSMinX(lineBounds) - x + firstWordXLocationInLayoutManager, point.y - NSMinY(lineBounds) + paragraph->bounds.origin.y) inTextContainer:[htmlView textContainer] fractionOfDistanceThroughGlyph:fraction];
}

- (NSComparisonResult)compareToCharacterIndex:(unsigned int)characterIndex inHTMLView:(OHHTMLView *)htmlView;
{
    NSRange characterRange;

    characterRange = [self characterRangeInHTMLView:htmlView];
    if (characterIndex < characterRange.location)
        return NSOrderedDescending;
    else if (characterIndex < NSMaxRange(characterRange))
        return NSOrderedSame;
    else
        return NSOrderedAscending;
}

- (NSRange)characterRangeInHTMLView:(OHHTMLView *)htmlView;
{
    NSRange characterRange;
    OFStaticArray *words;
    OHWord *firstWord, *lastWord;

    words = [htmlView words];
    firstWord = [words objectAtIndex:wordRange.location];
    characterRange.location = firstWord->characterRange.location;
    if (wordRange.length == 1)
        lastWord = firstWord;
    else
        lastWord = [words objectAtIndex:NSMaxRange(wordRange) - 1];
    characterRange.length = NSMaxRange(lastWord->characterRange) - characterRange.location;
    return characterRange;
}

- (OHWord *)attachmentWordInHTMLView:(OHHTMLView *)htmlView;
{
    OHWord *word;

    // Attachment words are always the only word in the lay
    if (wordRange.length != 1)
        return nil;

    word = [[htmlView words] objectAtIndex:wordRange.location];
    if (word->type == OHWordTypeAttachment)
        return word;

    return nil;
}

// Debugging

- (NSMutableDictionary *)debugDictionary;
{
    NSMutableDictionary *debugDictionary;

    debugDictionary = [super debugDictionary];
    [debugDictionary setObject:NSStringFromRange(wordRange) forKey:@"wordRange"];
    [debugDictionary setObject:[NSString stringWithFormat:@"%0.1f", x] forKey:@"x"];
    [debugDictionary setObject:[NSString stringWithFormat:@"%0.1f", width] forKey:@"width"];
    [debugDictionary setObject:[NSString stringWithFormat:@"%0.1f", firstWordXLocationInLayoutManager] forKey:@"firstWordXLocationInLayoutManager"];
    return debugDictionary;
}

@end

@implementation OHLay (Private)

- (BOOL)calculatesWidthIndependentlyOfTextStorageInHTMLView:(OHHTMLView *)htmlView;
{
    OHWord *word;

    // Specially sized words are always the only word in the lay
    if (wordRange.length != 1)
        return NO;

    word = [[htmlView words] objectAtIndex:wordRange.location];
    return word->type == OHWordTypeAttachment || word->type == OHWordTypeTab;
}

@end

