/* This source code is provided "as is" and without warranty, express 	*/
/* or implied.								*/

#import <Foundation/Foundation.h>
#import <EOControl/EOControl.h>
#import <EOAccess/EOAccess.h>
#import "Movie.h"
#import "Actor.h"
#import "Director.h"

void usage(void)
{
    NSLog(@"Usage: LeakTester -batchsize <batchsize> -runs <number of 
runs> -trials <number of trials> -hang <BOOL> 
WARNING!!!! THIS PROGRAM 
REMOVES ALL EXISTING DATA IN THE TABLES USED");
}

void logReport(NSString *modelPath, int batchsize, int runs, int trials)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    EOModel *model;

    model = [[[EOModel alloc] initWithContentsOfFile:modelPath]
autorelease];

    NSLog(@"%@, batchsize = %i, runs = %i, trials = %i", [model
adaptorName], batchsize, runs, trials);

    [pool release];
}



void clearTables(NSString *modelPath)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    EOModel *model;
    EOAdaptor *adaptor;
    EOAdaptorContext *context;
    EOAdaptorChannel *channel;
    EOSQLExpression *expression;

    model = [[[EOModel alloc] initWithContentsOfFile:modelPath]
autorelease];
    adaptor = [EOAdaptor adaptorWithModel:model];
    context = [adaptor createAdaptorContext];
    channel = [context createAdaptorChannel];

    NSLog(@"Dumping old data from tables...");

    [channel openChannel];

    expression = [[adaptor expressionClass] expressionForString:@"delete 
from LKMOVIE"];
    [channel evaluateExpression:expression];

    expression = [[adaptor expressionClass] expressionForString:@"delete 
from LKACTOR"];
    [channel evaluateExpression:expression];

    expression = [[adaptor expressionClass] expressionForString:@"delete 
from LKDIRECTOR"];
    [channel evaluateExpression:expression];

    // Informix complains if we do a COMMIT here; OK for Sybase and
Oracle.
    // expression = [[adaptor expressionClass]
expressionForString:@"commit"];
    // [channel evaluateExpression:expression];

    [channel closeChannel];

    [pool release];
}


void doit(NSString *modelPath, int batchsize, int runs)
{
    NSAutoreleasePool *pool, *pool0, *pool1, *pool2;
    EOEditingContext *editingContext = [EOEditingContext new]; // context 
with defaultCoordinator
    Movie *movie;
    Movie *lastMovie;
    NSArray *movies;
    NSArray *actors;
    NSArray *lastActors;
    Actor *actor;
    Director *director;
    int i;
    int j;
    int priKey;
    int m;

    // For apps with models stored in the main bundle, the following is
    // unnecessary, since the defaultGroup will be automatically built 
from
    // the contents of the main bundle and and any referenced frameworks.
    pool = [[NSAutoreleasePool alloc] init];
    [EOModelGroup setDefaultGroup:[[EOModelGroup new] autorelease]];
    [[EOModelGroup defaultGroup] addModelWithFile:modelPath];

    pool0 = [[NSAutoreleasePool alloc] init];
    // Create and insert root set
    NSLog(@"Inserting root set...");
    priKey = 0;
    for ( i = 0 ; i < runs ; i++ ) {
        pool1 = [[NSAutoreleasePool alloc] init];
        for ( j = 0 ; j < batchsize ; j++ ) {
            priKey++;
            movie = [[[Movie alloc] init] autorelease];
            [movie setMovieId:[NSDecimalNumber
decimalNumberWithMantissa:priKey exponent:0 isNegative:NO]];
            [movie setTitle:[NSString
stringWithFormat:@"Movie_%i",priKey]];
            [editingContext insertObject:movie];
        }
        [editingContext saveChanges];
        [[editingContext rootObjectStore] invalidateAllObjects];
        [[editingContext undoManager] forgetAll];
        [pool1 release];
    }
    [pool0 release];

    priKey = 0;
    for ( i = 0 ; i < runs ; i++ ) {
        pool1 = [[NSAutoreleasePool alloc] init];
        pool2 = [[NSAutoreleasePool alloc] init];
        NSLog(@"Fetching Movies batch and adding objects for run
#%i...",i);
        movies = [editingContext
objectsWithFetchSpecification:[EOFetchSpecification
fetchSpecificationWithEntityName:@"Movie" qualifier:[EOQualifier
qualifierWithQualifierFormat:@"(movieId > %d) AND (movieId <= %d)",
i*batchsize, (i+1)*batchsize] sortOrderings:nil]];

        for ( j = 0 ; j < [movies count] ; j++ ) {
            movie = [movies objectAtIndex:j];
            priKey++;
            director = [[movie director] self];  // Just to tickle the 
fault
            director = [[Director alloc] init];
            [editingContext insertObject:director];
            [director setDirectorId:[NSDecimalNumber
decimalNumberWithMantissa:priKey exponent:0 isNegative:NO]];
            [director setName:[NSString
stringWithFormat:@"First_%i",priKey]];
            [movie addObject:director
toBothSidesOfRelationshipWithKey:@"director"];
            [director release];
            for ( m = 0 ; m < batchsize ; m++ ) {
                priKey++;
                actor = [[Actor alloc] init];
                [editingContext insertObject:actor];
                [actor setActorId:[NSDecimalNumber
decimalNumberWithMantissa:priKey exponent:0 isNegative:NO]];
                [actor setName:[NSString
stringWithFormat:@"First_%i",priKey]];
                [movie addObject:actor
toBothSidesOfRelationshipWithKey:@"actors"];
                [actor release];
            }
        }

        NSLog(@"Saving...");
        [editingContext saveChanges];

        [editingContext invalidateAllObjects];
        [[editingContext rootObjectStore] invalidateAllObjects];
        [[editingContext undoManager] forgetAll];
        [pool2 release];
        [pool1 release];
    }


    for ( i = 0 ; i < runs ; i++ ) {
        pool1 = [[NSAutoreleasePool alloc] init];
        pool2 = [[NSAutoreleasePool alloc] init];
        NSLog(@"Fetching Movies batch and mutating for run #%i...",i);
        movies = [editingContext
objectsWithFetchSpecification:[EOFetchSpecification
fetchSpecificationWithEntityName:@"Movie" qualifier:[EOQualifier
qualifierWithQualifierFormat:@"(movieId > %d) AND (movieId <= %d)",
i*batchsize, (i+1)*batchsize] sortOrderings:nil]];

        for ( j = 0 ; j < [movies count] ; j++ ) {
            movie = [movies objectAtIndex:j];
            priKey++;
            director = [[movie director] self];  // Just to tickle the 
fault
            director = [[Director alloc] init];
            [editingContext insertObject:director];
            [director setDirectorId:[NSDecimalNumber
decimalNumberWithMantissa:priKey exponent:0 isNegative:NO]];
            [director setName:[NSString
stringWithFormat:@"First_%i",priKey]];
            [movie addObject:director
toBothSidesOfRelationshipWithKey:@"director"];
            [director release];
            if ( j % 2 ) {
                actors = [[movie actors] shallowCopy];
                lastMovie = [movies objectAtIndex:j-1];
                lastActors = [[lastMovie actors] shallowCopy];
                for ( m = 0 ; m < [actors count] ; m++ ) {
                    [movie removeFromActors:[actors objectAtIndex:m]];
                }
                for ( m = 0 ; m < [lastActors count] ; m++ ) {
                    [movie addToActors:[lastActors objectAtIndex:m]];
                }
                for ( m = 0 ; m < [lastActors count] ; m++ ) {
                    [lastMovie removeFromActors:[lastActors
objectAtIndex:m]];
                }
                for ( m = 0 ; m < [actors count] ; m++ ) {
                    [lastMovie addToActors:[actors objectAtIndex:m]];
                }
                [actors release];
                [lastActors release];
            }
        }

        NSLog(@"Saving mutations...");
        [editingContext saveChanges];

        [editingContext invalidateAllObjects];
        [[editingContext rootObjectStore] invalidateAllObjects];
        [[editingContext undoManager] forgetAll];
        [pool2 release];
        [pool1 release];
    }
    [EOModelGroup setDefaultGroup: nil];
    [pool release];
    [editingContext release];
}


void main (int argc, const char *argv[])
{
    NSAutoreleasePool * pool;
    BOOL hang;
    int batchsize, runs, trials;
    NSString *modelPath;
    int trial;

    pool = [[NSAutoreleasePool alloc] init];
    hang = [[NSUserDefaults standardUserDefaults] boolForKey:@"hang"];
    batchsize = [[NSUserDefaults standardUserDefaults]
integerForKey:@"batchsize"];
    if (!batchsize) batchsize = 1;
    runs = [[NSUserDefaults standardUserDefaults] integerForKey:@"runs"];
    if (!runs) runs = 1;
    trials = [[NSUserDefaults standardUserDefaults]
integerForKey:@"trials"];
    if (!trials) trials = 1;
    modelPath = [[[NSBundle mainBundle] bundlePath]
stringByAppendingPathComponent: @"LeakTester.eomodeld"];

    logReport(modelPath, batchsize, runs, trials);

    for (trial = 0; trial < trials; trial++) {
        NSLog(@"Trial %i beginning...", trial);
        clearTables(modelPath);
        doit(modelPath, batchsize, runs);
        NSLog(@"Trial %i done.", trial);
    }

    logReport(modelPath, batchsize, runs, trials);

    [pool release];

    if (hang) {
        NSLog(@"Hit RETURN to exit...");
        getchar();
    }

    NSLog(@"Done.");
    exit(0);       // insure the process exit status is 0
}