#!/usr/bin/python

import gst
import gobject
import gtk
import os

class Player(object):
    def __init__(self):
        self._player = gst.element_factory_make('playbin2', \
                'player'+str(hash(self)))
        self._format = gst.Format(gst.FORMAT_TIME)

    @property
    def duration(self):
        try:
            value, format = self._player.query_duration(self._format, None)
        except:
            value = -1

        return value

    def get_volume(self):
        return self._player.get_property('volume')

    def set_volume(self, volume):
        self._player.set_property('volume', volume)

    volume = property(fget=get_volume, fset=set_volume)

    def get_position(self):
        try:
            value, format = self._player.query_position(self._format, None)
        except:
            value = -1

        return value

    def set_position(self, position):
        self._player.seek_simple(self._format, \
                gst.SEEK_FLAG_FLUSH, position)

    position = property(fset=set_position, fget=get_position)

    def load(self, filename):
        if filename.startswith('http://'):
            uri = filename
        else:
            uri = 'file://' + os.path.abspath(filename)
        self._player.set_property('uri', uri)

    def play(self):
        self._player.set_state(gst.STATE_PLAYING)

    def pause(self):
        self._player.set_state(gst.STATE_PAUSED)

    def stop(self):
        self._player.set_state(gst.STATE_NULL)

class Updater(object):
    TIMEOUT = 500

    def __init__(self):
        self._source_id = None
        self._target = None

    def set_target(self, callback):
        self._target = callback

    def start(self):
        self.stop()
        self._source_id = gobject.timeout_add(self.TIMEOUT, self._callback)

    def stop(self):
        if self._source_id:
            gobject.source_remove(self._source_id)

    def _callback(self):
        if self._target:
            self._target()
        return True

class Deck(gtk.VBox, Player):
    def __init__(self):
        gtk.VBox.__init__(self)
        Player.__init__(self)
        self._is_seeking = False

        self._filename_entry = gtk.Entry()
        self._filename_entry.connect('changed', self._on_filename_changed)
        self.add(self._filename_entry)

        bb = gtk.HButtonBox()
        self.add(bb)

        for icon, callback in (\
                (gtk.STOCK_MEDIA_PLAY, lambda b: self.play()), \
                (gtk.STOCK_MEDIA_PAUSE, lambda b: self.pause()), \
                (gtk.STOCK_MEDIA_STOP, lambda b: self.stop())):
            b = gtk.Button()
            b.set_name('HildonButton-finger')
            b.set_image(gtk.image_new_from_stock(icon, gtk.ICON_SIZE_BUTTON))
            b.connect('clicked', callback)
            bb.add(b)

        def change_position(a):
            if self._is_seeking:
                self.position = a.get_value()

        self._position_adjustment = gtk.Adjustment(upper=1.)
        self._position_adjustment.connect('value-changed', change_position)

        def start_seeking(*args):
            self._is_seeking = True

        def stop_seeking(*args):
            self._is_seeking = False

        def format_value(scale, value):
            value = value / 10**9
            mins = value // (60)
            value = value-mins*60
            if value < 10:
                fix = '0'
            else:
                fix = ''
            return '%02d:%s%02.2f' % (mins, fix, value)

        self._position_hscale = gtk.HScale(self._position_adjustment)
        self._position_hscale.connect('button-press-event', start_seeking)
        self._position_hscale.connect('button-release-event', stop_seeking)
        self._position_hscale.connect('format-value', format_value)
        self.add(self._position_hscale)

        self._volume_adjustment = gtk.Adjustment(upper=1.)
        self._volume_adjustment.connect('value-changed', \
                lambda a: self.set_volume(a.get_value(), False))

        self._volume_hscale = gtk.HScale(self._volume_adjustment)
        self._volume_hscale.set_digits(2)
        self.add(self._volume_hscale)

        def update_position():
            if not self._is_seeking:
                self._position_adjustment.upper = self.duration
                self._position_adjustment.value = self.position

        self._position_updater = Updater()
        self._position_updater.set_target(update_position)

    def play(self):
        Player.play(self)
        self._position_updater.start()

    def pause(self):
        Player.pause(self)
        self._position_updater.stop()

    def stop(self):
        Player.stop(self)
        self._position_updater.stop()

    def set_position(self, position, set_adjustment=True):
        if set_adjustment:
            self._position_adjustment.value = position
        Player.set_position(self, position)

    def set_volume(self, volume, set_adjustment=True):
        if set_adjustment:
            self._volume_adjustment.value = volume
        Player.set_volume(self, volume)

    def _on_filename_changed(self, entry):
        filename = entry.get_text()
        if os.path.isfile(filename):
            self.load(filename)
            entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse('green'))
        else:
            entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse('red'))

class DeckSlider(gtk.HScale):
    def __init__(self, decks):
        gtk.HScale.__init__(self)
        self.decks = decks
        self._adjustment = gtk.Adjustment(upper=1.)
        self._adjustment.connect('value-changed', self._on_value_changed)
        self.set_digits(2)
        self.set_adjustment(self._adjustment)

    def _on_value_changed(self, adjustment):
        value = adjustment.value
        count = len(self.decks)
        for index, deck in enumerate(self.decks):
            position = float(index)/float(count-1)
            deck.set_volume(min(1., max(0, 1.-abs(value-position)*(count-1.))))


if __name__ == '__main__':
    DECKS = 2

    w = gtk.Window()
    w.connect('destroy', gtk.main_quit)

    v = gtk.VBox()
    w.add(v)

    h = gtk.HBox()
    v.add(h)

    for i in range(DECKS):
        h.add(Deck())

    v.add(DeckSlider(h.get_children()))

    w.show_all()
    gtk.main()


