#!/usr/bin/python

#Audio Tools, a module and set of tools for manipulating audio data
#Copyright (C) 2007-2010  Brian Langenberger

#This program 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.

#This program 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 this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


import os
import os.path
import sys
import audiotools
import audiotools.toc
import tempfile
import subprocess
import gettext

gettext.install("audiotools",unicode=True)

MAX_CPUS = audiotools.MAX_JOBS

def convert_to_wave(audiofile,wave_filename,messenger):
    if ((audiofile.sample_rate() == 44100) and
        (audiofile.channels() == 2) and
        (audiofile.bits_per_sample() == 16)):  #already CD quality
        audiofile.to_wave(wave_filename)
    else:                                      #convert to CD quality
        pcm = audiotools.PCMConverter(
            audiofile.to_pcm(),
            sample_rate=44100,
            channels=2,
            channel_mask=audiotools.ChannelMask.from_channels(2),
            bits_per_sample=16)
        audiotools.WaveAudio.from_pcm(wave_filename,pcm)
        pcm.close()

    messenger.info(u"* %s" % (messenger.filename(audiofile.filename)))

if (__name__ == '__main__'):
    parser = audiotools.OptionParser(
        usage=_(u"%prog [options] <track 1> [track 2] ..."),
        version="Python Audio Tools %s" % (audiotools.VERSION))

    parser.add_option("-c","--cdrom",dest="dev",
                      default=audiotools.DEFAULT_CDROM,
                      help=_(u"cdrom device to use"))

    parser.add_option("-s","--speed",dest="speed",
                      default=20,
                      type="int",
                      help=_(u"the speed to burn the CD at"))

    parser.add_option('--cue',
                      action='store',
                      type='string',
                      dest='cuesheet',
                      metavar='FILENAME',
                      help=_(u'the cuesheet to use for writing tracks'))

    parser.add_option('-j','--joint',
                      action='store',
                      type='int',
                      default=MAX_CPUS,
                      dest='max_processes',
                      help=_(u'the maximum number of processes to run at a time'))

    parser.add_option('-V','--verbose',
                      action='store',
                      dest='verbosity',
                      choices=["quiet","normal","debug"],
                      default="normal",
                      help=_(u'the verbosity level to execute at'))

    (options,args) = parser.parse_args()
    msg = audiotools.Messenger("track2cd",options)

    if (options.max_processes < 1):
        msg.error(_(u'You must run at least 1 process at a time'))
        sys.exit(1)
    else:
        max_processes = options.max_processes

    audiofiles = audiotools.open_files(args,sorted=False,messenger=msg)

    if (len(audiofiles) < 1):
        msg.error(_(u"You must specify at least 1 supported audio file"))
        sys.exit(1)

    if ((len(audiofiles) == 1) and
        (audiofiles[0].get_cuesheet() is not None)):
        #writing a single file with an embedded cuesheet
        #so extract its contents to wave/cue and call cdrdao

        if (not audiotools.BIN.can_execute(audiotools.BIN['cdrdao'])):
            msg.error(_(u'Unable to find "cdrdao" executable'))
            msg.info(_(u'Please install "cdrdao" to burn CDs'))
            sys.exit(1)

        cuesheet = audiofiles[0].get_cuesheet()

        temptoc = tempfile.NamedTemporaryFile(suffix='.toc')
        tempwav = tempfile.NamedTemporaryFile(suffix='.wav')

        temptoc.write(audiotools.toc.TOCFile.file(
                cuesheet,os.path.basename(tempwav.name)))
        temptoc.flush()

        msg.info(_(u"Converting audio file"))

        if ((audiofiles[0].sample_rate() == 44100) and
            (audiofiles[0].channels() == 2) and
            (audiofiles[0].bits_per_sample() == 16)):
            temptrack = audiotools.WaveAudio.from_pcm(
                tempwav.name,
                audiofiles[0].to_pcm())
        else:
            temptrack = audiotools.WaveAudio.from_pcm(
                    tempwav.name,
                    audiotools.PCMConverter(audiofiles[0].to_pcm(),
                                            44100,2,16))

        os.chdir(os.path.dirname(tempwav.name))
        cdrdao_args = [audiotools.BIN["cdrdao"],"write"]

        cdrdao_args.append("--device")
        cdrdao_args.append(options.dev)

        cdrdao_args.append("--speed")
        cdrdao_args.append(str(options.speed))

        cdrdao_args.append(temptoc.name)

        if (options.verbosity != 'quiet'):
            subprocess.call(cdrdao_args)
        else:
            devnull = open(os.devnull,'wb')
            sub = subprocess.Popen(cdrdao_args,
                                   stdout=devnull,
                                   stderr=devnull)
            sub.wait()
            devnull.close()

        temptoc.close()
        tempwav.close()

    elif (options.cuesheet is not None):
        #writing tracks with a cuesheet,
        #so combine them into a single wave/cue and call cdrdao

        if (not audiotools.BIN.can_execute(audiotools.BIN['cdrdao'])):
            msg.error(_(u'Unable to find "cdrdao" executable'))
            msg.info(_(u'Please install "cdrdao" to burn CDs'))
            sys.exit(1)

        if (len(set([f.sample_rate() for f in audiofiles])) != 1):
            msg.error(_(u"All audio files must have the same sample rate"))
            sys.exit(1)

        if (len(set([f.channels() for f in audiofiles])) != 1):
            msg.error(_(u"All audio files must have the same channel count"))
            sys.exit(1)

        if (len(set([f.bits_per_sample() for f in audiofiles])) != 1):
            msg.error(_(u"All audio files must have the same bits per sample"))
            sys.exit(1)

        try:
            toc = audiotools.read_sheet(options.cuesheet)
        except audiotools.SheetException,err:
            msg.error(unicode(err))
            sys.exit(1)

        temptoc = tempfile.NamedTemporaryFile(suffix='.toc')
        tempwav = tempfile.NamedTemporaryFile(suffix='.wav')

        temptoc.write(audiotools.toc.TOCFile.file(
                toc,os.path.basename(tempwav.name)))
        temptoc.flush()

        msg.info(_(u"Converting audio files"))

        if ((audiofiles[0].sample_rate() == 44100) and
            (audiofiles[0].channels() == 2) and
            (audiofiles[0].bits_per_sample() == 16)):
            temptrack = audiotools.WaveAudio.from_pcm(
                tempwav.name,
                audiotools.PCMCat(iter([af.to_pcm() for af in audiofiles])))
        else:
            #this presumes a cuesheet and non-CD audio
            #though theoretically possible, it's difficult to
            #envision a case in which this happens
            temptrack = audiotools.WaveAudio.from_pcm(
                    tempwav.name,
                    audiotools.PCMConverter(audiotools.PCMCat(
                        iter([af.to_pcm() for af in audiofiles])),
                                            44100,2,16))

        os.chdir(os.path.dirname(tempwav.name))
        cdrdao_args = [audiotools.BIN["cdrdao"],"write"]

        cdrdao_args.append("--device")
        cdrdao_args.append(options.dev)

        cdrdao_args.append("--speed")
        cdrdao_args.append(str(options.speed))

        cdrdao_args.append(temptoc.name)

        if (options.verbosity != 'quiet'):
            subprocess.call(cdrdao_args)
        else:
            devnull = open(os.devnull,'wb')
            sub = subprocess.Popen(cdrdao_args,
                                   stdout=devnull,
                                   stderr=devnull)
            sub.wait()
            devnull.close()

        temptoc.close()
        tempwav.close()
    else:
        #writing tracks without a cuesheet,
        #so extract them to waves and call cdrecord

        if (not audiotools.BIN.can_execute(audiotools.BIN['cdrecord'])):
            msg.error(_(u'Unable to find "cdrecord" executable'))
            msg.info(_(u'Please install "cdrecord" to burn CDs'))
            sys.exit(1)

        exec_args = [audiotools.BIN['cdrecord']]

        exec_args.append("-speed")
        exec_args.append(str(options.speed))

        exec_args.append("-dev")
        exec_args.append(options.dev)

        exec_args.append("-dao")
        exec_args.append("-pad")
        exec_args.append("-audio")

        temp_pool = []
        wave_files = []
        queue = audiotools.ExecQueue()

        msg.info(_(u"Converting audio files"))
        for audiofile in audiofiles:
            if (isinstance(audiofile,audiotools.WaveAudio)):
                wave_files.append(audiofile.filename)
            else:
                f = tempfile.mkstemp(suffix='.wav')
                temp_pool.append(f)
                wave_files.append(f[1])
                queue.execute(convert_to_wave,
                              (audiofile,
                               f[1],
                               msg))

        queue.run(max_processes)

        exec_args += wave_files

        if (options.verbosity != 'quiet'):
            subprocess.call(exec_args)
        else:
            devnull = open(os.devnull,'wb')
            sub = subprocess.Popen(exec_args,
                                   stdout=devnull,
                                   stderr=devnull)
            sub.wait()
            devnull.close()

        for (fd,f) in temp_pool:
            os.close(fd)
            os.unlink(f)
        del(temp_pool)
