/* 
   NSNumber.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <Foundation/common.h>
#include <Foundation/NSCoder.h>
#include <Foundation/NSException.h>
#include <Foundation/NSString.h>

#include <Foundation/exceptions/NSValueExceptions.h>

#include <extensions/objc-runtime.h>

#include "NSConcreteNumber.h"
#include "NSConcreteValue.h"

/*
 * Temporary number used to allocate and initialize NSNumbers 
 * through initWith... methods in constructs like [[NSNumber alloc] initWith...
 */

@interface NSTemporaryNumber : NSNumber
@end

@implementation NSTemporaryNumber

- initWithBool:(BOOL)value
{
    [self autorelease];
    return [[NSBoolNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithChar:(char)value
{
    [self autorelease];
    return [[NSCharNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithUnsignedChar:(unsigned char)value
{
    [self autorelease];
    return [[NSUCharNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithShort:(short)value
{
    [self autorelease];
    return [[NSShortNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithUnsignedShort:(unsigned short)value
{
    [self autorelease];
    return [[NSUShortNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithInt:(int)value
{
    [self autorelease];
    return [[NSIntNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithUnsignedInt:(unsigned int)value
{
    [self autorelease];
    return [[NSUIntNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithLong:(long)value
{
    [self autorelease];
    return [[NSLongNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithUnsignedLong:(unsigned long)value
{
    [self autorelease];
    return [[NSULongNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithLongLong:(long long)value
{
    [self autorelease];
    return [[NSLongLongNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithUnsignedLongLong:(unsigned long long)value
{
    [self autorelease];
    return [[NSULongLongNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithFloat:(float)value
{
    [self autorelease];
    return [[NSFloatNumber alloc]
	initValue:&value withObjCType:NULL];
}

- initWithDouble:(double)value
{
    [self autorelease];
    return [[NSDoubleNumber alloc]
	initValue:&value withObjCType:NULL];
}

@end

/*
 *  NSNumber class implementation
 */

@implementation NSNumber

+ (id)allocWithZone:(NSZone*)zone
{
    return NSAllocateObject( (self == [NSNumber class]) ? 
	    [NSTemporaryNumber class] : self, 0, zone);
}

/*
 * Determines the concrete value class
 * Only numbers
 */

+ (Class)concreteClassForObjCType:(const char*)type
{
    if (!type)
	THROW([[NSNumberException new] setReason:@"NULL type"]);

    if (Strlen(type) == 1) {
	switch(*type) {
	    case _C_CHR:	return [NSCharNumber class];
	    case _C_UCHR:	return [NSUCharNumber class];
	    case _C_SHT:	return [NSShortNumber class];
	    case _C_USHT:	return [NSUShortNumber class];
	    case _C_INT:	return [NSIntNumber class];
	    case _C_UINT:	return [NSUIntNumber class];
	    case _C_LNG:	return [NSLongNumber class];
	    case _C_ULNG:	return [NSULongNumber class];
	    case _C_FLT:	return [NSFloatNumber class];
	    case _C_DBL:	return [NSDoubleNumber class];
	    case 'q':		return [NSLongLongNumber class];
	    case 'Q':		return [NSULongLongNumber class];
	}
    }

    if (self == [NSNumber class]) {
	THROW([[NSNumberException new]
		    initWithFormat:@"Invalid number type '%s'", type]);

	return nil;
    }
    else 
	return [super concreteClassForObjCType:type];
}

+ (NSNumber*)numberWithBool:(BOOL)value
{
    return [[[NSBoolNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithChar:(char)value
{
    return [[[NSCharNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithUnsignedChar:(unsigned char)value
{
    return [[[NSUCharNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithShort:(short)value
{
    return [[[NSShortNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithUnsignedShort:(unsigned short)value
{
    return [[[NSUShortNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithInt:(int)value
{
    return [[[NSIntNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithUnsignedInt:(unsigned int)value
{
    return [[[NSUIntNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithLong:(long)value
{
    return [[[NSLongNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithUnsignedLong:(unsigned long)value
{
    return [[[NSULongNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithLongLong:(long long)value
{
    return [[[NSLongLongNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithUnsignedLongLong:(unsigned long long)value
{
    return [[[NSULongLongNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithFloat:(float)value
{
    return [[[NSFloatNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

+ (NSNumber*)numberWithDouble:(double)value
{
    return [[[NSDoubleNumber alloc] 
		initValue:&value withObjCType:NULL] 
		autorelease];
}

/* These methods are not written in concrete subclassses */

- (unsigned)hash
{
	return [self unsignedIntValue];
}

- (NSComparisonResult)compare:(NSNumber*)otherNumber
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (BOOL)isEqualToNumber:(NSNumber*)aNumber
{
    return [self compare:aNumber] == NSOrderedSame;
}

- (BOOL)isEqual:aNumber
{
    return [aNumber isKindOfClass:[NSNumber class]] 
	    && [self isEqualToNumber:aNumber];
}

- (NSString*)description
{
    return [self descriptionWithLocale:nil];
}

- (NSString*)descriptionWithLocale:(NSDictionary*)locale
{
    [self subclassResponsibility:_cmd];
    return nil;
}

/* Access methods are implemented in concrete subclasses */

- (BOOL)boolValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (char)charValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (unsigned char)unsignedCharValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (short)shortValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (unsigned short)unsignedShortValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (int)intValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (unsigned int)unsignedIntValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (long)longValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (unsigned long)unsignedLongValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (long long)longLongValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (unsigned long long)unsignedLongLongValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (float)floatValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (double)doubleValue
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (NSString*)stringValue
{
    return [self descriptionWithLocale:nil];
}

@end
