#! /usr/bin/env python

import os
from gi.repository import Gtk
import gobject
import time

import playlist
import time_fmt

scriptdir = os.path.dirname(os.path.abspath(__file__))

class SyncGui:

  def __init__(self):
    """construct a SyncGui object"""
    self.builder = Gtk.Builder()
    self.builder.add_from_file(scriptdir + "/sync_gui.glade")
    self.widPlaylistView = self.builder.get_object("PlaylistView")
    self.widPlaylistStore = self.builder.get_object("PlaylistStore")
    self.widPosition = self.builder.get_object("Position")
    self.widPositionScale = self.builder.get_object("PositionScale")
    self.widPositionAt = self.builder.get_object("PositionAt")
    self.widPositionRemaining = self.builder.get_object("PositionRemaining")
    self.widBtnPause = self.builder.get_object("Pause")
    self.widBtnPlay = self.builder.get_object("Play")
    self.widStatus = self.builder.get_object("Status")
    self.configPlaylistColumns()
    handlers = {
      "onDestroy":          self.onDestroy,
      "onPlaylistClick":    self.onPlaylistClick,
      "onPlaylistDblClick": self.onPlaylistDblClick,
      "onNewPosition":      self.onNewPosition,
      "onPrevious":         self.onPrevious,
      "onBackward":         self.onBackward,
      "onStop":             self.onStop,
      "onPause":            self.onPause,
      "onPlay":             self.onPlay,
      "onForward":          self.onForward,
      "onNext":             self.onNext,
    }
    self.builder.connect_signals(handlers)
    self.playlist = playlist.Playlist()
    self.playlist.read("playlist.txt")
    self.playlist.update(self.widPlaylistStore)
    self.widStatus.push(0, "TODO...")
    self.stEntryIdx = -1 # no entry selected
    self.stName = "" # no current entry name
    self.stDuration = 0 # current entry has zero size
    self.stPosition = 0 # at begin of current entry
    self.stPlaying = False # not playing
    gobject.timeout_add(10, self.onTimer10ms)
    self.updateDuration()
    self.updateButtonVisibility()

  def configPlaylistColumns(self):
    """configure the columns of the playlist widget at program start"""
    i = 1 # first column is index (not shown)
    for title in ["Name", "Dauer"]:
      column = Gtk.TreeViewColumn(title)
      self.widPlaylistView.append_column(column)
      cell = Gtk.CellRendererText()
      column.pack_start(cell, False)
      column.add_attribute(cell, "text", i)
      i = i + 1

  def showPosition(self):
    """update the position texts next to the position slider"""
    # format current time and remaining time
    posAt = time_fmt.sec2str(self.stPosition)
    posRemaining = time_fmt.sec2str(self.stDuration - self.stPosition)
    self.widPositionAt.set_text(posAt)
    self.widPositionRemaining.set_text(posRemaining)

  def updatePositionState(self):
    """update the position in the state, but not the slider"""
    # calculate (virtual) start time of playing
    # i.e. the time the playing would have had started to arrive at the
    # current position now if it had played continuosly
    self.stPlayStart = time.time() - self.stPosition
    # update position texts
    self.showPosition()

  def updatePosition(self):
    """update the position including the position slider"""
    # update GUI slider
    self.widPositionScale.set_value(self.stPosition)
    # update position state
    self.updatePositionState()

  def updateDuration(self):
    """update the duration (i.e. range for the slider) based on the current
       playlist entry"""
    # get duration of new playlist entry
    self.stDuration = 0
    if self.stEntryIdx >= 0:
      entry = self.playlist.entries[self.stEntryIdx]
      if entry["type"] == "normal":
        self.stDuration = entry["duration"]
    # set position to begin
    self.stPosition = 0
    # update value range
    self.widPosition.set_upper(self.stDuration)
    # update position of slider
    self.updatePosition()

  def updateButtonVisibility(self):
    """update the visibility of the buttons based on if playing or not"""
    self.widBtnPause.set_visible(self.stPlaying)
    self.widBtnPlay.set_visible(not self.stPlaying)

  def onDestroy(self, widget):
    """window will be destroyed"""
    Gtk.main_quit()

  def onPlaylistClick(self, widget):
    """playlist entry has been clicked or selected"""
    # get index of selected entry
    idx = -1
    sel = self.widPlaylistView.get_selection()
    if sel is not None:
      (model, it) = sel.get_selected()
      if it is not None:
        (idx, ) = model.get(it, 0)
    print("DEBUG: playlist click idx=%d" % (idx))
    # update playlist entry
    self.stEntryIdx = idx
    # update duration
    self.updateDuration()

  def onPlaylistDblClick(self, widget, row, col):
    """playlist entry has been double-clicked"""
    # get index of selected entry
    idx = -1
    sel = self.widPlaylistView.get_selection()
    if sel is not None:
      (model, it) = sel.get_selected()
      if it is not None:
        (idx, ) = model.get(it, 0)
    print("DEBUG: playlist double-click idx=%d" % (idx))
    # update playlist entry
    self.stEntryIdx = idx
    # update duration
    self.updateDuration()
    # start playing
    # TODO

  def onNewPosition(self, widget, scroll, value):
    """slider has been moved to a new position"""
    print("DEBUG: new position " + str(value));
    # clamp position to valid range
    if value < 0:
      value = 0
    if value > self.stDuration:
      value = self.stDuration
    # update current position - and play start time if playing
    self.stPosition = value
    # update position state (do not touch the slider)
    self.updatePositionState()

  def onPrevious(self, widget):
    """previous button as been pressed"""
    print("DEBUG: previous")

  def onBackward(self, widget):
    """backward button has been pressed"""
    print("DEBUG: backward")

  def onStop(self, widget):
    """stop button has been pressed"""
    print("DEBUG: stop")
    self.stPlaying = False
    self.stPosition = 0 # stop goes back to begin
    self.updatePosition()
    self.updateButtonVisibility()

  def onPause(self, widget):
    """pause button has been pressed"""
    print("DEBUG: pause")
    self.stPlaying = False
    self.updateButtonVisibility()

  def onPlay(self, widget):
    """play button has been pressed"""
    print("DEBUG: play")
    self.stPlaying = True
    self.updatePosition()
    self.updateButtonVisibility()

  def onForward(self, widget):
    """forward button has been pressed"""
    print("DEBUG: forward")

  def onNext(self, widget):
    """next button has been pressed"""
    print("DEBUG: next")

  def onTimer10ms(self):
    """timer callback, every 10ms"""
    # update position if playing
    if self.stPlaying:
      self.stPosition = time.time() - self.stPlayStart
      if self.stPosition > self.stDuration:
        self.stPosition = 0 # FIXME: go to next entry
      self.updatePosition()
    return True

# main application entry point
if __name__ == "__main__":
  app = SyncGui()
  Gtk.main()