#include <config.h>
#include <stdio.h>

#include <Foundation/NSString.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDate.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSProcessInfo.h>
#include <Foundation/NSAutoreleasePool.h>

#include <eoaccess/EOModel.h>
#include <eoaccess/EOEntity.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EOAttributeOrdering.h>
#include <eoaccess/EORelationship.h>
#include <eoaccess/EOQualifier.h>

#include <eoaccess/EOAdaptor.h>
#include <eoaccess/EOAdaptorContext.h>
#include <eoaccess/EOAdaptorChannel.h>

#include <eoaccess/EODatabase.h>
#include <eoaccess/EODatabaseContext.h>
#include <eoaccess/EODatabaseChannel.h>

#include <eoaccess/EOGenericRecord.h>
#include <eoaccess/EONull.h>


/*
 *  Adaptor's Delegate
 */

@interface AdaptorDelegate : NSObject
@end

@implementation AdaptorDelegate
- (BOOL)adaptor:(EOAdaptor*)adaptor willReportError:(NSString*)error
{
    NSLog (@"Adaptor %@ error: %@", [adaptor name], error);
    return YES;
}
@end


void main(int argc, char** argv, char** env)
{
    NSString* filename = @"models/testdb1-sybdb.eomodel";
    EOModel* model;
    id adaptor;
    id adaptorContext;
    id adaptorChannel;
    id database;
    id databaseContext;
    id databaseChannel;
    id delegate ;
    id pool;
    id sql;
    
    EOEntity *workerEntity, *projectEntity, *projworkEntity;
    id array, objects;
    EOGenericRecord* worker, *project, *projwork;
    id obj, row, val, key;
    int i;
    
#if LIB_FOUNDATION_LIBRARY
    [NSProcessInfo initializeWithArguments:argv count:argc environment:env];
#endif

    [NSAutoreleasePool enableDoubleReleaseCheck:YES];

    pool = [NSAutoreleasePool new];

    // Load model

    model = [[[EOModel alloc] initWithContentsOfFile:filename] autorelease];
    
    workerEntity = [model entityNamed:@"Worker"];
    projectEntity = [model entityNamed:@"Project"];
    projworkEntity = [model entityNamed:@"ProjWork"];

    // Make database/adaptor objects
    
    adaptor = [EOAdaptor adaptorWithModel:model];

    delegate = [AdaptorDelegate new];
    [adaptor setDelegate:delegate];
    
    database = [[[EODatabase alloc] initWithAdaptor:adaptor] autorelease];
    databaseContext = [database createContext];
    databaseChannel = [databaseContext createChannel];
    
    adaptorContext = [databaseContext adaptorContext];
    adaptorChannel = [databaseChannel adaptorChannel];
    
    [adaptorChannel setDebugEnabled:YES];
    [databaseChannel openChannel];

    /*
     *  Test begins here
     */

    // Cleanup

    [adaptorContext beginTransaction];
    sql = [NSString stringWithFormat:
	@"delete %@\ndelete %@\ndelete %@",
	[workerEntity externalName], 
	[projectEntity externalName], 
	[projworkEntity externalName]];
    [adaptorChannel evaluateExpression:sql];
    [adaptorContext commitTransaction];

    // Misc

    array = [[[NSMutableArray alloc] init] autorelease];
    objects = [[[NSMutableArray alloc] init] autorelease];
    
    // Worker - make
    
    row = [NSDictionary dictionaryWithObjectsAndKeys:
	[NSNumber numberWithInt:1], @"workerId",
	@"Ovidiu", @"name",
	nil];
    worker = [[EOGenericRecord alloc]
	initWithPrimaryKey:row entity:workerEntity];
    [array addObject:worker];

    row = [NSDictionary dictionaryWithObjectsAndKeys:
	[NSNumber numberWithInt:2], @"workerId",
	@"Mircea", @"name",
	nil];
    worker = [[EOGenericRecord alloc]
	initWithPrimaryKey:row entity:workerEntity];
    [array addObject:worker];

    row = [NSDictionary dictionaryWithObjectsAndKeys:
	[NSNumber numberWithInt:3], @"workerId",
	@"Scott", @"name",
	nil];
    worker = [[EOGenericRecord alloc]
	initWithPrimaryKey:row entity:workerEntity];
    [array addObject:worker];

    // Worker - insert

    [databaseContext beginTransaction];
    for (i = 0; i < [array count]; i++) {
	obj = [array objectAtIndex:i];
	[databaseChannel insertObject:obj];
    }
    [databaseContext commitTransaction];

    // Workers - fetch and compare
    
    [databaseContext beginTransaction];
    [databaseChannel selectObjectsDescribedByQualifier:
	    [workerEntity qualifier]
	fetchOrder:
	    [NSArray arrayWithObject:
		[EOAttributeOrdering 
		attributeOrderingWithAttribute:
		    [workerEntity attributeNamed:@"workerId"]
		ordering:
		    EOAscendingOrder]
	    ]
    ];

    while ((obj = [databaseChannel fetchWithZone:NULL]))
	[objects addObject:obj];
    [databaseContext commitTransaction];
    
    if ([array count] != [objects count])
	printf("bad worker count\n");
    for (i = 0; i < [array count]; i++)
	if ([array objectAtIndex:i] != [objects objectAtIndex:i])
	    printf("workers at index %d are not the same\n", i);
    
    NSLog(@"First object is %@", [[objects objectAtIndex:0] description]);

    // Worker - modify
    
    worker = [array objectAtIndex:0];
    [worker takeValuesFromDictionary:[NSDictionary 
	dictionaryWithObjectsAndKeys:
	@"Ovidiu Predescu", @"name", 
	nil]];

    worker = [array objectAtIndex:1];
    [worker takeValuesFromDictionary:[NSDictionary 
	dictionaryWithObjectsAndKeys:
	@"Mircea Oancea", @"name",
	nil]];
	
    worker = [array objectAtIndex:2];
    [worker takeValuesFromDictionary:[NSDictionary 
	dictionaryWithObjectsAndKeys:
	@"Scott Christley", @"name",
	nil]];

    // Worker - update

    [databaseContext beginTransaction];
    for (i = 0; i < [array count]; i++) {
	obj = [array objectAtIndex:i];
	[databaseChannel updateObject:obj];
    }
    [databaseChannel updateObject:obj];
    [databaseContext commitTransaction];

    // Foget all objects

    [database forgetObject:obj];

    // ProjWork - make
    
    [array removeAllObjects];
    
    row = [NSDictionary dictionaryWithObjectsAndKeys:
	[NSNumber numberWithInt:1], @"workerId",
	[NSNumber numberWithInt:100], @"projectId",
	nil];
    projwork = [[EOGenericRecord alloc]
	initWithPrimaryKey:row entity:projworkEntity];
    [array addObject:projwork];

    row = [NSDictionary dictionaryWithObjectsAndKeys:
	[NSNumber numberWithInt:2], @"workerId",
	[NSNumber numberWithInt:100], @"projectId",
	nil];
    projwork = [[EOGenericRecord alloc]
	initWithPrimaryKey:row entity:projworkEntity];
    [array addObject:projwork];

    row = [NSDictionary dictionaryWithObjectsAndKeys:
	[NSNumber numberWithInt:3], @"workerId",
	[NSNumber numberWithInt:100], @"projectId",
	nil];
    projwork = [[EOGenericRecord alloc]
	initWithPrimaryKey:row entity:projworkEntity];
    [array addObject:projwork];

    // ProjWork - insert

    [databaseContext beginTransaction];
    for (i = 0; i < [array count]; i++) {
	obj = [array objectAtIndex:i];
	[databaseChannel insertObject:obj];
    }
    [databaseContext commitTransaction];

    // ProjWork - fetch and compare
    
    [databaseContext beginTransaction];
    [databaseChannel selectObjectsDescribedByQualifier:
	    [projworkEntity qualifier]
	fetchOrder:nil
    ];

    [objects removeAllObjects];
    while ((obj = [databaseChannel fetchWithZone:NULL]))
	[objects addObject:obj];
    [databaseContext commitTransaction];
    
    for (i = 0; i < [objects count]; i++) {
	NSLog(@"ProjWork:[%d] %@", i, [[objects objectAtIndex:i] description]);
    }

    // Step on to-one fault
    
    obj = [objects objectAtIndex:i-1];
    row = [obj valuesForKeys:[NSArray arrayWithObject:@"toWorker"]];
    worker = [row objectForKey:@"toWorker"];
    [worker self];
    NSLog(@"Worker[fault]: %@", [worker description]);

    // Step to-many simple fault
    
    obj = [worker valuesForKeys:[NSArray arrayWithObject:@"toProjwork"]];
    obj = [obj objectForKey:@"toProjwork"];
    [obj self];
    NSLog(@"Worker[toProjwork]: %d", [obj count]);
    
    // Step to-many flattened fault

    obj = [worker valuesForKeys:[NSArray arrayWithObject:@"toProjects"]];
    obj = [obj objectForKey:@"toProjects"];
    [obj self];
    NSLog(@"Worker[toProjects]: %d", [obj count]);
    
    [pool release];
    
    exit(0);
}

