#import "AppDelegate.h"

@interface AppDelegate()

- (NSString*)applicationSupportFolder;

@end

@implementation AppDelegate

- (IBAction)addImage:(id)sender;
{
    NSManagedObject *recipe = [[recipeArrayController selectedObjects] lastObject];
    if (!recipe) return;
    
    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
    
    [openPanel setCanChooseDirectories:NO];
    [openPanel setCanCreateDirectories:NO];
    [openPanel setAllowsMultipleSelection:NO];
    
    [openPanel beginSheetForDirectory:nil
                                 file:nil
                       modalForWindow:window
                        modalDelegate:self
                       didEndSelector:@selector(addImageSheetDidEnd:returnCode:contextInfo:)
                          contextInfo:recipe];
}

- (void)addImageSheetDidEnd:(NSOpenPanel*)openPanel 
                 returnCode:(NSInteger)returnCode 
                contextInfo:(NSManagedObject*)recipe
{
    if (returnCode == NSCancelButton) return;
    NSString *path = [openPanel filename];
    
    //Build the path we want the file to be at
    NSString *destPath = [self applicationSupportFolder];
    destPath = [destPath stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
    [[NSFileManager defaultManager] copyPath:path toPath:destPath handler:nil];
    [recipe setValue:destPath forKey:@"imagePath"];
}

- (NSString *)applicationSupportFolder 
{
  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
  return [basePath stringByAppendingPathComponent:@"GrokkingRecipes"];
}

- (NSManagedObjectModel *)managedObjectModel 
{
  if (managedObjectModel) return managedObjectModel;
  
  NSString *path = [[NSBundle mainBundle] pathForResource:@"DataModel"
                                                   ofType:@"momd"];
  if (!path) {
    path = [[NSBundle mainBundle] pathForResource:@"DataModel"
                                           ofType:@"mom"];
  }
  NSAssert(path != nil, @"Unable to find DataModel in main bundle");
  NSURL *url = [NSURL fileURLWithPath:path];
  managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];
  return managedObjectModel;
}

//START:progressivelyMigrateURLMethodName
- (BOOL)progressivelyMigrateURL:(NSURL*)sourceStoreURL
                         ofType:(NSString*)type 
                        toModel:(NSManagedObjectModel*)finalModel 
                          error:(NSError**)error
{
  //END:progressivelyMigrateURLMethodName
  //START:progressivelyMigrateURLHappyCheck
  NSDictionary *sourceMetadata = 
  [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:type
                                                             URL:sourceStoreURL
                                                           error:error];
  if (!sourceMetadata) return NO;
  
  if ([finalModel isConfiguration:nil 
      compatibleWithStoreMetadata:sourceMetadata]) {
    *error = nil;
    return YES;
  }
  //END:progressivelyMigrateURLHappyCheck
  //START:progressivelyMigrateURLFindModels
  //Find the source model
  NSManagedObjectModel *sourceModel = [NSManagedObjectModel 
                                       mergedModelFromBundles:nil
                                       forStoreMetadata:sourceMetadata];
  NSAssert(sourceModel != nil, ([NSString stringWithFormat:
                                 @"Failed to find source model\n%@", 
                                 sourceMetadata]));
  
  //Find all of the mom and momd files in the Resources directory
  NSMutableArray *modelPaths = [NSMutableArray array];
  NSArray *momdArray = [[NSBundle mainBundle] pathsForResourcesOfType:@"momd" 
                                                          inDirectory:nil];
  for (NSString *momdPath in momdArray) {
    NSString *resourceSubpath = [momdPath lastPathComponent];
    NSArray *array = [[NSBundle mainBundle] 
                      pathsForResourcesOfType:@"mom" 
                      inDirectory:resourceSubpath];
    [modelPaths addObjectsFromArray:array];
  }
  NSArray* otherModels = [[NSBundle mainBundle] pathsForResourcesOfType:@"mom" 
                                                            inDirectory:nil];
  [modelPaths addObjectsFromArray:otherModels];

  if (!modelPaths || ![modelPaths count]) {
    //Throw an error if there are no models
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setValue:@"No models found in bundle" 
            forKey:NSLocalizedDescriptionKey];
    //Populate the error
    *error = [NSError errorWithDomain:@"Zarra" code:8001 userInfo:dict];
    return NO;
  }
  //END:progressivelyMigrateURLFindModels

  //See if we can find a matching destination model
  //START:progressivelyMigrateURLFindMap
  NSMappingModel *mappingModel = nil;
  NSManagedObjectModel *targetModel = nil;
  NSString *modelPath = nil;
  for (modelPath in modelPaths) {
    targetModel = [[NSManagedObjectModel alloc] 
                   initWithContentsOfURL:[NSURL fileURLWithPath:modelPath]];
    mappingModel = [NSMappingModel mappingModelFromBundles:nil 
                                            forSourceModel:sourceModel 
                                          destinationModel:targetModel];
    //If we found a mapping model then proceed
    if (mappingModel) break;
    //Release the target model and keep looking
    [targetModel release], targetModel = nil;
  }
  //We have tested every model, if nil here we failed
  if (!mappingModel) {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setValue:@"No models found in bundle" 
            forKey:NSLocalizedDescriptionKey];
    *error = [NSError errorWithDomain:@"Zarra" 
                                 code:8001 
                             userInfo:dict];
    return NO;
  }
  //END:progressivelyMigrateURLFindMap
  //We have a mapping model and a destination model.  Time to migrate
  //START:progressivelyMigrateURLMigrate
  NSMigrationManager *manager = [[NSMigrationManager alloc] 
                                 initWithSourceModel:sourceModel
                                 destinationModel:targetModel];

  NSString *modelName = [[modelPath lastPathComponent] 
                         stringByDeletingPathExtension];
  NSString *storeExtension = [[sourceStoreURL path] pathExtension];
  NSString *storePath = [[sourceStoreURL path] stringByDeletingPathExtension];
  //Build a path to write the new store
  storePath = [NSString stringWithFormat:@"%@.%@.%@", storePath, 
                  modelName, storeExtension];
  NSURL *destinationStoreURL = [NSURL fileURLWithPath:storePath];
  
  if (![manager migrateStoreFromURL:sourceStoreURL 
                               type:type 
                            options:nil 
                   withMappingModel:mappingModel 
                   toDestinationURL:destinationStoreURL 
                    destinationType:type 
                 destinationOptions:nil 
                              error:error]) {
    return NO;
  }
  //END:progressivelyMigrateURLMigrate
  //Migration was successful, move the files around to preserve the source
  //START:progressivelyMigrateURLMoveAndRecurse
  NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
  guid = [guid stringByAppendingPathExtension:modelName];
  guid = [guid stringByAppendingPathExtension:storeExtension];
  NSString *appSupportPath = [storePath stringByDeletingLastPathComponent];
  NSString *backupPath = [appSupportPath stringByAppendingPathComponent:guid];
  
  NSFileManager *fileManager = [NSFileManager defaultManager];
  if (![fileManager moveItemAtPath:[sourceStoreURL path]
                            toPath:backupPath
                             error:error]) {
    //Failed to copy the file
    return NO;
  }
  //Move the destination to the source path
  if (![fileManager moveItemAtPath:storePath
                            toPath:[sourceStoreURL path]
                             error:error]) {
    //Try to back out the source move first, no point in checking it for errors
    [fileManager moveItemAtPath:backupPath
                         toPath:[sourceStoreURL path]
                          error:nil];
    return NO;
  }
  //We may not be at the "current" model yet, so recurse
  return [self progressivelyMigrateURL:sourceStoreURL
                                ofType:type 
                               toModel:finalModel 
                                 error:error];
  //END:progressivelyMigrateURLMoveAndRecurse
}

//START:persistentStoreCoordinator
- (NSPersistentStoreCoordinator*)persistentStoreCoordinator;
{
  if (persistentStoreCoordinator) return persistentStoreCoordinator;

  NSFileManager *fileManager = [NSFileManager defaultManager];
  NSString *applicationSupportFolder = [self applicationSupportFolder];
  if ( ![fileManager fileExistsAtPath:applicationSupportFolder
                          isDirectory:NULL] ) {
    [fileManager createDirectoryAtPath:applicationSupportFolder
                            attributes:nil];
  }

  NSURL *url = [NSURL fileURLWithPath:[applicationSupportFolder 
  stringByAppendingPathComponent:@"GrokkingRecipes.xml"]];
  NSManagedObjectModel *mom = [self managedObjectModel];
  persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
                                initWithManagedObjectModel:mom];

  //END:persistentStoreCoordinator
  //START:progressivelyMigrateCall
  NSError *error = nil;
  if (![self progressivelyMigrateURL:url
                              ofType:NSXMLStoreType
                             toModel:mom
                               error:&error]) {
    [[NSApplication sharedApplication] presentError:error];
    return nil;
  }
  //END:progressivelyMigrateCall
  //START:persistentStoreCoordinator
  
  if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType
                                                configuration:nil
                                                          URL:url
                                                      options:nil
                                                        error:&error]) {
    [[NSApplication sharedApplication] presentError:error];
    return nil;
  }
  return persistentStoreCoordinator;
}
//END:persistentStoreCoordinator

- (NSManagedObjectContext*)managedObjectContext
{
  if (managedObjectContext) return managedObjectContext;
  
  NSPersistentStoreCoordinator *coord = [self persistentStoreCoordinator];
  
  if (!coord) return nil;
  
  managedObjectContext = [[NSManagedObjectContext alloc] init];
  [managedObjectContext setPersistentStoreCoordinator: coord];
  
  NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
  [request setEntity:[NSEntityDescription entityForName:@"Type"
                                 inManagedObjectContext:managedObjectContext]];
  
  NSError *error = nil;
  NSArray *result = [managedObjectContext executeFetchRequest:request 
                                                        error:&error];
  NSAssert(error == nil, [error localizedDescription]);
  if ([result count]) return managedObjectContext;
  
  //The types table has not been populated
  NSArray *types;
  types = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"RecipeTypes"];
  
  for (NSString *type in types) {
    NSManagedObject *object = [NSEntityDescription 
                               insertNewObjectForEntityForName:@"Type" 
                               inManagedObjectContext:managedObjectContext];
    [object setValue:type forKey:@"name"];
  }
  
  return managedObjectContext;
}
 
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window
{
    return [[self managedObjectContext] undoManager];
}
 
- (IBAction) saveAction:(id)sender 
{
  NSError *error = nil;
  if (![[self managedObjectContext] save:&error]) {
    [[NSApplication sharedApplication] presentError:error];
  }
}
 
- (NSApplicationTerminateReply)applicationShouldTerminate:(id)sender
{
  NSError *error = nil;
  
  if (!managedObjectContext) return NSTerminateNow;
  if (![managedObjectContext commitEditing]) {
    //Failed to commit editing.
    return NSTerminateCancel;
  }
  if (![managedObjectContext hasChanges]) return NSTerminateNow;
  if (![managedObjectContext save:&error]) {
    int alertReturn = NSRunAlertPanel(nil, @"Could not save changes while quitting. Quit anyway?" , @"Quit anyway", @"Cancel", nil);
    if (alertReturn == NSAlertAlternateReturn) NSTerminateCancel;	
  }
  return NSTerminateNow;
}
 
- (void) dealloc
{
    [managedObjectContext release], managedObjectContext = nil;
    [persistentStoreCoordinator release], persistentStoreCoordinator = nil;
    [managedObjectModel release], managedObjectModel = nil;
    [super dealloc];
}

@end
