# -*- coding: utf-8 -*-
# Copyright (c) 2006 Matt Keenan <matt.keenan@sun.com>
#

import re
import os
import shutil
import gmenu
import sys


class menuLocation:
    """Class for locating Menu location from specified .desktop file using
       gnome-menus."""

    def __init__(self):
        pass

    def buildMenuPath(self, start_path, path_to_add):
        """Build menu path, e.g. String with '|' separated fields."""
        if len(start_path) == 0:
            return path_to_add
        elif start_path == "|":
            return start_path + path_to_add
        else:
            return start_path + "|" + path_to_add

    def findDesktopFile (self, menu_tree_directory, path, desktop_file_id):
        """Locate first occurrance of .desktop file within a given menu tree."""
        path = self.buildMenuPath(path, menu_tree_directory.name)
        for item in menu_tree_directory.contents:
            if isinstance (item, gmenu.Directory):
                retval = self.findDesktopFile (item,
                                               path,
                                               desktop_file_id)
                if retval:
                    return retval
            elif isinstance (item, gmenu.Entry):
                if item.desktop_file_id == desktop_file_id:
                    path = self.buildMenuPath(path, item.name)
                    return path
            elif isinstance (item, gmenu.Alias) and \
                 isinstance (item.item, gmenu.Entry):
                if item.item.desktop_file_id == desktop_file_id:
                    path = self.buildMenuPath(path, item.name)
                    return path
        return None

    def allDefaultApplicationItems (self, menu_tree_directory, path):
        for item in menu_tree_directory.contents:
            if isinstance(item, gmenu.Directory):
                self.allDefaultApplicationItems (item, path)
            elif isinstance (item, gmenu.Entry):
                menu_path = self.findDesktopFile (menu_tree_directory, path, item.desktop_file_id)
                if menu_path:
                    print >> sys.stdout, menu_path
                    #self.outputMenuXml(sys.stdout, menu_path)
            elif isinstance (item, gmenu.Alias) and \
                 isinstance (item.item, gmenu.Entry):
                menu_path = self.findDesktopFile (menu_tree_directory, path, item.item.desktop_file_id)
                if menu_path:
                    print >> sys.stdout, menu_path
                    #self.outputMenuXml(sys.stdout, menu_path)

    def outputDefaultApplicationItems(self):
        """Output all items on the default applications menu."""
        tree = gmenu.lookup_tree('applications.menu', gmenu.FLAGS_INCLUDE_EXCLUDED)
        if tree is not None:
            path = self.buildMenuPath("", tree.root.name)
            self.allDefaultApplicationItems(tree.root, path)
        else:
            print >> sys.stderr, "Failed to get menu tree for default applications.menu"

    def outputMenuXml(self, output_file, menu_path):
        """Output XML for menu item location."""
        # Split path into sections
        output_file.write("<menu>\n")
        items = menu_path.split('|')
        for i in range(len(items)):
            if i == 0:
                # Main Menu
                output_file.write("  <mainmenu>\n")
                output_file.write("    <item>%s</item>\n" % items[i])
                output_file.write("  </mainmenu>\n")
            elif i == (len(items)-1):
                # Menu Item
                output_file.write("  <menuitem>\n")
                output_file.write("    <item>%s</item>\n" % items[i])
                output_file.write("  </menuitem>\n")
            else:
                # Sub Menu
                output_file.write("  <submenu>\n")
                output_file.write("    <item>%s</item>\n" % items[i])
                output_file.write("  </submenu>\n")
        output_file.write("</menu>\n")

    def menuIsSubmenu(self, menu_file, menu_path):
        """Check if menu is submenu, using python version of grep."""
        search=re.compile(menu_file).search
        for file in os.listdir(menu_path):
            if '.menu' in file:
                full_file = os.path.join(menu_path, file)
                for index, line in enumerate(open(full_file)):
                    if (search(line)):
                        return True
        return False

    def removeTmpFiles(self, tmp_desktop_file, tmp_xdg_data_dir):
        """Remove tmp files used in locating menu."""
        if os.path.isfile(tmp_desktop_file):
            os.remove(tmp_desktop_file)
        if os.path.isdir(tmp_xdg_data_dir):
            os.rmdir(tmp_xdg_data_dir)

    def locateMenu(self, dot_desktop_file_id):
        """Locate Menu item, output first occurrence in xml."""

        if dot_desktop_file_id == 'ALL':
            # Test mode so just output list of menu items on default menu
            self.outputDefaultApplicationItems()
            return None
        elif not os.path.isfile(dot_desktop_file_id):
            print >> sys.stderr, "Error : .desktop file : %s, does not exist." % dot_desktop_file_id
            return None

        # Get basename of dot_desktop_file_id
        desktop_base_id=os.path.basename(dot_desktop_file_id)

        tmp_xdg_data_dirs='/tmp'
        tmp_xdg_data_dirs_applications='/tmp/applications'

        # Need to set XDG_DATA_DIRS to tmp area and copy desktop file there.
        os.environ['XDG_DATA_DIRS']=tmp_xdg_data_dirs
        if not os.path.isdir(tmp_xdg_data_dirs_applications):
            os.mkdir(tmp_xdg_data_dirs_applications)
        tmp_dot_desktop_file_id=os.path.join(tmp_xdg_data_dirs_applications, desktop_base_id)
        if os.path.isfile(tmp_dot_desktop_file_id):
            os.remove(tmp_dot_desktop_file_id)
        shutil.copy(dot_desktop_file_id, tmp_xdg_data_dirs_applications)

        # Process menu files in $XDG_CONFIG_DIRS/menus/*
        # First call should be to check the default applications menu
        # then process all others, Do not process menus that appear
        # as submenus on other items.

        # Get defailt applications.menu Tree
        tree = gmenu.lookup_tree('applications.menu', gmenu.FLAGS_INCLUDE_EXCLUDED)

        if tree is not None:
            menu_item_path = self.findDesktopFile (tree.root, "", desktop_base_id)
        else:
            print >> sys.stderr, "Failed to get menu tree for default applications.menu"
            self.removeTmpFiles(tmp_dot_desktop_file_id, tmp_xdg_data_dirs_applications)
            return None

        if menu_item_path:
            #self.outputMenuXml(out_file, menu_item_path)
            self.removeTmpFiles(tmp_dot_desktop_file_id, tmp_xdg_data_dirs_applications)
            return menu_item_path
        else:
            # Looks like we gotta search some more
    
            # Test if XDG_CONFIG_DIRS Is set, if not the use the default of
            # /etc/xdg

            if 'XDG_CONFIG_DIRS' in os.environ:
                xdg_config_dirs=os.environ['XDG_CONFIG_DIRS']
            else:
                xdg_config_dirs='/etc/xdg'
                os.environ['XDG_CONFIG_DIRS']=xdg_config_dirs

            xdg_paths = xdg_config_dirs.split(':')

            # Process all dirs in XDG_CONFIG_DIRS
            for path_idx in range(len(xdg_paths)): 
                menu_path=os.path.join(xdg_paths[path_idx], 'menus')
                if os.path.isdir(menu_path):
                    for menu_file in os.listdir(menu_path):
                        # Make sure suffix is ".menu", not applications.menu, and not a submenu
                        if '.menu' in menu_file and \
                            menu_file != 'applications.menu' and \
                            not self.menuIsSubmenu(menu_file, menu_path):
            
                            tree = gmenu.lookup_tree(menu_file, gmenu.FLAGS_INCLUDE_EXCLUDED)
                            if tree is not None:
                                menu_item_path = self.findDesktopFile (tree.root, "", desktop_base_id)
                                if menu_item_path:
                                    #self.outputMenuXml(out_file, menu_item_path)
                                    self.removeTmpFiles(tmp_dot_desktop_file_id, tmp_xdg_data_dirs_applications)
                                    return menu_item_path
        self.removeTmpFiles(tmp_dot_desktop_file_id, tmp_xdg_data_dirs_applications)
        return None

# Perform some tests when ran standalone
if __name__ == '__main__':
    """No arguments prints all default applications menu.
       Arg 1 = Optinal desktop file to search for in default applications menu.
       Arg 2 = Optional output file."""

    test = menuLocation()

    args = sys.argv[1:]
    if len(args) == 1:
        desktop_file=args[0]
        output_file = ""
    elif len(args) == 2:
        desktop_file=args[0]
        output_file=args[1]
    else:
        desktop_file = 'ALL'
        output_file = ""


    if len(output_file) > 0:
        try:
            out_file = file(output_file, 'w')
        except:
            print >> sys.stderr, "Error : Failed to open output file %s." % output_file
            print >> sys.stderr, "      : Defaulting to standard output."
            out_file = sys.stdout
    else:
        out_file = sys.stdout

    menu_path=test.locateMenu(desktop_file)

    if menu_path is not None:
        out_file.write(menu_path)
        out_file.write('\n')
