class Packetizer (log.Base):
	"""
	A packetizer that is used to read and write to an underlying
	stream (like a Transport). Should be inherited by such a class.
	The subclass should implement:

		rawWrite(msg) --- write this msg to the stream.
		   Typically handled at the Transport level (2 classes higher)

		packetizeError(e) --- report an error with the stream.
		   Typically handled by the dispatcher (1 class higher).

		dispatch(msg) --- emit a packetized imcoming message.
			Typically handled by the Dispatcher (1 class higher)

	The subclass should call packetizeData(m) whenever it has
	data to stuff in to the packetizer's input path, and call
	send(m) whenever it wants to stuff data into the packetizer's
	output path.
	"""

	#-------------------------------

	# The two states we can be in
	FRAME = 1
	DATA = 2

	# Results of getting
	OK = 0
	WAIT = 1
	ERR = -1

	#-------------------------------

	def __init__ (self, log_obj):
		self._ring = Ring()
		self._state = self.FRAME
		self._next_msg_len = 0
		log.Base.__init__(self, log_obj)

	#-------------------------------

	def send (self, msg):
		b2 = msgpack.packb(msg)
		b1 = msgpack.packb(len(b2))
		self.rawWrite(b1)
		self.rawWrite(b2)

	#-------------------------------

	def __getFrame (self):
		"""
		Internal method: get the frame part of a stream.
		"""

		if len(self._ring) is 0: return self.WAIT
		f0 = self._ring.grab(1)
		if not f0: return self.WAIT

		frame_len = msgpackFrameLen(ord(f0))
		if not frame_len:
			self.packetizeError("Bad frame header received")
			return self.ERR

		# Now we know have many bytes to grab for real.  It's a function 
		# of the first byte of the stream....
		f_full = self._ring.grab(frame_len)
		if not f_full: return self.WAIT

		try:
			r = unpackType(f_full, int)
			# pull the frame out of the input stream...
			self._ring.consume(frame_len)
			self._next_msg_len = r
			self._state = self.DATA
			return self.OK
		except UnpackValueError:
			self.packetizeError("Bad decoding in frame header; unpackb failed")
		except UnpackTypeError as e:
			self.packetizeError("Bad data type in frame header: {0}".format(e))
		return self.ERR

	#-------------------------------

	def __getPayload(self):
		"""
		Internal method: get the msg part of the stream.
		"""
		l = self._next_msg_len
		if l > len(self._ring): return self.WAIT
		buf = self._ring.grab(l)
		if not buf: return self.WAIT

		try:
			msg = unpackType(buf, list)
			self._ring.consume(l)
			self._state = self.FRAME
			self.dispatch(msg)
			return self.OK
		except UnpackValueError:
			self.packetizeError("Bad encoding found in data; len={0}"
				.format(l))
		except UnpackTypeError as e:
			self.packetizeError("In data: {0}".format(e))
		return self.ERR

	#-------------------------------

	def packetizeData(self, msg):
		"""
		To be called wheneve new data arrives on the transport.
		This method will stuff the new data into the bufffer ring
		and then attempt to fetch as many messages as possible
		from the stream, stopping if either there's a wait condition
		or if an error occurred.
		"""
		self._ring.buffer(msg)
		go = self.OK
		while go is self.OK:
			if self._state is self.FRAME:
				go = self.__getFrame()
			else:
				go = self.__getPayload()

	#-------------------------------

	def packetizerReset(self):
		"""
		To be called on an error; flush out the packetizer and return it
		to its normal state.
		"""
		self._state = self.FRAME
		self._ring = Ring()