##########################################################################
#
# FileSystem.py: handle dirs and filesystem operations.
#
#  Python-CDD is a library to make easier to build applications to
#  Custom Debian Distributions.
#  See http://projetos.ossystems.com.br/python-cdd for more information.
#
# ====================================================================
# Copyright (c) 2002-2005 O.S. Systems.	 All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.
#
#########################################################################
# Authors: Otavio Salvador <otavio@ossystems.com.br>
#		   Marco Presi <zufus@debian.org>
#		   Free Ekanayaka <free@agnula.org>
#		   Vagrant Cascadian <vagrant@freegeek.org>
#
# Class to handle dirs and filesystem operations.


import os
import re
import hashlib
import sys
import stat
import logging
log = logging.getLogger("cdd.FileSystem")

from gzip import GzipFile
from bz2  import BZ2File

# Set the compress format to gzip.
GZIP = 2

# Set the compress format to bzip2.
BZIP2 = 4

class FileSystem:
	"""
	This class provides methods to manage dirs and files.
	"""

	def __init__(self, base, *args):
		"""
		Initialize the object

		Parameters:

		 - *base*: the base directory where all methods will run
		"""
		self.__errors = [13]

		others = list(args)
		self.__base = os.path.join(base, *others)

		### add a alias to self.remove method
		self.unlink = self.remove

	def exists(self, name):
		"""
		Check if a file or directory named 'name' exists inside of
		base directory.

		Parameters:

		 - *name*: the name of file or directory to be checked
		"""
		return os.path.exists(os.path.join(self.__base, name))

	def mkdir(self, name):
		"""
		Create a directory 'name' inside of 'base'

		Parameters:

		 - *name*: the directory name
		"""
		log.debug("Creating %s..." % name)

		dirname = os.path.join(self.__base, name)
		try:
			if os.path.isdir(dirname):
				pass
			elif not self.exists(name):
				os.makedirs (dirname)
			else:
				log.debug("Unable to create directory %s" % dirname)
				return False
		except OSError, (errno,strerror):
			log.debug("%s" % strerror)
			return False

		return True

	def remove(self, name):
		"""
		Remove file 'name' inside of 'base'

		p.s.: same as FileSystem.unlink

		Parameters:

		 - *name*: the name of file to be removed
		"""

		target = os.path.join(self.__base, name)
		log.debug("Removing %s" % target)

		try:
			os.remove(target)
		except OSError, (errno, strerror):
			log.debug(strerror)
			return False

	def rmdir(self, name, empty = False):
		"""
		Remove a directory and if empty == True it remove all empty
		between it.

		Parameters:

		 - *name*: the name of directory to be removed

		 - *empty*: a boolean variable to set if directory is empty
		   (default value: False)
		"""
		log.debug("Removing %s" % name)

		os.rmdir(os.path.join(self.__base, name))

		if empty:
			head, tail = os.path.split(name)
			if not tail:
				head, tail = os.path.split(head)
			while head and tail:
				log.debug("Removing %s" % head)
				try:
					os.rmdir(os.path.join(self.__base, head))
				except:
					break
				head, tail = os.path.split(head)

	def base(self):
		"""
		Return FileSystem base
		"""
		return self.__base

	def md5sum (self, filename):
		"""
		Return md5sum for given file

		Parameters:

		 - *filename*: the name of file to get md5sum
		"""
		try:
			file_to_hash = open(os.path.join(self.__base, filename), 'r')
		except IOError:
			raise

		md5hash = hashlib.md5()
		md5hash.update(file_to_hash.read())
		file_to_hash.close()
		return md5hash.hexdigest()

	def sha1sum (self, filename):
		"""
		Return sha1sum for given file

		Parameters:

		 - *filename*: the name of file to get sha1sum
		"""
		try:
			file_to_hash = open(os.path.join(self.__base, filename), 'r')
		except IOError:
			log.debug("Can't open %s for sha1sum." % filename)
			raise

		sha1hash = hashlib.sha1()
		sha1hash.update(file_to_hash.read())
		file_to_hash.close()
		return sha1hash.hexdigest()

	def size (self, filename):
		"""
		Return the size of the given file

		Parameters:

		 - *filename*: the name of file to get size
		"""
		try:
			size = os.stat(os.path.join(self.__base, filename))[stat.ST_SIZE]
		except OSError:
			raise

		return size

	def uncompress (self, filename):
		"""
		Uncompress a given file

		Parameters:

		 - *filename*: the name of file to be uncompressed
		"""
		log.debug('Uncompressing %s' % filename)

		for format in (GzipFile, BZ2File):
			try:
				compressed_file = format(os.path.join(self.__base, filename),'r')
			except IOError, msg:
				raise

			try:
				data = compressed_file.read()

				# We already read all data, go out!
				break
			except IOError, msg:
				pass
		else:
			raise IOError, "Invalid format"

		compressed_file.close()

		output_file = open(os.path.join(self.__base,
									   filename.rstrip('.gz')), "wb")
		output_file.write(data)
		output_file.close()
		return True

	def compress (self, filename, format = GZIP):
		"""
		Compress a file

		Parameters:

		 - *filename*: the name of file to be compressed

		 - *format*: the compress format (default value: gzip)
		"""
		log.debug('Compressing ' + filename)

		if format == GZIP:
			class_ref = GzipFile
			extension = '.gz'
		elif format == BZIP2:
			class_ref = BZ2File
			extension = '.bz2'
		else:
			raise SyntaxError

		try:
			input_file = open(os.path.join(self.__base, filename), 'r')
			output_file = class_ref(os.path.join(self.__base,
												filename + extension), "wb")
		except IOError, msg:
			raise

		output_file.write(input_file.read())
		output_file.close()
		input_file.close()
		return True

	def match (self, expression):
		"""
		Find a file which any pattern

		Parameters:

		 - *expression*: the pattern to be matched
		 Return value:
			 list of matching files RELATIVE to base
		"""
		log.debug("Matching a pattern")

		retfiles = []
		comp_exp = re.compile(expression)
		for root, dirs, files in os.walk(self.__base):
			for name in files:
				if comp_exp.findall(name):
					filename = os.path.join(root[len(self.__base) + 1:], name)
					retfiles.append(filename)

		return retfiles
