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

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>
#import <AppKit/AppKit.h>
#import "OWScriptEvent.h"
#import "OWScriptEventHandlerHolder.h"
#import "OHHTMLAnchor.h"
#import "OHInlineImageCell.h"

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniHTML/View.subproj/OHHTMLView-TrackingRect.m,v 1.8 2000/01/20 00:28:04 kc Exp $")

@implementation OHHTMLView_TrackingRect

static BOOL OHHTMLViewTrackingRectDebug = NO;

- initWithRectangle:(NSRect)aRect anchor:(OHHTMLAnchor *)anAnchor
{
    [super init];
    rectangle = aRect;
    anchorOrCell = anAnchor;
    objectType = OHHVTR_Anchor;
    isInside = NO;
    return self;
}

- initWithRectangle:(NSRect)aRect imageCell:(OHBasicCell *)aCell
{
    [super init];
    rectangle = aRect;
    anchorOrCell = aCell;
    objectType = OHHVTR_ImageCell;
    isInside = NO;
    OBASSERT([anchorOrCell respondsToSelector:@selector(eventHandlers)]);
    return self;
}

- (NSRect)rectangle
{
    return rectangle;
}

- (NSTrackingRectTag)tag
{
    return tag;
}

- (void)setTag:(NSTrackingRectTag)newTag
{
    tag = newTag;
}

- (BOOL)isInside
{
    return isInside;
}

- (void)mouseEntered:(NSEvent *)theEvent
{
    OWScriptEventHandlerHolder *handlers;
    OHHTMLAnchor *link;

    isInside = YES;
    
    switch (objectType) {
        case OHHVTR_Anchor:
            handlers = [(OHHTMLAnchor *)anchorOrCell eventHandlers];
            [OWScriptEvent sendBackgroundEvent:OWSE_MouseEntered
                                     fromEvent:theEvent
                                    instigator:anchorOrCell
                                     responder:handlers];
            break;
        case OHHVTR_ImageCell:
            link = [(OHInlineImageCell *)anchorOrCell link];
            if (link) {
                [OWScriptEvent sendBackgroundEvent:OWSE_MouseEntered
                                         fromEvent:theEvent
                                        instigator:link
                                         responder:[link eventHandlers]];
            }
            handlers = [(OHInlineImageCell *)anchorOrCell eventHandlers];
            [OWScriptEvent sendBackgroundEvent:OWSE_MouseEntered
                                     fromEvent:theEvent
                                    instigator:anchorOrCell
                                     responder:handlers];
            break;
        default:
            if (OHHTMLViewTrackingRectDebug)
                NSLog(@"Unimplemented mouse-entry event type");
            /* TODO */
            return;
    }
}

/* NOTE theEvent may be nil, if this is being sent as a result of the tracking rect being removed from the window or otherwise altered */
- (void)mouseExited:(NSEvent *)theEvent
{
    OWScriptEventHandlerHolder *handlers;
    OHHTMLAnchor *link;

    isInside = NO;

    switch(objectType) {
        case OHHVTR_Anchor:
            handlers = [(OHHTMLAnchor *)anchorOrCell eventHandlers];
            [OWScriptEvent sendBackgroundEvent:OWSE_MouseExited
                                     fromEvent:theEvent
                                    instigator:anchorOrCell
                                     responder:handlers];
            break;
        case OHHVTR_ImageCell:
            handlers = [(OHInlineImageCell *)anchorOrCell eventHandlers];
            [OWScriptEvent sendBackgroundEvent:OWSE_MouseExited
                                     fromEvent:theEvent
                                    instigator:anchorOrCell
                                     responder:handlers];
            link = [(OHInlineImageCell *)anchorOrCell link];
            if (link) {
                [OWScriptEvent sendBackgroundEvent:OWSE_MouseExited
                                         fromEvent:theEvent
                                        instigator:link
                                         responder:[link eventHandlers]];
            }
            break;
        default:
            if (OHHTMLViewTrackingRectDebug)
                NSLog(@"Unimplemented mouse-exited event type");
            /* TODO */
            return;
    }
}

/* This assumes that sizeof(float) == sizeof(unsigned), which is true on all supported architectures at the moment (all are 32 bits) */
static inline unsigned hash_float(float f)
{
    return *(unsigned *)&((float)f);
}

- (unsigned)hash
{
    unsigned hashval;

#define STIR_HASH(h) h = ( h << 3 ) ^ ( (h & 0xFF000000) >> 24 )

    /* we should arguably hash on the size of the rect also, but two tracking rects should never have the same origin in practice so this is good enough */
    hashval = hash_float(rectangle.origin.x);
    STIR_HASH(hashval);
    hashval ^= hash_float(rectangle.origin.y);
    STIR_HASH(hashval);
    hashval ^= (unsigned)anchorOrCell;
    hashval ^= (unsigned)objectType;

    return hashval;
}

- (BOOL)isEqual:anObject
{
    OHHTMLView_TrackingRect *other;
    
    if (![anObject isMemberOfClass:isa])
        return NO;

    other = (OHHTMLView_TrackingRect *)anObject;

    if (other->anchorOrCell != self->anchorOrCell ||
        other->objectType != self->objectType)
        return NO;

    /* Rectangles are rounded to pixel boundaries (==integers) so a straightforward comparison is ok here */
    if (other->rectangle.origin.x != self->rectangle.origin.x ||
        other->rectangle.origin.y != self->rectangle.origin.y ||
        other->rectangle.size.width != self->rectangle.size.width ||
        other->rectangle.size.height != self->rectangle.size.height)
        return NO;
    
    return YES;
}

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

    debugDictionary = [super debugDictionary];

    [debugDictionary setBoolValue:isInside forKey:@"isInside"];
    [debugDictionary setIntValue:tag forKey:@"tag"];
    [debugDictionary setObject:[NSString stringWithFormat:@"origin=(%g %g) size=(%g %g)", rectangle.origin.x, rectangle.origin.y, rectangle.size.width, rectangle.size.height] forKey:@"rectangle"];
    
    switch(objectType) {
        case OHHVTR_Anchor:    typename = @"Anchor"; break;
        case OHHVTR_ImageCell: typename = @"ImageCell"; break;
        default:               typename = @"unknown"; break;
    }
    [debugDictionary setObject:typename forKey:@"objectType"];

    return debugDictionary;
}
    
@end
