#! /usr/bin/python
"""
#+
# NAME:
#	burnfiles
# PURPOSE:
#	Burn DVD from L1A files
# CATEGORY:
#	gen/python
# CALLING SEQUENCE:
#	burnfiles <source> [<destination>]
# INPUTS:
#	<source>		fully-qualified directory
#				source directory with files to be put on CD/DVD
#	<destination>		fully-qualified directory
#				directory where files are moved after
#				the CD/DVD has been burned.
#				If omitted then this is the same as <source>.
#				Files are moved to <destination> only if the CD/DVD
#				burned succesfully. If -test is set then files
#				stay in <source>; if burning fails or -img is set
#				then files are moved back to <source>.
# OPTIONAL INPUTS:
#	-pattern=pattern	file name wildcard
#				default: *
#	-sort			sort files before selecting files
#	-dvd			burn a DVD (instead of a CD)
#	-img			make the CD image file, but don't burn CD/DVD
#	-nosudo			launch cdrecord without using sudo
#	-remove			remove the files after burning the CD/DVD
#				(ignored if -test or -img is set)
#	-verbose		display messages
#	-temp=temp		location where CD image is created
#				default: $TUB
#	-count=count		number of CD/DVD copies
#	-speed=speed		write speed (default is different for CD and DVD
#				and is set in burndisk.py)
#	-dev=dev		SCSI dev id for the CD/DVD writer; default: 0,0,0
#	-email=email		comma-separated list of email addresses to which
#				to send an email upon completion.
#	-test			test run
#	-before			number of days
#				if a file is found with a modification time
#				more than 'before' days before the current time
#				than file selection is terminated.
#
#	Size can be specified with postfix G or GB for gigabyte, M or MB for
#	megabyte, k or kB for kilobyte or just bytes with no postfix.
#
#	-max_disk_size=max_disk_size
#				files are selected until the combined
#				file size is upto max_disk_size bytes.
#				DVD default: 4500MB
#				CD  default:  650MB
#				(these values still need tuning)
#	-min_disk_size=min_disk_size
#				the combined file size must be at least
#				this many bytes or no disk is burned.
#				DVD default: 4200MB
#				CD  default:  600MB
#	-min_file_size=min_file_size
#				default: 500kB
#				see PROCEDURE
#	-logfile=logfile	log file where content of CD/DVD is logged.
#				A list of file names is appened to <logfile>
#				only if burning of CD/DVD was succesfull.
#	-checksum=checksumfile	checksum filename or filename wildcard to copy to the CD/DVD.
#	-timestamp		add timestamp to file name in logfile
#	-unblock		skips test for running instances of l1a.py in ftp mode.
# OUTPUTS:
#	CD/DVD (maybe)
# CALLS:
#	burndisk
# SEE ALSO:
# EXAMPLE:
#	burnfiles.py -dvd -count=2 /storage/temp
# PROCEDURE:
#	Selected files are moved to a temporary subdirectory of
#	<directory>
#
#	If -remove IS set then the temporary directory and its content are
#	deleted after burning the CD/DVD.
#	If -remove is NOT set then the files are moved back
#
#	The CD img file is created in $temp and is also deleted after
#	processing (see href=burndisk=).
# MODIFICATION HISTORY:
#	FEB-2004, Paul Hick (UCSD/CASS)
#	MAR-2004, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
#		Added -speed, -verbose, -dev, -clean keywords.
#		Changed to dictionary for list of args for burndisk
#		Lowered max_disk_size for DVDs.
#		Fixed bug for non-existent log file.
#	APR-2004, Paul Hick (UCSD/CASS)
#		Changed min_file_size default from 100kB to 500kB.
#	MAY-2004, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
#		Added -before keyword
#	JUN-2004, Paul Hick (UCSD/CASS)
#		Added -noblock keyword
#	AUG-2004, Paul Hick (UCSD/CASS; pphick@ucsd.edu)
#		Replace -sudo by -nosudo.
#-
"""
import sys, os, tempfile, glob
from datetime import datetime

from tiny import hide_env, args, start, is_there, count_instances, tomb
from burndisk import burndisk

datetime.now()

def burnfiles_remove( burndir, verbose ):

	# Delete files and burndir

	for file in os.listdir(burndir):
		tmp = os.path.join(burndir, file)
		if verbose:
			print 'remove', hide_env(tmp)
		os.remove( tmp )

	os.rmdir( burndir )
	if verbose:
		print 'remove', hide_env(burndir)

	return

def burnfiles_move( burndir, source, verbose, hardmove ):

	if hardmove:
		print 'hard move across devices\n'

	for file in os.listdir(burndir):
		srce = os.path.join(burndir, file)
		dest = os.path.join(source , file)
		if verbose:
			print '   ', hide_env(srce)
		if hardmove:
			os.spawnlp( os.P_WAIT, 'mv', 'mv', srce, dest )
		else:
			os.rename( srce, dest )

	if verbose:
		print '-->', hide_env(source)

	os.rmdir( burndir )
	if verbose:
		print 'remove', hide_env(burndir)

	return

def burnfiles_log( logfile, burndir, verbose, timestamp ):

	t = datetime.now()
	t = '%d'%t.year+'/%02d'%t.month+'/%02d'%t.day+' %02d'%t.hour+':%02d'%t.minute+':%02d'%t.second

	if logfile != '':
		if os.path.exists(logfile):
			content = (open(logfile,'r')).read()
			content = content.split('\n')
		else:
			content = []

		# Determine next sequence number from log file
		# Check for / in time stamp

		ndvd = 0
		for tmp in content:
			if tmp.find('/') != -1:
				ndvd = (tmp.split(' '))[0]
		ndvd = int(ndvd)+1

		print 'ID', ndvd, ' Time', t

		# Update logfile with info for new dvd

		content = ( ['', '%d'%ndvd+' '+t, ''] )

		lst = os.listdir(burndir)

		if timestamp:
			for i in range( len(lst) ):
				lst[i] = lst[i]+' %d'%((os.stat(os.path.join(burndir, lst[i]))).st_mtime)

		content.extend( lst )

		if os.path.exists(logfile):
			(open(logfile,'a')).write( '\n'.join(content)+'\n')
		else:
			(open(logfile,'w')).write( '\n'.join(content)+'\n')
		
	return

#===========

if __name__ == '__main__':

	arg  = args( sys.argv )
	narg = len(arg)

	sort     = is_there( '-sort'     , sys.argv )
	move	 = is_there( '-move'	 , sys.argv )
	remove   = is_there( '-remove'   , sys.argv )
	hardmove = is_there( '-hardmove' , sys.argv )
	test     = is_there( '-test'	 , sys.argv )
	clean    = is_there( '-clean'    , sys.argv )
	logfile  = start   ( '-logfile=' , sys.argv )
	before   = start   ( '-before='  , sys.argv )
	checksum = start   ( '-checksum=', sys.argv )
	unblock  = is_there( '-unblock'  , sys.argv )

	if not unblock:
		if count_instances( 'l1a.py coreftp.plh.af.mil', 1 ) >= 1:
			sys.exit()

	check_time = before != ''
	if check_time:
		import time
		cutoff_time = time.time()-int(before)*86400

	pattern	 = start   ( '-pattern=', sys.argv )
	if pattern == '': pattern = '*'

	make_dvd  = is_there( '-dvd'      , sys.argv )
	make_img  = is_there( '-img'      , sys.argv )
	verbose   = is_there( '-verbose'  , sys.argv )
	timestamp = is_there( '-timestamp', sys.argv )

	burn_args = dict	\
		( [				\
		  ( 'make_tar', is_there( '-tar'   , sys.argv ) ),	\
		  ( 'make_dvd', is_there( '-dvd'   , sys.argv ) ),	\
		  ( 'make_img', is_there( '-img'   , sys.argv ) ),	\
		  ( 'nosudo'  , is_there( '-nosudo', sys.argv ) ),	\
		  ( 'verbose' , is_there( '-verbose',sys.argv ) ),	\
		  ( 'tempdir' , start   ( '-temp=' , sys.argv ) ),	\
		  ( 'count'   , start   ( '-count=', sys.argv ) ),	\
		  ( 'speed'   , start   ( '-speed=', sys.argv ) ),	\
		  ( 'dev'     , start   ( '-dev='  , sys.argv ) ),	\
		  ( 'mode'    , start   ( '-mode=' , sys.argv ) ),	\
		  ( 'email'   , start   ( '-email=', sys.argv ) ),	\
		  ( 'exe'     , start   ( '-exe='  , sys.argv ) )	] )

	max_disk_size = start( '-max_disk_size=', sys.argv )
	min_disk_size = start( '-min_disk_size=', sys.argv )
	min_file_size = start( '-min_file_size=', sys.argv )

	if narg > 1:

		source = arg[1]
		if source == '.':
			source = os.getcwd()

		if narg > 2:
			destination = arg[2]
			if destination == '.':
				destination = os.getcwd()
		else:
			destination = source

	else:

		source = raw_input('Directory: ')
		source = os.path.expandvars( source )

		destination = source

	if not os.path.isdir(source):
		print 'source is not a directory: ', source
		sys.exit(1)

	if not os.path.isdir(destination):
		print 'destination is not a directory: ', destination
		sys.exit(1)

	# Pick up list of *.buf files in source until the next
	# file pushes the total file above l1a_size_max.
	# l1a: list of file names (no path)

	# 650MB CD: max
	#		681443328	(OK)
	#		682491904	(too big)
	# 4.5GB DVD:	4707186688	(OK)
	#		4707608576	(too big)

	if max_disk_size == '':
		if make_dvd:
			max_disk_size = '4489MB'	# 4500MB
		else:
			max_disk_size =  '650MB'	# 650 MB

	if min_disk_size == '':
		if make_dvd:
			min_disk_size = '4200MB'
		else:
			min_disk_size =  '600MB'

	if min_file_size == '':
		min_file_size = '500kB'

	# Convert to MB as floating point numbers

	max_disk_size = tomb( max_disk_size )
	min_disk_size = tomb( min_disk_size )
	min_file_size = tomb( min_file_size )

	pattern = os.path.join( source, pattern ) 
	file_list = glob.glob( pattern )

	if len(file_list) == 0:
		print 'no files found matching', pattern
		sys.exit()

	if sort:
		file_list.sort()

	# Name for temporary directory to put the files.

	tempfile.tempdir = source
	burndir = tempfile.mktemp()

	size = 0.0
	max_flag = 0

	if not test:

		# Create temp directory to be put on DVD

		os.mkdir( burndir )

		# Put a copy of the logfile on the CD/DVD. This is the old copy
		# of the logfile (the current CD/DVD has not yet been entered).

		if logfile != '':
			if os.path.exists( logfile ):
				os.spawnlp( os.P_WAIT, 'cp', 'cp', logfile, burndir )
				size = size+tomb( '%d'%(os.stat(logfile)).st_size )

		if checksum != '':
			if glob.glob(checksum):
				for file in glob.glob(checksum):
					os.spawnlp( os.P_WAIT, 'cp', 'cp', file , burndir)
					size = size+tomb( '%d'%(os.stat(file)).st_size )

	for file in file_list:

		if check_time:
			if (os.stat(file)).st_mtime > cutoff_time:
				print '    Too recent:', hide_env(file)
				print '    Release in:', ((os.stat(file)).st_mtime - cutoff_time)/3600, 'hours'
				break

		file_size = tomb( '%d'%(os.stat(file)).st_size )

		# Keep adding files until max_disk_size is reached
		# The selected files are moved into the temporary subdirectory
		# 'burndir' of 'source'

		# Initially (max_flag = False) files are added until max_disk_size is
		# reached. Once a file is encountered that pushed the total size
		# above max_disk_size (max_flag = True) files are only added if they
		# are larger than min_file_size.

		new_size = size+file_size

		if new_size > max_disk_size:

			if clean:
				break

			max_flag = 1

		elif not max_flag or file_size > min_file_size:

			print '   ', hide_env(file), round(file_size,6), 'MB'
			if not test:
				os.rename( file, os.path.join(burndir, (os.path.split(file))[1]) )
			size = new_size
 
	if not test:
		print '-->', hide_env(burndir)

	if size < min_disk_size:

		print '\nTotal size is', round(size,3), '/', max_disk_size, 'MB\n',\
			  'Total size is less than', min_disk_size, 'MB\n',			\
			  'No disk was burned. Override with -min_disk_size=<value>.\n'

		if not test:
			burnfiles_move( burndir, source, verbose, 0 )

	elif test:

		print '\nTotal size is', round(size,3), '/', max_disk_size, 'MB\n'

	else:					# Burn the disk.

		print '\nTotal size is', round(size,3), '/', max_disk_size, 'MB\n'

		try:
			status = burndisk([burndir], burn_args)

		except:				# If something goes wrong, put the files back

			burnfiles_move( burndir, source, verbose, 0 )
			print 'burnfiles, problem in burndisk. cdrecord key expired ???'
			status = 1

		else:

			if make_img:		# Create CD-image only
				if move:
					burnfiles_move( burndir, destination, verbose, hardmove )
				else:
					burnfiles_move( burndir, source, verbose, 0 )

			elif status != 0:	# Burndisk returned error status
				burnfiles_move( burndir, source, verbose, 0 )

			else:
				burnfiles_log( logfile, burndir, verbose, timestamp )

				if remove:	# Remove files and burndir
					burnfiles_remove ( burndir, verbose )
				else:		# Move files to destination
					burnfiles_move( burndir, destination, verbose, hardmove )

	sys.exit()
