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

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

require 'ToodleDoSyncer.rb'

class AppDelegate < NSObject
  
  attr_accessor :contextWindow,:browserWindows, :preferenceWindow, :contextWindowC, :templatesWindow
  
  def applicationDidFinishLaunching(notification)
	@@appDelegate=self
    self.performSelector('openAllWindows',
              withObject: nil,
              afterDelay: 0.0)
			
	@doSyncing = NSUserDefaults.standardUserDefaults.boolForKey('dotMacSyncing')
	syncClient.setSyncAlertHandler(self,selector:'client:mightWantToSyncEntityNames:') if @doSyncing
	#ILCrashReporter.defaultReporter.launchReporterForCompany_reportAddr("TwinForces","pierce@twinforces.com")
	#RuNSNotificationCenter.defaultCenter.addObserver_selector_name_object(self,'managedObjectContextDidChange:',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 = NSMutableSet.alloc.init
	url = NSURL.fileURLWithPath(NSBundle.mainBundle.pathForResource("RubyFrictionless_DataModel",ofType:"momd"),isDirectory:false)
	@managedObjectModel = NSManagedObjectModel.alloc.initWithContentsOfURL(url)
    
    return @managedObjectModel
  end
  
  # Change this path/code to point to your App's data store.
  def applicationSupportFolder
    NSHomeDirectory().stringByAppendingPathComponent(File.join('Library', 'Application Support', 'Frictionless'))
  end
  
  def managedObjectContext
    return @managedObjectContext if @managedObjectContext
    
    fileManager = NSFileManager.defaultManager
    storeFolder = applicationSupportFolder
    unless fileManager.fileExistsAtPath(storeFolder, isDirectory:nil)
      fileManager.createDirectoryAtPath(storeFolder, attributes:nil)
    end
	@shouldRefreshSync=false
    oldurl = NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("Frictionless.xml"))
    url = NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3"))
	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(NSArray.arrayWithObject(NSBundle.mainBundle()),forSourceModel:managedObjectModel,destinationModel:managedObjectModel)
		if (!mappingModel) then
			print "auto mapping didn't work\n"
		    nopurl = NSURL.fileURLWithPath(NSBundle.mainBundle.pathForResource_ofType("XMLupdate",isDirectory:"xcmappingmodel"),false)
			mappingModel=NSMappingModel.alloc.initWithContentsOfURL(nopurl)
		end
		if (mappingModel) then
			print "mapping model: #{mappingModel}\n"
			migrateManager= NSMigrationManager.alloc.initWithSourceModel(managedObjectModel,destinationModel:managedObjectModel)
			print "migrateManager: #{migrateManager}\n"
			result,error=migrateManager.migrateStoreFromURL(
			oldurl,type:NSXMLStoreType,options:nil,withMappingModel:mappingModel,toDestinationURL:url,destinationType:NSSQLiteStoreType,destinationOptions:nil)
			print "migration result, error: #{result},#{error}\n"
		else
		    print "no mapping model!\n"
			exit
		end
	end
	unless fileManager.fileExistsAtPath(storeFolder.stringByAppendingPathComponent("Frictionless.sqlite3"))
	 @shouldRefreshSync=true
	 @storeMetaDeta=nil
	end
    
    coordinator = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(managedObjectModel)
    # FIXME: cannot get errors
	options = { :NSMigratePersistentStoresAutomaticallyOption => NSNumber.numberWithBool(true) }
	persistentStore=coordinator.addPersistentStoreWithType("SQLite", configuration:nil, URL:url, options:options, error:error)
    if persistentStore then
      @managedObjectContext = NSManagedObjectContext.alloc.init
      @managedObjectContext.setPersistentStoreCoordinator(coordinator)
	  fastsyncurl= NSURL.fileURLWithPath(storeFolder.stringByAppendingPathComponent("FrictionlessFastSync.fastSync"))
	  coordinator.setStoresFastSyncDetailsAtURL(fastsyncurl,forPersistentStore:persistentStore)
    else
      NSApplication.sharedApplication.presentError(error)
    end
    $managedObjectContext=@managedObjectContext;
    return @managedObjectContext
  end
  
  def syncClient
	clientIdentifier=NSBundle.mainBundle.bundleIdentifier
	syncManager = ISyncManager.sharedManager
	client= syncManager.clientWithIdentifier(clientIdentifier)
	if (client == nil)
		if (syncManager.registerSchemaWithBundlePath( 
		    NSBundle.mainBundle.pathForResource_ofType("FrictionlessSyncSchema","syncschema")))
			path=NSBundle.mainBundle.pathForResource_ofType("RubySelfClient","plist")
			client=syncManager.registerClientWithIdentifier_descriptionFilePath(clientIdentifier,path)
			client.setShouldSynchronize_withClientsOfType(true, ISyncClientTypeApplication)
			client.setShouldSynchronize_withClientsOfType(true, ISyncClientTypeDevice)
			client.setShouldSynchronize_withClientsOfType(true, ISyncClientTypeServer)
			client.setShouldSynchronize_withClientsOfType(true, ISyncClientTypePeer)
		end
	end
	client
  end
  
  def client(client,mightWantToSyncEntityNames: names)
     print "client syncing #{names}\n"
     saveAction(self)
  end

  def windowWillReturnUndoManager(window)
    return managedObjectContext.undoManager
  end
  
  def showContextWindow(sender)
   	app=NSApplication.sharedApplication
    @contextWindowC=NSWindowController.alloc.initWithWindowNibName_owner("ContextsWindow",self) if not @contextWindow
    @contextWindowC.showWindow(self)
    @contextWindow.makeKeyAndOrderFront(nil)
  end
  
  def showTemplatesWindow(sender)
   	app=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 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
		error=managedObjectContext.persistentStoreCoordinator.syncWithClient_inBackground_handler_error(syncClient,false,self) if syncClient
	rescue 
		print $!
	end
	print "sync finished!\n"
  end
  
 def toodleSync(sender)
	@toodle=ToodleDoSyncer.alloc.init unless @toodle
	@toodle.syncAll(managedObjectContext)
  end
  
 def mailSync(sender)
	@mailSyncer=MailSyncer.alloc.init unless @mailSyncer
	@mailSyncer.syncMail(managedObjectContext)
  end
  
 def toodleClean(sender)
	@toodle=ToodleDoSyncer.alloc.init unless @toodle
	@toodle.clean(managedObjectContext)
  end
  
  def saveAction(sender)
    error = nil
    unless managedObjectContext.save?(error)
      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 = NSTerminateNow
    
    context = managedObjectContext
    if context
      if context.commitEditing?
        # FIXME: cannot get errors
        error = OCObject.new
        unless context.save?(error)
		  error.userInfo.valueForKey("NSDetailedErrors").each { |e| print e.userInfo}
          errorResult = NSApplication.sharedApplication.presentError?(error)
        
          if errorResult
            reply = NSTerminateCancel
          else
            alertReturn = OSX.NSRunAlertPanel(nil, "Could not save changes while quitting. Quit anyway?", "Quit anyway", "Cancel", nil)
            if alertReturn == NSAlertAlternateReturn
              reply = NSTerminateCancel
            end
          end
        end
      else
        reply = 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 false # don't ssince for now.
  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
				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
