#!/usr/bin/env python
"""
A python module with logging to animatea a financial 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.1 $ $Date: 2008/07/08 06:55:20 $'


# \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{Classes}
class AnimateFinance(Display2):
	"""
	Presents animation of reversible random walk.
	"""
	def __init__(self, root, x0=0, x1=500, y0=0, y1=100):
		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(10)
		Scale(self.buttonframe, from_=50, to=10, \
			      resolution=10, \
			      orient=VERTICAL, \
			      label='batch', font=BOLD, \
			      variable=self.batch).pack(fill=Y)

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


		yinc = -5
		while yinc < y1:
			yinc += 5
			self.axis(x0, yinc, x1 - x0, fill='light salmon')
			self.write('%d' % yinc, x1, yinc+1, font=BOLD)
		
		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')
		sd = 0.05
		sd2by2 = sd*sd/2
		t, credit = self.x0, 0.5*(self.y0 + self.y1)
		self.dot((t, credit), fill='yellow', width=4, tags='objects')
		while t < self.x1:
			t0, credit0 = t, credit
			t += 1
			credit *= math.exp(self.rng.normalvariate(0.0, sd)-sd2by2)
			self.line(t0, credit0, t, credit, width=2, tags='objects')
		self.iterations += 1
		print
	

	def iterate(self):
		m, dm = 10, self.batch.get()
		for i in range(self.iterations, self.iterations + dm):
			self.simulate()
			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('A financial martingale')
	animation = AnimateFinance(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.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.1 2008/07/08 06:55:20 wskendall Exp wskendall $'
