#!/usr/bin/env python
#coding:utf-8


import sys, os, threading, time, shutil
sys.path.append(os.path.dirname(__file__) + "/../share/appimaged")
import AppImageKit.AppImage, pyinotify # Needs to be bundled


watch_directories = ['/Applications/', os.path.expanduser('~/Applications'), os.path.expanduser('~/Downloads')]


class EventHandler(pyinotify.ProcessEvent):
    
    def __init__(self, watcher):
        self.watcher = watcher

    def process_default(self, event):
        if "IN_ISDIR" in event.maskname:
            return # Is not a file
        print "\n" + event.maskname + " " + event.pathname
        name = os.path.basename(event.pathname)
        if "IN_MOVED_FROM" in event.maskname:
            print "* %s has been moved away, trying to uninstall the desktop integration" % (name)
            ai = AppImageKit.AppImage.AppImage(event.pathname)
            ai.uninstall_desktop_integration()
            return
        if "IN_DELETE" in event.maskname:
            print "* %s has been deleted, trying to uninstall the desktop integration" % (name)
            ai = AppImageKit.AppImage.AppImage(event.pathname)
            ai.uninstall_desktop_integration()
            return
        if "IN_MODIFY" in event.maskname: # File is being modified
            print "* Skipping %s since being modified" % (name)
            return False
        if event.pathname.endswith(".part"): # File is still being downloaded
            print "* Skipping %s since still being downloaded" % (name)
            return False
        if os.path.basename(event.pathname).startswith(".org."):  # File is still being downloaded (Chromium)
            print "* Skipping %s since still being downloaded" % (name)
            return False
        if event.pathname.endswith("download"): # File is still being downloaded
            print "* Skipping %s since still being downloaded" % (name)
            return False
        if event.pathname in self.watcher.files: # To prevent from infinite loop
            print "* Ignoring %s since recently processed" % (name)
            # print self.watcher.files
            return
        if os.path.getsize(event.pathname) < 100000:
            print "* Ignoring %s since too small, maybe download in progress" % (name)
            return
        self.watcher.files.append(event.pathname) # To prevent from infinite loop
        ai = AppImageKit.AppImage.AppImage(event.pathname)
        #air = AppImageKit.AppImage.AppImage(os.path.realpath(event.pathname)) # Follow symlinks
        if ai.check_whether_is_appimage() == False:
            print "* %s is not an AppImage" % (name)
            return
        if not os.access(event.pathname, os.X_OK):
            result = AppImageKit.timesavers.run_shell_command("make_appimage_executable '" + event.pathname + "'")
            self.watcher.files.append(event.pathname) # To prevent from infinite loop
            print result
        if not os.access(event.pathname, os.X_OK):
            print "* %s is not executable, trying to uninstall the desktop integration" % (name)
            ai = AppImageKit.AppImage.AppImage(event.pathname)
            ai.uninstall_desktop_integration()
            return # File is not executable
        print "* Processing %s" % (ai)
        try: 
            ai.install_desktop_integration()
        except: 
            print "Error installing desktop integration"
        t = threading.Timer(3.0, self.remove, [event.pathname]) # To prevent from infinite loop
        try: t.start() # To prevent from infinite loop
        except: print "Failed to start timer"

    def remove(self, file): # To prevent from infinite loop
        try: self.watcher.files.remove(file)
        except: "Failed removing from self.watcher.files"
        
            
class Watcher(object):
    
    def __init__(self, watch_directories):
        self.files = []
        self.wm = pyinotify.WatchManager()
        self.notifier = pyinotify.ThreadedNotifier(self.wm, EventHandler(self)) # self.notifier = pyinotify.Notifier(self.wm, EventHandler(self)) #        
        for directory in watch_directories:
            if not os.path.isdir(directory):
                continue
            watch = self.wm.add_watch(str(directory), pyinotify.EventsCodes.ALL_FLAGS["ALL_EVENTS"], rec=True)
            # IN_CLOSE_NOWRITE IN_CLOSE_WRITE IN_DELETE IN_MOVED_FROM
            if not self.wm.get_wd(str(directory)): # if watch[str(directory)] > 0:
                try: self.wm.rm_watch(watch[str(directory)]) # Seems to fail sometimes
                except: print "Removing watch failed"
                watch_directories.remove(str(directory))
        self.notifier.start() # self.notifier.loop()
        print "AppImage Daemon launched, watching %s" % (watch_directories)
        
if __name__=='__main__':
    loc = os.path.dirname(AppImageKit.AppImage.AppImage("/").desktopfile_path)
    print "Refreshing %s" % (loc)
    try: 
        shutil.rmtree(os.path.expanduser(loc))
        os.mkdir(os.path.expanduser(loc))
    except: 
        pass
    appimages = []
    for directory in watch_directories:
        try: appimages = appimages + AppImageKit.AppImage.get_appimages_in_dir(directory)
        except: pass
    for appimage in appimages:
        print appimage
        try: appimage.install_desktop_integration()
        except: pass
    try:
        w = Watcher(watch_directories)
        while True:
            time.sleep(1) # Need to stay around so that we can catch Ctrl-C
    except KeyboardInterrupt:
        w.notifier.stop()
        print "Exiting"
