//
//  SyncHelper.m
//  SyncServices
//
//  Created by Marcus S. Zarra on 7/22/08.
//  Copyright 2008 Zarra Studios LLC. All rights reserved.
//

#import "SyncHelper.h"
#import "AppDelegate.h"

@interface SyncHelper()

- (ISyncClient*)syncClient:(NSError**)error;

@end

@implementation SyncHelper

static BOOL chatty = NO;

//START:registerSyncClient
- (id)initWithDelegate:(id)_delegate error:(NSError**)error
{
  if (!(self = [super init])) return nil;
  delegate = _delegate;
  
  ISyncClient *client = [self syncClient:error];
  if (!client) {
    [self autorelease];
    return nil;
  }
  [client setSyncAlertHandler:self
                     selector:@selector(client:mightWantToSyncEntityNames:)];
  if (![self performSync:error]) {
    [self autorelease];
    return nil;
  }
  
  return self;
}
//END:registerSyncClient

#pragma mark Sync Services Methods
#pragma mark -

//START:client:mightWantToSyncEntityNames
- (void)client:(ISyncClient*)client mightWantToSyncEntityNames:(NSArray*)names
{
  NSError *error = nil;
  if (![delegate save:&error]) {
    [NSApp presentError:error];
  }
}
//END:client:mightWantToSyncEntityNames

//START:performSync
- (BOOL)performSync:(NSError**)err;
{
  ISyncClient *client = [self syncClient:err];
  if (!client) return NO;
  
  NSLog(@"Starting sync");
  NSPersistentStoreCoordinator *store = [delegate persistentStoreCoordinator];
  return [store syncWithClient:client
                  inBackground:YES
                       handler:self
                         error:err];
}
//END:performSync

//START:syncClientAlreadyRegistered
- (ISyncClient*)syncClient:(NSError**)error
{
  NSString *ident = [[NSBundle mainBundle] bundleIdentifier];
  ISyncClient *client;
  NSDictionary *dict;
  ISyncManager *manager = [ISyncManager sharedManager];
  NSBundle *mainBundle = [NSBundle mainBundle];
  
  @try {
    client = [manager clientWithIdentifier:ident];
    if (client) return client;
//END:syncClientAlreadyRegistered
//START:syncClientNeedToRegisterSchema
    NSString *path = [mainBundle pathForResource:@"GrokkingRecipes" 
                                          ofType:@"syncschema"];
    if (![manager registerSchemaWithBundlePath:path]) {
      NSString *err = NSLocalizedString(@"Failed to register the schema",
        @"Failed to register the schema error message");
      dict = [NSDictionary dictionaryWithObject:err
                                         forKey:NSLocalizedDescriptionKey];
      *error = [NSError errorWithDomain:@"PragProg"
                                   code:8001
                               userInfo:dict];
      return nil;
    }
//END:syncClientNeedToRegisterSchema
//START:syncClientNeedToRegisterClient
    path = [mainBundle pathForResource:@"ClientDescription"
                                ofType:@"plist"];
    client = [manager registerClientWithIdentifier:ident 
                               descriptionFilePath:path];
//END:syncClientNeedToRegisterClient
//START:syncClientWhatShouldWeSync
    [client setShouldSynchronize:YES 
               withClientsOfType:ISyncClientTypeApplication];
    [client setShouldSynchronize:YES
               withClientsOfType:ISyncClientTypeDevice];
    [client setShouldSynchronize:YES 
               withClientsOfType:ISyncClientTypeServer];
    [client setShouldSynchronize:YES 
               withClientsOfType:ISyncClientTypePeer];
//END:syncClientWhatShouldWeSync
//START:syncClientWhatBlewUp
  } @catch (NSException *exception) {
    dict = [NSDictionary dictionaryWithObject:[exception reason]
                                       forKey:NSLocalizedDescriptionKey];
    *error = [NSError errorWithDomain:@"PragProg"
                                 code:8002
                             userInfo:dict];
    return nil;
  }
  return client;
}
//END:syncClientWhatBlewUp

#pragma mark NSPersistentStoreCoordinatorSyncing
#pragma mark -

- (NSArray*)managedObjectContextsToMonitorWhenSyncingPersistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator
{
  NSLog(@"%@:%s entered", [self class], _cmd);
  return [NSArray arrayWithObject:[delegate managedObjectContext]];
}

- (NSArray*)managedObjectContextsToReloadAfterSyncingPersistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator
{
  NSLog(@"%@:%s entered", [self class], _cmd);
  return [NSArray arrayWithObject:[delegate managedObjectContext]];
}

//START: didFinishSyncSession
- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
              didFinishSyncSession:(ISyncSession*)session
{
  NSManagedObjectContext *moc = [delegate managedObjectContext];
  NSSet *deleted = [moc deletedObjects];
  NSMutableSet *updated = [NSMutableSet setWithSet:[moc insertedObjects]];
  [updated unionSet:[moc updatedObjects]];
  
  NSError *error = nil;
  [delegate updateMetadataForObjects:updated 
                   andDeletedObjects:deleted 
                               error:&error];
  if (error) {
    NSLog(@"%@:%s error updating after sync: %@", [self class], _cmd, error);
  }
  [moc save:&error];
  if (error) {
    NSLog(@"%@:%s error saving after sync: %@", [self class], _cmd, error);
  }
}
//END: didFinishSyncSession

- (ISyncChange*)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
                           willApplyChange:(ISyncChange*)change 
                           toManagedObject:(NSManagedObject*)managedObject 
                             inSyncSession:(ISyncSession*)session
{
  NSLog(@"pull %@", [change description]);
  return change;
}

- (NSDictionary*)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
                             willPushRecord:(NSDictionary*)record 
                           forManagedObject:(NSManagedObject*)managedObject 
                              inSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
  return record;
}

- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
                    didApplyChange:(ISyncChange*)change 
                   toManagedObject:(NSManagedObject*)managedObject 
                     inSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
}

- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
              didCancelSyncSession:(ISyncSession*)session 
                             error:(NSError*)error
{
  NSLog(@"%@:%s entered", [self class], _cmd);
}

- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
                  didCommitChanges:(NSDictionary*)changes 
                     inSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
}

- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
       didPullChangesInSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
}

- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
       didPushChangesInSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
}

- (BOOL)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
    willDeleteRecordWithIdentifier:(NSString*)identifier 
                     inSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
  return YES;
}

- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
      willPullChangesInSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
}

- (void)persistentStoreCoordinator:(NSPersistentStoreCoordinator*)coordinator 
      willPushChangesInSyncSession:(ISyncSession*)session
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
}

- (BOOL)persistentStoreCoordinatorShouldStartSyncing:(NSPersistentStoreCoordinator*)coordinator
{
  if (chatty) NSLog(@"%@:%s entered", [self class], _cmd);
  return YES;
}

@end
