
__author__ = 'Miroslav Bauer'


import os
import saga
import hashlib
from logger import Logger
from os.path import basename


log = Logger('dmorph.filecache')


class FileCache:
	""" Class providing cache of files present on resource.
	    It is used to prevent jobs to unnecessarily upload files
	    to resource when they are already present at resource's cachedir.
	    Jobs then do just a local copy from cachedir to job's directory,
	    which is way faster than a remote copy.

	    WARNING: As of now, cache directory must be created manually
	    on the resource as <scratchdir>/filecache.
	"""

	def __init__(self, xfer, cachedir):
		self.cachedir = '%s://%s' % (xfer, cachedir)
		self.cache    = {}

	def get(self, filenames):
		''' For a filename given, this returns a path to a file
		    in target resource's cache directory.
		    If file with filename isn't in cache yet, a new entry
		    is made using filename, filesize and file's md5 digest.

		    When True is returned in the first result field, caller must
		    copy file into cache directory, to a 'ftarget' path given
		    in the second field.
		'''
		filepaths = []
		for filename in filenames:
			if os.path.isdir(filename):
				raise KeyError('Cannot cache directory: %s' % filename)
			filepath   = None
			fsize = os.path.getsize(filename)
			fhash = self._computeDigest(filename)
			try:
				origfile, filepath = self.cache[(filename, fsize, fhash)]
				filepaths.append([False, (filepath, origfile)])
			except KeyError:
				log.debug('Cache miss for: %s' % filename)
				filepaths.append([True, self._insert(filename, fsize, fhash)])
		return filepaths

	def _insert(self, filename, fsize, fhash):
		ftarget = '%s/%s-%s' % (self.cachedir, basename(filename), fhash)
		self.cache[(filename, fsize, fhash)] = (filename, ftarget)
		return (ftarget, filename)

	def _computeDigest(self, filename):
		with open(filename, 'rb') as f:
			h = hashlib.md5(f.read()).hexdigest()
		return h
