#!/usr/bin/python

import os
import sys
import time
import getopt

import lastfm.client
import lastfm.marshaller
from lastfm.config import SaneConfParser

def popen(argv, mode='r'):
    command = ' '.join(argv)
    return os.popen(command.encode(sys.getfilesystemencoding()), mode)

def quotemeta(s):
    for meta in ('\\', '$', '`', '"', '\n'):
        s = s.replace(meta, '\\' + meta)
    return '"%s"' % s

class Command:
    def __init__(self, user_opts):
        self.user_opts = user_opts.split()

class CdParanoia(Command):
    def open(self, device, number):
        return popen(['cdparanoia', '-r'] + self.user_opts + ['-d', device,
            '%d' % number, '-'])

class APlay(Command):
    def open(self):
        return popen(['aplay', '-q', '-t', 'raw', '-c', '2', '-r', '44100',
            '-f', 'S16_LE'] + self.user_opts, 'w')

class Bfp(Command):
    def open(self):
        return popen(['bfp'] + self.user_opts, 'w')

class OggEnc(Command):
    def open(self, song, path):
        argv = ['oggenc', '-Q', '-r'] + self.user_opts
        try: argv += ['-a', quotemeta(song['artist'])]
        except KeyError: pass
        try: argv += ['-t', quotemeta(song['title'])]
        except KeyError: pass
        try: argv += ['-l', quotemeta(song['album'])]
        except KeyError: pass
        try: argv += ['-N', '%d' % song['number']]
        except KeyError: pass
        try: argv += ['-c', quotemeta('musicbrainz_trackid=%s' % song['mbid'])]
        except KeyError: pass
        argv += ['-o', quotemeta('%s.ogg' % path), '-']
        return popen(argv, 'w')

class Lame(Command):
    def open(self, song, path):
        argv = ['lame', '--quiet', '-rx'] + self.user_opts
        try: argv += ['--ta', quotemeta(song['artist'])]
        except KeyError: pass
        try: argv += ['--tt', quotemeta(song['title'])]
        except KeyError: pass
        try: argv += ['--tl', quotemeta(song['album'])]
        except KeyError: pass
        try: argv += ['--tn', '%d' % song['number']] # XXX
        except KeyError: pass
        argv += ['-', quotemeta('%s.mp3' % path)]
        return popen(argv, 'w')

def sox_open(path):
    return popen(['sox', path, '-t', 'raw', '-s', '-w', '-r', '44100', '-c',
        '2', '-'])

rippers = {'cdparanoia': CdParanoia}
players = {'aplay': APlay, 'bfp': Bfp}
encoders = {'oggenc': OggEnc, 'lame': Lame}

def_path = '%(artist)s/%(album)s/%(number)02d - %(title)s'

if __name__ == '__main__':
    shortopts = 'd:e:qc'
    longopts = ['device=', 'encoder', 'quiet', 'continue']

    try:
        opts, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
    except getopt.GetoptError, e:
        print >>sys.stderr, 'peel: %s' % e
        sys.exit(1)

    device = '/dev/cdrom'
    quiet = False

    cp = SaneConfParser()
    cp.read([os.path.expanduser('~/.peelrc')])

    rip_cmd = cp.get('commands', 'rip', 'cdparanoia')
    play_cmd = cp.get('commands', 'play', 'aplay')
    enc_cmd = cp.get('commands', 'encode', 'oggenc')

    rip_opts = cp.get('options', rip_cmd, '')
    play_opts = cp.get('options', play_cmd, '')
    enc_opts = cp.get('options', enc_cmd, '')

    path_tmpl = cp.get('output', 'path', def_path)

    for opt, arg in opts:
        if opt in ('--device', '-d'):
            device = arg
        elif opt in ('--quiet', '-q'):
            quiet = True
        elif opt in ('--continue', '-c'):
            device = None
        elif opt in ('--encoder', '-e'):
            enc_cmd = arg

    try:
        ripper = rippers[rip_cmd](rip_opts)
        player = players[play_cmd](play_opts)
        encoder = encoders[enc_cmd](enc_opts)
    except KeyError, e:
        print >>sys.stderr, 'unknown command: %s' % e.args[0]

    if not quiet:
        play = player.open()
        cli = lastfm.client.Client('peel')
        cli.open_log()

    for song in lastfm.marshaller.load_documents(sys.stdin):
        print "Track %(number)s: %(title)s..." % song

        if device:
            rip = ripper.open(device, song['number'])
        else:
            rip = sox_open('track%02d.cdda.wav' % song['number'])

        safe_song = {}
        for k, v in song.iteritems():
            try:
                safe_song[k] = v.replace('/', '_')
            except AttributeError:
                safe_song[k] = v

        path = path_tmpl % safe_song
        dir = os.path.dirname(path)
        if dir and not os.path.isdir(dir):
            os.makedirs(dir)

        enc = encoder.open(song, path)

        while True:
            buf = rip.read(4096)
            if buf:
                enc.write(buf)
                if not quiet:
                    play.write(buf)
            else:
                break

        if not quiet:
            if song['length'] >= 30:
                song['time'] = time.gmtime()
                cli.submit(song)
                cli.log.info('Sent %s to daemon' % lastfm.repr(song))
