#!/usr/bin/env python

import os, sys, time

import dircollection, touchlogger, partitioner


class Error(Exception):
	pass


def buildBurnTree(dirCollection, treeRoot, verbose=0):
	for dir in dirCollection:
		for fName in dir:
			targetName = os.path.join(treeRoot, dir.getDirName(), fName)
			if not os.path.exists(os.path.dirname(targetName)):
				os.makedirs(os.path.dirname(targetName))
			if verbose:
				print "Linking %s to %s"%(os.path.join(dir.getRootDir(), 
					dir.getDirName(), fName), targetName)
			os.symlink(os.path.join(dir.getRootDir(), dir.getDirName(),
				fName), targetName)


def _makeBurnTrees(options, newFiles, burntDb):
	dc = dircollection.DirCollection(newFiles, 
		newFiles.getRootDir(), topLevel=options.topLevel)
	numCDs = dc.getTotalSize()/options.containerSize
	print "Total size: %d; will create %d trees"%(dc.getTotalSize(),
		numCDs)
	cdDirs = [partitioner.Container(options.containerSize) 
		for i in range(numCDs)]
	if len(cdDirs)>1 and not "%d" in options.treesRoot:
		raise Error, "More than one tree but no %d in trees-root"

	p = partitioner.Partitioner(cdDirs, dc)
	p.partition()

	for cdInd, dirCollection in enumerate(cdDirs):
		if "%d" in options.treesRoot:
			targName = options.treesRoot%cdInd
		else:
			targName = options.treesRoot
		if os.path.exists(targName):
			raise Error("Tree %s already exists"%targName)

		buildBurnTree(dirCollection, targName)

		processedFiles = touchlogger.RelativeFileSet(targName,
			setName=time.strftime("%y%m%d%H%M%S"))
		burntDb.update(processedFiles)
		print "Made tree %s with setname %s"%(targName, 
			processedFiles.getSetName())


def _makeOptionParser():
	import optparse, sizeoption
	parser = optparse.OptionParser(usage="%prog [options] checkroot\n"
		"searches new files wrt. burnt db and builds trees of"
		" symlinks to\nthe new files sutiable for burning on CDs",
		option_class=sizeoption.SizeOption)
	parser.add_option("-a", "--add-only", action="store_true",
		dest="addOnly", help="Only add files to db, don't build burn trees",
		default=False)
	parser.add_option("-c", "--container-size", dest="containerSize",
		default=700*1000*1000, help="Make containers BYTES bytes (k,m,g allowed)"
		" large", metavar="BYTES", type="size")
	parser.add_option("-d", "--db-file", dest="dbFile",
		default="burntDb", help="Use FILE as database for burnt files")
	parser.add_option("-n", "--no-update", dest="storeDb",
		action="store_false", help="Don't update burntDb", default=True)
	parser.add_option("-p", "--toplevel", dest="toplevelDir", 
		default=None, help="use DIR as checkroot instead of argument (useful"
		" when you're adding files from a directory different from checkroot)",
		metavar="DIR")
	parser.add_option("-r", "--trees-root", dest="treesRoot", 
		default="/usr/media/cdimg%d", help="Build burn trees in DIR"
		" (which should contain a %d if there's more than one CD)",
		metavar="DIR")
	parser.add_option("-t", "--top-level", dest="topLevel",
		action="store_true", help="Partition on top-level (instead of"
		" bottom-level) directories", default=False)
	return parser


def main():
	optparser =_makeOptionParser()
	options, args = optparser.parse_args()
	if len(args)!=1:
		optparser.print_help()
		sys.exit(0)

	if options.toplevelDir is None:
		rootDir = args[0]
	else:
		rootDir = options.toplevelDir
	try:
		burntDb = touchlogger.DbFileSet(options.dbFile)
	except IOError:
		sys.stderr.write("Creating new burnt DB %s.\n"%options.dbFile)
		burntDb = touchlogger.DbFileSet()

	newFiles = touchlogger.RelativeFileSet(rootDir,
		collectDir=args[0],
		setName=time.strftime("%y%m%d%H%M%S"))
	newFiles.diffInPlace(burntDb)
	if not options.addOnly:
		_makeBurnTrees(options, newFiles, burntDb)
	else:
		burntDb.update(newFiles)
	if options.storeDb:
		burntDb.save(options.dbFile)


if __name__=="__main__":
	import traceback
	try:
		main()
	except Exception, msg:
		if not isinstance(msg, SystemExit):
			traceback.print_exc()
			sys.stderr.write("%s: Fatal error: %s\n"%(sys.argv[0], msg))
		sys.exit(1)
