#!/usr/bin/env python

#
# superopen.py
#
# Copyright (C) 2011 Christian Hergert <chris@dronelabs.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/>.
#

from gettext import gettext
from gi.repository import Gdk
from gi.repository import Gio
from gi.repository import GnomeBuilder
from gi.repository import GObject
from gi.repository import Gtk
import os

import relevance

SUFFIXES = {
    '.c': '.h',
    '.h': '.c',

    '.cc': '.hh',
    '.hh': '.cc',

    '.cxx': '.hxx',
    '.hxx': '.cxx',

    '.cpp': '.hpp',
    '.hpp': '.cpp',
}

def split(word):
    delimiters = ['.', '-']
    for c in delimiters:
        word = ' '.join(word.split(c))
    return word

class SuperOpen(GObject.Object, GnomeBuilder.WindowPlugin):
    __gtype_name__ = 'GbSuperOpen'

    dialog = GObject.property(type=Gtk.Widget)
    window = GObject.property(type=GnomeBuilder.Window)

    def do_activate(self):
        self.dialog = Gtk.Window()
        self.dialog.props.default_height = 200
        self.dialog.props.default_width = 400
        self.dialog.props.skip_pager_hint = True
        self.dialog.props.title = _("Builder")
        self.dialog.props.transient_for = self.props.window
        self.dialog.props.window_position = Gtk.WindowPosition.CENTER_ON_PARENT
        self.dialog.connect('delete-event', lambda a,b: Gtk.Widget.hide_on_delete(a))

        accel_group = Gtk.AccelGroup()
        accel_group.connect(Gdk.KEY_Escape, 0, 0, lambda *_: self.dialog.hide())
        self.dialog.add_accel_group(accel_group)

        vbox = Gtk.VBox()
        vbox.props.visible = True
        self.dialog.add(vbox)

        entry = Gtk.Entry()
        entry.connect('changed', self.entryChanged)
        entry.connect('activate', self.entryActivate)
        entry.props.has_frame = False
        entry.props.visible = True
        vbox.pack_start(entry, False, True, 0)
        self.entry = entry

        scroller = Gtk.ScrolledWindow()
        scroller.props.visible = True
        vbox.add(scroller)
        self.scroller = scroller

        treeview = Gtk.TreeView()
        treeview.props.headers_visible = False
        treeview.props.visible = True
        scroller.add(treeview)
        self.treeview = treeview

        column = Gtk.TreeViewColumn()
        treeview.append_column(column)

        cell = Gtk.CellRendererPixbuf()
        column.pack_start(cell, False)
        column.add_attribute(cell, 'icon-name', 4)

        cell = Gtk.CellRendererText(size_points=9.0)
        column.pack_start(cell, True)
        column.add_attribute(cell, 'markup', 1)

        #cell = Gtk.CellRendererProgress()
        #cell.props.width = 100
        #column.pack_start(cell, False)
        #column.add_attribute(cell, 'value', 3)

        # fulltext, highlight, split words, relevance
        self.model = Gtk.ListStore(str, str, str, float, str, GnomeBuilder.ProjectFile)

        def getfiles(project):
            files = project.props.files
            for i in xrange(files.get_count()):
                f = files.get_item(i)
                yield f

            targets = project.props.targets
            for i in xrange(targets.get_count()):
                target = targets.get_item(i)
                files = target.props.files
                for j in xrange(files.get_count()):
                    f = files.get_item(j)
                    yield f

        for f in getfiles(self.window.props.project):
            self.model.append(row=(f.props.path,
                                   f.props.path,
                                   split(f.props.path),
                                   100.0,
                                   f.props.icon_name,
                                   f))

        # TODO: handle file added/removed

        def visibleFunc(model, iter, data):
            score, = model.get(iter, 3)
            return score > 0.0
        filter = self.model.filter_new(None)
        filter.set_visible_func(visibleFunc, None)

        sorted = Gtk.TreeModelSort(model=filter)
        sorted.set_sort_column_id(3, Gtk.SortType.DESCENDING)
        treeview.set_model(sorted)

        treeview.connect('row-activated', self.rowActivated)

        self.dialog.set_focus(entry)

        actions = Gtk.ActionGroup(name='SuperOpenActions')

        uiData = """
                 <ui>
                   <menubar name="menubar">
                     <menu action="file">
                       <placeholder name="file-open-actions">
                        <menuitem action="superopen"/>
                       </placeholder>
                     </menu>
                     <menu action="view">
                       <menuitem action="switchtoheader"/>
                     </menu>
                   </menubar>
                 </ui>
                 """

        action = Gtk.Action("superopen", _("Quick Open"), "The Super Awesome Open Fun Time", Gtk.STOCK_OPEN)
        action.props.visible = True
        Gtk.AccelMap.add_entry("<SuperOpen>/superopen", Gdk.KEY_o, Gdk.ModifierType.SHIFT_MASK | Gdk.ModifierType.CONTROL_MASK)
        action.set_accel_path("<SuperOpen>/superopen")
        action.connect('activate', lambda *_: self.show_super_open())
        actions.add_action(action)

        action = Gtk.Action("switchtoheader", _("Switch to Header"), None, None)
        action.props.visible = True
        Gtk.AccelMap.add_entry("<SuperOpen>/switchtoheader", Gdk.KEY_F4, 0)
        action.set_accel_path("<SuperOpen>/switchtoheader")
        action.connect('activate', lambda *_: self.switch_src_hdr())
        actions.add_action(action)
        self.switch_action = action

        ui = self.props.window.get_ui_manager()
        ui.insert_action_group(actions, 0)
        ui.add_ui_from_string(uiData)

    def do_update_state(self):
        self.switch_action.props.sensitive = False

        def is_src_hdr(view):
            f = view.props.file
            p = f.get_path()
            for s in SUFFIXES:
                if p.endswith(s):
                    return True
            return False

        # Add way to get view.
        view = self.props.window.props.view
        if type(view) == GnomeBuilder.ViewSource:
            self.switch_action.props.sensitive = is_src_hdr(view)

    def switch_src_hdr(self):
        w = self.props.window
        v = w.props.view
        f = v.props.file
        p = f.get_path()
        for s in SUFFIXES:
            if p.endswith(s):
                p = p[:-len(s)] + SUFFIXES[s]

                # Try to see if this view exists.
                for view in self.props.window.get_layout().get_views():
                    if isinstance(view, GnomeBuilder.ViewSource):
                        op = view.props.path
                        if op == p:
                            parent = view.get_parent().get_parent()
                            if isinstance(parent, GnomeBuilder.ViewStack):
                                parent.focus_view(view)
                                view.grab_focus()
                                return

                g = Gio.file_new_for_path(p)
                v = GnomeBuilder.ViewSource(file=g, visible=True)
                w.add_view(v)
                return

    def show_super_open(self):
        self.entry.set_text('')
        iter = self.treeview.get_model().get_iter_first()
        if iter:
            self.treeview.get_selection().select_iter(iter)
        self.scroller.get_vscrollbar().props.adjustment.props.value = 0.0
        self.dialog.props.window_position = Gtk.WindowPosition.CENTER_ON_PARENT
        self.dialog.present()
        self.entry.grab_focus()

    def entryChanged(self, entry, *args):
        text = split(entry.get_text())
        format = '<b><u>%s</u></b>'
        for row in self.model:
            row[1] = relevance.formatCommonSubstrings(row[0], text, format)
            row[3] = float(relevance.score(row[2], text)) * 100.0
        iter = self.treeview.get_model().get_iter_first()
        if iter:
            self.treeview.get_selection().select_iter(iter)

    def rowActivated(self, treeview, path, *args):
        model = treeview.get_model()
        iter = model.get_iter(path)
        if iter:
            item, icon = model.get(iter, 5, 4)
            gfile = item.props.file
            view = self.props.window.get_view_for_file(gfile)
            if view:
                view.get_parent().get_parent().focus_view(view)
            else:
                view = GnomeBuilder.ViewSource(file=gfile, icon_name=icon, visible=True)
                self.window.add_view(view)
            self.dialog.hide()
            view.grab_focus()

    def entryActivate(self, entry):
        model, iter = self.treeview.get_selection().get_selected()
        if iter:
            path = model.get_path(iter)
            column = self.treeview.get_columns()[0]
            self.treeview.row_activated(path, column)
