# copy_base.py
#
# Copyright (C) 2013 to 2014 Tim Marston <tim@edm.am>
#
# This file is part of stdhome (hereafter referred to as "this program").
# See http://ed.am/dev/stdhome for more information.
#
# This program 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.
#
# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.


import filecmp, os, shutil
from walker import Walker
import stdhome.the as the


class CopyBaseWalker( Walker ):
	"""The copy-base walker traverses a walklist ruthlessly mirroring src to dst.
	It is designed to be the base class of both the copy-in and copy-out walker,
	both of which are specialisations of this purpose.  See them for more
	information.
	"""


	def __init__( self ):
		self.check_src_symlinks = False
		self.check_dst_symlinks = False
		self.check_dst_ignores = False


	def process( self, rel_file, src, dst ):

		# ignore?
		if self.check_dst_ignores and the.config.ignores.matches( rel_file ):
			if the.verbose >= 2:
				self.print_op( rel_file, '%s#%s' % ( src.type, dst.type ) )
			return True

		# src entity is directory
		if src.type == 'd':

			# if dst entity doesn't exist, create directory (no need to recurse,
			# since we're copying the whole directory)
			if dst.type == '_':
				if the.verbose >= 2: self.print_op( rel_file, 'd>_' )
				shutil.copytree( src.file, dst.file, True )

			# if dst entity is a directory, copy permissions, as required (and
			# recurse)
			elif dst.type == 'd':
				# TODO: should check permission and only do as necessary
				if the.verbose >= 2: self.print_op( rel_file, 'd>d' )
				shutil.copystat( src.file, dst.file )
				return True

			# if dst entity is a symlink to a directory, and this is an
			# acceptable substitute, just recurse
			elif self.check_dst_symlinks and dst.link_type == 'd' and \
					the.config.symlinks.matches( rel_file ):
				if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
				return True

			# if dst entity is a file or symlink in home dir, replace it with
			# directory (no need to recurse, since we're copying the whole
			# directory)
			elif dst.type == 'f' or dst.type == 'l':
				if the.verbose >= 2: self.print_op( rel_file, 'd>' + dst.type )
				os.unlink( dst.file )
				shutil.copytree( src.file, dst.file, True )

			else:
				raise NotImplementedError()

		# src entity is file
		elif src.type == 'f':

			# if dst entity doesn't exist, copy file
			if dst.type == '_':
				if the.verbose >= 2: self.print_op( rel_file, 'f>_' )
				shutil.copy( src.file, dst.file )
				shutil.copystat( src.file, dst.file )

			# if dst entity is a file, replace it only if it differs
			elif dst.type == 'f':
				if not filecmp.cmp( src.file, dst.file ):
					if the.verbose >= 2: self.print_op( rel_file, 'f>f' )
					os.unlink( dst.file )
					shutil.copy( src.file, dst.file )
					shutil.copystat( src.file, dst.file )
				else:
					if the.verbose >= 2: self.print_op( rel_file, 'f=f' )

			# if dst entity is a directory, replace it with file
			elif dst.type == 'd':
				if the.verbose >= 2: self.print_op( rel_file, 'f>d' )
				shutil.rmtree( dst.file )
				shutil.copy( src.file, dst.file )
				shutil.copystat( src.file, dst.file )

			# if dst entity is a symlink, replace it with file
			elif dst.type == 'l':
				if the.verbose >= 2: self.print_op( rel_file, 'f>l' )
				os.unlink( dst.file )
				shutil.copy( src.file, dst.file )
				shutil.copystat( src.file, dst.file )

			else:
				raise NotImplementedError()

		# src entity is a symlink
		elif src.type == 'l':

			# if dst entity doesn't exist, copy symlink
			if dst.type == '_':
				if the.verbose >= 2: self.print_op( rel_file, 'l>_' )
				os.symlink( os.readlink( src.file ), dst.file )

			# if dst entity is a symlink, replace it only if it differs
			elif dst.type == 'l':
				if os.readlink( src.file ) != os.readlink( dst.file ):
					if the.verbose >= 2: self.print_op( rel_file, 'l>l' )
					os.unlink( dst.file )
					os.symlink( os.readlink( src.file ), dst.file )
				else:
					if the.verbose >= 2: self.print_op( rel_file, 'l=l' )

			# if dst entity is a file, replace it with symlink
			elif dst.type == 'f':
				if the.verbose >= 2: self.print_op( rel_file, 'l>f' )
				os.unlink( dst.file )
				os.symlink( os.readlink( src.file ), dst.file )

			# if dst entity is a directory...
			elif dst.type == 'd':

				# if src entity is a symlink to a directory, and this is an
				# acceptable substitute, just recurse
				if self.check_src_symlinks and src.link_type == 'd' and \
						the.config.symlinks.matches( rel_file ):
					if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
					return True

				# else replace it with a symlink
				if the.verbose >= 2: self.print_op( rel_file, 'l>d' )
				shutil.rmtree( dst.file )
				os.symlink( os.readlink( src.file ), dst.file )

			else:
				raise NotImplementedError()

		# src entity is missing
		elif src.type == '_':

			# if dst entity doesn't exist, we're good
			if dst.type == '_':
				pass;

			# if dst entity is a file or symlink, delete it
			elif dst.type == 'f' or dst.type == 'l':
				if the.verbose >= 2: self.print_op( rel_file, '_>' + dst.type )
				os.unlink( dst.file )

			# if dst entity is a directory, delete it
			elif dst.type == 'd':
				if the.verbose >= 2: self.print_op( rel_file, '_>d' )
				shutil.rmtree( dst.file )

			else:
				raise NotImplementedError()

		# if we got here, we don't want to recurse...
		return False
