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

"""
#    Copyright (C) 2012  Pawel Stolowski <stolowski@gmail.com>
#                  2012  Mark Tully <markjtully@gmail.com>

#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.

#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.

#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import sys
import re
import os.path
import gettext

# Unity imports
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Gtk
from gi.repository import Unity

_ = gettext.gettext

BUS_NAME = "net.launchpad.scope.help.manpages"

EVERYTHING = 0
OFFICIAL = 1
COMMUNITY = 2
TECHNICAL = 3
TAGS = 4


class Daemon (object):
    """ Manpages Daemon searches manpages for results matching query
    """
    def __init__(self):
        """ Sets up the manpages scope
        """
        self.scope = Unity.Scope.new("/net/launchpad/scope/help/manpages")

        # Listen for changes and requests
        self.scope.props.search_in_global = False
        self.scope.connect("search-changed", self.on_search_changed)
        self.scope.connect("filters-changed", self.on_filters_changed)
        self.scope.connect("notify::active", self.on_lens_active)
        self.scope.export()

    def on_filters_changed(self, *_):
        """ Called when a filter is clicked.  Queue's a new search
        """
        self.scope.queue_search_changed(Unity.SearchType.DEFAULT)

    def on_lens_active(self, *_):
        """ Called when the lens is activated.  Queue's a new search
        """
        if self.scope.props.active:
            self.scope.queue_search_changed(Unity.SearchType.DEFAULT)

    def on_search_changed(self, scope, search=None, search_type=0, cancellable=None):
        """ Called when the search is changed.  Gets the search string and search
        type and passes it to update_results_model()
        Args:
          scope: a scope object
          search: the search string
          search_type: the search type (global or local)
          cancellable: cancellable object
        """
        if hasattr(search, "props"):
            search_string = search.props.search_string
        else:
            search_string = ""

        if search_type == Unity.SearchType.DEFAULT:
            results = scope.props.results_model
        else:
            results = scope.props.global_results_model

        print "Search changed to: '%s'" % search_string
        self.update_results_model(search_string, results)
        if hasattr(search, "finished"):
            search.finished()

    def update_results_model(self, search, model, _global=False):
        """ Takes the search string and runs apropos <search string>
        Parses its results and adds them to the model
        Args:
          search: the search string
          model: the model object that results are added to
        """
        if _global and search:
            if search != "au":  # for the "au" search, we *do* show a result
                model.clear()   # in the global dash. Why not? ;-)
                return
        model.clear()

        scope_active = False
        e = self.scope.get_filter("sources")
        if not e.props.filtering:
            scope_active = True
        for source in e.options:
            if source.props.id == "manpages":
                if source.props.active:
                    scope_active = True

        if scope_active:
            import subprocess
            icon_hint = Gio.ThemedIcon.new("text-x-generic").to_string()
            result = []
            if len(search) > 2:   # don't run apropos for strings shorter than 3 chars
                apropos = subprocess.Popen(["apropos", search], stdout=subprocess.PIPE)
                os.waitpid(apropos.pid, 0)
                out = apropos.communicate()[0]
                for line in out.split("\n"):
                    regex_apropos = re.compile('^(.+?)\s+\((\d+)\)\s+-\s(.+?)$')
                    m = regex_apropos.match(line)
                    if m:
                        result.append((m.group(1), m.group(2), m.group(3)))
            for res in result:
                uri = 'man:' + res[0] + '(' + res[1] + ')'
                icon_theme = Gtk.IconTheme()
                icon_info = icon_theme.lookup_icon(res[0], 128, 0)
                if icon_info:
                    icon_hint = Gio.ThemedIcon.new(res[0]).to_string()
                model.append(uri, icon_hint, TECHNICAL, 'x-scheme-handler/man', res[0], res[2], uri)

if __name__ == "__main__":
    session_bus_connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
    session_bus = Gio.DBusProxy.new_sync(session_bus_connection, 0, None,
        'org.freedesktop.DBus', '/org/freedesktop/DBus',
        'org.freedesktop.DBus', None)
    result = session_bus.call_sync('RequestName',
        GLib.Variant("(su)", (BUS_NAME, 0x4)), 0, -1, None)
    result = result.unpack()[0]
    # We could try to do some automated rescue when this happens:
    if result != 1:
        print >> sys.stderr, "Failed to own name %s. Bailing out." % BUS_NAME
        print >> sys.stderr, "Do you have another instance running?"
        raise SystemExit(1)
    daemon = Daemon()
    print "entering the main loop"
    GObject.MainLoop().run()
