"""
Q7Z: P7Zip GUI
Designed by Chris Giles

File:		Thread.py
Purpose:	Thread handler
"""


### Imports

# Q7Z 1
import	Import

# PyQt4
try :
	from	PyQt4	import QtCore
	# Visible?
	if not Import.Init.bInvisible :
		from	PyQt4	import QtGui
except :
	Import.slException( "PyQt4" )

# Q7Z 2
import	Display, Main, Profile, Settings


### Threads

# Create
class tCreate( QtCore.QThread ) :

	def create( self ) :

		# Declare process
		self.pArc = QtCore.QProcess()

		# Declare paths
		dSource = QtCore.QDir()
		dOutput = QtCore.QDir()

		# Initialise command arguments
		slArcCmdArgs = QtCore.QStringList()
		slArcCmdArgs.clear()

		# Source
		dSource.setPath( self.oAI.Source.slFiles.__getitem__(self.oAI.Source.iNext) )

		# Archive name
		sArcName = QtCore.QString()
		# Post tarball and BZ2 or GZ
		if not self.oAI.Type.bTarball and ( self.oAI.Type.sAlgorithm == "BZip2" or self.oAI.Type.sAlgorithm == "GZip" ) :
			sArcName = dSource.dirName()
		# User specified
		elif self.oAI.Dest.bName and not self.oAI.Dest.sName.isEmpty() :
			sArcName = self.oAI.Dest.sName
		# One input only, separate archives or post-tarball
		elif self.oAI.Source.slFiles.count() == 1 or self.oAI.Dest.bSeparate :
			sArcName = dSource.dirName()
		# Multiple inputs and only one archive
		else :
			sArcName = Settings.Application.sName

		dSource.cdUp()

		# Command
		sCmd = Settings.Command.s7z

		# Algorithm
		if self.oAI.Type.bTarball or self.oAI.Type.sAlgorithm == "Tar" :

			# Linux
			if Settings.sOS == "Linux" :

				# Command
				sCmd = Settings.Command.sTar

				# Exclusions
				slWildcards = QtCore.QStringList( Settings.Setting.Exclusions.sWildcards.split( ' ' ) )
				while not slWildcards.isEmpty() and not slWildcards.first().isEmpty() :
					slArcCmdArgs.append( "--exclude=" + slWildcards.takeFirst() )

				# Working directory
				slArcCmdArgs.append( "-cC" )
				slArcCmdArgs.append( dSource.path() )

				# Use relative paths for tarball contents
				if not self.oAI.Dest.bSeparate :
					for iEntry in range(0,self.oAI.Source.slFiles.count()) :
						self.oAI.Source.slFiles.replace( iEntry , dSource.relativeFilePath( self.oAI.Source.slFiles.__getitem__( iEntry ) ) )
				else :
					self.oAI.Source.slFiles.replace( self.oAI.Source.iNext , dSource.relativeFilePath( self.oAI.Source.slFiles.__getitem__( self.oAI.Source.iNext ) ) )

				# Output file
				slArcCmdArgs.append( "-f" )

			else :

				# Format
				slArcCmdArgs.append( "-ttar" )

				# Exclusions
				slWildcards = QtCore.QStringList( Settings.Setting.Exclusions.sWildcards.split( ' ' ) )
				while not slWildcards.isEmpty() and not slWildcards.first().isEmpty() :
					slArcCmdArgs.append( "-x!" + slWildcards.takeFirst() )

				# Method
				slArcCmdArgs.append( "u" )
				slArcCmdArgs.append( "-uq0" )

				# Yes to all
				slArcCmdArgs.append( "-y" )

				# Recursive
				slArcCmdArgs.append( "-r" )

			sExt = ".tar"

		else :

			# Format
			if self.oAI.Type.sAlgorithm == "7-Zip" :
				slArcCmdArgs.append( "-t7z" )
				sExt = ".7z"

				# Scrambled
				if not self.oAI.Security.bScramble :
					slArcCmdArgs.append( "-mhe=off" )
				else :
					slArcCmdArgs.append( "-mhe=on" )

				# Solid
				if not self.oAI.Type.bSolid :
					slArcCmdArgs.append( "-ms=off" )
				else :
					slArcCmdArgs.append( "-ms=on" )

				# Volumes
				if self.oAI.Type.sVolume == "Floppy" :
					slArcCmdArgs.append( "-v1440k" )
				elif self.oAI.Type.sVolume == "CD" :
					slArcCmdArgs.append( "-v720000k" )
				elif self.oAI.Type.sVolume == "DVD" :
					slArcCmdArgs.append( "-v4700000000b" )

			elif self.oAI.Type.sAlgorithm == "BZip2" :
				slArcCmdArgs.append( "-tbzip2" )
				sExt = ".bz2"
			elif self.oAI.Type.sAlgorithm == "Zip" :
				slArcCmdArgs.append( "-tzip" )
				sExt = ".zip"
			elif self.oAI.Type.sAlgorithm == "GZip" :
				slArcCmdArgs.append( "-tgzip" )
				sExt = ".gz"

			# Compression level
			if self.oAI.Type.sComp == "None" :
				slArcCmdArgs.append( "-mx0" )
			elif self.oAI.Type.sComp == "Fastest" :
				slArcCmdArgs.append( "-mx1" )
			elif self.oAI.Type.sComp == "Fast" :
				slArcCmdArgs.append( "-mx3" )
			elif self.oAI.Type.sComp == "Normal" :
				slArcCmdArgs.append( "-mx5" )
			elif self.oAI.Type.sComp == "Maximum" :
				slArcCmdArgs.append( "-mx7" )
			elif self.oAI.Type.sComp == "Ultra" :
				slArcCmdArgs.append( "-mx9" )

			# Method
			if self.oAI.Type.sMethod == "Update" :
				slArcCmdArgs.append( "u" )
			elif self.oAI.Type.sMethod == "Sync" :
				slArcCmdArgs.append( "u" )
				slArcCmdArgs.append( "-uq0" )
			elif self.oAI.Type.sMethod == "Add" :
				slArcCmdArgs.append( "a" )

			# Yes to all
			slArcCmdArgs.append( "-y" )

			# Password
			if self.oAI.Security.bPassword and not self.oAI.Security.sPassword.isEmpty() :
				slArcCmdArgs.append( "-p" + self.oAI.Security.sPassword )

			# SFX
			if self.oAI.Type.bSFX :
				slArcCmdArgs.append( "-sfx" )
				sExt = ".exe"

			# Recursive
			slArcCmdArgs.append( "-r" )

			# Exclusions
			slWildcards = QtCore.QStringList( Settings.Setting.Exclusions.sWildcards.split( ' ' ) )
			while not slWildcards.isEmpty() and not slWildcards.first().isEmpty() :
				slArcCmdArgs.append( "-x!" + slWildcards.takeFirst() )

		# Destination
		if not self.oAI.Dest.bDest or self.oAI.Dest.sDir.isEmpty() or self.oAI.Type.bTarball :
			dOutput.setPath( dSource.path() + Settings.Path.sSep )
		else :
			dOutput.setPath( self.oAI.Dest.sDir + Settings.Path.sSep )

		# Structured
		if self.oAI.Dest.bStructured and not self.oAI.Type.bTarball :
			dOutput.setPath( dOutput.path() + Settings.Path.sSep + dSource.path().replace( ':' , '' ) + Settings.Path.sSep )

		dOutput.setPath( dOutput.path() + Settings.Path.sSep + sArcName + sExt )
		self.oAI.Dest.sFullPath = dOutput.path()

		# Append archive and file(s)
		slArcCmdArgs.append( dOutput.path() )

		# Input
		# Post-tarball
		if self.oAI.Dest.bSeparate or not self.oAI.Type.bTarball and ( self.oAI.Type.sAlgorithm == "BZip2" or self.oAI.Type.sAlgorithm == "GZip" ) :
			slArcCmdArgs.append( self.oAI.Source.slFiles.__getitem__(self.oAI.Source.iNext) )
		# Single archive
		else :
			slArcCmdArgs << self.oAI.Source.slFiles

		# Volumes
		if self.oAI.Type.sVolume != "Unlimited" :
			self.oAI.Dest.sFullPath += ".001"

		# Verbose
		if Settings.Init.bVerbose is True :
			print "Creating ..."
			print "Input = " + self.oAI.Source.slFiles.__getitem__(self.oAI.Source.iNext)
			print "Archive = " + self.oAI.Dest.sFullPath

			# Command Arguments
			print "Command = " + sCmd
			print "Arguments ="
			for iEntry in range(0,slArcCmdArgs.count()) :
				print "\t" + slArcCmdArgs.__getitem__( iEntry )

		# Execute process
		self.pArc.start( sCmd , slArcCmdArgs )

		if self.pArc.waitForStarted( Settings.Timer.Process.iPause ) :

			# Verbose
			if Settings.Init.bVerbose is True :
				print "Create: Started"

			iWaitCount = 0

			while not self.pArc.waitForFinished( Settings.Timer.Process.iPause ) :
				if iWaitCount >= Settings.Timer.Process.iWaitMax :
					# Verbose
					if Settings.Init.bVerbose is True :
						print "tCreate: Timed Out"
					break
				iWaitCount += 1
				# Search for percentage indicator
				#sArcData = QtCore.QString( self.pArc.readData(512) )

			# Analyse exit-code
			if self.pArc.exitCode() is not 0 :

				# Progress
				self.oAI.iProgress = 0
				self.oAI.bSuccess = False

				sError = str(self.pArc.readAllStandardOutput())
				if sError == "" and sExt != ".tar" :
					sError = "Error: Unspecified<br>Perhaps a segmentation fault occurred."

				self.emit( QtCore.SIGNAL( "error(QString)" ) , sError )

				print "Create: Failure: "  + str(self.oAI.Source.iNext) + ": " + self.oAI.Source.slFiles.__getitem__(self.oAI.Source.iNext)
				print ""

				# Invisible?
				if Import.Init.bInvisible :
					Display.slExit()

			else :

				# Progress
				if not self.oAI.Dest.bSeparate :
					self.oAI.iProgress = 100
				else :
					self.oAI.iProgress = 100 * (self.oAI.Source.iNext+1) / self.oAI.Source.slFiles.count()

				# Adjust percentage
				if self.oAI.bTest :
					self.oAI.iProgress /= 2

				if self.oAI.Type.bTarball :

					# Progress
					if not self.oAI.Dest.bSeparate :
						self.oAI.iProgress /= 2
					else :
						self.oAI.iProgress -= 50 / self.oAI.Source.slFiles.count()

					self.oAI.Type.bTarball = False
					self.oAI.Source.slFiles.replace( self.oAI.Source.iNext , self.oAI.Dest.sFullPath )

				else :

					# Delete
					if self.oAI.Source.slFiles.__getitem__(self.oAI.Source.iNext).endsWith( ".tar" ) :
						QtCore.QFile.remove( self.oAI.Source.slFiles.__getitem__(self.oAI.Source.iNext) )

					# Verbose
					if Settings.Init.bVerbose is True :
						print "Create: Success: " + str(self.oAI.Source.iNext)
						print ""

					"""
					# Separate
					if self.oAI.Dest.bSeparate :
						self.oAI.Source.iNext += 1
					"""

			self.emit( QtCore.SIGNAL( "done(int)" ) , self.oAI.iProgress )

		else :
			self.emit( QtCore.SIGNAL( "error(QString)" ) , "Error: Could not start 'P7Zip'; View the INSTALL file for more info." )

		#return self.oAI


	def stop( self ) :

		# Stop archiving
		if self.pArc.state() == QtCore.QProcess.Running :
			self.pArc.kill()
		else :
			tiExtract.stop()

		# Exit
		self.exit()


	def run( self ) :

		self.setPriority( QtCore.QThread.LowPriority )

		# Process
		#self.pArc = QtCore.QProcess()

		# Profile
		#self.oAI = QtCore.QObject()
		self.oAI = Profile.oInfo

		# Operation
		self.oAI.iOperation = Settings.Operation.iCreate

		# GUI
		Display.slGuiProfileGetCreate( self.oAI )

		# Execute archiver
		for self.oAI.Source.iNext in range(0,self.oAI.Source.slFiles.count()) :

			# Set GUI
			self.emit( QtCore.SIGNAL( "operation(int)" ) , Settings.Operation.iCreate )

			# Tarball first
			if self.oAI.Type.sAlgorithm == "BZip2" or self.oAI.Type.sAlgorithm == "GZip" :
				self.oAI.Type.bTarball = True

			# Create tarball first?
			if self.oAI.Type.bTarball :
				self.create()

			# Create archive
			if self.oAI.bSuccess :
				self.create()
			else :
				break

			if self.oAI.bSuccess :

				if not self.oAI.Dest.bSeparate :

					# Test the archive
					if self.oAI.bTest :

						# Operation
						self.oAI.iSource = Settings.Operation.iCreate
						self.oAI.iOperation = Settings.Operation.iTest

						# Get GUI
						Display.slGuiProfileGetExtract( self.oAI )

						# Archive
						self.oAI.Source.slFiles.clear()
						self.oAI.Source.slFiles.append( self.oAI.Dest.sFullPath )

						# Set GUI
						self.emit( QtCore.SIGNAL( "operation(int)" ) , Settings.Operation.iTest )

						# Test
						tiExtract.extract( self.oAI )

					break

				else :
					self.oAI.Source.iNext += 1

			else :
				break

			# Start event loop
			#self.exec_()

		else :
			self.emit( QtCore.SIGNAL( "error(QString)" ) , "" )

		# Invisible?
		if Import.Init.bInvisible :
			Display.slExit()


# Extract the archive
class tExtract( QtCore.QThread ) :

	def extract( self , oAI ) :

		# Declare process
		self.pArc = QtCore.QProcess()

		# Declare paths
		dSource = QtCore.QDir()
		dOutput = QtCore.QDir()

		# Initialise command arguments
		slArcCmdArgs = QtCore.QStringList()
		slArcCmdArgs.clear()

		# Command
		sCmd = Settings.Command.s7z

		# Source
		dSource.setPath( oAI.Source.slFiles.first() )
		dSource.makeAbsolute()

		# Beneath
		sBeneath = dSource.dirName()
		sBeneath.resize( sBeneath.lastIndexOf( '.' ) )
		dSource.cdUp()

		# Destination
		if not oAI.Dest.bDest or oAI.Dest.sDir.isEmpty() :
			dOutput.setPath( dSource.path() + Settings.Path.sSep )
		else :
			dOutput.setPath( oAI.Dest.sDir + Settings.Path.sSep )

		# Filename
		if oAI.Dest.bName :
			dOutput.setPath( dOutput.path() + Settings.Path.sSep + oAI.Dest.sName )

		if oAI.Dest.bBeneath :
			dOutput.setPath( dOutput.path() + Settings.Path.sSep + sBeneath )

		# Algorithm
		if Settings.sOS == "Linux" and oAI.Source.slFiles.first().endsWith( ".tar" ) \
			and oAI.iOperation == Settings.Operation.iExtract :

			# Command
			sCmd = Settings.Command.sTar

			# Extract
			slArcCmdArgs.append( "-xf" )

			# Archive
			slArcCmdArgs.append( oAI.Source.slFiles.first() )

			# Output directory
			if not dOutput.exists() :
				dOutput.mkpath( dOutput.path() )

			slArcCmdArgs.append( "-C" )
			slArcCmdArgs.append( dOutput.path() + Settings.Path.sSep )

		else :

			# Extract or test
			if oAI.iOperation == Settings.Operation.iExtract :
				slArcCmdArgs.append( "x" )
			else :
				slArcCmdArgs.append( "t" )

			# Answer 'yes' to all queries
			slArcCmdArgs.append( "-y" )

			# Recursive
			slArcCmdArgs.append( "-r" )

			# Exclusions
			slWildcards = QtCore.QStringList( Settings.Setting.Exclusions.sWildcards.split( ' ' ) )
			while not slWildcards.isEmpty() and not slWildcards.first().isEmpty() :
				slArcCmdArgs.append( "-x!" + slWildcards.takeFirst() )

			# Output directory
			slArcCmdArgs.append( "-o" + dOutput.path() + Settings.Path.sSep )

			# Password
			if oAI.Security.bPassword and not oAI.Security.sPassword.isEmpty() :
				slArcCmdArgs.append( "-p" + oAI.Security.sPassword )
			else :
				slArcCmdArgs.append( "-pNoPassword" )

			# Archive
			slArcCmdArgs.append( oAI.Source.slFiles.first() )

		# Verbose
		if Settings.Init.bVerbose is True :
			print "Extracting ..."
			print "Operation = " + str( oAI.iOperation )
			print "Archive = " + oAI.Source.slFiles.first()
			print "Destination = " + dOutput.path() + Settings.Path.sSep

			# Command Arguments
			print "Command = " + sCmd
			print "Arguments ="
			for iEntry in range(0,slArcCmdArgs.count()) :
				print "\t" + slArcCmdArgs.__getitem__( iEntry )

		# Execute process
		self.pArc.start( sCmd , slArcCmdArgs )

		if self.pArc.waitForStarted( Settings.Timer.Process.iPause ) :

			# Verbose
			if Settings.Init.bVerbose is True :
				print "Extract/Test: Started"

			iWaitCount = 0

			while not self.pArc.waitForFinished( Settings.Timer.Process.iPause ) :
				if iWaitCount >= Settings.Timer.Process.iWaitMax :
					# Verbose
					if Settings.Init.bVerbose is True :
						print "tExtract: Timed Out"
					break
				iWaitCount += 1
				"""
				# Get output
				sArcData = QtCore.QString( self.pArc.readData(512) )

				# Password?
				if sArcData.contains( "Enter password" ) :

					# Verbose
					if Settings.Init.bVerbose is True :
						print "Password Requested"

					idPassword = QtGui.QInputDialog()
					Main.uiMain.leEPassword.setText( idPassword.getText( QtGui.QWidget, "Q7Z: Enter Password" , "Q7Z: Enter Password" ) )
				"""

			# Analyse exit-code
			if self.pArc.exitCode() is not 0 :

				oAI.iProgress = 0
				oAI.bSuccess = False

				sError = str(self.pArc.readAllStandardOutput())
				if sError == "" :
					sError = "Error: Unspecified<br>Perhaps a segmentation fault occurred."

				self.emit( QtCore.SIGNAL( "error(QString)" ) , sError )

				print "Extract/Test: Failure: " + oAI.Source.slFiles.first()
				print ""

				# Invisible?
				if Import.Init.bInvisible :
					Display.slExit()

			else :

				oAI.iProgress = 100
				#oAI.bSuccess = True

				# Verbose
				if Settings.Init.bVerbose is True :
					print "Extract/Test: Success"
					print ""

			self.emit( QtCore.SIGNAL( "done(int)" ) , oAI.iProgress )

		else :
			self.emit( QtCore.SIGNAL( "error(QString)" ) , "Error: Could not start 'P7Zip'; View the INSTALL file for more info." )

		return oAI


	def stop( self ) :

		try :
			# Stop archiving
			if self.pArc.state() == QtCore.QProcess.Running :
				self.pArc.kill()
		except :
			pass

		# Exit
		self.exit()


	def run( self ) :

		self.setPriority( QtCore.QThread.LowPriority )

		# Process
		#self.pArc = QtCore.QProcess()

		# Profile
		#self.oAI = QtCore.QObject()
		self.oAI = Profile.oInfo

		# Operation
		self.oAI.iSource = Settings.Operation.iExtract

		# Get GUI
		Display.slGuiProfileGetExtract( self.oAI )

		# Execute archiver
		if not self.oAI.Source.slFiles.first().isEmpty() :

			# Extract or test
			if self.oAI.bTest :
				self.oAI.iOperation = Settings.Operation.iTest
				self.emit( QtCore.SIGNAL( "operation(int)" ) , Settings.Operation.iTest )
			else :
				self.oAI.iOperation = Settings.Operation.iExtract
				self.emit( QtCore.SIGNAL( "operation(int)" ) , Settings.Operation.iExtract )

			# Extract/Test archive
			self.extract( self.oAI )

			# Start event loop
			self.exec_()

		else :
			self.emit( QtCore.SIGNAL( "error(QString)" ) , "" )

		# Invisible?
		if Import.Init.bInvisible :
			Display.slExit()


### Variables

tiCreate	= tCreate()
tiExtract	= tExtract()


### Slots

# Start the 'create' thread
def slCreate() :

	# Stop
	slArcStop()

	# Disable GUI
	Display.slDisableGUI()

	# Start
	tiCreate.start()
	#tiCreate.setPriority( QtCore.QThread.LowPriority )
	tiCreate.msleep( 50 )

	# Invisible?
	if Import.Init.bInvisible :
		# Start event loop
		tiCreate.exec_()


# Start the 'extract' thread
def slExtract() :

	# Stop
	slArcStop()

	# Disable GUI
	Display.slDisableGUI()

	# Start
	tiExtract.start()
	#tiExtract.setPriority( QtCore.QThread.LowPriority )
	tiExtract.msleep( 50 )


# Finished
def slArcDone( iRetVal ) :

	#print "slArcDone()"

	# Exit threads
	#slArcExit()

	# Progress
	Display.slProgress( iRetVal )

	# Reset GUI
	if iRetVal == 100 :
		Display.slEnableGUI()

	# Default and storage
	if iRetVal == 100 and Settings.Input.iNumArgs >= 3 :
		if Settings.Init.bAutoExit :
			Display.slExit()


# Error
def slError( sError ) :

	#print "slError"

	# Stop
	slArcStop()

	# Error
	Display.slError( sError )


# Stop Archiving
def slArcStop() :

	#print "slArcStop()"

	# Exit threads
	slArcExit()

	# Reset GUI
	Display.slEnableGUI()


# Exit threads
def slArcExit() :

	#print "slArcExit()"

	# Create
	if tiCreate.isRunning() :
		#tiCreate.stop()
		tiCreate.exit()
		tiCreate.wait( Settings.Timer.Process.iPause / 2 )

	# Extract
	if tiExtract.isRunning() :
		#tiExtract.stop()
		tiExtract.exit()
		tiExtract.wait( Settings.Timer.Process.iPause / 2 )
