#!/usr/bin/env python

# ---------------------------------------------------------------------
# $Id: gaff-brasero 236 2008-08-16 03:33:22Z daaugusto $
#
#   gaff-brasero (created on Sun Aug  3 00:08:52 BRT 2008)
#
#   Genetic Algorithm File Fitter (gaffitter)
#
#   Copyright (C) 2008 Douglas A. Augusto
#
# This file is part of gaffitter.
#
# gaffitter 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 3 of the License, or (at
# your option) any later version.
#
# gaffitter 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 gaffitter; if not, see <http://www.gnu.org/licenses/>.
#
# ---------------------------------------------------------------------

import sys

if sys.version[:5] < "2.4":
   print "This script requires Python 2.4 or later."
   sys.exit( 1 )

import os
import subprocess
import getopt
from urllib import quote

def Usage( exitcode = 0 ):
   print "Usage: gaff-brasero --{dvd|dvd-dl|cd|cd74|cd90|cd99|size n} [OPTION] files/dirs"
   print ""
   print "Options:"
   print "  --dvd"
   print "     create volumes of 4.38GB (DVD)"
   print "  --dvd-dl"
   print "     create volumes of 7.95GB (Dual Layer DVD)"
   print "  --cd"
   print "     create volumes of 702MB (80min CD)"
   print "  --cd74"
   print "     create volumes of 650MB (74min CD)"
   print "  --cd90"
   print "     create volumes of 790MB (90min CD)"
   print "  --cd99"
   print "     create volumes of 870MB (99min CD)"
   print "  --size n[k,m,g,t]"
   print "     create custom volumes of n KiB/MiB/GiB/TiB each"
   print "  --split"
   print "     just splits the input (i.e. preserves original order)"
   print "  --vols n"
   print "     maximum number of volumes (default = as much as possible)"
   print "  -v"
   print "     verbose mode"
   print ""
   print "Example: gaff-brasero --dvd *"
   print ""
   print "Powered by GAFFitter (http://gaffitter.sf.net)"

   sys.exit( exitcode )

def FindInPath( program ):
   if os.path.sep in program:
      if os.path.exists( program ):
         return program
   else:
      for path in os.path.expandvars( "$PATH" ).split( os.path.pathsep ):
         if os.path.exists( os.path.join( path, program ) ):
            return os.path.join( path, program )
   return None

def CheckPrograms( programs ):
   found = {}
   for p in programs:
      for x in programs[p]:
         path = FindInPath( x )
         if path: 
            found[p] = path
            break
      if not path: Error( "Error: %s is required, aborting." %p )
   return found

def Error( msg = "", code = 1 ):
   print >> sys.stderr, ">", msg; sys.stderr.flush()
   sys.exit( code )

def SizeInBytes( size ):
   # transform the given size in bytes
   try:
      if size[-1].isalpha():
         if size[-1] in ('t', 'T'):
            size = float( size[:-1] ) * pow( 1024, 4 )
         elif size[-1] in ('g', 'G'):
              size = float( size[:-1] ) * pow( 1024, 3 )
         elif size[-1] in ('m', 'M'):
              size = float( size[:-1] ) * pow( 1024, 2 )
         elif size[-1] in ('k', 'K'):
              size = float( size[:-1] ) * 1024
   except:   
      Error( "Error while parsing the given size '%s', aborting" %size )

   if int( size ) <= 0:
      Error( "Target size must be greater than zero, aborting" )

   return int( size )

def CheckGAFFitterVersion( path, minimum ):
   try:
      gaffitter = subprocess.Popen( [path, '--version'], stdout=subprocess.PIPE )
      gaffitter_ver = gaffitter.stdout.read().split( ' ' )[1]
      gaffitter.stdout.close()
   except:
      Error( "Could not check the GAFFitter's version." )
   if gaffitter_ver < minimum:
      Error( "This script requires GAFFitter %s or later." %minimum )

def main():
   print "GAFFitter Brasero (v0.1.0)\n"

   # Get the options
   try:
       opts, files = getopt.getopt( sys.argv[1:], "hv:", ["help", "dvd",
       "dvd-dl", "cd", "cd74", "cd90", "cd99", "size=", "split", "vols="] )
   except getopt.GetoptError, err:
       print str( err ) # will print something like "option -a not recognized"
       Usage(2)

   params = { 'size': None, 'vols': None, 'verbose': False, 'split': '' }

   for o, a in opts:
       if o in ("-h", "--help"):
           Usage()
       elif o == "-v":
           params["verbose"] = True
       elif o == "--cd":
           params["size"] = "700m"
       elif o == "--cd74":
           params["size"] = "650m"
       elif o == "--cd90":
           params["size"] = "790m"
       elif o == "--cd99":
           params["size"] = "870m"
       elif o == "--dvd":
           params["size"] = "4480m"
       elif o == "--dvd-dl":
           params["size"] = "8140m"
       elif o == "--size":
           params["size"] = a
       elif o == "--split":
           params["split"] = "--split"
       elif o == "--vols":
           params["vols"] = a
       else:
           assert False, "Unhandled option"

   programs = CheckPrograms( {'gaffitter':['gaffitter'], 'brasero':['brasero'], 
                              'xargs':['xargs'], 'du':['du']} )

   CheckGAFFitterVersion( programs['gaffitter'], "0.6.0" )

   if not params["size"] or ( params["vols"] and params["vols"] <= 0 ): Usage(2)

   params["size"] = SizeInBytes( params["size"] )


   print "Running gaffitter...",
   gaffitter_cmd = r"%s - -z -Z --hs -0 %s -t %s -B 2048 --di --di-b" \
                    % (programs["gaffitter"], params["split"], params["size"])

   if params["verbose"]: print "(%s)" % gaffitter_cmd,

   sys.stdout.flush()

   try:
      du_cmd = r"%s -0 %s -0 -Lbs" %(programs['xargs'], programs["du"])
      du = subprocess.Popen( du_cmd.split(), stdin=subprocess.PIPE, 
                                                 stdout=subprocess.PIPE )
      du.stdin.write( "".join( [ "%s\0" %x for x in files ] ) )
      du.stdin.close()

      if du.wait(): 
         raise
   except:
      Error( "An error occurred while running du." )

   try:
      gaffitter = subprocess.Popen( gaffitter_cmd.split(), stdin=du.stdout, 
                                                        stdout=subprocess.PIPE )
      du.stdout.close()
      if gaffitter.wait(): 
         raise
      out = gaffitter.stdout.read()
      gaffitter.stdout.close()
   except:
      Error( "An error occurred while running GAFFitter." )

   bins = [ "%s\0" %x for x in out[:-1].split( "\0\0" ) ]

   print "done.\n"
   total = len( bins )

   vol = 1
   for bin in bins:
      print "[%d/%d] Selected files:\n\n%s\n" % ( vol, total, 
                                                  bin.replace('\0','\n')[:-1] )
      brasero_cmd = r"%s -0 %s" %(programs['xargs'], programs["brasero"])

      print "[%d/%d] Calling Brasero..." %(vol, total),
      if params['verbose']: print r"(%s)" %brasero_cmd,

      sys.stdout.flush()

      #bin = [ os.path.realpath( x ).replace( ' ', '%20' ) for x in bin.split( '\0' )[:-1] ]
      bin = [ quote( os.path.realpath( x ) ) for x in bin.split( '\0' )[:-1] ]
      try:
         if params['verbose']:
            brasero = subprocess.Popen( brasero_cmd.split(), stdin=subprocess.PIPE )
         else:
            brasero = subprocess.Popen( brasero_cmd.split(), stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE )

         brasero.stdin.write( ''.join( ["file://%s\0" %x for x in bin] ) )
         brasero.stdin.close()

         if brasero.wait(): 
            raise
      except:
         Error( "An error occurred while running Brasero, aborting" )

      print "done.\n"

      vol += 1
   
   sys.exit(0)

if __name__ == "__main__":
    sys.exit(main())
