/* 
   SybaseValues.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
   Date: October 1996

   This file is part of the GNUstep Database Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <Foundation/NSData.h>
#include <Foundation/NSString.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSUtilities.h>

#include <extensions/NSException.h>

#include <eoaccess/common.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EOCustomValues.h>
#include <eoaccess/exceptions/EOFExceptions.h>
#include <eoaccess/EOQuotedExpression.h>

#include "SybaseAdaptor.h"
#include "SybaseChannel.h"
#include "SybaseValues.h"
#include "SybaseExceptions.h"

void __sybase_values_linking_function (void)
{
}

@implementation NSString (SybaseValueCreation)

+ valueFromBytes:(const char*)bytes
  length:(unsigned)length
  sybaseType:(int)type
  attribute:(EOAttribute*)attribute
  adaptorChannel:(SybaseChannel*)channel
  zone:(NSZone*)zone
{
    char buffer[256];

    switch (type) {
	case SYBTEXT:
	case SYBIMAGE:
	case SYBBINARY:
	case SYBVARBINARY:
	case SYBCHAR:
	case SYBVARCHAR:
	    return [[[self allocWithZone:zone]
			initWithCString:bytes length:length]
			autorelease];
	case SYBBIT:
	    return [[[self allocWithZone:zone]
			initWithCString:Ltoa (*(DBBIT*)bytes, buffer, 10)]
			autorelease];
	case SYBINT1:
	    return [[[self allocWithZone:zone]
			initWithCString:Ltoa (*(DBTINYINT*)bytes, buffer, 10)]
			autorelease];
	case SYBINT2:
	    return [[[self allocWithZone:zone]
			initWithCString:Ltoa (*(DBSMALLINT*)bytes, buffer, 10)]
			autorelease];
	case SYBINT4:
	    return [[[self allocWithZone:zone]
			initWithCString:Ltoa (*(DBINT*)bytes, buffer, 10)]
			autorelease];
	case SYBFLT8: {
	    id value = [[NSNumber numberWithDouble:*(DBFLT8*)bytes]
			    stringValue];
	    if ([self isKindOfClass:[NSMutableString class]])
		value = [[value mutableCopyWithZone:zone] autorelease];
	    return value;
	}
	case SYBMONEY:
	case SYBDATETIME:
	    /* Convert the type value using the dbconvert() function */
	    if (dbconvert ([channel dbProcess],
			   type, bytes, length,
			   SYBCHAR, buffer, -1) == -1)
		return nil;
	    else return [[[self allocWithZone:zone] initWithCString:buffer]
			    autorelease];
	default:
	    THROW([[DataTypeMappingNotSupportedException alloc]
		    initWithFormat:@"mapping between %@ and sybase type %d "
				@"not supported", [self description], type]);
    }

    return nil;
}

- (NSString*)stringValueForSybaseType:(int)type
  attribute:(EOAttribute*)attribute
{
    switch (type) {
	case SYBBINARY:
	case SYBVARBINARY:
	    return [[NSData dataWithBytes:[self cString]
			    length:[self cStringLength]]
			stringValueForSybaseType:type attribute:attribute];
	case SYBCHAR:
	case SYBVARCHAR:
	case SYBDATETIME:
	case SYBTEXT:
	    return [[[[EOQuotedExpression alloc]
		    initWithExpression:self quote:@"\"" escape:@"\"\""]
		    autorelease]
		    expressionValueForContext:nil];
	case SYBBIT:
	case SYBINT1:
	case SYBINT2:
	case SYBINT4:
	case SYBFLT8:
	    return self;
	case SYBMONEY:
	    return [NSString stringWithFormat:@"$%@", self];
	case SYBIMAGE:
	    NSAssert (0, @"image type should not be handled here!");
	default:
	    THROW([[DataTypeMappingNotSupportedException alloc]
		    initWithFormat:@"mapping between %@ and sybase type %d "
				@"not supported", [isa description], type]);
    }

    return nil;
}

@end /* NSString (SybaseValueCreation) */


@implementation NSNumber (SybaseValueCreation)

+ valueFromBytes:(const char*)bytes
  length:(unsigned)length
  sybaseType:(int)type
  attribute:(EOAttribute*)attribute
  adaptorChannel:(SybaseChannel*)channel
  zone:(NSZone*)zone
{
#define RETURN_NUMBER(DATABASE_TYPE, defaultMethod) \
    if (!valueType) \
	return [NSNumber defaultMethod *(DATABASE_TYPE*)bytes]; \
    if ([valueType isEqual:@"c"]) \
	return [NSNumber numberWithChar:*(DATABASE_TYPE*)bytes]; \
    if ([valueType isEqual:@"C"]) \
	return [NSNumber numberWithUnsignedChar:*(DATABASE_TYPE*)bytes]; \
    else if ([valueType isEqual:@"s"]) \
	return [NSNumber numberWithShort:*(DATABASE_TYPE*)bytes]; \
    else if ([valueType isEqual:@"S"]) \
	return [NSNumber numberWithUnsignedShort:*(DATABASE_TYPE*)bytes]; \
    else if ([valueType isEqual:@"i"]) \
	return [NSNumber numberWithInt:*(DATABASE_TYPE*)bytes]; \
    else if ([valueType isEqual:@"I"]) \
	return [NSNumber numberWithUnsignedInt:*(DATABASE_TYPE*)bytes]; \
    else if ([valueType isEqual:@"f"]) \
	return [NSNumber numberWithFloat:*(DATABASE_TYPE*)bytes]; \
    else if ([valueType isEqual:@"d"]) \
	return [NSNumber numberWithDouble:*(DATABASE_TYPE*)bytes]; \
    else THROW([[InvalidValueTypeException alloc] initWithType:valueType]);

    NSString* valueType = [attribute valueType];

    switch (type) {
	case SYBBIT:
	    RETURN_NUMBER(DBBIT, numberWithChar:);
	case SYBINT1:
	    RETURN_NUMBER(DBTINYINT, numberWithChar:);
	case SYBINT2:
	    RETURN_NUMBER(DBSMALLINT, numberWithShort:);
	case SYBINT4:
	    RETURN_NUMBER(DBINT, numberWithInt:);
	case SYBFLT8:
	    RETURN_NUMBER(DBFLT8, numberWithDouble:);
	case SYBMONEY: {
	    double value;
	    /* Convert the money value using the dbconvert() function */
	    if (dbconvert ([channel dbProcess],
			   type, bytes, length,
			   SYBFLT8, &value, sizeof(double)) == -1)
		return nil;
	    else {
		bytes = (void*)&value;
		RETURN_NUMBER(double, numberWithDouble:);
	    }
	}
	default:
	    THROW([[DataTypeMappingNotSupportedException alloc]
		    initWithFormat:@"mapping between %@ and sybase type %d "
				@"not supported", [self description], type]);
    }

    return nil;

#undef RETURN_NUMBER
}

- (NSString*)stringValueForSybaseType:(int)type
  attribute:(EOAttribute*)attribute
{
    switch (type) {
	case SYBBIT:
	case SYBINT1:
	case SYBINT2:
	case SYBINT4:
	case SYBFLT8:
	    return [self stringValue];
	case SYBMONEY:
	    return [NSString stringWithFormat:@"$%@", self];
	default:
	    THROW([[DataTypeMappingNotSupportedException alloc]
		    initWithFormat:@"mapping between %@ and sybase type %d "
				@"not supported", [isa description], type]);
    }

    return nil;
}

@end /* NSNumber (SybaseValueCreation) */


@implementation NSData (SybaseValueCreation)

+ valueFromBytes:(const char*)bytes
  length:(unsigned)length
  sybaseType:(int)type
  attribute:(EOAttribute*)attribute
  adaptorChannel:(SybaseChannel*)channel
  zone:(NSZone*)zone
{
    switch (type) {
	case SYBTEXT:
	case SYBIMAGE:
	case SYBBINARY:
	case SYBVARBINARY:
	case SYBCHAR:
	case SYBVARCHAR:
	case SYBINT1:
	case SYBINT2:
	case SYBINT4:
	case SYBFLT8:
	    return [[[self allocWithZone:zone]
			initWithBytes:bytes length:length]
			autorelease];
	default:
	    THROW([[DataTypeMappingNotSupportedException alloc]
		    initWithFormat:@"mapping between %@ and sybase type %d "
				@"not supported", [self description], type]);
    }

    return nil;
}

- (NSString*)stringValueForSybaseType:(int)type
  attribute:(EOAttribute*)attribute
{
    const char* bytes = [self bytes];

    switch (type) {
	case SYBINT1:
	    return [[NSNumber numberWithChar:*(char*)bytes] stringValue];
	case SYBINT2:
	    return [[NSNumber numberWithShort:*(short*)bytes] stringValue];
	case SYBINT4:
	    return [[NSNumber numberWithInt:*(int*)bytes] stringValue];
	case SYBFLT8:
	    return [[NSNumber numberWithDouble:*(double*)bytes] stringValue];
	case SYBCHAR:
	case SYBVARCHAR:
	case SYBBINARY:
	case SYBVARBINARY:
	case SYBIMAGE: {
	    int length = [self length];
	    int final_length;
	    char *description, *temp;
	    int i;

	    if (!length)
		return @"NULL";

	    final_length = 2 + 2 * length + 1;
	    description = Malloc (final_length);
	    temp = description + 2;

	    description[0] = 0;
	    strcat (description, "0x");
	    for (i = 0; i < length; i++, temp += 2)
		sprintf (temp, "%02X", (unsigned char)bytes[i]);
	    *temp = 0;
	    return [[[NSString alloc] 
		    initWithCStringNoCopy:description
		    length:Strlen(description)
		    freeWhenDone:YES]
		autorelease];
	}
	case SYBTEXT:
	    NSAssert (0, @"text type should not be handled here!");
	default:
	    THROW([[DataTypeMappingNotSupportedException alloc]
		    initWithFormat:@"mapping between %@ and sybase type %d "
				@"not supported", [isa description], type]);
    }

    return nil;
}

@end /* NSData (SybaseValueCreation) */


@implementation NSCalendarDate (SybaseValueCreation)

+ valueFromBytes:(const char*)bytes
  length:(unsigned)length
  sybaseType:(int)type
  attribute:(EOAttribute*)attribute
  adaptorChannel:(SybaseChannel*)channel
  zone:(NSZone*)zone
{
    DBDATEREC daterec;

    switch (type) {
	case SYBDATETIME:
	    if (dbdatecrack ([channel dbProcess], &daterec, (DBDATETIME*)bytes)
		== SUCCEED) {
		return [[channel class]
			    dateForAttribute:attribute
					year:daterec.dateyear
				       month:daterec.datemonth + 1
					 day:daterec.datedmonth
					hour:daterec.datehour
				      minute:daterec.dateminute
				      second:daterec.datesecond
					zone:zone];
	    }
	default:
	    THROW([[DataTypeMappingNotSupportedException alloc]
		    initWithFormat:@"mapping between %@ and sybase type %d "
				@"not supported", [self description], type]);
    }

    return nil;
}

- (NSString*)stringValueForSybaseType:(int)type
  attribute:(EOAttribute*)attribute
{
    if (type == SYBDATETIME) {
	[self setTimeZone:[attribute serverTimeZone]];
	return [[[[EOQuotedExpression alloc]
		    initWithExpression:[self descriptionWithCalendarFormat:
						@"%b %d %Y %I:%M:%S%p"]
		    quote:@"\"" escape:@"\"\""]
		    autorelease]
		    expressionValueForContext:nil];
    }
    else
	THROW([[DataTypeMappingNotSupportedException alloc]
		initWithFormat:@"mapping between %@ and sybase type %d not "
			       @"supported", [isa description], type]);

    return nil;
}

@end /* NSCalendarDate (SybaseValueCreation) */


@implementation EONull (SybaseValueCreation)

- (NSString*)stringValueForSybaseType:(int)type
  attribute:(EOAttribute*)attribute
{
    return @"NULL";
}

@end


@implementation NSObject (SybaseValueCreation)

+ valueFromBytes:(const char*)bytes
  length:(unsigned)length
  sybaseType:(int)type
  attribute:(EOAttribute*)attribute
  adaptorChannel:(SybaseChannel*)channel
  zone:(NSZone*)zone
{
    if ([self instancesRespondToSelector:@selector(initWithString:type:)])
	return [[[self allocWithZone:zone]
	    initWithString:[NSString stringWithCString:bytes length:length]
	    type:[attribute valueType]]
	    autorelease];
    else if ([self instancesRespondToSelector:@selector(initWithData:type:)])
	return [[[self allocWithZone:zone]
	    initWithData:[NSData dataWithBytes:bytes length:length]
	    type:[attribute valueType]]
	    autorelease];
    else
	THROW([[DataTypeMappingNotSupportedException alloc]
		initWithFormat:@"mapping between %@ and sybase type %d not "
			       @"supported", [self description], type]);

    return nil;
}

- (NSString*)stringValueForSybaseType:(int)type
  attribute:(EOAttribute*)attribute
{
    if ([self respondsToSelector:@selector(stringForType:)])
	return [[self stringForType:[attribute valueType]]
		    stringValueForSybaseType:type attribute:attribute];
    else if ([self respondsToSelector:@selector(dataForType:)])
	return [[self dataForType:[attribute valueType]]
		    stringValueForSybaseType:type attribute:attribute];
    else
	THROW([[DataTypeMappingNotSupportedException alloc]
		initWithFormat:@"mapping between %@ and sybase type %d not "
			       @"supported", [isa description], type]);

    return nil;
}

@end /* NSObject (SybaseValueCreation) */
