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

#import <Foundation/Foundation.h>
#import <objc/objc-runtime.h>
#import <OmniBase/OmniBase.h>

#import "NSString-OFExtensions.h"

RCS_ID("$Header: /Network/Developer/Source/CVS/OmniGroup/OmniFoundation/OFUtilities.m,v 1.9 1999/03/30 21:25:33 kc Exp $")

void OFSetIvar(NSObject *object, NSString *ivarName, NSObject *ivarValue)
{
    Ivar ivar;
    id *ivarSlot;

    // TODO:At some point, this function should take a void * and should look at the type of the ivar and deal with scalar values correctly.

    ivar = class_getInstanceVariable(*(Class *) object, [ivarName cString]);
    OBASSERT(ivar);

    ivarSlot = (id *)((char *)object + ivar->ivar_offset);

    if (*ivarSlot != ivarValue) {
	[*ivarSlot release];
	*ivarSlot = [ivarValue retain];
    }
}

NSObject *OFGetIvar(NSObject *object, NSString *ivarName)
{
    Ivar ivar;
    id *ivarSlot;

    ivar = class_getInstanceVariable(*(Class *) object, [ivarName cString]);
    OBASSERT(ivar);

    ivarSlot = (id *)((char *)object + ivar->ivar_offset);

    return *ivarSlot;
}

static const char *hexTable = "0123456789abcdef";

char *OFNameForPointer(id object, char *pointerName)
{
    char firstChar, *p = pointerName;
    const char *className;
    unsigned long pointer;

    if (!object) {
	*pointerName++ = '*';
	*pointerName++ = 'N';
	*pointerName++ = 'I';
	*pointerName++ = 'L';
	*pointerName++ = '*';
	*pointerName++ = '\0';
	return p;
    }

    if (OBPointerIsClass(object)) {
	firstChar = '+';
        pointer = (unsigned long)object;
    } else {
	firstChar = '-';
	pointer = (unsigned long)object->isa;
    }

    // Rather than calling sprintf, we'll just format the string by hand.  This is much faster.

    // Mark whether it is an instance or not
    *pointerName++ = firstChar;

    // Write the class name
    // BUG: We don't actually enforce the name length limit
    if (!(className = ((Class)pointer)->name))
	className = "Bogus name!";

    while ((*pointerName++ = *className++))
	;

    // Back up over the trailing null
    pointerName--;
    *pointerName++ = ' ';
    *pointerName++ = '(';

    // Write the pointer as hex
    *pointerName++ = '0';
    *pointerName++ = 'x';

    pointer = (unsigned long) object;
    pointerName += 7;

    // 8
    *pointerName-- = hexTable[pointer & 0xf];
    pointer >>= 4;

    // 7
    *pointerName-- = hexTable[pointer & 0xf];
    pointer >>= 4;

    // 6
    *pointerName-- = hexTable[pointer & 0xf];
    pointer >>= 4;

    // 5
    *pointerName-- = hexTable[pointer & 0xf];
    pointer >>= 4;

    // 4
    *pointerName-- = hexTable[pointer & 0xf];
    pointer >>= 4;

    // 3
    *pointerName-- = hexTable[pointer & 0xf];
    pointer >>= 4;

    // 2
    *pointerName-- = hexTable[pointer & 0xf];
    pointer >>= 4;

    // 1
    *pointerName-- = hexTable[pointer & 0xf];

    pointerName += 9;

    *pointerName++ = ')';
    *pointerName++ = '\0';

    return p;
}

char *OFStackAllocatedNameForPointer(id object)
{
    static char bufs[16][OF_MAX_CLASS_NAME_LEN];
    static unsigned int bufIndex = 0;
    char *ret;

    ret = bufs[bufIndex];
    OFNameForPointer(object, ret);
    bufIndex = (bufIndex + 1) % 16;
    return ret;
}

BOOL OFInstanceIsKindOfClass(id instance, Class aClass)
{
    Class sourceClass = instance->isa;

    while (sourceClass) {
        if (sourceClass == aClass)
            return YES;
        sourceClass = sourceClass->super_class;
    }
    return NO;
}

NSString *OFDescriptionForObject(id object, NSDictionary *locale, unsigned indentLevel)
{
    if ([object isKindOfClass:[NSString class]])
        return object;
    else if ([object respondsToSelector:@selector(descriptionWithLocale:indent:)])
        return [(id)object descriptionWithLocale:locale indent:indentLevel + 1];
    else  if ([object respondsToSelector:@selector(descriptionWithLocale:)])
        return [(id)object descriptionWithLocale:locale];
    else
        return [NSString stringWithFormat: @"%@%@",
            [NSString spacesOfLength:(indentLevel + 1) * 4],
            [object description]];
}

#ifdef WIN32
#ifdef YELLOW_BOX

#warning We currently assume that Yellow Box for Windows supports print-object in gdb
// If this assumption is correct, we should remove this warning.  If incorrect, we should reenable this code in Yellow Box.

#else

// This code is needed for print-object support in gdb on Windows

#import <objc/hashtable.h>
#import <objc/objc-runtime.h>
#import <windows.h>

static BOOL isClass(void *ptr)
{
    NXHashTable *classes = objc_getClasses();
    NXHashState state = NXInitHashState(classes);
    Class cls = (Class)0;

    if (ptr == nil)
        return NO;
    while (NXNextHashState(classes, &state, (void *)&cls)) {
        if (cls == ptr)
            return YES;
    }
    return NO;
}

const char *printForDebugger(struct objc_object *obj)
{
    static NSString *string = nil;

    if (obj == nil)
        return "<nil>";

    if (string != nil) {
        [string release];   // let go of string from previous call
        string = nil;
    }

    if (IsBadReadPtr(obj, 4) || (!isClass(obj->isa) && !isClass(obj)))
        return "<not an object>";

    if (![obj respondsToSelector: @selector(description)])
        return "<object does not respond to PrintObject method>";

    NS_DURING {
        string = [obj description];
    } NS_HANDLER {
        return "<object raised exception generating description>";
    } NS_ENDHANDLER;

    if (string == nil || [string isEqualToString:@""]) {
        if (string)
            string = nil;
        return "<object returns empty description>";
    }

    [string retain];      // hold onto for gdb until called again
    return [string lossyCString];
}

#endif
#endif
