#!/usr/bin/env python3
#
# 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 Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA

# Copyright 2012,2013 Andrew Holmes <andrew.g.r.holmes@gmail.com>

"""gnome-man

"""

import re
import subprocess

from gi.repository import Gtk, Pango


# Exceptions
###############################################################################

class GnomeManError(Exception):
    """."""
    pass


# Classes
###############################################################################

class ManPage(Gtk.ScrolledWindow):
    """A widget for displaying man pages."""

    _history = []
    _ptr = -1

    title = None
    section = None

    def __init__(self, title='', section=None):
        """."""

        # Setup the widget
        Gtk.ScrolledWindow.__init__(self)

        self._textbuffer = Gtk.TextBuffer()
        textview = Gtk.TextView(buffer=self._textbuffer,
                                      editable=False,
                                      border_width=2)
        textview.modify_font(Pango.FontDescription('monospace'))

        self.add(textview)

        #
        if title and section:
            self.set_page(title, section)

    def get_page(self):
        """."""
        return self._textbuffer.get_text() # no args?

    #FIXME
    def set_page(self, title, section):
        """."""

        try:
            output = subprocess.check_output(['man', section, title])
        except subprocess.CalledProcessError as exc:
            raise GnomeManError(exc.strerror)

        self.title, self.section = title, section

        # check if we are rewriting the 'future'
        if len(self._history) > (self._ptr + 1):
            self._history = self._history[:self._ptr]

        # append the page to history and increment the pointer
        self._history.append((self.title, self.section))
        self._ptr += 1

        # prep the output and set TextBuffer
        output = re.sub('.\x08', '', output.decode('utf-8'))
        self._textbuffer.set_text(output)

    def next_page(self):
        """."""
        self.set_page(*self._history[self._ptr + 1])
        self._ptr += 1

    def prev_page(self):
        """."""
        self.set_page(*self._history[self._ptr - 1])
        self._ptr -= 1


class GnomeMan:
    """."""

    _needle = ''

    def __init__(self):
        """."""

        # init the UI
        self.builder = Gtk.Builder()
        self.builder.add_from_file('gnome-man.ui')
        self.builder.add_from_file('gnome-man-menu.ui')
        self.builder.connect_signals(self)

        # populate a ListStore with regexed apropos output
        store = Gtk.ListStore(str, str, str)
        output = subprocess.check_output(['apropos', '.']).decode('utf-8')
        pattern = re.compile('(.+)\s+\({1}.*(\d{1}).*\){1}\s+-\s+(.+)')

        for line in output.splitlines():
            match = pattern.match(line)
            store.append(match.group(1, 2, 3))

        # wrap the ListStore in a TreeModelFilter, attach it to the TreeView
        self.model = Gtk.TreeModelFilter(child_model=store)
        self.model.set_visible_func(self.visible_func)
        self.builder.get_object('SearchView').set_model(self.model)

        # add an empty page
        nb = self.builder.get_object('Notebook')
        nb.append_page(ManPage(), Gtk.Label(''))

        self.builder.get_object('Window').show_all()

    # Non-Handlers
    # -------------------------------------------------------------------------

    def get_current_tab(self):
        pass


    # Handlers
    # -------------------------------------------------------------------------

    #FIXME: do-able without a handler?
    def delete_event(self, *args):
        """."""
        Gtk.main_quit()

    #FIXME: infinite loop
    def search_changed(self, searchentry, user_data=None):
        """Called when SearchEntry emits the 'search-changed' signal."""

        treeview = self.builder.get_object('SearchView')
        treeview.get_selection().unselect_all()

        self._needle = searchentry.get_text()
        self.model.refilter() #FIXME: infinite loop happens here

    #FIXME
    def back_clicked(self, *args):
        """Called when BackButton emits the 'clicked' signal."""

        notebook = self.builder.get_object('Notebook')
        page = notebook.get_nth_page(notebook.get_current_page())

        page.prev_page()

    #FIXME
    def forward_clicked(self, *args):
        """Called when ForwardButton emits the 'clicked' signal."""

        notebook = self.builder.get_object('Notebook')
        page = notebook.get_nth_page(notebook.get_current_page())

        page.next_page()

    #FIXME
    def selection_changed(self, treeselection, user_data=None):
        """Called when TreeSelection emits the 'changed' signal."""
        
        model, treeiter = treeselection.get_selected()

        # treeiter is None if nothing is selected
        if treeiter:
            #
            notebook = self.builder.get_object('Notebook')
            page = notebook.get_nth_page(notebook.get_current_page())

            page.title = model.get_value(treeiter, 0)
            page.section = model.get_value(treeiter, 1)

            page.set_page(page.title, page.section)
            notebook.set_tab_label_text(page, page.title)

    #FIXME
    def visible_func(self, treemodel, treeiter, user_data=None):
        """."""

        title = treemodel.get_value(treeiter, 0)
        #section = treemodel.get_value(treeiter, 1)

        if (self._needle and title) and self._needle in title:
            return True

        return False


if __name__ == '__main__':

    win = GnomeMan()
    Gtk.main()
