/*--------------------------------------------------------------------------
 *
 * 	MiscTableSource
 *
 * 	Inherits From:	List
 *
 * 	Conforms To:	NXTransport,DBTableDataSources
 *
 * 	Declared In:	MiscTableSource.m
 *
 *
 * 	$Log$
 *
 * Copyright (C) 1995 Vince Demarco <vdemarco@bou.shl.com>
 * Use is governed by the MiscKit license
 *--------------------------------------------------------------------------*/
#import <objc/Object.h>
#import <appkit/nextstd.h>
#import <remote/NXConnection.h>
#import <remote/NXProxy.h>
#import <machkit/NXPort.h>
#import <machkit/reference.h>
#import <remote/transport.h>
#import <stdlib.h>
#import <ctype.h>

#import <misckit/MiscTableSource.h>
#import <misckit/MiscListSorting.h>
#import <dbkit/DBValue.h>

@implementation MiscTableSource

- (unsigned int)rowCount
{
    return [self count];
}

- (unsigned int)columnCount
{
    return columnCount;
}

- setColumnCount:(unsigned int)aInt
{
    columnCount = aInt;
    return self;
}

- getValueFor:identifier at:(unsigned int) aPosition into:aValue
{
    Method methodStruct;
    SEL    aSelector;
    id     theObject;

    theObject    = [self objectAt:aPosition];
    aSelector	 = [self getSelectorForIdentifer:(const char *)identifier];
    methodStruct = class_getInstanceMethod([theObject class],(SEL)aSelector);

    if (aSelector && methodStruct && theObject){
	/* Here i am checking the return type of the the method (Well I hope that I am checking)
	 *                                                      (the Return type of the method)
	 */
	switch(methodStruct->method_types[0]){
	case _C_ID:
	    [aValue setObjectValue:(id)[theObject perform:aSelector]];
	    break;
	case _C_SHT:
	case _C_USHT:
	    {
		short int val = ((int(*)(id,SEL))[theObject methodFor:aSelector])
		    (theObject,aSelector);
		[aValue setIntValue:val];
		break;
	    }
	case _C_INT:
	case _C_UINT:
	    {
		int val = ((int(*)(id,SEL))[theObject methodFor:aSelector])
		    (theObject,aSelector);
		[aValue setIntValue:val];
		break;
	    }
	case _C_LNG:
	case _C_ULNG:
	    {
		long int val = ((int(*)(id,SEL))[theObject methodFor:aSelector])
		    (theObject,aSelector);
		[aValue setIntValue:val];
		break;
	}
	case _C_FLT:
	    {
		float val = ((float(*)(id,SEL))[theObject methodFor:aSelector])
		    (theObject,aSelector);
		[aValue setFloatValue:val];
		break;
	}
	case _C_DBL:
	    {
		double val = ((double(*)(id,SEL))[theObject methodFor:aSelector])
		    (theObject,aSelector);
		[aValue setDoubleValue:val];
		break;
	}
	case _C_CHARPTR:
	    {
		const char *val = ((const char*(*)(id,SEL))[theObject methodFor:aSelector])
		    (theObject,aSelector);
		[aValue setStringValue:val];
		break;
	    }
	default:
	    break;
	}
    }else{
	[aValue setNull];
	if(theObject){
	    NXLogError("[%s:%s]: %s has no getSelector for \"%s\" at %u",
		       [[self class] name],sel_getName(_cmd),[[theObject class] name], identifier, aPosition);
	}else{
	    NXLogError("[%s:%s]: attempt to get value at position %u which is not in the list",
		       [[self class] name],sel_getName(_cmd),aPosition);
	}
    }
    return self;
}

- setValueFor:identifier at:(unsigned int) aPosition from:aValue
{
    SEL         aSelector;
    Method      methodStruct;
    const char *type;
    int         offset;
    id          theObject;

    theObject    = [self objectAt:aPosition];
    aSelector	 = [self setSelectorForIdentifer:(const char *)identifier];
    methodStruct = class_getInstanceMethod([theObject class],(SEL)aSelector);

    if (aSelector && methodStruct && theObject && method_getNumberOfArguments(methodStruct) == 3){

	method_getArgumentInfo(methodStruct,2,&type,&offset);
	switch(type[0]){
	case _C_ID:
	    ((void(*)(id,SEL,id))[theObject methodFor:aSelector])
		(theObject,aSelector,[aValue objectValue]);
	    break;
	case _C_SHT:
	case _C_USHT:
	    ((void(*)(id,SEL,short int))[theObject methodFor:aSelector])
		(theObject,aSelector,[aValue intValue]);
	    break;
	case _C_INT:
	case _C_UINT:
	    ((void(*)(id,SEL,int))[theObject methodFor:aSelector])
		(theObject,aSelector,[aValue intValue]);
	    break;
	case _C_LNG:
	case _C_ULNG:
	    ((void(*)(id,SEL,long int))[theObject methodFor:aSelector])
		(theObject,aSelector,[aValue intValue]);
	    break;
	case _C_FLT:
	    ((void(*)(id,SEL,float))[theObject methodFor:aSelector])
		(theObject,aSelector,[aValue floatValue]);
	    break;
	case _C_DBL:
	    ((void(*)(id,SEL,double))[theObject methodFor:aSelector])
		(theObject,aSelector,[aValue doubleValue]);
	    break;
	case _C_CHARPTR:
	    ((void(*)(id,SEL,const char *))[theObject methodFor:aSelector])
		(theObject,aSelector,[aValue stringValue]);
	    break;
	default:
	    return nil;
	    break;
	}
    }else{
	return nil;
    }
    return self;
}

- (SEL)getSelectorForIdentifer:(const char *)identifier
{
    if (identifier){
	if (sel_getUid(identifier)){            /* Try just identifer */
	    return sel_getUid(identifier);
	}else{
	    char *selectorName;

	    selectorName = alloca(strlen(identifier)+8); /* Try getIdentifer */
	    sprintf(selectorName,"get%s",identifier);
	    selectorName[3] = toupper(selectorName[3]);
	    return sel_getUid(selectorName);
	}
    }
    return (SEL)0;
}

- (SEL)setSelectorForIdentifer:(const char *)identifier
{
    if (identifier){
	char *selectorName;

	selectorName = alloca(strlen(identifier)+8); /* Try getIdentifer */
	sprintf(selectorName,"set%s:",identifier);
	selectorName[3] = toupper(selectorName[3]);

	if (sel_getUid(selectorName)){        /* try setIdentifer: */
	    return sel_getUid(selectorName);
	}else{                                /* try setidentifer: */
	    sprintf(selectorName,"set%s:",identifier);
	    return sel_getUid(selectorName);
	}
    }
    return (SEL)0;
}

- sortUsingIdentifer:(const char *)identifer ascending:(BOOL)aBool
{
    return [self sortUsing:[self getSelectorForIdentifer:identifer] ascending:aBool];
}

/* Archiving Methods */
- read:(NXTypedStream *)stream
{
    [super read:stream];
    NXReadType(stream,"i",&columnCount);
    return self;
}

- write:(NXTypedStream *)stream
{
    [super write:stream];
    NXWriteType(stream,"i",&columnCount);
    return self;
}

/* NXTransport Protocol*/
- encodeRemotelyFor:(NXConnection *)connection freeAfterEncoding:(BOOL *)flagp isBycopy:(BOOL)isBycopy
{
    if (isBycopy)
        return self;
    return [super encodeRemotelyFor:connection freeAfterEncoding:flagp isBycopy:isBycopy];
}

- encodeUsing:(id <NXEncoding>)portal
{
    int     n = [self count];
    int counter;

    [portal encodeData:&columnCount ofType:"i"];
    [portal encodeData:&n ofType:"i"];
    for (counter = 0; counter < n; ++counter) {
        id      anObject = [self objectAt:counter];
        [portal encodeObjectBycopy:anObject];
    }
    return self;
}

- decodeUsing:(id <NXDecoding>)portal
{
    int counter, n;

    [portal decodeData:&columnCount ofType:"i"];
    [portal decodeData:&n ofType:"i"];
    [self initCount:n];
    for (counter = 0; counter < n; ++counter) {
        id anObject = [portal decodeObject];
        [self addObject:anObject];
    }
    return self;
}

@end
