#import "PGResultRow.h"

static id
convert_type(PGresult *res, int col, OFString *str)
{
	switch (PQftype(res, col)) {
	case 16:  /* BOOLOID */
		if ([str isEqual: @"t"])
			return [OFNumber numberWithBool: YES];
		else
			return [OFNumber numberWithBool: NO];
	case 21:  /* INT2OID */
		return [OFNumber numberWithInt16: (int16_t)[str decimalValue]];
	case 23:  /* INT4OID */
		return [OFNumber numberWithInt32: (int32_t)[str decimalValue]];
	case 20:  /* INT8OID */
		return [OFNumber numberWithInt64: (int64_t)[str decimalValue]];
	case 700: /* FLOAT4OID */
		return [OFNumber numberWithFloat: [str floatValue]];
	case 701: /* FLOAT8OID */
		return [OFNumber numberWithDouble: [str doubleValue]];
	}

	return str;
}

@interface PGResultRowEnumerator: OFEnumerator
{
	PGResult *result;
	PGresult *res;
	int row, pos, count;
}

- initWithResult: (PGResult*)result
	     row: (int)row;
@end

@interface PGResultRowKeyEnumerator: PGResultRowEnumerator
@end

@interface PGResultRowObjectEnumerator: PGResultRowEnumerator
@end

@implementation PGResultRow
+ rowWithResult: (PGResult*)result
	    row: (int)row
{
	return [[[self alloc] initWithResult: result
					 row: row] autorelease];
}

- initWithResult: (PGResult*)result_
	     row: (int)row_
{
	self = [super init];

	result = [result_ retain];
	res = [result PG_result];
	row = row_;

	return self;
}

- (void)dealloc
{
	[result release];

	[super dealloc];
}

- (size_t)count
{
	int i, count, fields = PQnfields(res);

	for (i = count = 0; i < fields; i++)
		if (!PQgetisnull(res, row, i))
			count++;

	return count;
}

- (id)objectForKey: (id)key
{
	int col;

	if ([key isKindOfClass: [OFNumber class]])
		col = [key intValue];
	else
		col = PQfnumber(res, [key UTF8String]);

	if (PQgetisnull(res, row, col))
		return nil;

	return convert_type(res, col,
	    [OFString stringWithUTF8String: PQgetvalue(res, row, col)]);
}

- (OFEnumerator*)keyEnumerator
{
	return [[[PGResultRowKeyEnumerator alloc]
	    initWithResult: result
		       row: row] autorelease];
}

- (OFEnumerator*)objectEnumerator
{
	return [[[PGResultRowObjectEnumerator alloc]
	    initWithResult: result
		       row: row] autorelease];
}

- (int)countByEnumeratingWithState: (of_fast_enumeration_state_t*)state
			   objects: (id*)objects
			     count: (int)count
{
	int i, j;

	if (state->extra[0] == 0) {
		state->extra[0] = 1;
		state->extra[1] = PQnfields(res);
	}

	if (count > SIZE_MAX - state->state)
		@throw [OFOutOfRangeException exceptionWithClass: [self class]];

	if (state->state + count > state->extra[1])
		count = state->extra[1] - state->state;

	for (i = j = 0; i < count; i++) {
		if (PQgetisnull(res, row, state->state + i))
			continue;

		objects[j++] = [OFString stringWithUTF8String:
		    PQfname(res, state->state + i)];
	}

	state->state += count;
	state->itemsPtr = objects;
	state->mutationsPtr = (unsigned long*)self;

	return j;
}
@end

@implementation PGResultRowEnumerator
- initWithResult: (PGResult*)result_
	     row: (int)row_
{
	self = [super init];

	result = [result_ retain];
	res = [result PG_result];
	row = row_;
	count = PQnfields(res);

	return self;
}

- (void)reset
{
	pos = 0;
}
@end

@implementation PGResultRowKeyEnumerator
- (id)nextObject
{
	if (pos >= count)
		return nil;

	while (pos < count && PQgetisnull(res, row, pos))
		pos++;

	if (pos >= count)
		return nil;

	return [OFString stringWithUTF8String: PQfname(res, pos++)];
}
@end

@implementation PGResultRowObjectEnumerator
- (id)nextObject
{
	if (pos >= count)
		return nil;

	while (pos < count && PQgetisnull(res, row, pos))
		pos++;

	if (pos >= count)
		return nil;

	return convert_type(res, pos,
	    [OFString stringWithUTF8String: PQgetvalue(res, row, pos++)]);
}
@end
