#!/usr/bin/env python
# -*- mode: python; coding: koi8-r -*-


import string, os

import gtk, gobject
from gtk import glade

from bookmarks import *
from miscutils import *
from bookbuffer import BookBuffer
from config import charsets_list, default_encoding
#import filters

##----------------------------------------------------------------------
##----------------------------------------------------------------------

def get_file_size(fn):
    try:
        size = os.stat(fn)[6]
    except:
        return ''
    if size < 1024:
        return ' [%d]' % size
    elif size < 1024*1024:
        return ' [%.2f KB]' % (size/1024.0)
    else:
        return ' [%.2f MB]' % (size/1024.0/1024)

##----------------------------------------------------------------------

class BookmarksWindow(gobject.GObject):

    COLUMN_FOLDER      = 0
    COLUMN_FILENAME    = 1
    COLUMN_AUTHOR      = 2
    COLUMN_NAME        = 3
    COLUMN_PERCENT     = 4
    COLUMN_DESCRIPTION = 5
    COLUMN_PATH        = 6 # invisible: path to file
    COLUMN_MARK        = 7 # invisible: mark number

    hide_bookmarks = 0
    hide_cover = 0
    show_category = '' # '' == show all
    notebook_current_page = 0

    def __init__(self, bookmarks):

        gobject.GObject.__init__(self)

        glade_file = get_glade_file()
        self.wTree = glade.XML(glade_file, 'bookmarks_window')

        self.categories_list = bookmarks.categories_list
        self.bookmarks = bookmarks
        self.all_books = bookmarks.get_all_books()

        if self.show_category and not self.categories_list.has_key(self.show_category):
            self.show_category = ''

        self.create_window()

##----------------------------------------------------------------------

    def create_window(self):

        self.window = self.wTree.get_widget('bookmarks_window')

        dic = {
            'hide_cover_toggled'      : self.hide_cover_toggled_cb,
            'hide_bookmarks_toggled'  : self.hide_bookmarks_toggled_cb,
            'apply_cliced_cb'         : self.apply_cliced_cb,
            'close_clicked_cb'        : lambda x: self.window.destroy(),
            'delete_clicked_cb'       : self.delete_clicked_cb,
            'add_category_cb'         : self.add_category_cb,
            'load_cliced_cb'          : lambda x: self.emit_load_file(),
            'add_file_cb'             : self.add_file_cb,
            'notebook_switch_page_cb' : self.notebook_switch_page_cb,
            }

        self.wTree.signal_autoconnect(dic)

        self.paned = self.wTree.get_widget('paned')
        #self.show_category_menu = self.wTree.get_widget('show_category_menu')
        self.show_category_combo = self.wTree.get_widget('show_category_combo')
        self.show_category_combo.set_model(gtk.ListStore(gobject.TYPE_STRING,
                                                         gobject.TYPE_STRING))
        self.show_category_combo.connect('changed',
                                         self.show_category_combo_changed_cb)
        cell = gtk.CellRendererText()
        self.show_category_combo.pack_start(cell, True)
        self.show_category_combo.add_attribute(cell, 'text', 0)

        self.category_menu = self.wTree.get_widget('category_menu')
        self.encoding_menu = self.wTree.get_widget('encoding_menu')
        self.file_label = self.wTree.get_widget('file_label')
        self.author_entry = self.wTree.get_widget('author_entry')
        self.name_entry = self.wTree.get_widget('name_entry')
        self.cover_image = self.wTree.get_widget('cover_image')

        description_view = self.wTree.get_widget('description_view')
        self.description_buffer = BookBuffer()
        description_view.set_buffer(self.description_buffer)

        preview_view = self.wTree.get_widget('preview_view')
        self.text_buffer = BookBuffer()
        preview_view.set_buffer(self.text_buffer)

        # -- treeview --
        treeview = self.wTree.get_widget('books_treeview')
        for text, label in ((self.COLUMN_FOLDER,      'Category'   ),
                            (self.COLUMN_FILENAME,    'File'       ),
                            (self.COLUMN_AUTHOR,      'Author'     ),
                            (self.COLUMN_NAME,        'Name'       ),
                            (self.COLUMN_PERCENT,     '%'          ),
                            (self.COLUMN_DESCRIPTION, 'Description')):
            column = gtk.TreeViewColumn(label, gtk.CellRendererText(), text=text)
            column.set_resizable(True)
            column.set_sort_column_id(text)
            treeview.append_column(column)
        treeview.connect('row-activated',
                         lambda w, row, col: self.emit_load_file())

        # -- treview selection --
        selection = treeview.get_selection()
        selection.connect('changed', lambda w: self.treeview_show_selected())
        selection.set_mode(gtk.SELECTION_MULTIPLE)

        #treeview.set_search_column(self.COLUMN_FILENAME)

        self.treeview = treeview

        # -- model --
        model = gtk.TreeStore(gobject.TYPE_STRING, # category
                              gobject.TYPE_STRING, # filename
                              gobject.TYPE_STRING, # author
                              gobject.TYPE_STRING, # name
                              gobject.TYPE_INT,    # percent
                              gobject.TYPE_STRING, # description
                              gobject.TYPE_STRING, # path
                              gobject.TYPE_STRING) # mark number
        self.model = model
        treeview.set_model(model)

        # -- charsets --
        self.encoding_submenu = gtk.Menu()
        for i in charsets_list:
            item = gtk.MenuItem(i)
            self.encoding_submenu.append(item)
            item.connect_object('activate', self.encodind_submenu_activate_cb, i)
            item.show()
        self.encoding_menu.set_menu(self.encoding_submenu)


        if self.hide_bookmarks:
            self.wTree.get_widget('hide_bookmarks_button').set_active(True)
        else:
            self.update_treeview()

        self.cover_image.set_from_stock(
            gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_DND)
        if self.hide_cover:
            self.wTree.get_widget('hide_cover_button').set_active(True)
        else:
            self.update_treeview()


        # -- show category --
        self.update_show_category_menu()

        # -- categories --
        self.update_category_menu()

##----------------------------------------------------------------------

    def get_selected_list(self):

        sel = self.treeview.get_selection()

        selected_list = []
        sel.selected_foreach(lambda model, path, iter:
                             selected_list.append((model, path, iter)))

        return selected_list

##----------------------------------------------------------------------

    def hide_bookmarks_toggled_cb(self, w):
        self.hide_bookmarks = w.get_active()
        self.update_treeview()

    def hide_cover_toggled_cb(self, w):
        self.hide_cover = w.get_active()
        if self.hide_cover:
            self.cover_image.hide()
        else:
            self.cover_image.show()
            self.treeview_show_selected()

##----------------------------------------------------------------------

    def notebook_switch_page_cb(self, w, p, page):
        self.notebook_current_page = page
        self.treeview_show_selected()

##----------------------------------------------------------------------

    def encodind_submenu_activate_cb(self, enc):

        selected_list = self.get_selected_list()

        if len(selected_list) != 1:
            return

        model, path, iter = selected_list[0]
        filename = model.get_value(iter, self.COLUMN_PATH)

        if self.all_books.has_key(filename):
            b = self.all_books[filename]
            b.encoding = enc
            self.treeview_show_selected()

##----------------------------------------------------------------------

    def category_submenu_activate_cb(self, new_cat):

        selected_list = self.get_selected_list()

        for model, path, iter in selected_list:
            filename = model.get_value(iter, self.COLUMN_PATH)
            if not filename: continue
            if self.hide_bookmarks:
                self.bookmarks.move_book(filename, new_cat=new_cat)
            else:
                parent_iter = model.iter_parent(iter)
                old_cat = model.get_value(parent_iter, self.COLUMN_FOLDER)
                self.bookmarks.move_book(filename, old_cat, new_cat)
            self.emit('book-changed', filename)

        self.update_treeview()

##----------------------------------------------------------------------

    def show_category_combo_changed_cb(self, combo):
        n = combo.get_active()
        model = combo.get_model()
        cat = model[n][1]
        self.show_category = cat
        self.update_treeview()

##     def show_category_submenu_activate_cb(self, cat):
##         #BookmarksWindow.show_category = cat # '' == show all
##         self.show_category = cat
##         self.update_treeview()

##----------------------------------------------------------------------

    def treeview_show_selected(self):

        selected_list = self.get_selected_list()
        if len(selected_list) != 1:
            self.clear_view()
            return

        model, path, iter = selected_list[0]

        filename = model.get_value(iter, self.COLUMN_PATH)

        if filename:
            try:
                fd =  open(filename)
            except Exception, err:
                self.clear_view()
                error_dialog(self.window, 'Can\'t open file ',
                             '%s: %s' % (filename, str(err)))
                return
            else:
                fd.close()

        mark = model.get_value(iter, self.COLUMN_MARK)

        if self.all_books.has_key(filename):
            b = self.all_books[filename]

            self.file_label.set_text(
                fix_filename(b.filename)+get_file_size(b.filename))

##             if mark: # mark selected
##                 m = self.bookmarks.get_mark(b, int(mark))
##                 if m:
##                     if self.notebook_current_page == 0:
##                         if m.author:
##                             self.author_entry.set_text(m.author)
##                         else:
##                             self.author_entry.set_text(b.author)
##                         if m.name:
##                             self.name_entry.set_text(m.name)
##                         else:
##                             self.name_entry.set_text(b.name)
##                         self.description_buffer.set_text(m.description)
##                     elif self.notebook_current_page == 1:
##                         # update text preview
##                         self.text_buffer.set_book(b)
##                         try:
##                             self.text_buffer.preview(m.offset)
##                         except Exception, err:
##                             error_dialog(self.window,
##                                          'Can\'t open file ',
##                                          '%s: %s' % (b.filename, str(err)))
##             else: # book selected

            if self.notebook_current_page == 0:

                self.author_entry.set_text(b.author)
                self.name_entry.set_text(b.name)
                self.description_buffer.set_text(b.description)

                if not self.hide_cover:
                    self.set_cover(b)

                # update encoding menu
                n = 0
                for enc in charsets_list:
                    if b.encoding == enc:
                        break
                    n = n+1
                # bug ?
                self.encoding_menu.remove_menu()
                self.encoding_submenu.set_active(n)
                self.encoding_menu.set_menu(self.encoding_submenu)

                # update category menu
                n = 0
                categories_list = list(self.categories_list)
                categories_list.sort()
                #for cat in self.categories_list:
                for cat in categories_list:
                    if b.filename in self.categories_list[cat]:
                        break
                    n = n+1
                self.category_menu.remove_menu()
                self.category_submenu.set_active(n)
                self.category_menu.set_menu(self.category_submenu)
                #self.set_category_menu(b)

            elif self.notebook_current_page == 1:
                # update text preview
                self.text_buffer.set_book(b)
                try:
                    self.text_buffer.preview()
                except Exception, err:
                    error_dialog(self.window,
                                 'Can\'t open file ',
                                 '%s: %s' % (b.filename, str(err)))


        else: # category selected
            self.clear_view()


    def set_cover(self, book):
        self.text_buffer.set_book(book)

        try:
            cover = self.text_buffer.binary_content(cover_only=True)
            if cover:
                import tempfile
                id, ct, data = cover
                tmp_file = tempfile.mktemp()
                try:
                    open(tmp_file, 'w').write(data)
                    pb = gtk.gdk.pixbuf_new_from_file(tmp_file)
                    h1 = pb.get_height()
                    w1 = pb.get_width()
                    h2 = self.cover_image.allocation.height
                    pb = pb.scale_simple(int(w1*float(h2)/h1),
                                         h2,
                                         gtk.gdk.INTERP_BILINEAR)
                    self.cover_image.set_from_pixbuf(pb)
                finally:
                    os.remove(tmp_file)
            else:
                self.cover_image.set_from_stock(
                    gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_DND)

        except Exception, err:
            error_dialog(self.window, 'Can\'t open file ',
                         '%s: %s' % (book.filename, str(err)))


##----------------------------------------------------------------------

    def add_file_cb(self, w):

        dialog = OpenFileDialog(self.window, select_multiple=1)

        filename = dialog.filename
        files_list = dialog.files_list

        if not files_list: return

        dir = os.path.dirname(filename) + os.sep

        for f in files_list:
            path = os.path.join(dir, f)
            try:
                open(path)
            except Exception, err:
                error_dialog(self.window, 'Can\'t open file ',
                             '%s: %s' % (path, str(err)))
                continue

            if not self.all_books.has_key(path):
                try:
                    if self.show_category:
                        book = self.text_buffer.new_book(path,
                                                         self.show_category)
                    else:
                        book = self.text_buffer.new_book(path)

                except Exception, err:
                    error_dialog(self.window, 'Can\'t open file ',
                                 '%s: %s' % (path, str(err)))
                    continue
                self.bookmarks.add_new_book(book)

            self.update_treeview()
            # файлы fb2 могут добавить кат-рию
            self.update_category_menu()
            self.update_show_category_menu()
            self.emit('book-added')

##----------------------------------------------------------------------

    def add_category_cb(self, w, a=None):

        #model = self.treeview.get_model()

        cat_name = entry_dialog(self.window, 'New category', 'Category: ')

        if cat_name:

            if self.bookmarks.categories_list.has_key(cat_name):
                #Name must be unique
                error_dialog(self.window, 'Category already exist')

            else:
                if self.show_category == '' and not self.hide_bookmarks:
                    cat_iter = self.model.append(None)
                    self.model.set(cat_iter, self.COLUMN_FOLDER, cat_name)
                self.bookmarks.make_new_category(cat_name)

                self.update_show_category_menu()
                self.update_category_menu()

##----------------------------------------------------------------------

    def apply_cliced_cb(self, w):

        selected_list = self.get_selected_list()

        if len(selected_list) != 1:
            return

##         model, path, iter = selected_list[0]
##         filename = model.get_value(iter, self.COLUMN_PATH)

        self.change_apply()

##----------------------------------------------------------------------

    def change_apply(self):

        selected_list = self.get_selected_list()

        if len(selected_list) != 1:
            return
        if self.notebook_current_page != 0:
            return

        model, path, iter = selected_list[0]

        filename = model.get_value(iter, self.COLUMN_PATH)
        mark = model.get_value(iter, self.COLUMN_MARK)

        if self.all_books.has_key(filename):
            b = self.all_books[filename]

            if mark: # selected mark rows
                m = self.bookmarks.get_mark(b, int(mark))
                if m:

                    m.author = self.author_entry.get_text()
                    m.name = self.name_entry.get_text()
                    start, end = self.description_buffer.get_bounds()
                    m.description = self.description_buffer.get_text(start,
                                                                     end, True)
            else:

                b.author = self.author_entry.get_text()
                b.name = self.name_entry.get_text()
                start, end = self.description_buffer.get_bounds()
                b.description = self.description_buffer.get_text(start,
                                                                 end, True)
            self.bookmarks.save_book(b)
            self.update_treeview()

        self.clear_view()
        self.emit('book-changed', filename)

##----------------------------------------------------------------------

    def emit_load_file(self):

        selected_list = self.get_selected_list()

        if len(selected_list) == 0:
            return

        model, path, iter = selected_list[0]

        filename = model.get_value(iter, self.COLUMN_PATH)
        mark = model.get_value(iter, self.COLUMN_MARK)

        if mark:
            if self.all_books.has_key(filename):
                b = self.all_books[filename]
                m = self.bookmarks.get_mark(b, int(mark))
                if m:
                    b.current_position = m.position
                    b.current_offset = m.offset
                    self.emit('load-file', filename)

        elif filename:
            self.emit('load-file', filename)

##----------------------------------------------------------------------

    def delete_clicked_cb(self, w):

        selected_list = self.get_selected_list()

        if len(selected_list) == 0:
            return

        # remove mark
        for model, path, iter in selected_list:

            filename = model.get_value(iter, self.COLUMN_PATH)
            mark = model.get_value(iter, self.COLUMN_MARK)

            if mark: # mark
                self.bookmarks.remove_bookmark(filename, int(mark))

        # remove book
        for model, path, iter in selected_list:

            filename = model.get_value(iter, self.COLUMN_PATH)
            mark = model.get_value(iter, self.COLUMN_MARK)

            if not mark and filename: # book
                if self.hide_bookmarks:
                    cat = None
                else:
                    parent_iter = model.iter_parent(iter)
                    cat = model.get_value(parent_iter, self.COLUMN_FOLDER)
                if self.bookmarks.remove_book(filename, cat):
                    self.emit('book-removed', filename)

        # remove category
        for model, path, iter in selected_list:

            filename = model.get_value(iter, self.COLUMN_PATH)
            mark = model.get_value(iter, self.COLUMN_MARK)

            if not mark and not filename: # category

                cat = model.get_value(iter, self.COLUMN_FOLDER)

                if len(self.bookmarks.categories_list[cat]) != 0:
                    error_dialog(self.window, 'Category must be empty')
                else:
                    del self.bookmarks.categories_list[cat]
                    if self.show_category == cat:
                        # ставим первый попавшийся
                        self.show_category \
                            = list(self.bookmarks.categories_list)[0]

                    self.update_show_category_menu()
                    self.update_category_menu()

        self.clear_view()
        self.update_treeview()
        self.emit('bookmarks-changed', filename)

##----------------------------------------------------------------------

    def clear_view(self):
        self.file_label.set_text('')
        self.author_entry.set_text('')
        self.name_entry.set_text('')
        self.description_buffer.set_text('')
        self.text_buffer.set_text('')
        self.cover_image.set_from_stock(
            gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_DND)


##     def set_category_menu(self, book):
##         for mi in self.category_submenu.get_children():
##             if mi.get_child():
##                 cat = mi.get_child().get_text()
##                 if book.filename in self.categories_list[cat]:
##                     print cat

##----------------------------------------------------------------------

    def update_category_menu(self):
        # update categories menu
        self.category_submenu = gtk.Menu()
        categories_list = list(self.categories_list)
        categories_list.sort()
        if len(categories_list) == 0: # add 'Default' item
            i = 'Default'
            item = gtk.MenuItem(i)
            item.get_child().set_use_underline(False)
            self.category_submenu.append(item)
            item.connect_object('activate',
                                self.category_submenu_activate_cb, i)
            item.show()
        else:
            for i in categories_list:
                item = gtk.MenuItem(i)
                item.get_child().set_use_underline(False)
                self.category_submenu.append(item)
                item.connect_object('activate',
                                    self.category_submenu_activate_cb, i)
                item.show()

        # set category of selected book
        selected_list = self.get_selected_list()
        if len(selected_list) == 1:
            model, path, iter = selected_list[0]
            filename = model.get_value(iter, self.COLUMN_PATH)
            if self.all_books.has_key(filename):
                b = self.all_books[filename]
                n = 0
                for cat in categories_list: #self.categories_list:
                    if b.filename in self.categories_list[cat]:
                        break
                    n = n+1
                self.category_submenu.set_active(n)

        self.category_menu.set_menu(self.category_submenu)

##----------------------------------------------------------------------

    def update_show_category_menu(self):

        model = self.show_category_combo.get_model()
        model.clear()
        model.append(['All', ''])
        categories_list = list(self.categories_list)
        categories_list.sort()
        for cat in categories_list:
            model.append([cat, cat])

        if self.show_category == '':
            self.show_category_combo.set_active(0)
        else:
            n = categories_list.index(self.show_category)+1
            self.show_category_combo.set_active(n)


##     def update_show_category_menu(self):

##         self.show_category_submenu = gtk.Menu()

##         item = gtk.MenuItem('All')
##         self.show_category_submenu.append(item)
##         item.connect_object('activate',
##                             self.show_category_submenu_activate_cb, '')
##         item.show()

##         categories_list = list(self.categories_list)
##         categories_list.sort()
##         for i in categories_list: #self.categories_list:
##             item = gtk.MenuItem(i)
##             item.get_child().set_use_underline(False)
##             self.show_category_submenu.append(item)
##             item.connect_object('activate',
##                                 self.show_category_submenu_activate_cb, i)
##             item.show()

##         if self.show_category == '':
##             self.show_category_submenu.set_active(0)
##         else:
##             #n=list(self.categories_list).index(self.show_category)+1
##             n = categories_list.index(self.show_category)+1
##             self.show_category_submenu.set_active(n)

##         self.show_category_menu.set_menu(self.show_category_submenu)

##----------------------------------------------------------------------

    def update_treeview(self):

        model = self.model
        model.clear()

        if not self.hide_bookmarks:

            for cat in self.categories_list: # categories
                if self.show_category != '' and cat != self.show_category:
                    continue

                iter = model.append(None)
                model.set(iter, self.COLUMN_FOLDER, cat)

                for filename in self.categories_list[cat]: # books
                    book = self.all_books[filename]
                    filename = fix_filename(os.path.basename(filename))
                    book_iter = model.append(iter)

                    desc = fix_description(book.description, 100)
                    model.set(book_iter,
                              self.COLUMN_FILENAME, filename,
                              self.COLUMN_NAME, book.name,
                              self.COLUMN_AUTHOR, book.author,
                              self.COLUMN_PERCENT, int(book.current_position*100),
                              self.COLUMN_DESCRIPTION, desc,
                              self.COLUMN_PATH, book.filename)

##                     for mark in book.marks_list: # marks
##                         mark_iter = model.append(book_iter)
##                         desc = fix_description(mark.description, 100)
##                         model.set(mark_iter,
##                                   self.COLUMN_NAME, mark.name,
##                                   self.COLUMN_AUTHOR, mark.author,
##                                   self.COLUMN_PERCENT, int(mark.position*100),
##                                   self.COLUMN_DESCRIPTION, desc,
##                                   self.COLUMN_PATH, book.filename,
##                                   self.COLUMN_MARK, mark.number)


        else: # self.hide_bookmarks == 1

            for filename in self.all_books:
                book = self.all_books[filename]
                if self.show_category != '' \
                   and not self.show_category in book.categories:
                    continue

                filename = fix_filename(os.path.basename(filename))
                book_iter = model.append(None)

                desc = fix_description(book.description, 100)
                model.set(book_iter,
                          self.COLUMN_FOLDER, ' '.join(book.categories),
                          self.COLUMN_FILENAME, filename,
                          self.COLUMN_NAME, book.name,
                          self.COLUMN_AUTHOR, book.author,
                          self.COLUMN_PERCENT, int(book.current_position*100),
                          self.COLUMN_DESCRIPTION, desc,
                          self.COLUMN_PATH, book.filename)


        #self.treeview.set_model(model)

        self.treeview.expand_all()

##----------------------------------------------------------------------
##----------------------------------------------------------------------

# signals
# load file
gobject.signal_new('load-file', BookmarksWindow,
                   gobject.SIGNAL_RUN_LAST,
                   gobject.TYPE_NONE,
                   (gobject.TYPE_STRING,))
# book removed
gobject.signal_new('book-removed', BookmarksWindow,
                   gobject.SIGNAL_RUN_LAST,
                   gobject.TYPE_NONE,
                   (gobject.TYPE_STRING,))
# добавления
gobject.signal_new('book-added', BookmarksWindow,
                   gobject.SIGNAL_RUN_LAST,
                   gobject.TYPE_NONE,
                   ())
# book changed (name, author, desc, category, etc)
gobject.signal_new('book-changed', BookmarksWindow,
                   gobject.SIGNAL_RUN_LAST,
                   gobject.TYPE_NONE,
                   (gobject.TYPE_STRING,))

gobject.signal_new('bookmarks-changed', BookmarksWindow,
                   gobject.SIGNAL_RUN_LAST,
                   gobject.TYPE_NONE,
                   (gobject.TYPE_STRING,))

##----------------------------------------------------------------------

if(__name__=='__main__'):

    bookmarks = Bookmarks()
    bw = BookmarksWindow(bookmarks)
    bw.set_default_size(600, 600)
    bw.paned.set_position(300)
    bw.connect('destroy', lambda w: gtk.main_quit())
    bw.show_all()
    gtk.main()


