#!/usr/bin/env python

# Copyright (C) 2005 Maciej Katafiasz
#
# Author: Maciej Katafiasz
#
# This file is part of Purrr programme.
#
# Purrr 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 2, or (at your
# option) any later version.
#
# Purrr 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 Purrr; see the file COPYING. If not, write to the
# Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.

import sys, os, re
import optparse
import urllib

from renamer import FileRenamer
from matcher import FileMatcher
import constants

try:
    import pygtk
    pygtk.require('2.0')
    import gtk, gtk.glade, gnome, gnome.ui
    import gobject
except:
    print "**ERROR**: Non pode importar PyGTK"
    print "Precisas PyGTK 2.0 e Libglade para usar Purrr"
    print "Por favor, instalao antes de continuar"
    sys.exit(os.EX_CONFIG)


usage = "%prog [options] files..."
parser = optparse.OptionParser(usage = usage, version = constants.version)
parser.set_defaults(as_extension = False, input_files = None,
                    initial_warning = False, as_uris = False)
parser.add_option("-x", "--extension", action="store_true", dest="as_extension",
                  help="usar interface reducida, apropiada para administradores de ficheiros")
parser.add_option("-s", "--standalone", action="store_false", dest="as_extension",
                  help="usar interface completa")
parser.add_option("-u", "--uri-list", action="store_true", dest="as_uris",
                  help="tratamento de URIs, no lugar de ficheiros locais")
parser.add_option("--show-initial-warning", action="store_true", dest="initial_warning")

options, input_files = parser.parse_args()

if not input_files:
    options.as_extension = False

gnome.program_init(constants.name, constants.version)


class Info:
    pass

class Callbacks:
    def __init__(self):
        self.file_selection_work_pending = False
        self.non_local_warning_message_visible = False
        self.selection_info_visible = False
        if options.initial_warning:
            self.show_non_local_warning()
        self.complete_can_close = False
        self.dirs = {}
        self.file_chooser = None
    
    def file_selection_changed_cb(self, sel):
        self.file_selection_work_pending = True
        gobject.idle_add(cb.do_file_selection_changed_cb, sel)

    def do_file_selection_changed_cb(self, sel):
        if self.file_selection_work_pending:
            model = filtered.get_model()
            for f in filtered:
                p = f.path
                p = filtered.convert_path_to_child_path(p)
                model[p][1] = model[p][0]
            apply_template(app.get_widget("template_entry").get_text())
            self.file_selection_work_pending = False
        self.activity_occured()
        return False

    def show_non_local_warning(self):
        self.non_local_warning_message_visible = True
        app.get_widget("non_local_warning_message").show()

    def hide_non_local_warning(self):
        if self.non_local_warning_message_visible:
            self.non_local_warning_message_visible = False
            app.get_widget("non_local_warning_message").hide()

    def show_clash_warning(self):
        self.clash_warning_visible = True
        app.get_widget("clash_warning_message").show()

    def hide_clash_warning(self):
        self.clash_warning_visible = False
        app.get_widget("clash_warning_message").hide()

    def show_selection_info(self):
        self.selection_info_visible = True
        app.get_widget("selection_info_message").show()

    def hide_selection_info(self):
        self.selection_info_visible = False
        app.get_widget("selection_info_message").hide()

    def activity_occured(self):
        self.hide_non_local_warning()
        if self.complete_can_close:
            self.complete_can_close = False
            app.get_widget("quit_button").set_label(gtk.STOCK_CANCEL)

    def insert_uris_in_list(self, uris):
        # Parse URIs. If URIs isn't file:///, drop it on the floor
        for uri in uris:
            uri = uri.strip()
            if uri[0] == "#": continue
            if uri.find("file:///") != 0:
                self.show_non_local_warning()
                continue
            uri = urllib.url2pathname(uri[7:])
            insert_file_in_list(uri)

    def files_drop_cb(self, widget, context, x, y, selection, targetType, time):
        self.activity_occured()
        lst = selection.data.splitlines()
        self.insert_uris_in_list(lst)
        apply_template(app.get_widget("template_entry").get_text())

        # TreeModelFilter doesn't support drag and drop, silence it
        widget.stop_emission("drag-data-received")


def drag_drop_cb(widget, context, x, y, timestamp):
    # TreeModelFilter doesn't support drag and drop, silence it
    widget.stop_emission("drag-drop")

def quit_cb(*args):
    gtk.main_quit()

def apply_template(template):
    info = Info()
    renamer.init(template)
    model = filtered.get_model()
    filtered.refilter()
    results = {}
    app.get_widget("rename_button").set_sensitive(True)
    app.get_widget("rename_menu").set_sensitive(True)
    cb.hide_clash_warning()
    for r in active_rows():
        p = filtered.get_path(r.iter)
        p = filtered.convert_path_to_child_path(p)
        name = model[p][0]
        info.old_name = name
        idx = name.rfind(".")
        if idx == -1: idx = len(name)
        info.extension = name[idx + 1:]
        info.base_name = name[:idx]
        new_name = renamer.next_name(info)
        try:
            r = results[new_name]
            app.get_widget("rename_button").set_sensitive(False)
            app.get_widget("rename_menu").set_sensitive(False)
            cb.show_clash_warning()
        except KeyError:
            results[new_name] = None
        model[p][1] = new_name
    if selection_active():
        cb.show_selection_info()
    else:
        cb.hide_selection_info()


def template_changed_cb(entry):
    apply_template(entry.get_text())
    cb.activity_occured()

def filter_changed_cb(renderer, path, model):
    row = model[path]
    val = row[0]
    name = row[3]
    matcher.enable_filter(name, not val)
    row[0] = not val
    apply_template(app.get_widget("template_entry").get_text())
    cb.activity_occured()

def files_filter(model, iter, *user_data):
    val = model[iter][0]
    return bool(val and (not matcher.match(val)))

def add_files_cb(button):
    if not cb.file_chooser:
        cb.file_chooser = gtk.FileChooserDialog(title = "Selecciona ficheiros a renomear",
                                                parent = main_window,
                                                buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                                           gtk.STOCK_OPEN, gtk.RESPONSE_OK))
        cb.file_chooser.set_select_multiple(True)
        
    resp = cb.file_chooser.run()
    cb.file_chooser.hide()
    if resp == gtk.RESPONSE_OK:
        cb.insert_uris_in_list(cb.file_chooser.get_uris())
        apply_template(app.get_widget("template_entry").get_text())

def remove_files_cb(button):
    rows = []
    for r in active_rows():
        rows.append(gtk.TreeRowReference(
            filtered, filtered.convert_path_to_child_path(r.path)))
    for r in rows:
        i = files.get_iter(r.get_path())
        files.remove(i)
    apply_template(app.get_widget("template_entry").get_text())

def insert_file_in_list(f):
    try:
        os.stat(f)
        name = os.path.basename(f)
        dirname = os.path.dirname(f) or os.getcwd()
        files.insert_before(None, None, (name, None, dirname,
                                         True, None, gtk.STOCK_GO_FORWARD))
    except OSError:
        pass

def selection_active():
    return len(file_list.get_selection().get_selected_rows()[1]) > 0

def active_rows():
    sel = file_list.get_selection().get_selected_rows()
    model = sel[0]
    if len(sel[1]) > 0:
        for path in sel[1]:
            yield model[path]

    elif model:
        for row in model:
            yield row
    else:
        # Empty file list, do nothing
        pass

def rename_cb(*args):
    for r in active_rows():
        try:
            os.rename(r[2] + "/" + r[0], r[2] + "/" + r[1])
        except OSError:
            continue
        print r[2] + "/" + r[0] + " -> " + r[2] + "/" + r[1]
        p = r.path
        files[filtered.convert_path_to_child_path(p)][0] = r[1]
    cb.activity_occured()
    cb.complete_can_close = True
    app.get_widget("quit_button").set_label(gtk.STOCK_CLOSE)

    
def populate_file_list(files_list):
    if options.as_uris:
        cb.insert_uris_in_list(input_files)
    else:
        for i in files_list:
            insert_file_in_list(i)
    apply_template(app.get_widget("template_entry").get_text())

def about_cb(*args):
    about_box = gnome.ui.About(constants.name,
                               constants.version,
                               constants.copyright,
                               constants.comments,
                               constants.authors,
                               constants.documenters,
                               constants.translators,
                               gtk.gdk.pixbuf_new_from_file(constants.logo))
    about_box.show()
    


app = gtk.glade.XML(constants.glade_file)
app.signal_autoconnect(locals())

renamer = FileRenamer("")
matcher = FileMatcher()
cb = Callbacks()

files = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
                      gobject.TYPE_STRING, gobject.TYPE_BOOLEAN,
                      gobject.TYPE_STRING, gobject.TYPE_STRING)
filtered = files.filter_new()
filtered.set_visible_func(files_filter)
filters = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING,
                        gobject.TYPE_STRING, gobject.TYPE_STRING)

file_list = app.get_widget("file_list")
file_list.set_model(filtered)
file_list.set_reorderable(True)
sel = file_list.get_selection()
sel.set_mode(gtk.SELECTION_MULTIPLE)

file_list.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("text/uri-list", 0, 0)], gtk.gdk.ACTION_COPY)
file_list.connect("drag-data-received", cb.files_drop_cb)

renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Nome antigo", renderer, text = 0, sensitive = 3)
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
column.set_resizable(True)
file_list.append_column(column)

renderer = gtk.CellRendererPixbuf()
renderer.set_property("stock_id", gtk.STOCK_GO_FORWARD)
column = gtk.TreeViewColumn("  ", renderer)
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
file_list.append_column(column)

renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Nome Novo", renderer, text = 1)
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
column.set_resizable(True)
file_list.append_column(column)

filter_list = app.get_widget("filter_list")
filter_list.set_model(filters)

renderer = gtk.CellRendererToggle()
renderer.connect("toggled", filter_changed_cb, filters)
column = gtk.TreeViewColumn("Enabled", renderer, active = 0)
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
column.set_resizable(True)
filter_list.append_column(column)

renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Name", renderer, text = 1)
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
filter_list.append_column(column)

renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Regex", renderer, text = 2)
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
column.set_resizable(True)
filter_list.append_column(column)

for f in matcher.filters:
    filters.insert_before(None, (f["enabled"],
                                 f["description"],
                                 f["filter"],
                                 f["name"]))

populate_file_list(input_files)
    
sel = file_list.get_selection()
sel.connect("changed", cb.file_selection_changed_cb)

main_window = app.get_widget("main_window")

main_window.show()

# Workaround Glade bug
fp = app.get_widget("files_paned")
fp.set_position(-1)

if options.as_extension:
    arb = app.get_widget("add_remove_buttons")
    mb = app.get_widget("menu_bar")
    arb.hide()
    mb.hide()

gtk.main()
