#!/usr/bin/env python3.3

import os, sys, dvdtrackrip, string, re, argparse, shutil

class MapParseError(Exception):
	"""Exception raised for nonvalid mapfile Sysntax.

	Attributes:
		linenumber --
		line -- Content of Line where error occurs

	"""
	def __init__(self, linenumber, line):
		self.linenumber = linenumber
		self.line = line

def printwarning(message):
	sys.stderr.write('\033[93m' + message + '\033[0m' '\n')

def printerror(message):
	sys.stderr.write('\033[91m' + message + '\033[0m' + '\n')

def getTracksInThreshold(dvdSourceInfo, thresholds):
	#threshold must be an array, but at the moment only the two first values are used
	tracksInThreshold = []
	#if only one threshold is passed, the value is used for both borders
	if len(thresholds) is 1:
		thresholds.append(str(int(thresholds[0].strip(string.ascii_letters)) + 1)+thresholds[0].strip(string.digits))
	#if the threshold contains a 'm', 'h' suffixes the values are counted as minutes or hours
	#for comparing with lsdvd these are converted in seconds
	for i, value in enumerate(thresholds):
		if 'm' in value:
			thresholds[i] = int(value.strip(string.ascii_letters)) * 60
		if 'h' in value:
			thresholds[i] = int(value.strip(string.ascii_letters)) * 3600

	for index in dvdSourceInfo['track']:
		if round(index['length']) in range( int(thresholds[0]), int(thresholds[1]) ):
			tracksInThreshold += [index['ix']]
	return tracksInThreshold

def parseMapFile(mapfile):
	source_dest_file = open(mapfile, "r")
	source_dest_data = source_dest_file.readlines()
	source_dest_file.close()
	source_dest_map = []
	entryCounter = 0

	for i, line in enumerate(source_dest_data):
		if not re.search(r"^\ *#", line):

			#DVD Source Infos
			match = re.search(r"^([^\t@]*)@([^@]*)@(.*)$", line)
			if match:
				source_dest_map += [{'source_part': match.group(1), 'options': match.group(2), 'dest_part': match.group(3), 'tracks': []}]
				entryCounter = 0

				parser = argparse.ArgumentParser()
				parser.add_argument('-t', '--threshold', nargs='*')
				args = parser.parse_args(source_dest_map[-1]['options'].split())

				dvdSourceInfo = dvdtrackrip.getDvdTrackInfo(os.path.join(dvdsource, source_dest_map[-1]['source_part'].strip('/')), 0)
				for index, absolutetrack in enumerate(getTracksInThreshold(dvdSourceInfo, args.threshold)):
					source_dest_map[-1]['tracks'] += [{'absolutetrack': absolutetrack, 'basename': str(absolutetrack)}]
				#print(source_dest_map[-1]['tracks'])

			#detailed per Trackinfos
			match = re.search(r"^\t([^@]*)@([^@]*)@(.*)$", line)
			if match:
				try:
					source_dest_map[-1]['tracks'] += [{'absolutetrack': match.group(1), 'options': match.group(2), 'basename': match.group(3)}]
				except (IndexError, TypeError):
					raise MapParseError(i, line)

			#simple per Trackinfos
			match = re.search(r"^\t([^@\n]*)$", line)
			if match:
				try:
					source_dest_map[-1]['tracks'][entryCounter]['basename'] = match.group(1).strip()
					entryCounter += 1
				except (IndexError, TypeError):
					raise MapParseError(i, line)

			#Blanklines define the end of a dvdsource
			match = re.search(r"^\s$", line)
			if match:
				source_dest_map += [None]

	for dvdstructure in source_dest_map:
		if dvdstructure is None:
			source_dest_map.remove(dvdstructure)
	return source_dest_map


if __name__ == "__main__":
	#BEGIN main
	try:
		parser = argparse.ArgumentParser(description='rip a specific track from a DVD Strukture into a mkv container without reencoding' )

		parser.add_argument("-s", "--source", metavar="DVDSOURCE", help="the DVD sourcestrukture where to rip from")
		parser.add_argument("-m","--mapfile", metavar="MAPFILE", help="a Mapfile")

		parser.add_argument("-t", "--tracksToRip", metavar="TRACK", nargs='*', type=int, help="the Track on the DVD to rip; if omit all tracks are ripped")
		parser.add_argument("-n", "--name", default="", help="Name of the Track (without extension), or a Suffix for a whole disk")

		parser.add_argument("-d", "--destination", default=".", help="Directory where to save the ripped file")
		#parser.add_argument("-v", "--verbose", action="count", default=0, help="set verbosity level [default: %(default)s]")
		args = parser.parse_args()
		#print(args)

		if args.source is None and args.mapfile is None:
			parser.print_usage()
			parser.exit(status=1, message="No arguments were passed")

		if args.source is None:
			dvdsource = "."
		dvdsource = os.path.abspath(dvdsource)

		if os.access(args.destination, os.F_OK) and os.path.isdir(args.destination):
			destination = os.path.abspath(args.destination)
		else:
			raise FileNotFoundError("destination_directory:",destination)

		if args.mapfile is not None:
			for dvdstructure in parseMapFile(args.mapfile):
				for track in dvdstructure['tracks']:

					if not os.path.lexists( os.path.join(destination, dvdstructure['dest_part'].strip('/')) ):
						os.makedirs(os.path.join(destination, dvdstructure['dest_part'].strip('/')), exist_ok=True)

					print( "Ripping Track %d\tfrom DVD-Structure '%s'\tas '%s.mkv'\to '%s'" % \
						(track['absolutetrack'], os.path.join(dvdsource, dvdstructure['source_part']), \
						track['basename'], os.path.join(destination, dvdstructure['dest_part'])) )

					try:
						dvdtrackrip.dvdtrackrip( os.path.join(dvdsource, dvdstructure['source_part'].strip('/')), track['absolutetrack'], os.path.join(destination, dvdstructure['dest_part'], track['basename'] + ".mkv") )
					except shutil.Error as error:
						print("\033[92m", error.args[0], "\033[0m", file=sys.stderr)

		else:
			absolutetrack = args.tracksToRip
			episodebasename	= args.name

			dvdSourceInfo = dvdtrackrip.getDvdTrackInfo(dvdsource, 0)
			if args.tracksToRip is None:
				absolutetrack = list(range(1, len(dvdSourceInfo['track'])+1))

			for x in absolutetrack:
				if args.name is "" or len(absolutetrack) > 1:
					episodebasename = str(x)+"."+args.name

				#colored heading for current sequence
				print( "Ripping Track %d\tfrom DVD-Structure '%s'\tas '%s.mkv'\to '%s'" % (x, dvdsource, episodebasename, destination) )

				try:
					dvdtrackrip.dvdtrackrip(dvdsource, x, destination+ "/" +episodebasename + ".mkv")
				except shutil.Error as error:
					print("\033[92m", error.args[0], "\033[0m", file=sys.stderr)

	#END main

	except MapParseError as error:
		printerror("The mapfile seems not be valid!")
		printerror("Linenumber:" + str(error.args[0]) +  "->" + error.args[1])

	except dvdtrackrip.DvdTrackError as error:
		printerror("In Dvd source '" + error.args[0] + "' exists no Track number " + error.args[1])

	except dvdtrackrip.DvdSourceError as error:
		printerror("The path '" + error.args[0] + "' contains no valid DVD source")
		printerror("stderr: \n" + error.args[1])

	except FileNotFoundError as error:
		printerror("FileNotFound Error")
		printerror("Context:" + error.args[0])
		printerror("File:" + error.args[1])
		if error.args[0] is "dvdtrackrip":
			printerror(error.args[1] + "is missing, but it should be there thus converters seemd to work correct")

	except shutil.Error as error:
		printwarning(error.args[0])

	except SubProcessError as error:
		printerror( "\nCommand:" + error.args[0] )
		printerror( "\nSubprocessor stderr: " + error.args[1] )
		printerror( "\nReturncode:" + str(error.args[2]) )
