#!/usr/bin/env python
"""
A python module with logging to animate Thackeray's martingale.

Metadata:

  - Author:    W.S.Kendall
  - Copyright: opensource (c) W.S.Kendall 2008
  - Date:      2008-07-04
  - URL:       http://www.wilfridkendall.co.uk
  - Generator: '/home/wilfrid/lib/python/generate.py' version 1.18.


Command-line options::

  usage: %prog [options] arguments
    --logging=LOGGING:	   logging verbosity level.

Notes:
 - values for 'logging' option:
 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'
 (decreasing level of verbosity), with 'WARNING' as default.

"""
__RCSID__ = '$Revision: 1.2 $ $Date: 2011/07/07 18:35:03 $'


# \section{Definitions}
# We need the logging module regardless of whether the #logging# option
# is deployed, since #logging.info# calls are scattered through the text.
import logging, math, random

from Tkinter import Checkbutton, DoubleVar, IntVar, LAST, Scale, Tk, \
    VERTICAL, X, Y

from display2 import ITALICS, BOLD, Display2

#\section{Exceptions}
class StopError(Exception):
        """
        Error to be raised on a Stop.
        """
        pass

#\section{Classes}
class AnimateThackeray(Display2):
	"""
	Presents animation of reversible random walk.
	"""
	def __init__(self, root, x0=0, x1=10, y0=-127, y1=1):
		Display2.__init__(self, root, xlo=x0, xhi=x1, ylo=y0, yhi=y1)

		self.rng = random.Random()

		# Buttons for actions.
		self.button(text='SIMULATE', font=BOLD, foreground='darkgreen',\
				    command=self.simulate)
		self.button(text='ITERATE', font=BOLD, foreground='darkgreen', \
				    command=self.iterate)
		
		# Scales for adjustments.
		self.batch = IntVar()
		self.batch.set(200)
		Scale(self.buttonframe, from_=1000, to=100, \
			      resolution=100, \
			      orient=VERTICAL, \
			      label='batch', font=BOLD, \
			      variable=self.batch).pack(fill=Y)

		self.button(text='WIPE', font=BOLD, foreground='firebrick', \
				    command=self.wipe)


		yinc = -0.5
		while yinc > y0:
			yinc *= 2
			self.axis(x0, yinc, x1 - x0, fill='light salmon')
			if yinc < -2:
				self.write('%d' % yinc, x1-1.5, yinc+2, font=BOLD)
		self.axis(x0, y1, x1 - x0, fill='blue', width=2)
		self.axis(x0, 0, x1 - x0, fill='black')
		for xinc in range(x0+1, x1+1):
			self.axis(xinc, y0, y1 - y0, fill='skyblue', xalign=False)
		
		self.x0, self.x1, self.y0, self.y1 = x0, x1, y0, y1
		
		self.iterations = 1

		self.rng.seed(2)
		
	def simulate(self):
		"""
		Run a single animation.
		"""
		self.canvas.itemconfig('objects', fill='lightgray')
		self.canvas.dtag('objects')
		self.canvas.delete('cycle')
		self.write('%4d' % self.iterations, 0, self.y0-4, font=BOLD, tags='cycle')
		t, credit, stake = self.x0, 0, 0.5
		self.dot((t, credit), fill='yellow', width=4, tags='objects')
		while t < self.x1 and credit <=0:
			t0, credit0 = t, credit
			t += 1
			stake *= 2
			credit += self.rng.choice([stake, -stake])
			self.line(t0, credit0, t, credit, width=2, tags='objects')
			if credit<=self.y0+1:
				self.dot((t, credit), width=8, fill='red')
				raise StopError
			elif credit<=0:
				self.dot((t, credit), fill='red', width=3, tags='objects')
			else:
				self.dot((t, credit), fill='royalblue', width=4)
		self.iterations += 1
		#print
	

	def iterate(self):
		m, dm = 10, self.batch.get()
		for i in range(self.iterations, self.iterations + dm):
			try:
			        self.simulate()
			except StopError:
			        break
			self.canvas.update_idletasks()
		self.iterations += dm
	
	def togglewipe(self):
		"""
		Wipe all objects and change checkbuttona ppearance.
		"""
		if self.toggle.get():
			self.togglecb['text'] = 'EQUILIBRIUM'
			self.togglecb['foreground'] = 'black'
		else:
			self.togglecb['text'] = '2 PT START '
			self.togglecb['foreground'] = 'red'
		self.wipe()

	def wipe(self):
		"""
		Wipe all objects.
		"""
		self.chain, self.iterations = None, 1
		self.occupied, self.rev_trans = set(), []
		for i in range(self.y0, self.y1+1):
			self.rev_trans.append([0,0])
		
		self.canvas.delete('objects', 'active', 'transient', 'prob', 'record')
		
	def adjust_prob(self, p):
		"""
		Callback if slider is adjusted.
		"""
		self.wipe()
		self.iterations, self.p = 1, float(p)


#\section{Exceptions}
class Error(Exception):
	"""
	Generic exception to be raised by this module.
	"""

# \section{Functions}
def main(opt, arguments):
	"""
	The action of the module as script should be concentrated here!
	"""
	root = Tk()
	root.title('Thackeray\'s martingale')
	animation = AnimateThackeray(root)
	
	root.mainloop()




# \section{Main script}
if __name__ == '__main__':
	import optionparse
	option, args = optionparse.parse(__doc__, version=__RCSID__)

	if option.logging is not None:
		logging.basicConfig(level=logging.__dict__[option.logging])
		logging.info('Logging level %s' % option.logging)


	# The action of the module as script should be concentrated
	# in the following function, to facilitate profiling.
	main(option, args)


	logging.info('Finishing')
	raise SystemExit


# \newpage \scriptsize
# \begin{multicols}{2}
# \section{History}
# \text{ }
#\\ $Log: animatethackeray.pyw,v $
#\\ Revision 1.2  2011/07/07 18:35:03  wilfrid
#\\ Fixed a potential raise without specified exception.
#\\
#\\ Revision 1.1  2009/04/10 15:47:57  wilfrid
#\\ Checkin
#\\
#\\ Revision 1.1  2008/07/08 06:55:20  wskendall
#\\ Initial revision
#\\
#\\ Revision 1.1  2008/07/05 10:33:33  wskendall
#\\ Initial revision
#\\
#
# \end{multicols}

'$Id: animatethackeray.pyw,v 1.2 2011/07/07 18:35:03 wilfrid Exp wilfrid $'
