#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import gtk
import commands
import sys
from popen2 import Popen3
from os.path import basename

# The /dev/video* device to use
DEVICE = '/dev/video0'
# The media player you wish to use to play the stream
VIDEO_PLAYER = '/usr/bin/mplayer'
# Arguments to pass to the video player executable
PLAYER_ARGUMENTS = '-msglevel all=-1 -nolirc -cache 4096 -vf pp=lb ' + DEVICE

class RemoteGui(object):
    """ RemoteGui(device, [ video_player, args ] """
    def __init__(self, device, video_player):
    ######################################################
    #   GUI Code                                         #
    ######################################################
        # Create the main window
        self.main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.main_window.set_position(gtk.WIN_POS_MOUSE)
        self.main_window.set_title('PVRmote')
        self.main_window.set_default_size(165, 0)
        # Add an icon to the main window from stock
        icon = self.main_window.render_icon(gtk.STOCK_ABOUT, gtk.ICON_SIZE_MENU)
        self.main_window.set_icon(icon)
        # Connect the signal handler to destroy the window
        self.main_window.connect("destroy", gtk.main_quit)
        # Create the table to hold the buttons and add it to self.main_window
        self.main_table = gtk.Table(rows=7, columns=3, homogeneous=True)
        # Add the table to the window
        self.main_window.add(self.main_table)
        # Create a HBox to hold the channel: [###] info
        self.top_hbox = gtk.HBox(False, 2)
        # Add the box to the top of the table
        self.main_table.attach(self.top_hbox,0,3,0,1)
        # Show the new hbox
        self.top_hbox.show()
        # Create the 'Channel:' label, attach it to the hbox, and show it
        self.lbl_channel = gtk.Label()
        self.lbl_channel.set_markup('<span weight="bold">Channel:</span>')
        self.top_hbox.pack_start(self.lbl_channel, False, False)
        self.lbl_channel.show()
        # Create the gtk.Entry to display the current channel
        self.ent_channel = gtk.Entry()
        self.top_hbox.pack_end(self.ent_channel, False, False)
        self.ent_channel.set_width_chars(4)
        # Connect a handler to track changes in the ent_channel Entry
        self.ent_channel.connect('changed', self.on_ent_channel_changed)
        # If the user presses enter in the ent_channel Entry run on_watched_clicked
        self.ent_channel.connect('activate', self.on_watched_clicked)
        self.ent_channel.show()
        # Create an array of buttons #1 through #9
        self.btn_numbers = [ [gtk.Button(str(i)), i] for i in range(1,10) ]
        # Add 0, Composite and S-video buttons
        self.btn_numbers += [[gtk.Button('Comp'),-2],[gtk.Button('0'),0],[gtk.Button('Svid'),-1]]
        # Add Channel +, Set (change channel without opening a video player), Channel -
        self.btn_numbers += [ [ gtk.Button('Ch-'), -1, self.on_channel_step ],
                              [ gtk.Button('Set'), False, self.on_watched_clicked ],
                              [ gtk.Button('Ch+'), 1, self.on_channel_step ] ]
        # Explain what 'Set', 'Comp' and 'Svid' buttons do with tooltips
        self.btn_numbers[13][0].set_tooltip_text('Change the channel without trying to open a video player')
        self.btn_numbers[9][0].set_tooltip_text('Switch to the Composite input')
        self.btn_numbers[11][0].set_tooltip_text('Switch to the S-Video input')
        for i in range(len(self.btn_numbers)):
            # Attach the number buttons to the table
            self.main_table.attach(self.btn_numbers[i][0], i%3, i%3+1, i/3+1, i/3+2)
            # Add a signal handler for each button
            if len(self.btn_numbers[i]) <= 2:
                self.btn_numbers[i][0].connect('clicked', self.button_handler, self.btn_numbers[i][1])
            else:
                self.btn_numbers[i][0].connect('clicked', self.btn_numbers[i][2], self.btn_numbers[i][1])
            # Make the button visible
            self.btn_numbers[i][0].show()
        # Create a 'Watch!' button
        self.btn_watch = gtk.Button('Watch!')
        # Attach btn_watch to the bottom of the table
        self.main_table.attach(self.btn_watch,0,3,6,7)
        # Add it's signal handler
        self.btn_watch.connect('clicked', self.on_watched_clicked)
        # Show the 'Watch!' button
        self.btn_watch.show()
        # Make the table visible
        self.main_table.show()
        # Show the main window
        self.main_window.show()
    ######################################################
    #   non-GUI Initialization Code                      #
    ######################################################
        self.video_player = video_player
        # Have we clicked 'watch' since the last text entry?
        self.watch_clicked = True
        channel = self.get_channel(device)
        self.current_channel = 0
        self.special_text = { 'Svid' : -1, 'Comp' : -2, -1 : 'Svid', -2 : 'Comp' }
        self.device = device
        self.video_player_process = Popen3(None)

        self.ent_channel.set_text(self.special_text.get(channel, channel))
        self.watch_clicked = True

    def button_handler(self, sender, number):
        if ( self.watch_clicked and number >= 0 ) or self.current_channel < 0:
            self.ent_channel.set_text(self.special_text.get(number, str(number)))
        elif not self.watch_clicked and number >= 0:
            self.ent_channel.set_text(self.ent_channel.get_text() + str(number))
        elif number < 0:
            self.ent_channel.set_text(self.special_text[number])
        # If the user presses the 'Comp' or 'Svid' button, switch to it right away
        if number < 0:
            self.on_watched_clicked(None, False)

    def on_watched_clicked(self, sender, play_video = True):
        self.watch_clicked = True
        if self.current_channel != int(self.get_channel(self.device)):
            print 'switch', self.current_channel, self.get_channel(self.device)
            if self.current_channel >= 0:
                if self.get_current_input(self.device) != 0:
                    self.set_input(0, self.device)
                self.set_channel(self.current_channel, self.device)
            elif self.current_channel < 0:
                if self.get_current_input(self.device) != abs(self.current_channel):
                    self.set_input(abs(self.current_channel), self.device)
        if play_video:
            self.start_video_player(self.video_player)

    def on_ent_channel_changed(self, sender):
        self.watch_clicked = False
        text = self.ent_channel.get_text()
        if self.special_text.has_key(text):
            self.current_channel = self.special_text[text]
        else:
            try:
                self.current_channel = int(text)
            except ValueError:
                self.current_channel = 0

    def on_channel_step(self, sender, arg):
        if self.current_channel >= 0 and self.current_channel + arg >= 0:
            self.ent_channel.set_text(str(self.current_channel + arg))
            self.on_watched_clicked(None, False)

    def get_current_input(self, device):
        return int(commands.getoutput('v4l2-ctl -I -d ' + device).split(':')[1].split(' ')[1])

    def set_input(self, ninput, device):
        return commands.getstatusoutput('v4l2-ctl -i %d -d %s' % (ninput, device))[0]

    def set_channel(self, channel, device):
        return commands.getstatusoutput('ivtv-tune -c %d -d %s' % (channel, device))

    def get_channel(self,device):
        """ Finds the channel that the card is currently tuned to
        Warning! returns strings for channel numbers and ints for other inputs"""
        cinput = self.get_current_input(device)
        if cinput != 0:
            channel = cinput * -1
        else:
            raw_channel_list = [ channel.split('\t') for channel in commands.getoutput('ivtv-tune -l -d ' + device).split('\n')[1:]]
            channel_list = dict()
            for channel_num, freq in raw_channel_list:
                channel_list[float(freq)] = channel_num
            current_freq = float(commands.getoutput('v4l2-ctl -F -d ' + device).split('(')[1].split(' ')[0])
            channel = channel_list[current_freq]
        return channel

    def start_video_player(self, video_player):
        player = basename(video_player[0])
        # Check if the video player is already running
        if self.video_player_process.poll() != -1 and commands.getstatusoutput('ps -A | grep -E %s$' % player)[0] != 0:
            self.video_player_process = Popen3('%s %s' % (video_player[0], video_player[1]))

if __name__ == '__main__':
    needed_apps = [ 'ivtv_tune', 'v4l2-ctl' ]
    for app in needed_apps:
        if commands.getstatusoutput('which ' + app)[0] != 0:
            print 'ERROR: Missing ' + app
            sys.exit(1)
    RemoteGui(DEVICE, [ VIDEO_PLAYER, PLAYER_ARGUMENTS])
    gtk.main()