from __future__ import with_statement

"""A multi-producer, multi-consumer mailbox that can contain one item."""

import thread as T
import sys

# get_ident = T.get_ident
DEBUG = False
# DEBUG = True

start_new_thread = T.start_new_thread
get_ident = T.get_ident



class State(object):
	def __init__(self, fname=None, llen=79):
		self.fd = open(fname, 'w')
		self.llen = llen
		self.offset = {}
		self.lock = T.allocate_lock()
		self.spaces = ' ' * (llen-1) + '\n'
		self.tname = {}

	def write(self, name, *s):
		with self.lock:
			me = T.get_ident()
			tname = self.tname.get(me,me)
			self._write('LC:', name, '\tth:', tname, '\t', *s)
			self._write('TH:', tname, '\to:', name, '\t', *s)

	def _write(self, ty, name, *s):
		try:
			o = self.offset[name]
		except KeyError:
			o = len(self.offset) * self.llen
			self.offset[name] = o
		s = '%s %s: %s\n' % (ty, name, ''.join( [ str(q) for q in s ] ) )
		ls = len(s)
		if ls > self.llen:
			s = s[:self.llen-1] + '\n'
		elif ls < self.llen:
			s = s[:-1] + self.spaces[ls-1:]
		assert len(s) == self.llen
		self.fd.seek(o, 0)
		self.fd.write(s)
		self.fd.flush()
		sys.stderr.write(s)
		sys.stderr.flush()

	def setname(self, name, me=None):
		with self.lock:
			if me is None:
				me = T.get_ident()
			self.tname[me] = name

	def getName(self):
		with self.lock:
			me = T.get_ident()
			tmp = self.tname.get(me, me)
		return tmp

if __debug__:
	Lockstate = State('g_threading.state')
	Lockstate.setname('Main Thread')
else:
	Lockstate = None


class Lock(object):
	ls = Lockstate

	def __init__(self, verbose=0, name=None):
		self.lock = T.allocate_lock()
		self.verbose = verbose
		self.name = '%s%d' % (name, id(self))

	def acquire(self):
		if __debug__ and self.verbose > 1:
			self.ls.write(self.name, 'Lock.before acquire')
		self.lock.acquire()
		if __debug__ and self.verbose > 1:
			self.ls.write(self.name, 'Lock.after acquire')

	def release(self):
		if __debug__ and not self.lock.locked():
			if self.verbose:
				self.ls.write(self.name, 'ERR: release but not locked.')
			raise AssertionError, "Release of unlocked lock: %s" % self.name
		self.lock.release()
		if __debug__ and self.verbose > 1:
			self.ls.write(self.name, 'Lock.released')

	def locked(self):
		return self.lock.locked()

	def __del__(self):
		if __debug__ and self.lock.locked():
			if self.verbose:
				self.ls.write(self.name, 'ERR: __del__ while locked.')
			raise AssertionError, "__del__ of locked Lock: %s" % self.name

	def __enter__(self):
		self.lock.acquire()
		return self

	def __exit__(self, ty, value, traceback):
		self.lock.release()
		return False


class Condition(object):
	ls = Lockstate

	def __init__(self, lock=None, verbose=0, name=None):
		if lock is None:
			self.lock = T.allocate_lock()
		else:
			self.lock = lock
		self.waiters = []
		self.verbose = verbose
		self.name = '%s%d' % (name, id(self))

	def wait(self):
		"""Called from lock."""
		if __debug__ and not self.lock.locked():
			if self.verbose:
				self.ls.write(self.name, 'ERR: wait but not locked.')
			raise AssertionError, "Wait on unlocked Condition: %s" % self.name
		tmplock = T.allocate_lock()
		tmplock.acquire()
		self.waiters.append( tmplock )
		self.lock.release()
		if __debug__ and self.verbose:
			self.ls.write(self.name, 'Condition.before wait')
		tmplock.acquire()
		self.lock.acquire()
		if __debug__ and self.verbose:
			self.ls.write(self.name, 'Condition.after wait')

	def acquire(self):
		if __debug__ and self.verbose > 1:
			self.ls.write(self.name, 'Condition.before acquire')
		self.lock.acquire()
		if __debug__ and self.verbose > 1:
			self.ls.write(self.name, 'Condition.after acquire')

	def release(self):
		if __debug__ and not self.lock.locked():
			if self.verbose:
				self.ls.write(self.name, 'ERR: release but not locked.')
			raise AssertionError, "Release of unlocked Condition: %s" % self.name
		self.lock.release()
		if __debug__ and self.verbose > 1:
			self.ls.write(self.name, 'Condition.released')

	def notify(self):
		if __debug__ and not self.lock.locked():
			if self.verbose:
				self.ls.write(self.name, 'ERR: notify but not locked.')
			raise AssertionError, "Notify on unlocked Condition: %s" % self.name
		if __debug__ and self.verbose:
			self.ls.write(self.name, 'Condition.notify')
		try:
			self.waiters.pop(0).release()
		except IndexError:
			prinfo('Condition: notify when no one is waiting', self.name)


	def notifyAll(self):
		if __debug__ and not self.lock.locked():
			if self.verbose:
				self.ls.write(self.name, 'ERR: notifyAll but not locked.')
			raise AssertionError, "NotifyAll on unlocked Condition: %s" % self.name
		if __debug__ and self.verbose:
			self.ls.write(self.name, 'Condition.notify %d' % len(self.waiters))
		if not self.waiters:
			prinfo('Condition: notifyAll when no one is waiting', self.name)
		for tmplock in self.waiters:
			tmplock.release()
		self.waiters = []


	def __del__(self):
		if __debug__ and self.lock.locked():
			if self.verbose:
				self.ls.write(self.name, 'ERR: __del__ while locked, w:%d.' % len(self.waiters))
			raise AssertionError, "__del__ of locked Condition: %s, with %d waiters" % (self.name, len(self.waiters))


if __debug__:
	_prinfo_lock = T.allocate_lock()
	def prinfo(*s):
		global _prinfo_lock
		if not DEBUG:
			return
		_prinfo_lock.acquire()
		sys.stderr.flush()
		sys.stdout.flush()
		tid = '%s: ' % Lockstate.getName()
		sys.stderr.writelines([tid] + [ str(q) for q in s] + ['\n'])
		sys.stderr.flush()
		_prinfo_lock.release()
else:
	def prinfo(*s):
		pass


