# 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
from stdhome.file_matcher import FileMatcher
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, updated_files = None ):
		self.accept_list = FileMatcher()


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

		# 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 > 1: self.print_op( rel_file, 'd>_' )
				os.mkdir( dst.file )
				shutil.copystat( src.file, dst.file )
				return False

			# 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 > 1: self.print_op( rel_file, 'd>d' )
				shutil.copystat( src.file, dst.file )
				return True

			# TODO: if dst entity is a symlink to a directory, and rel_file is
			# matched by the acceptable symlinks file matcher, just recurse
			elif dst.link_type == 'd' and self.accept_list.match( rel_file ):
				if the.verbose > 1: 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 > 1: self.print_op( rel_file, 'd>' + dst.type )
				os.unlink( dst.file )
				os.mkdir( dst.file )
				shutil.copystat( src.file, dst.file )
				return False

			else:
				raise NotImplementedError()

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

			# if dst entity doesn't exist, copy file
			if dst.type == '_':
				if the.verbose > 1: 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 > 1: 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 > 1: self.print_op( rel_file, 'f=f' )

			# if dst entity is a directory, replace it with file
			elif dst.type == 'd':
				if the.verbose > 1: 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 > 1: 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
		if src.type == 'l':

			# if dst entity doesn't exist, copy symlink
			if dst.type == '_':
				if the.verbose > 1: 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 > 1: self.print_op( rel_file, 'l>l' )
					os.unlink( dst.file )
					os.symlink( os.readlink( src.file ), dst.file )
				else:
					if the.verbose > 1: self.print_op( rel_file, 'l=l' )

			# if dst entity is a file, replace it with symlink
			elif dst.type == 'f':
				if the.verbose > 1: 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, replace it with symlink
			elif dst.type == 'd':
				if the.verbose > 1: 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
		if 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 > 1: 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 > 1: self.print_op( rel_file, '_>d' )
				shutil.rmtree( dst.file )

			else:
				raise NotImplementedError()

		# non-directories can not be recursed in to
		return False
