"""
Pympc is a client for Music Player Daemon.
Copyright (C) 2004  Magnus Bjernstad <bjernstad@gmail.com>

This file is part of Pympc.

Pympc 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 of the License, or
(at your option) any later version.

Pympc 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 Pympc; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""
import gtk, pympc, base_dialog, mpdclient, re, find

COLS = {'File': 0,
        'Artist': 1,
        'Album': 2,
        'Title': 3,
        'Genre': 4,
        'Date': 5,
        'Track': 6,
        'Composer': 7,
        'Performer': 8,
        'Comment': 9,
        'Id': 10 }

class Playlist:
    def __init__(self, app, conf, gui_file):
        self.app = app
        format = conf['playlist.format'] 
        self.gui_popup = gtk.glade.XML(gui_file, 'pl_popup')
        self.gui_popup.signal_autoconnect(self)

        self.gui_main = gtk.glade.XML(gui_file, 'pl_root')
        self.gui_main.signal_autoconnect(self)
        self.w = self.gui_main.get_widget
        self.contents = self.w('pl_root')

        self.tree = gtk.ListStore(str,str,str,str,str,str,str,str,str,str,str,bool)
        self.cell = gtk.CellRendererText()
        targets = [('STRING', 0, 0)]

        self.filter = self.tree.filter_new()
        self.w('view').set_model(self.filter)
        self.w('view').get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        self.w('view').enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
                targets, gtk.gdk.ACTION_MOVE)
        self.w('view').set_enable_search(False)
        self.w('view').enable_model_drag_dest(targets, gtk.gdk.ACTION_MOVE)
        self.cell.set_property('background-set', False)
        self.cell.set_property('background', conf['playlist.active_color'])

        if conf['playlist.show_filter']:
            flt = self.Filter(self.filter)
            self.contents.pack_start(flt.get_contents(), expand=False)

    def find_same(self, type):
        (model, pathlist) = \
                self.w('view').get_selection().get_selected_rows()
        string = model[pathlist[0]][COLS[type]]
        index = find.METADATA.index(type)+1 # add one for the always present 'file' col
        self.app.find.gui_main.get_widget('combo').set_active(index)
        self.app.find.gui_main.get_widget('entry').set_text(string)
        #self.app.find.find(string, type)
        self.app.set_current_page('find')
        return

    def update(self):
        if not self.app.c:
            return
        pl = self.tree
        curr_len = len(pl)
        to_append = []
        try:
            for s in self.app.c.plchanges(self.app.prev_status.playlist):
                md = s.metadata
                # Modify existing songs
                if md['Title'] == '': md['Title'] = md['file']
                if int(md['Pos']) < curr_len:
                    iter = pl.get_iter_from_string(md['Pos'])
                    pl.set(iter,
                        0, md['file'],    1, md['Artist'],   2, md['Album'],
                        3, md['Title'],   4, md['Genre'],    5, md['Date'],
                        6, md['Track'],   7, md['Composer'], 8, md['Performer'],
                        9, md['Comment'], 10, md['Id'],      11, False)
                else:
                    # Add songs
                    to_append.append(s)
            self.append_songs(to_append)
            # Remove songs
            new_length = self.app.c.status().playlistLength
            curr_len = len(pl)
            for s in range(curr_len - new_length):
                iter = pl.get_iter_from_string(str(len(pl)-1))
                pl.remove(iter)
        except mpdclient.MpdError:
            self.app.c = False

    def append_songs(self, songs):
        append = self.tree.append
        for s in songs:
            md = s.metadata
            data = [md['file'],    md['Artist'],   md['Album'],
                    md['Title'],   md['Genre'],    md['Date'],
                    md['Track'],   md['Composer'], md['Performer'],
                    md['Comment'], int(md['Id'])] 
            append(data+ [False])

    def center(self):
        try:
            if self.app.status.stateStr() in ('pause', 'play') and len(self.filter) > 0:
                index = self.filter.convert_child_path_to_path(self.app.status.song)
                if index != None and index >= 0:
                    self.w('view').scroll_to_cell(index, use_align=True, row_align=0.5)
                    self.w('view').queue_draw()
        except:
            pass

    def delete(self, ids):
        if not self.app.c:
            return
        if len(ids) > 0:
            try:
                self.app.c.deleteid(ids)
            except mpdclient.MpdError:
                self.app.c = False

    def save(self, filename):
        if not self.app.c or filename == '':
            return
        try:
            self.app.c.save(filename)
            self.app.lib.reload()
        except mpdclient.MpdError:
            self.app.c = False

    def jump_to_song(self, songid):
        if not self.app.c:
            return
        try:
            self.app.c.playid(songid)
        except mpdclient.MpdError:
            self.app.c = False

    def clear(self):
        if not self.app.c:
            return
        try:
            self.app.c.clear()
        except mpdclient.MpdError:
            self.app.c = False

    def shuffle(self):
        if not self.app.c:
            return
        try:
            self.app.c.shuffle()
        except mpdclient.MpdError:
            self.app.c = False

    def add_stream(self, stream):
        if not self.app.c or stream == '':
            return
        try:
            self.app.c.add([stream])
        except mpdclient.MpdError:
            self.app.c = False

    def popup_menu(self, button, time):
        w = self.gui_popup.get_widget
        (model, pathlist) = \
                self.w('view').get_selection().get_selected_rows()
        sel = len(pathlist) > 0
        have_find = pympc.TABS.get('find', False)

        w('delete').set_sensitive(sel)
        w('same_artist').set_sensitive(sel and have_find)
        w('same_album').set_sensitive(len(pathlist) > 0 and have_find)
        w('crop').set_sensitive(len(pathlist) > 0)
        w('pl_popup').popup(None, None, None, button, time)
    def on_popup_menu(self, widget): self.popup_menu(0, 0)
    def on_add_stream(self, action):
        AddStreamDialog(self.app, self.app.w('window'))
    def on_save(self, action):
        SavePlaylistDialog(self.app, self.app.w('window'))

    def on_delete(self, action):
        (model, pathlist) = \
                self.w('view').get_selection().get_selected_rows()
        if len(pathlist) > 0:
            self.delete( [int(self.filter[x][COLS['Id']]) for x in pathlist] )
            self.w('view').set_cursor(pathlist[0])
    def on_clear(self, action): self.clear()
    def on_shuffle(self, action): self.shuffle()
    def on_crossfade(self, action):
        CrossfadeDialog(self.app, self.app.w('window'))
    def on_same_artist(self, action): self.find_same('Artist')
    def on_same_album(self, action): self.find_same('Album')
    def on_change_song(self, treeview, path, view_column):
        self.jump_to_song(int(self.filter[path][COLS['Id']]))
    def on_button_press(self, widget, event):
        if event.button == 3:
            return self.w('view').get_selection().count_selected_rows() > 1
    def on_button_release(self, widget, event):
        if event.button == 3:
            self.popup_menu(event.button, event.time)
    def on_crop(self, action):
        (model, pathlist) = self.w('view').get_selection().get_selected_rows()
        sel_pos = [x[0] for x in pathlist]
        sel_ids = [int(model[x][COLS['Id']]) for x in sel_pos]
        all_ids = [int(x[COLS['Id']]) for x in model]
        to_del = filter(lambda x: x not in sel_ids, all_ids)
        self.delete(to_del)

    def on_drag_drop(self, widget, drag_context, x, y, timestamp):
        widget.emit_stop_by_name('drag-drop')
        model, sel_rows = drag_context.get_source_widget().get_selection().get_selected_rows()
        sel_ids = [int(model[x][COLS['Id']]) for x in sel_rows]
        sel_ids.reverse()
        drop_info = widget.get_dest_row_at_pos(0, y)
        if drop_info == None:
            return
        new_pos, pos_type = drop_info
        new_pos = new_pos[0]
        if not self.app.c:
            return
        for id in sel_ids:
            try:
                self.app.c.moveid(id, new_pos)
            except mpdclient.MpdError:
                self.app.c = False
                return
        widget.set_cursor(new_pos)

    def set_columns(self, cols):
        """
        Chooses the columns (and their size in pixels) to be visible in the
        playlist.
        Example:
            set_columns([('album',200), ('artist',150)])
        """
        for (name, width) in cols:
            name = name.capitalize()
            new = gtk.TreeViewColumn(name,
                    self.cell,
                    text=COLS[name],
                    background_set=len(COLS))
            new.set_resizable(True)
            new.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
            new.set_fixed_width(width)
            self.w('view').append_column(new)
        self.w('view').set_headers_visible(len(cols) > 1)

    class Filter:
        def __init__(self, model_filter):
            self.box = gtk.HBox()
            self.entry = gtk.Entry()
            combo = gtk.combo_box_new_text()
            combo.append_text('Filename')
            for x in find.METADATA:
                combo.append_text(x)
            combo.set_active(0)
            model_filter.set_visible_func(self.visible_func, combo)
            self.entry.connect('changed', self.on_new_filter, model_filter)
            combo.connect('changed', self.on_new_type, model_filter)
            self.box.pack_start(gtk.Label('Filter:'), expand=False)
            self.box.pack_start(self.entry)
            self.box.pack_start(combo, expand=False)
            #tooltips = gtk.Tooltips() # hmm, this doesn't show
            #tooltips.set_tip(self.entry, 'Show only songs with metadata of the specified type matching the expression')

        def get_contents(self):
            return self.box
        def on_new_type(self, combobox, model_filter):
            model_filter.refilter()
        def visible_func(self, model, iter, combo):
            try:
                return bool(re.search(self.entry.get_text().lower(), model.get_value(iter, combo.get_active()).lower()))
            except:
                pass
        def on_new_filter(self, widget, model_filter):
            model_filter.refilter()

class AddStreamDialog(base_dialog.SimpleInputDialog):
    def __init__(self, app, parent):
        base_dialog.SimpleInputDialog.__init__(self, app, parent, 'Add HTTP stream', 'Location')
        entry = gtk.Entry()
        entry.set_width_chars(50)
        self.vbox.pack_start(entry)
        self.connect('response', self.__on_response, entry)
        self.start()
    def __on_response(self, dialog, response_id, entry):
        if response_id == gtk.RESPONSE_ACCEPT:
            filename = entry.get_text()
            self.app.pl.add_stream(filename)

class SavePlaylistDialog(base_dialog.SimpleInputDialog):
    def __init__(self, app, parent):
        base_dialog.SimpleInputDialog.__init__(self, app, parent, 'Save playlist', 'Playlist name (without .m3u suffix)')
        entry = gtk.Entry()
        self.vbox.pack_start(entry)
        self.connect('response', self.__on_response, entry)
        self.start()
    def __on_response(self, dialog, response_id, entry):
        if response_id == gtk.RESPONSE_ACCEPT:
            filename = entry.get_text()
            self.app.pl.save(filename)

class CrossfadeDialog(base_dialog.SimpleInputDialog):
    def __init__(self, app, parent):
        base_dialog.SimpleInputDialog.__init__(self, app, parent, 'Crossfade time', 'Crossfade time in seconds')
        entry = gtk.SpinButton()
        entry.set_range(0, 100)
        entry.set_increments(1, 5)
        entry.set_value(self.app.status.xfade)
        entry.set_numeric(True)
        self.vbox.pack_start(entry)
        self.connect('response', self.__on_response, entry)
        self.start()
    def __on_response(self, dialog, response_id, entry):
        if response_id == gtk.RESPONSE_ACCEPT:
            seconds = entry.get_value_as_int()
            self.app.cmd_crossfade(seconds)
