from MetadataService.Service import Service
import os
from VolumeExpert import _VolumeExpert
from MetadataService.AdaptiveSleeper import AdaptiveSleeper
from MetadataService.ProducerConsumer import Full
from InfospaceObjects import Volume
import time

class VolumeCrawler(Service,AdaptiveSleeper):

	volume = None
	indexer = None
	log = None
	files_to_index = None
# 	dev_node = None

	def __init__(self,volume,expert,produce_method):
		# Contract:
		assert isinstance(volume,Volume)
		assert isinstance(expert,_VolumeExpert)
		assert callable(produce_method)
		
		Service.__init__(self,public_interface = None)
		self.set_component_name("FilesystemAgent.VolumeCrawler."+volume.get_mountpoint().replace(".","_"))
		AdaptiveSleeper.__init__(self)
		self.log.debug("Initializing")
# 		self.dev_node = os.stat(volume.get_mountpoint()).st_dev
		self.volume = volume
		self.expert = expert
		self.produce = produce_method # for the moment this is hardcoded, but the ideal sutiation would be that this is an instance of a Producer and the volume crawler manager is a Multi-consumer or some stuff like that
		self.log.debug("Initialized")

	# FIXME signaling the indexer to delete a file is the responsiblity of the process/service that found the file did not exist
	def run(self):
		self.log.info("Started")
		expert = self.expert
		# for the root
		self.add_to_queue(("FileFound",self.volume.get_mountpoint(),self.volume))
		self.sleep()
		# and for the children
		for root, dirs, files in os.walk(self.volume.get_mountpoint()):  
			if self.killed(): break

			for name in dirs[:]:
				if self.killed(): break
				filename = os.path.join(root,name)
				if os.path.ismount(filename): # do not cross FS (volume) boundaries
					self.log.debug("Removing %s from walk list (mountpoint of other volume)",filename)
					dirs.remove(name)
				elif not expert.is_appropriate(filename):
					self.log.debug("Removing %s from walk list (inappropriate)",filename)
					dirs.remove(name)
				
			for name in files:
				if self.killed(): break
				filename = os.path.join(root,name)
				if not expert.is_appropriate(filename):
					self.log.debug("Skipping %s (inappropriate)",filename)
					continue
				self.add_to_queue(("FileFound",filename,self.volume))
				self.sleep()
			
			for name in dirs:
				if self.killed(): break
				filename = os.path.join(root,name)
				self.add_to_queue(("FileFound",filename,self.volume))
				self.sleep()
		
		# at this point, the volume has been fully crawled, but files may still be in the queue for the filesystem crawler to consume.  The ideal case would be that the volume crawler now persists a datum that tells it "Do not crawl this volume on the next startup"
		self.add_to_queue(("FullyIndexed",self.volume))
		
		while not self.killed():
			self.log.debug("Fully crawled, now lingering")
			time.sleep(1)
		self.log.info("Finished")

	def add_to_queue(self,event):
		assert type(event) is tuple
		while not self.killed():
			try:
				self.produce(event,1)
				return
			except Full: pass

