#!/usr/bin/env python2
import subprocess
import cPickle
import os.path
import os
import sys
import pygtk
pygtk.require('2.0')
import gtk
from autojump import open_dic,get_dic_file

defaults={}
actions={}

#directory finding helpers, conforming to the XDG Base Directory Specification
def data_dir():
    xdg_data_dir = os.environ.get('XDG_DATA_HOME') or os.path.join(os.environ['HOME'], '.local', 'share')
    return os.path.join(xdg_data_dir, 'autojump')

def config_dir():
    xdg_config_dir = os.environ.get('XDG_CONFIG_HOME') or os.path.join(os.environ['HOME'], '.config')
    return os.path.join(xdg_config_dir, 'autojump')

#decorator truff
def action(validator,name=None):
    if name is None:
        def wrapper(action):
            actions[action.__name__]=(action,validator)
    else:
        def wrapper(action):
            actions[name]=(action,validator)
    return wrapper

#validator helper
def has_child_dir(dirname,recursion=0):
    def wrapper(path):
        k=recursion
        ii=""
        while k>=0:
            if os.path.isdir(os.path.join(path,ii,dirname)): return True
            k-=1
            ii=os.path.join("..",ii)
        return False
    return wrapper


def has_child_file(filename):
    def wrapper(path):
        return os.path.isfile(os.path.join(path,filename))
    return wrapper

def load_paths(maxpath=10):
    path_dict=open_dic(get_dic_file())
    path_dict=path_dict.items()
    path_dict.sort(key=lambda x: x[1],reverse=True)
    return [path for path,score in path_dict[:maxpath]]

def load_settings_file():
    filename = os.path.join(config_dir(), 'jumpapplet_py')
    #migration from old location
    old_filename =  os.path.join(os.environ['HOME'], '.jumpapplet_py')
    if not os.path.exists(filename) and os.path.exists(old_filename):
        os.rename(old_filename, filename)

    print "loading settings"
    global defaults
    dic_file = filename

    try:
        aj_file=open(dic_file,'r')
        defaults=cPickle.load(aj_file)
        aj_file.close()
    except IOError:
        print "no config file"
        pass

    if not "terminal" in defaults: defaults["terminal"]="gnome-terminal"
    if not "navigator" in defaults: defaults["navigator"]="nautilus"
    if not "maxpath" in defaults: defaults["maxpath"]=15
    if not "invert" in defaults: defaults["invert"]=False
    if not "collapse" in defaults: defaults["collapse"]=True

    create_actions()

def save_settings_file(filename=None):
    directory = config_dir()
    if not filename:
        filename = os.path.join(directory, 'jumpapplet_py')

    if not os.path.exists(directory):
        os.makedirs(directory)

    print "saving settings"
    dic_file= filename

    aj_file=open(dic_file,'w')
    cPickle.dump(defaults,aj_file)
    aj_file.close()


def get_actions(path):
    return [(name,action) for name,(action,validator) in actions.items() if validator(path)]

def popup(sender,button,activation):
    paths=load_paths(maxpath=defaults["maxpath"])

    if defaults["collapse"]:
        def collapse_home(path):
            return path.replace(os.path.expanduser('~'),"~")
    else:
        def collapse_home(path):
            return path

    menu=gtk.Menu()
    if defaults["invert"]:
        item=gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
        item.connect("activate",quit)
        menu.append(item)
        item=gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
        item.connect("activate",settings)
        menu.append(item)

        menu.append(gtk.SeparatorMenuItem())

        for path in reversed(paths):
            actions=get_actions(path)
            if not actions: continue

            item=gtk.MenuItem(collapse_home(path),use_underline=False)
            submenu=gtk.Menu()
            item.set_submenu(submenu)
            for name,action in actions:
                subitem=gtk.MenuItem(name)
                subitem.connect("activate",action,path)
                submenu.append(subitem)
            menu.append(item)
    else:
        for path in paths:
            actions=get_actions(path)
            if not actions: continue

            item=gtk.MenuItem(collapse_home(path),use_underline=False)
            submenu=gtk.Menu()
            item.set_submenu(submenu)
            for name,action in actions:
                subitem=gtk.MenuItem(name)
                subitem.connect("activate",action,path)
                submenu.append(subitem)
            menu.append(item)

        menu.append(gtk.SeparatorMenuItem())

        item=gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
        item.connect("activate",settings)
        menu.append(item)
        item=gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
        item.connect("activate",quit)
        menu.append(item)

    menu.show_all()
    menu.popup(None,None,gtk.status_icon_position_menu,button,activation,sender)

def settings(sender):
    window=gtk.Dialog("jump applet preferences",None,gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_SAVE,gtk.RESPONSE_OK,gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL))
    window.set_border_width(3)
    window.set_resizable(False)
    if os.path.isfile("icon.png"): window.set_icon_from_file("icon.png")
    elif os.path.isfile("/usr/share/autojump/icon.png"): window.set_icon_from_file("/usr/share/autojump/icon.png")

    vbox=gtk.Table(5,2)
    vbox.set_row_spacings(3)
    window.get_child().add(vbox)
    def add_string_setting(name,label,nsettings):
        label=gtk.Label(label+' ')
        label.set_alignment(1.,.5)
        entry=gtk.Entry()
        if name in defaults: entry.set_text(defaults[name])

        vbox.attach(label,0,1,nsettings,nsettings+1)
        vbox.attach(entry,1,2,nsettings,nsettings+1)
        return (name,entry)

    def add_integer_setting(name,label,nsettings):
        label=gtk.Label(label+' ')
        label.set_alignment(1.,.5)
        entry=gtk.SpinButton()
        entry.set_range(5,35)
        entry.set_numeric(True)
        entry.set_increments(1,5)
        entry.set_snap_to_ticks(True)
        if name in defaults: entry.set_value(defaults[name])

        vbox.attach(label,0,1,nsettings,nsettings+1)
        vbox.attach(entry,1,2,nsettings,nsettings+1)
        return (name,entry)

    def add_bool_setting(name,label,nsettings):
        entry=gtk.CheckButton(label=label,use_underline=False)
        if name in defaults: entry.set_active(defaults[name])

        vbox.attach(entry,0,2,nsettings,nsettings+1)
        return (name,entry)

    entries=[]
    entries.append(add_string_setting("terminal","Terminal program",0))
    entries.append(add_string_setting("navigator","Navigator program",1))
    entries.append(add_integer_setting("maxpath","Number of directories",2))
    entries.append(add_bool_setting("invert","List directories in reverse order",3))
    entries.append(add_bool_setting("collapse","Collapse home directory to ~",4))

    window.connect("response",save_settings,entries,window)
    window.show_all();

def save_settings(sender,response,entries,window):
    window.hide_all()
    if response!=gtk.RESPONSE_OK: return

    global defaults
    for name,entry in entries:
        try:
            defaults[name]=int(entry.get_text())
        except (ValueError,AttributeError):
            try:
                defaults[name]=entry.get_active()
            except AttributeError:
                defaults[name]=entry.get_text()

    save_settings_file()
    create_actions()

def init():
    load_settings_file()
    if os.path.isfile("icon.png"): icon=gtk.status_icon_new_from_file("icon.png")
    elif os.path.isfile("/usr/share/autojump/icon.png"): icon=gtk.status_icon_new_from_file("/usr/share/autojump/icon.png")
    else: icon=gtk.status_icon_new_from_icon_name("help")
    icon.set_visible(True)
    icon.connect("popup-menu",popup)

def quit(sender):
    gtk.main_quit()

######################################################
#insert other actions here using the action decorator#
######################################################
def create_actions():
    global actions
    actions={}

    @action(has_child_dir(".git",recursion=3))
    def gitk(sender,path):
        if not os.fork():
            os.chdir(path)
            subprocess.Popen(['gitk']).wait()
            sys.exit()

    @action(has_child_file("CMakeCache.txt"),"configure")
    def cmake(sender,path):
        if not os.fork():
            os.chdir(path)
            subprocess.Popen(['cmake-gui','.']).wait()
            sys.exit()

    @action(os.path.isdir)
    def terminal(sender,path):
        print "launch terminal '%s'" % defaults["terminal"]
        if not os.fork():
            try:
                if defaults["terminal"]=="konsole":
                    subprocess.Popen([defaults["terminal"],"--workdir=%s"%path]).wait()
                else:
                    os.chdir(path)
                    subprocess.Popen([defaults["terminal"]]).wait()
            except OSError:
                pass
            sys.exit()

    @action(os.path.isdir)
    def navigator(sender,path):
        print "launch navigator '%s'" % defaults["navigator"]
        if not os.fork():
            try:
                subprocess.Popen([defaults["navigator"],path]).wait()
            except OSError:
                pass
            sys.exit()

if __name__=='__main__':
    init()
    gtk.main()