#  RubyFrictionlessAppDelegate.rb
#  RubyFrictionless
#
#  Created by Pierce T. Wetter III on 4/3/07.
#  Copyright __MyCompanyName__ 2007 . All rights reserved.

require 'osx/cocoa'
OSX.require_framework 'CoreData'
OSX.require_framework('CoreGraphics')
OSX.require_framework('SyncServices')

require 'ToodleDoSyncer.rb'

class AppDelegate < OSX::NSObject
  
  attr_accessor :contextWindow,:browserWindows, :preferenceWindow, :contextWindowC, :templatesWindow
  
  def applicationDidFinishLaunching(notification)
	@@appDelegate=self
    objc_send :performSelector,'openAllWindows',
              :withObject, nil,
              :afterDelay, 0.0
	@doSyncing = NSUserDefaults.standardUserDefaults.boolForKey('dotMacSyncing')
	syncClient.setSyncAlertHandler_selector(self,'client:mightWantToSyncEntityNames:') if @doSyncing
    # And check if there were any errors the last time.
    SACrashReporter.submit
	#RuNSNotificationCenter.defaultCenter.addObserver_selector_name_object(self,'managedObjectContextDidChange:',OSX::NSManagedObjectContextObjectsDidChangeNotification,nil)
  end
  
  def openAllWindows
    actions=managedObjectContext.objectsForEntityNamed('Action')
	actions.each do |a|
		a.appLaunch
	end
    unless actions && actions.count > 0
      %w{Work Home Errands Online Email Phone Waiting Weekend}.each do |cname|
        context=NSEntityDescription.insertNewObjectForEntityForName_inManagedObjectContext("Context",managedObjectContext)
        managedObjectContext.processPendingChanges
        context.name=cname
      end
      [ "Professional @Work :area :note This is an area/role to hold your job related actions so you can easily focus on just those.",
        "Personal @Home :area :note This is an area/role to hold your home related actions so you can easily focus on just those.",
        "Spouse > Personal @Home :area :note This is possibly one of the roles you play in life. ",
        "Parent > Personal @Home :area :note This is possibly one of the roles you play in life.",
        "Child > Personal @Home :area :note This is possibly one of the roles you play in life.",
        "Homeowner > Personal @Home :area :note This is a good place to hold home maintenance tasks.",
        "Employee > Professional @Work :area :note To Quote Bob Dylan, you gotta serve somebody.",
        "Manager > Professional @Work :area :note Have people you have to manage?",
        "Coworker > Professional @Work :area :note I'm sure you work with people.",
        "Sample Project > Professional :note A project is anything with child actions or child projects",
        "Sample Action > Sample Project :note A Tasks is anything without any children",
        "Another Action > Sample Project :note A Tasks is anything without any children",
        ].each do |aname|
          newAction= NSEntityDescription.insertNewObjectForEntityForName_inManagedObjectContext("Action",managedObjectContext)
          newAction.smartName=aname
          managedObjectContext.processPendingChanges
        end
    end
    actions=managedObjectContext.objectWithValue_forKey_entityNamed("YES","noteOpen",'Action')
    if actions && actions.count > 0
      actions.each {|a| a.openJustNotes}
    end
    newBrowserWindow(self)
  end
  
  def newQuickEntryWindow(sender)
  end
  
  def buildPreferenceWindow
    @preferenceWindow=AMPreferenceWindowController.alloc.initWithAutosaveName("Preferences")
    defaults={}
    gp=GeneralPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self)
    sp=SparklePreferences.alloc.initWithController_appDelegate(@preferenceWindow,self)
    tp=TwitterPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self)
    tdp=ToodleDoPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self)
    mp=MailPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self)
    syp=SyncPreferences.alloc.initWithController_appDelegate(@preferenceWindow,self)
  end
  def preferenceWindow
     buildPreferenceWindow unless @preferenceWindow
     @preferenceWindow
  end
  
  def openPreferences(sender)
    preferenceWindow.showWindow(self)
    preferenceWindow.window.makeKeyAndOrderFront(self)
  end
    
  
  def managedObjectModel
    return @managedObjectModel if @managedObjectModel
    
    allBundles = OSX::NSMutableSet.alloc.init
	url = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.pathForResource_ofType("RubyFrictionless_DataModel","momd"),false)
	@managedObjectModel = OSX::NSManagedObjectModel.alloc.initWithContentsOfURL(url)
    
    return @managedObjectModel
  end
  
  # Change this path/code to point to your App's data store.
  def applicationSupportFolder
    OSX.NSHomeDirectory.stringByAppendingPathComponent(File.join('Library', 'Application Support', 'Frictionless'))
  end
  
  def managedObjectContext
    return @managedObjectContext if @managedObjectContext
    
	error = OSX::OCObject.new
    fileManager = OSX::NSFileManager.defaultManager
    storeFolder = applicationSupportFolder
    unless fileManager.fileExistsAtPath_isDirectory?(storeFolder, nil)
      fileManager.createDirectoryAtPath_attributes(storeFolder, nil)
    end
	@shouldRefreshSync=false
    oldurl = OSX::NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("Frictionless.xml"))
    url = OSX::NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3"))
	options = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true) }
	if (fileManager.fileExistsAtPath(storeFolder.stringByAppendingPathComponent("Frictionless.xml")) && !fileManager.fileExistsAtPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3"))) then


		print "migrating from XML store to SQL store\n"
		mappingModel=NSMappingModel.mappingModelFromBundles_forSourceModel_destinationModel(NSArray.arrayWithObject(NSBundle.mainBundle()),managedObjectModel,managedObjectModel)
		if (!mappingModel) then
			print "auto mapping didn't work, trying manual\n"
		    nopurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.pathForResource_ofType("XMLupdate","cdm"),false)
			print NSBundle.mainBundle.resourcePath()
			oldmomurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.resourcePath()+"/RubyFrictionless_DataModel.momd/RubyFrictionless_DataModel.mom",false)
			newmomurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.resourcePath()+"/RubyFrictionless_DataModel.momd/RubyFrictionless_DataModel_scheduling_ext.mom",false)
			oldMom = OSX::NSManagedObjectModel.alloc.initWithContentsOfURL(oldmomurl)
			newMom = OSX::NSManagedObjectModel.alloc.initWithContentsOfURL(newmomurl)

			mappingModel=NSMappingModel.mappingModelFromBundles_forSourceModel_destinationModel([NSBundle.mainBundle()],oldMom,newMom);
			if (mappingModel) then
				#print "mapping model: #{mappingModel}\n"
				migrateManager= NSMigrationManager.alloc.initWithSourceModel_destinationModel(oldMom,newMom)
				#print "migrateManager: #{migrateManager}\n"
				migrateoptions = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true), 
					:NSIgnorePersistentStoreVersioningOption =>NSNumber.numberWithBool(true)  }
				result,error=migrateManager.migrateStoreFromURL_type_options_withMappingModel_toDestinationURL_destinationType_destinationOptions_error(
				oldurl,OSX::NSXMLStoreType,migrateoptions,mappingModel,url,OSX::NSSQLiteStoreType,migrateoptions)
				print "migration result, error: #{result},#{error}\n"
			end
		end
		if (!mappingModel) then
			print "auto mapping2 didn't work\n"
		    nopurl = OSX::NSURL.fileURLWithPath_isDirectory(OSX::NSBundle.mainBundle.pathForResource_ofType("XMLupdate","cdm"),false)
			mappingModel=NSMappingModel.alloc.initWithContentsOfURL(nopurl)
			if (mappingModel) then
				#print "mapping model: #{mappingModel}\n"
				migrateManager= NSMigrationManager.alloc.initWithSourceModel_destinationModel(managedObjectModel,managedObjectModel)
				#print "migrateManager: #{migrateManager}\n"
				migrateoptions = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true), 
					:NSIgnorePersistentStoreVersioningOption =>NSNumber.numberWithBool(true)  }
				result,error=migrateManager.migrateStoreFromURL_type_options_withMappingModel_toDestinationURL_destinationType_destinationOptions_error(
				oldurl,OSX::NSXMLStoreType,migrateoptions,mappingModel,url,OSX::NSSQLiteStoreType,migrateoptions)
				print "migration result, error: #{result},#{error}\n"
			end
		end
		if (!mappingModel) then
		    print "no mapping model!\n"
			exit
		end
	end
	unless fileManager.fileExistsAtPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3"))
	 @shouldRefreshSync=true
	 @storeMetaDeta=nil
	end
    
    coordinator = OSX::NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(managedObjectModel)
    # FIXME: cannot get errors
	options = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true) }
	persistentStore=coordinator.addPersistentStoreWithType_configuration_URL_options_error(OSX::NSSQLiteStoreType, nil, url, options, error)
    if persistentStore then
      @managedObjectContext = OSX::NSManagedObjectContext.alloc.init
      @managedObjectContext.setPersistentStoreCoordinator(coordinator)
	  fastsyncurl= OSX::NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("FrictionlessFastSync.fastSync"))
	  coordinator.setStoresFastSyncDetailsAtURL_forPersistentStore(fastsyncurl,persistentStore)
    else
      OSX::NSApplication.sharedApplication.presentError(error)
    end
    $managedObjectContext=@managedObjectContext;
    return @managedObjectContext
  end
  
  def syncClient
	clientIdentifier=NSBundle.mainBundle.bundleIdentifier
	client=OSX::ISyncManager.sharedManager.clientWithIdentifier(clientIdentifier)
	if (client == nil)
		if (ISyncManager.sharedManager.registerSchemaWithBundlePath( 
		    NSBundle.mainBundle.pathForResource_ofType("FrictionlessSyncSchema","syncschema")))
			path=NSBundle.mainBundle.pathForResource_ofType("RubySelfClient","plist")
			client=ISyncManager.sharedManager.registerClientWithIdentifier_descriptionFilePath(clientIdentifier,path)
			client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypeApplication)
			client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypeDevice)
			client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypeServer)
			client.setShouldSynchronize_withClientsOfType(true, OSX::ISyncClientTypePeer)
		end
	end
	client
  end
  
  def client_mightWantToSyncEntityNames(client,names)
     print "client syncing #{names}\n"
     saveAction(self)
  end

  def windowWillReturnUndoManager(window)
    return managedObjectContext.undoManager
  end
  
  def showContextWindow(sender)
   	app=OSX::NSApplication.sharedApplication
    @contextWindowC=NSWindowController.alloc.initWithWindowNibName_owner("ContextsWindow",self) if not @contextWindow
    @contextWindowC.showWindow(self)
    @contextWindow.makeKeyAndOrderFront(nil)
  end
  
  def showTemplatesWindow(sender)
   	app=OSX::NSApplication.sharedApplication
    @templatesWindowC=NSWindowController.alloc.initWithWindowNibName_owner("Templates",self) if not @templatesWindow
    @templatesWindowC.showWindow(self)
    #@templatesWindowC.makeKeyAndOrderFront(nil)
  end
  
  def newBrowserWindow(sender)
    @browserWindows ||=[]
    ab=ActionBrowser.alloc.init
    ab.setup(self)
    @browserWindows << ab
    ab
  end
  
  def newActionFocusWindow(action)
    return newFocusWindow([action])
  end
  def newFocusWindow(focus)
    @browserWindows ||=[]
    ab=ActionBrowser.alloc.init
    ab.setup(self,focus)
    @browserWindows << ab
    ab
  end
  
  def AppDelegate.newFocusWindow(focus)
	@@appDelegate.newFocusWindow(focus)
  end
  def AppDelegate.newActionFocusWindow(focus)
	@@appDelegate.newActionFocusWindow(focus)
  end
  
  def newHelpWindow(sender)
	hw=HelpWindow.alloc.init
	hw.setup(self,nil)
	hw
  end
  
  def syncAction(sender)
	return unless @doSyncing
	#managedObjectContext.persistentStoreCoordinator.syncWithClient_inBackground_handler_error(syncClient,true,self,error) if syncClient
    print "starting sync\n"
	begin
		Action.startSync
		error=managedObjectContext.persistentStoreCoordinator.syncWithClient_inBackground_handler_error(syncClient,false,self) if syncClient
		Action.endSync
	rescue 
		print $!
	end
	print "sync finished!\n"
  end
  
 def toodleSync(sender)
	saveAction(sender)
	@toodle=ToodleDoSyncer.alloc.init unless @toodle
	@toodle.syncAll(managedObjectContext)
	saveAction(sender)
  end
  
 def mailSync(sender)
	saveAction(sender)
	@mailSyncer=MailSyncer.alloc.init unless @mailSyncer
	@mailSyncer.syncMail(managedObjectContext)
	saveAction(sender)
  end
  
 def toodleClean(sender)
	saveAction(sender)
	@toodle=ToodleDoSyncer.alloc.init unless @toodle
	@toodle.clean(managedObjectContext)
	saveAction(sender)
  end
  
  def saveAction(sender)
    error = nil
    unless managedObjectContext.save?(error)
      OSX::NSApplication.sharedApplication.presentError(error)
	  return
    end
	syncAction(sender) if @doSyncing
  end
  
  def saveInMainThread(sender)
    self.performSelector_withObject_afterDelay('saveAction:',sender,0.0)
  end
  
  def managedObjectContextsToMonitorWhenSyncingPersistentStoreCoordinator(psc)
    print "moctoMonitor\n"
	return [managedObjectContext]
  end
  
  def managedObjectContextsToReloadAfterSyncingPersistentStoreCoordinator(psc)
    print "moctoReload\n"
	return [managedObjectContext]
  end
    
  
  def applicationShouldTerminate(sender)
    reply = OSX::NSTerminateNow
    
    context = managedObjectContext
    if context
      if context.commitEditing?
        # FIXME: cannot get errors
        error = OSX::OCObject.new
        unless context.save?(error)
		  error.userInfo.valueForKey("NSDetailedErrors").each { |e| print e.userInfo}
          errorResult = OSX::NSApplication.sharedApplication.presentError?(error)
        
          if errorResult
            reply = OSX::NSTerminateCancel
          else
            alertReturn = OSX.NSRunAlertPanel(nil, "Could not save changes while quitting. Quit anyway?", "Quit anyway", "Cancel", nil)
            if alertReturn == OSX::NSAlertAlternateReturn
              reply = OSX::NSTerminateCancel
            end
          end
        end
      else
        reply = OSX::NSTerminateCancel
      end
    end
    return reply
  end

  def statusesReceived_forRequest(statuses,identifier)
	print statuses
  end

  def directMessagesReceived_messages_forRequest(messages,identifier)
  	print messages
  end


  def userInfoReceived_forRequest(userInfo,identifier)
	print userInfo
  end


  def requestSucceeded(requestIdentifier)
	print requestIdentifier
  end


  def requestFailed_withError(requestIdentifier,error)
    print "Twitter request failed! (#{requestIdentifier}) Error: #{error.localizedDescription} (#{error.userInfo})"
  end
  
  def persistentStoreCoordinatorShouldStartSyncing?(psc)
    print "persistentStoreCoordinatorShouldStartSyncing?\n"
	@syncChanges=false
	return true
  end
#  def persistentStoreCoordinatorShouldStartSyncing(psc)
#    print "persistentStoreCoordinatorShouldStartSyncing\n"
#	return true
#  end
  def persistentStoreCoordinator_willPushChangesInSyncSession(psc,session)
    print "persistentStoreCoordinator_willPushChangesInSyncSession\n"
  end
  def persistentStoreCoordinator_didPushChangesInSyncSession(psc,session)
    print "persistentStoreCoordinator_didPushChangesInSyncSession\n"
	@syncChanges=true
  end
  def persistentStoreCoordinator_willPullChangesInSyncSession(psc,session)
    print "persistentStoreCoordinator_willPullChangesInSyncSession\n"
  end
  def persistentStoreCoordinator_didPullChangesInSyncSession(psc,session)
    print "persistentStoreCoordinator_didPullChangesInSyncSession\n"
  end
  def persistentStoreCoordinator_didFinishSyncSession(psc,session)
    print "persistentStoreCoordinator_didFinishSyncSession\n"
	if @syncChanges then
			print "resetting action caches\n"
			actions=managedObjectContext.objectsForEntityNamed('Action')
			actions.each do |a|  #fix any bad parents
				a.reset_caches
			end
			actions.each do |a|  #fix any bad parents
				a.setParent(nil) if a.parent? and a.parent.hasAncestor?(a)
			end
	end
  end
  def persistentStoreCoordinator_didCancelSyncSession_error(psc,session,error)
    print "persistentStoreCoordinator_didCancelSyncSession_error\n"
  end
  def persistentStoreCoordinator_willPushRecord_forManagedObject_inSyncSession(psc,record,object,session)
     #print "pushing #{record} for object #{object}\n"
	 @syncChanges=true
     record
  end
  def persistentStoreCoordinator_willDeleteRecordRecordWithIdentifier_inSyncSession(psc,record,object,session)
    print "persistentStoreCoordinator_willDeleteRecordRecordWithIdentifier_inSyncSession\n"
	 @syncChanges=true
	return true
  end
  def persistentStoreCoordinator_willApplyChange_toManagedObject_inSyncSession(psc,change,object,session)
     print "changes #{change} for object #{object}\n"
	 object.syncON if object
	 @syncChanges=true
     change
  end
  def persistentStoreCoordinator_didApplyChange_toManagedObject_inSyncSession(psc,change,object,session)
    print "persistentStoreCoordinator_didApplyChange_toManagedObject_inSyncSession\n"
	 object.syncOFF if object
	 if (object && object.entity.name=="Action")
		object.setParent(nil) if object.parent? and object.hasAncestor?(object)
	 end
  end
  def persistentStoreCoordinator_didCommitChanges_toManagedObject_inSyncSession(psc,change,object,session)
    print "persistentStoreCoordinator_didCommitChanges_toManagedObject_inSyncSession\n"
	 @syncChanges=true
  end

end
