#import "PPImportOperation.h"

@interface PPImportOperation ()

- (NSManagedObjectModel*)managedObjectModel;
- (NSPersistentStoreCoordinator*)persistentStoreCoordinator;
- (NSManagedObjectContext*)managedObjectContext;
- (NSManagedObjectContext*)newContextToMainStore;

- (void)copyPropertiesFromObject:(NSManagedObject*)oldObject
                        toObject:(NSManagedObject*)newObject
                          parent:(NSString*)parentEntity;
- (id)objectOfType:(NSString*)entityName 
          withName:(NSString*)name;
- (id)associateObject:(NSManagedObject*)object 
               parent:(NSString*)name;
- (void)copyRecipe:(NSManagedObject*)recipe;

@end

@implementation PPImportOperation

@synthesize filePath = _filePath;
@synthesize lookup = _lookup;
@synthesize importContext = _importContext;
@synthesize mainContext = _mainContext;

//START:init
- (id)initWithDelegate:(AppDelegate*)delegate
              filePath:(NSString*)filePath
{
  if (!(self = [super init])) return nil;
  
  appDelegate = delegate;
  
  [self setFilePath:filePath];
  [self setImportContext:[self managedObjectContext]];
  NSManagedObjectContext *mainMOC = [self newContextToMainStore];
  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  [center addObserver:self
             selector:@selector(contextDidSave:) 
                 name:NSManagedObjectContextDidSaveNotification 
               object:mainMOC];
  [self setMainContext:mainMOC];
  [self setLookup:[NSMutableDictionary dictionary]];
  return self;
}
//END:init

- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  appDelegate = nil;
  [_filePath release], _filePath = nil;
  [_importContext release], _importContext = nil;
  [_mainContext release], _mainContext = nil;
  [super dealloc];
}

//START:contextDidSave
- (void)contextDidSave:(NSNotification*)notification
{
  SEL selector = @selector(mergeChangesFromContextDidSaveNotification:);
  [[appDelegate managedObjectContext] performSelectorOnMainThread:selector
                                                       withObject:notification
                                                    waitUntilDone:YES]; 
}
//END:contextDidSave

//START:mainstart
- (void)main
{
  NSFetchRequest *request = [[NSFetchRequest alloc] init];
  [request setEntity:[NSEntityDescription entityForName:@"Recipe"
                                 inManagedObjectContext:[self importContext]]];
  
  NSError *error = nil;
  NSArray *incomingRecipes = [[self importContext] executeFetchRequest:request
                                                                 error:&error];
  [request release], request = nil;
  if (error) {
    [NSApp presentError:error];
    return;
  }
  //END:mainstart
  
  //START:mainloop
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  for (NSManagedObject *recipe in incomingRecipes) {
    [self copyRecipe:recipe];
    [pool drain], pool = nil;
    pool = [[NSAutoreleasePool alloc] init];
  }
  [pool drain], pool = nil;
  //END:mainloop
  
  //START:mainsave
  error = nil;
  if (![[self mainContext] save:&error]) {
    [NSApp presentError:error];
    return;
  }
  //END:mainsave
}

- (NSManagedObjectModel*)managedObjectModel 
{
  NSString *path = [[NSBundle mainBundle] pathForResource:@"DataModel"
                                                   ofType:@"momd"];
  if (!path) {
    path = [[NSBundle mainBundle] pathForResource:@"DataModel" ofType:@"mom"];
  }
  NSURL *url = [NSURL fileURLWithPath:path];
  id mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];
  return [mom autorelease];
}

- (NSPersistentStoreCoordinator*)persistentStoreCoordinator
{
  NSManagedObjectModel *mom = [self managedObjectModel];
  id psc = [[NSPersistentStoreCoordinator alloc]
            initWithManagedObjectModel:mom];
  
  NSError *error = nil;
  NSPersistentStore *store;
  NSURL *url = [NSURL fileURLWithPath:[self filePath]];
  store = [psc addPersistentStoreWithType:NSXMLStoreType
                            configuration:nil
                                      URL:url
                                  options:nil
                                    error:&error];
  if (!store) {
    [[NSApplication sharedApplication] presentError:error];
    return nil;
  }
  return [psc autorelease];
}

- (NSManagedObjectContext*)managedObjectContext
{
  NSPersistentStoreCoordinator *coord = [self persistentStoreCoordinator];
  if (!coord) return nil;
  NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
  [moc setPersistentStoreCoordinator: coord];
  return [moc autorelease];
}

//START:newContextToMainStore
- (NSManagedObjectContext*)newContextToMainStore
{
  NSPersistentStoreCoordinator *coord = nil;
  coord = [appDelegate persistentStoreCoordinator];
  
  NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
  [moc setPersistentStoreCoordinator:coord];
  return [moc autorelease];
}
//END:newContextToMainStore

//START:copyPropertiesFromObjectProperties
- (void)copyPropertiesFromObject:(NSManagedObject*)oldObject
                        toObject:(NSManagedObject*)newObject
                          parent:(NSString*)parentEntity
{
  NSString *entityName = [[oldObject entity] name];
  NSArray *attKeys = [[[oldObject entity] attributesByName] allKeys];
  NSDictionary *attributes = [oldObject dictionaryWithValuesForKeys:attKeys];
  [newObject setValuesForKeysWithDictionary:attributes];
  //END:copyPropertiesFromObjectProperties
  
  //START:copyPropertiesFromObjectRelationships
  id oldDestObject = nil;
  id temp = nil;
  NSDictionary *relationships = [[oldObject entity] relationshipsByName];
  for (NSString *key in [relationships allKeys]) {
    NSRelationshipDescription *desc = [relationships valueForKey:key];
    NSString *destEntityName = [[desc destinationEntity] name];
    if ([destEntityName isEqualToString:parentEntity]) continue;

    if ([desc isToMany]) {
      NSMutableSet *newDestSet = [NSMutableSet set];
      for (oldDestObject in [oldObject valueForKey:key]) {
        temp = [[self lookup] objectForKey:[oldDestObject objectID]];
        if (!temp) {
          temp = [self associateObject:oldDestObject 
                                parent:entityName];
        }
        [newDestSet addObject:temp];
      }
      [newObject setValue:newDestSet forKey:key];
    } else {
      oldDestObject = [oldObject valueForKey:key];
      if (!oldDestObject) continue;
      temp = [[self lookup] objectForKey:[oldDestObject objectID]];
      if (!temp) {
        temp = [self associateObject:oldDestObject 
                              parent:entityName];
      }
      [newObject setValue:temp forKey:key];
    }
  }
}
//END:copyPropertiesFromObjectRelationships

//START:objectOfType
- (id)objectOfType:(NSString*)entityName withName:(NSString*)name
{
  NSFetchRequest *request = [[NSFetchRequest alloc] init];
  [request setEntity:[NSEntityDescription entityForName:entityName
                                 inManagedObjectContext:[self mainContext]]];
  [request setPredicate:[NSPredicate predicateWithFormat:@"name = %@", name]];
  
  NSError *error = nil;
  id result = [[[self mainContext] executeFetchRequest:request 
                                                 error:&error] lastObject];
  return result;
}
//END:objectOfType

//START:associateObject
- (id)associateObject:(NSManagedObject*)object parent:(NSString*)name
{
  NSManagedObjectContext *moc = [self mainContext];
  NSString *entityName = [[object entity] name];
  id temp = nil;
  
  if ([entityName isEqualToString:@"RecipeIngredient"]) {
    temp = [NSEntityDescription insertNewObjectForEntityForName:entityName 
                                         inManagedObjectContext:moc];
    [[self lookup] setObject:temp forKey:[object objectID]];
    [self copyPropertiesFromObject:object toObject:temp parent:name];
    return temp;
  }
  
  if (temp = [self objectOfType:entityName 
                       withName:[object valueForKey:@"name"]]) {
    [[self lookup] setObject:temp forKey:[object objectID]];
    return temp;
  }
  
  temp = [NSEntityDescription insertNewObjectForEntityForName:entityName
                                       inManagedObjectContext:moc];
  [[self lookup] setObject:temp forKey:[object objectID]];
  [self copyPropertiesFromObject:object toObject:temp parent:name];
  return temp;
}
//END:associateObject

//START:copyRecipe
- (void)copyRecipe:(NSManagedObject*)recipe
{
  NSString *entityName = [[recipe entity] name];
  
  NSManagedObject *newObject = [NSEntityDescription 
                                insertNewObjectForEntityForName:entityName 
                                inManagedObjectContext:[self mainContext]];
  
  [[self lookup] setObject:newObject forKey:[recipe objectID]];
  
  //Catch 22  Need to copy properties before changing the name but that
  //loads it into the context causing an infinite loop
  [self copyPropertiesFromObject:recipe toObject:newObject parent:entityName];
  [newObject setValue:@"untitled recipe" forKey:@"name"];
  
  NSUInteger index = 1;
  NSString *newName = [recipe valueForKey:@"name"];
  while ([self objectOfType:entityName withName:newName]) {
    NSString *origName = [recipe valueForKey:@"name"];
    newName = [NSString stringWithFormat:@"%@-%u", origName, index];
    ++index;
  }
  [newObject setValue:newName forKey:@"name"];
}
//END:copyRecipe

@end
