# Path: galah/base/prioritydict.py
# class PriorityDict(dict):
#     """Dictionary that can be used as a priority queue.
# 
#     Keys of the dictionary are items to be put into the queue, and values
#     are their respective priorities. All dictionary methods work as expected.
#     The advantage over a standard heapq-based priority queue is
#     that priorities of items can be efficiently updated (amortized O(1))
#     using code as 'thedict[item] = new_priority.'
# 
#     The 'smallest' method can be used to return the object with lowest
#     priority, and 'pop_smallest' also removes it.
# 
#     The 'sorted_iter' method provides a destructive sorted iterator.
#     """    
# 
#     def __init__(self, *args, **kwargs):
#         super(PriorityDict, self).__init__(*args, **kwargs)
#         self._rebuild_heap()
# 
#     def _rebuild_heap(self):
#         self._heap = [(v, k) for k, v in self.iteritems()]
#         heapify(self._heap)
# 
#     def smallest(self):
#         """
#         Return the item with the lowest priority as a named tuple
#         (priority, value).
# 
#         Raises IndexError if the object is empty.
# 
#         """
#         
#         heap = self._heap
#         v, k = heap[0]
#         while k not in self or self[k] != v:
#             heappop(heap)
#             v, k = heap[0]
#         return PriorityValuePair(v, k)
# 
#     def pop_smallest(self):
#         """
#         Return the item as a named tuple (priority, value) with the lowest
#         priority and remove it.
# 
#         Raises IndexError if the object is empty.
# 
#         """
#         
#         heap = self._heap
#         v, k = heappop(heap)
#         while k not in self or self[k] != v:
#             v, k = heappop(heap)
#         del self[k]
#         return PriorityValuePair(v, k)
# 
#     def __setitem__(self, key, val):
#         # We are not going to remove the previous value from the heap,
#         # since this would have a cost O(n).
#         
#         super(PriorityDict, self).__setitem__(key, val)
#         
#         if len(self._heap) < 2 * len(self):
#             heappush(self._heap, (val, key))
#         else:
#             # When the heap grows larger than 2 * len(self), we rebuild it
#             # from scratch to avoid wasting too much memory.
#             self._rebuild_heap()
# 
#     def setdefault(self, key, val):
#         if key not in self:
#             self[key] = val
#             return val
#         return self[key]
# 
#     def update(self, *args, **kwargs):
#         # Reimplementing dict.update is tricky -- see e.g.
#         # http://mail.python.org/pipermail/python-ideas/2007-May/000744.html
#         # We just rebuild the heap from scratch after passing to super.
#         
#         super(PriorityDict, self).update(*args, **kwargs)
#         self._rebuild_heap()
# 
#     def sorted_iter(self):
#         """Sorted iterator of the priority dictionary items.
# 
#         Beware: this will destroy elements as they are returned.
#         """
#         
#         while self:
#             yield self.pop_smallest()
# 
# Path: galah/base/config.py
# def load_config(domain):
#     """
#     Parses and loads up the Galah configuration file and extracts the
#     configuration data for the given domain (ex: "web" or "sheep").
# 
#     Global configuration options will always be returned, no matter the domain.
# 
#     The configuration file will only be loaded once per instance of the
#     interpreter, so feel free to call this function multiple times in many files
#     to avoid ugly dependency issues.
# 
#     """
# 
#     global loaded
# 
#     # Note whether this is the first time we're processing the configuraiton. If
#     # it is, we'll want to attach the global log handlers.
#     first_load = loaded is None
# 
#     # Load the configuration if it hasn't been loaded yet. Note this ensures
#     # that the configuration is only loaded once per instance of the
#     # interpreter
#     config_file = os.environ.get("GALAH_CONFIG_PATH",
#                                  "/etc/galah/galah.config")
#     if not loaded:
#         if os.path.isfile(config_file):
#             loaded = imp.load_source(
#                 "user_config_file", config_file
#             )
# 
#     local_config = {}
# 
#     # Note we make a **shallow** copy of the defaults here
#     user_config = dict(defaults)
# 
#     # If the configuration didn't load correctly, just use the defaults
#     if loaded:
#         user_config.update(loaded.config)
# 
#     # The prefix values we will look for when scanning the configuration file.
#     prefix = "%s/" % domain
#     global_prefix = "global/"
# 
#     # Grab all the configuration values for the given domain, and any global
#     # configuration values, then place the rest of the configuration values
#     # in there as well.
#     for k, v in user_config.items():
#         if k.startswith(prefix) and len(k) != len(prefix):
#             local_key = k[len(prefix):]
# 
#             local_config[local_key] = v
#         elif k.startswith(global_prefix) and len(k) != len(global_prefix):
#             global_key = k[len(global_prefix):]
# 
#             local_config[global_key] = v
#         elif "/" in k:
#             local_config[k] = v
# 
#     return local_config


from galah.base.prioritydict import PriorityDict
from collections import namedtuple
from galah.base.flockmail import InternalTestRequest
from galah.base.config import load_config
import datetime
import heapq

# Load Galah's configuration.
config = load_config("shepherd")

class FlockManager:
	class SheepInfo:
		__slots__ = ("environment", "servicing_request")

		def __init__(self, environment, servicing_request):
			self.environment = environment
			self.servicing_request = servicing_request

	def __init__(self, match_found, bleet_timeout, service_timeout):
		# The flock of sheep we are managing. Dictionary mapping sheep
		# identities to information on that sheep (specifically SheepInfo
		# instances).
		self._flock = {}

		# A priority queue where sheep are ordered by the time when they last
		# bleeted. The top of/smallest item in the priority queue is the sheep
		# who has bleeted the farthest amount of time ago.

# Path: galah/base/prioritydict.py
# class PriorityDict(dict):
#     """Dictionary that can be used as a priority queue.
# 
#     Keys of the dictionary are items to be put into the queue, and values
#     are their respective priorities. All dictionary methods work as expected.
#     The advantage over a standard heapq-based priority queue is
#     that priorities of items can be efficiently updated (amortized O(1))
#     using code as 'thedict[item] = new_priority.'
# 
#     The 'smallest' method can be used to return the object with lowest
#     priority, and 'pop_smallest' also removes it.
# 
#     The 'sorted_iter' method provides a destructive sorted iterator.
#     """    
# 
#     def __init__(self, *args, **kwargs):
#         super(PriorityDict, self).__init__(*args, **kwargs)
#         self._rebuild_heap()
# 
#     def _rebuild_heap(self):
#         self._heap = [(v, k) for k, v in self.iteritems()]
#         heapify(self._heap)
# 
#     def smallest(self):
#         """
#         Return the item with the lowest priority as a named tuple
#         (priority, value).
# 
#         Raises IndexError if the object is empty.
# 
#         """
#         
#         heap = self._heap
#         v, k = heap[0]
#         while k not in self or self[k] != v:
#             heappop(heap)
#             v, k = heap[0]
#         return PriorityValuePair(v, k)
# 
#     def pop_smallest(self):
#         """
#         Return the item as a named tuple (priority, value) with the lowest
#         priority and remove it.
# 
#         Raises IndexError if the object is empty.
# 
#         """
#         
#         heap = self._heap
#         v, k = heappop(heap)
#         while k not in self or self[k] != v:
#             v, k = heappop(heap)
#         del self[k]
#         return PriorityValuePair(v, k)
# 
#     def __setitem__(self, key, val):
#         # We are not going to remove the previous value from the heap,
#         # since this would have a cost O(n).
#         
#         super(PriorityDict, self).__setitem__(key, val)
#         
#         if len(self._heap) < 2 * len(self):
#             heappush(self._heap, (val, key))
#         else:
#             # When the heap grows larger than 2 * len(self), we rebuild it
#             # from scratch to avoid wasting too much memory.
#             self._rebuild_heap()
# 
#     def setdefault(self, key, val):
#         if key not in self:
#             self[key] = val
#             return val
#         return self[key]
# 
#     def update(self, *args, **kwargs):
#         # Reimplementing dict.update is tricky -- see e.g.
#         # http://mail.python.org/pipermail/python-ideas/2007-May/000744.html
#         # We just rebuild the heap from scratch after passing to super.
#         
#         super(PriorityDict, self).update(*args, **kwargs)
#         self._rebuild_heap()
# 
#     def sorted_iter(self):
#         """Sorted iterator of the priority dictionary items.
# 
#         Beware: this will destroy elements as they are returned.
#         """
#         
#         while self:
#             yield self.pop_smallest()
# 
# Path: galah/base/config.py
# def load_config(domain):
#     """
#     Parses and loads up the Galah configuration file and extracts the
#     configuration data for the given domain (ex: "web" or "sheep").
# 
#     Global configuration options will always be returned, no matter the domain.
# 
#     The configuration file will only be loaded once per instance of the
#     interpreter, so feel free to call this function multiple times in many files
#     to avoid ugly dependency issues.
# 
#     """
# 
#     global loaded
# 
#     # Note whether this is the first time we're processing the configuraiton. If
#     # it is, we'll want to attach the global log handlers.
#     first_load = loaded is None
# 
#     # Load the configuration if it hasn't been loaded yet. Note this ensures
#     # that the configuration is only loaded once per instance of the
#     # interpreter
#     config_file = os.environ.get("GALAH_CONFIG_PATH",
#                                  "/etc/galah/galah.config")
#     if not loaded:
#         if os.path.isfile(config_file):
#             loaded = imp.load_source(
#                 "user_config_file", config_file
#             )
# 
#     local_config = {}
# 
#     # Note we make a **shallow** copy of the defaults here
#     user_config = dict(defaults)
# 
#     # If the configuration didn't load correctly, just use the defaults
#     if loaded:
#         user_config.update(loaded.config)
# 
#     # The prefix values we will look for when scanning the configuration file.
#     prefix = "%s/" % domain
#     global_prefix = "global/"
# 
#     # Grab all the configuration values for the given domain, and any global
#     # configuration values, then place the rest of the configuration values
#     # in there as well.
#     for k, v in user_config.items():
#         if k.startswith(prefix) and len(k) != len(prefix):
#             local_key = k[len(prefix):]
# 
#             local_config[local_key] = v
#         elif k.startswith(global_prefix) and len(k) != len(global_prefix):
#             global_key = k[len(global_prefix):]
# 
#             local_config[global_key] = v
#         elif "/" in k:
#             local_config[k] = v
# 
#     return local_config


# Path: galah/shepherd/flockmanager.py
from galah.base.prioritydict import PriorityDict
from collections import namedtuple
from galah.base.flockmail import InternalTestRequest
from galah.base.config import load_config
import datetime
import heapq


# Load Galah's configuration.
config = load_config("shepherd")

class FlockManager:
	class SheepInfo:
		__slots__ = ("environment", "servicing_request")

		def __init__(self, environment, servicing_request):
			self.environment = environment
			self.servicing_request = servicing_request

	def __init__(self, match_found, bleet_timeout, service_timeout):
		# The flock of sheep we are managing. Dictionary mapping sheep
		# identities to information on that sheep (specifically SheepInfo
		# instances).
		self._flock = {}

		# A priority queue where sheep are ordered by the time when they last
		# bleeted. The top of/smallest item in the priority queue is the sheep
		# who has bleeted the farthest amount of time ago.

		self._bleet_queue = PriorityDict()