'''
Handle gcalctool interface gracefully

@author: Joel Feiner
@organization: UNC Chapel Hill
@owner: Regents of the University of California
@copyright: Copyright (c) 2007, Joel Feiner
@license: BSD

All rights reserved. This program and the accompanying materials are made 
available under the terms of the BSD license which is available at
http://www.opensource.org/licenses/bsd-license.php
'''
# import useful modules for Perks
import Perk, Task, AEConstants
from POR import POR
from i18n import bind, _

# metadata describing this Perk
__uie__ = dict(kind='perk', tier='gcalctool', all_tiers=False)

# to support translation of strings in this Perk, uncomment the following line
# and provide the proper domain and path to your translation file
# _ = bind(domain, locale_dir)

focus_change = False;

class CalcPerk(Perk.Perk):
  '''
  CalcPerk, perk for handling gcalctool. The getDescription method
  should be changed to return a translated description of this Perk. The 
  description should include a statement of which Tiers this Perk applies to
  by default.
  '''
  def init(self):
		# basic registration stuff
    self.registerTask(HandleFocusChange('read focus'))	
    self.registerTask(HandleCaretChange('read caret', tier=True))
    self.registerTask(ReadEquation('read equation'))
    self.registerTask(HandleStatusBar('status bar', tier=True))
		# this allows us to get keyboard input, only used for the CAPS+A hotkey for reading the equation
    kbd = self.getInputDevice('Keyboard')
    self.addInputModifiers(kbd, kbd.AEK_CAPS_LOCK)
    self.registerCommand(kbd, 'read equation', True, 
                         [kbd.AEK_CAPS_LOCK, kbd.AEK_A])
                         
  def getDescription(self):
    return _('Applies to gcalctool by default.')

class ReadEquation(Task.InputTask):
  '''
  Is invoked whenever user hits CAPS+A.  It reads the entire equation, character by character
  '''
  def execute(self, **kwargs):
  	# POR path: 0,0,1,0
		frame = self.getRootAcc()
		textBox = self.getAccFromPath(frame, 0, 1, 0, 0)
		textBoxText = self.getItemText(textBox)	
		self.mayStop()
		self.stopNow()
		for i in range(0, len(textBoxText)):
			curChar = textBoxText[i]
			self.sayInfo(text=curChar)

class HandleStatusBar(Task.PropertyTask):
	'''
	Handles text being displayed in the statusbar

	NOTE: this does not work because gcalctool only sometimes sends a property
        change event.  The code is retained here in case it works in the future
	'''
	# need this ugly little hack because we get this change
	#   event four times with no apparent difference between each message
	callCount = 0
	
	def execute(self, por, name, value, layer, **kwargs):
		self.callCount = self.callCount + 1
		if self.callCount == 4:
			statusText = self.getItemText(por)
			self.mayStop()
			self.inhibitMayStop()
			self.sayInfo(text=statusText)
			self.callCount = 0

class HandleFocusChange(Task.FocusTask):
  '''  
	Invoked when focus changes.  We use this to announce the proper text of buttons as the user
	arrows between them, or uses TAB.
  '''   
  def executeGained(self, por, **kwargs):
		if self.hasAccRole('push button', por):		# only do this if focus is for a button
			buttonText = self.getItemText(por)
			# for some buttons, we need to say special text
			if buttonText == "−":
				buttonText = "minus"
			elif buttonText == "+":
				buttonText = "plus"
			elif buttonText == "×":
				buttonText = "times"
			elif buttonText == "%":
				buttonText = "modulo"
			elif buttonText == "√":
				buttonText = "square root"
			elif buttonText == "±":
				buttonText = "negation"
			elif buttonText == "1/x":
				buttonText = "one slash ex"
			elif buttonText == "(":
				buttonText = "left parenthesis"
			elif buttonText == ")":
				buttonText = "right parenthesis"
			elif buttonText == "x2":
				buttonText = "squared"
			elif buttonText == "÷":
				buttonText = "divide"
			elif buttonText == "Abs":
				buttonText = "absolute value"
			elif buttonText == "Frac":
				buttonText = "fraction"
			elif buttonText == "Int":
				buttonText = "integer"
			elif buttonText == "Bksp":
				buttonText = "backspace"
			elif buttonText == "CE":
				buttonText = "clear entry"
			elif buttonText == "Clr":
				buttonText = "clear"
			elif buttonText == ".":
				buttonText = "dot"
			elif buttonText == "Sto":
				buttonText = "store"
			elif buttonText == "Rcl":
				buttonText = "recall"
			elif buttonText == "Exch":
				buttonText = "exchange"
			self.mayStop()
			self.inhibitMayStop()
			self.sayItem(text=buttonText)
			# this prevents the DefaultPerk's handler from saying the button text
			# again for the FocusChange event
			self.blockNTasks(1, Task.SelectorTask)
			# also required to prevent DefaultPerk from doing its job
			return False
	    
class HandleCaretChange(Task.CaretTask):
	'''
	Reads text that is added to the equation, either by keyboard or by clicking buttons
	'''
	oldString = ""		# copy of what was in the equation box
	cleared = 1				# set to 1 if the equation box was cleared
	startup = 1				# set to 1 initially, otherwise set to 0 (added to deal with startup issues)
	
	# when a button is pressed, a CaretChange:Deleted event is sent first, then a CaretChange:Moved
	# which is ignored, and finally a CaretChange:Inserted
	# we use the Deleted event to capture what the string was before the new text is added with the
	# Inserted event
	def executeDeleted(self, por, text, text_offset, **kwargs):
		self.oldString = text
	
	def executeInserted(self, por, text, text_offset, **kwargs):
		# extract the change in the text
		startString = ""
		endString = ""
		startIndex = text.find(self.oldString)
		# if we're just starting up, make sure to do the right thing with whatever's in the equation box
		if self.startup == 1:
			self.startup = 0
			startString = ""
			# question is whether to read everything here...there's no way to read
			#   the last thing entered. Hopefully, the user would start LSR
			#   before starting gcalctool to avoid this problem
			endString = "Current text: " + text
			startIndex = 0
			self.cleared = 0
		elif startIndex == -1:			# so we did not add any text at the beginning or end
			if self.cleared == 1:		# well, we were previously cleared, so now we're adding new text
																#   the 0 will be replaced by whatever was just added
				startString = ""
				endString = text
				self.cleared = 0
			else:
				if self.oldString == "0":		# just replaced a cleared calculator
					startString = ""
					endString = text
				elif text != "0":		# not a clearing, but an equals, so we should read whole result
					# ...but it could be a backspace, so let's check for that condition first
					# (if new string is prefix of old string, we had a backspace)
					if self.oldString.find(text) == 0:
						startString = ""
						endString = ""
					else:
						startString = ""
						endString = "Result is " + text
				else:			# we just got cleared, so mark it as such
					self.cleared = 1
		else:		# this is the normal case: something got added, so let's say it
			startString = text[0:startIndex]
			endString = text[(startIndex + len(self.oldString)):]
		
		if endString != "":
			self.mayStop()
			self.inhibitMayStop()

		if self.cleared == 1:
			self.sayInfo(text="Cleared")
		elif startString == "1/(":
			self.sayInfo(text="Invert")
		elif startString == "-(":
			self.sayInfo(text="Negate")
		elif endString == "Sqrt(":
			self.sayInfo(text="Square root")
		elif endString == '-':
			self.sayInfo(text="minus")
		elif endString == '*':
			self.sayInfo(text="times")
		elif endString == '/':
			self.sayInfo(text="divide")
		elif endString == '%':
			self.sayInfo(text="modulus")
		elif endString == "Int(":
			self.sayInfo(text="integer")
		elif endString == "Abs(":
			self.sayInfo(text="absolute value")
		elif endString == "Frac(":
			self.sayInfo(text="fraction")
		elif endString != "":
			self.sayInfo(text=endString)
