from wxPython.wx import *
from NCMap import *
from NCRenderer import *
import sys, Image
import BmpImagePlugin

# don't look for more plugins
Image._initialized = 1

#constants
DATA_DIR = 'Data\\'
MAX_Z = 65535.0
MIN_Z = 0.0
MAX_V = 6.0
MIN_V = 1.0

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

class NCApp(wxApp):
	#-----------------------------------------------
	#make a wx application
	#-----------------------------------------------
	def OnInit(self):
		self.frame = NCFrame(True)
		self.frame.Show(True)
		self.SetTopWindow(self.frame)
		return True

#------------------------------------------------------------------------------------#
		
class NCFrame(wxFrame):
	#-----------------------------------------------
	#build the frame that will respond to user events
	#-----------------------------------------------
	def __init__(self, fullscreen):
		self.screenWidth, self.screenHeight = wxDisplaySize()
		wxFrame.__init__(self, None, -1, 'BATS North Carolina Demo')
		
		if fullscreen:
			self.ShowFullScreen(True, wxFULLSCREEN_ALL)

		#load the pretty bitmap and get it ready for the screen
		pil_image = Image.open(DATA_DIR+'NCPretty.bmp', )
		size = pil_image.size
		pil_image = pil_image.resize((self.screenWidth, self.screenHeight),Image.BICUBIC)
		wx_image = apply(wxEmptyImage, pil_image.size)
		wx_image.SetData(pil_image.convert("RGB").tostring())
		self.bmp = wx_image.ConvertToBitmap()
		
		#create a map object
		transform = (11, 193, float(size[0])/self.screenWidth,
			float(size[1])/self.screenHeight)
		self.map = NCMap(DATA_DIR+'NCUgly.dmp', transform, DATA_DIR+'NCData.dmp')
		
		#create a renderer
		self.rend = NCRenderer(DATA_DIR+'NCTrans.dmp', DATA_DIR+'NCTextureJoy.ifr',
			DATA_DIR+'NCTextureMouse.ifr', 'Cache\\', 25, self.GetHandle())
		
		#init some variables
		self.last_info = {'oid' : None, 'curr_offset' : 0, 'prev_offset': 0}
		
		#initialize the joystick if it's available
		self.stick = wxJoystick()
		if self.stick != None:
			z = self.stick.GetZPosition()
			slope = (MAX_Z - MIN_Z)/(MIN_V - MAX_V)
			inter = MIN_Z - slope * MAX_V
			self.vel = (z-inter)/slope
			self.stick.SetCapture(self, 30)
		
		#hook the events
		EVT_PAINT(self, self.OnPaint)
		EVT_MOTION(self, self.OnMouseMotion)
		EVT_RIGHT_DOWN(self, self.OnMouseRightDown)
		EVT_LEFT_DOWN(self, self.OnMouseLeftDown)
		EVT_KEY_DOWN(self, self.OnKeyDown)
		EVT_JOY_MOVE(self, self.OnJoyDigitalMove)
		EVT_JOY_DOWN(self, self.OnJoyButton)
		EVT_JOY_ZMOVE(self, self.OnJoyZ)

	#-----------------------------------------------
	#redraw the pretty map
	#-----------------------------------------------
	def OnPaint(self, event):
		drawer = wxPaintDC(self)
		drawer.BeginDrawing()
		drawer.DrawBitmap(self.bmp, 0, 0)
		drawer.EndDrawing()

	#-----------------------------------------------
	#handle an escape event to quit the program
	#-----------------------------------------------
	def OnKeyDown(self, event):
		key = event.GetKeyCode()
		if key == WXK_ESCAPE: 
			self.Destroy()
			
	#-----------------------------------------------
	#handle mouse motion events
	#-----------------------------------------------		
	def OnMouseMotion(self, event):
		self.MotionEvent(event.GetX(), event.GetY())
		
	#-----------------------------------------------
	#handle mouse left click
	#-----------------------------------------------		
	def OnMouseLeftDown(self, event):
		self.ButtonZeroEvent(event.GetX(), event.GetY())

	#-----------------------------------------------
	#handle mouse right click
	#-----------------------------------------------
	def OnMouseRightDown(self, event):
		self.ButtonOneEvent(event.GetX(), event.GetY())

	#-----------------------------------------------
	#handle a joystick velocity change
	#-----------------------------------------------	
	def OnJoyZ(self, event):
		z = self.stick.GetZPosition()
		slope = (MAX_Z - MIN_Z)/(MIN_V - MAX_V)
		inter = MIN_Z - slope * MAX_V
		self.vel = (z-inter)/slope
	
	#-----------------------------------------------
	#handle a joystick move event
	#-----------------------------------------------
	def OnJoyDigitalMove(self, event):
		theta = self.stick.GetPOVPosition()
		if theta > 36000:
			return
		cx, cy = win32api.GetCursorPos()
		x = cx + round(math.sin(theta * math.pi/18000.0))*self.vel
		y = cy - round(math.cos(theta * math.pi/18000.0))*self.vel
		win32api.SetCursorPos((x,y))

	#-----------------------------------------------
	#handle a joystick button press
	#-----------------------------------------------	
	def OnJoyButton(self, event):
		b = event.GetButtonState()
		x,y = self.ScreenToClient(win32api.GetCursorPos())
		if b == 1:
			self.ButtonZeroEvent(x,y)
		elif b == 2:
			self.ButtonTwoEvent()
		elif b == 4:
			self.ButtonThreeEvent(x,y)
		elif b == 8:
			self.ButtonOneEvent(x,y)

 	#-----------------------------------------------
	#update local earcons and textures
	#-----------------------------------------------
	def MotionEvent(self, x, y):
		#set the query params for speed
		self.map.SetAreaSize(50)
		self.map.SetSampling(2)
		
		#get the ids in the local area
		mds = self.map.QueryArea(x,y)
		if mds != None:
 			self.rend.RenderAreaEarcons(mds)
 			
 		#get the id under the cursor
 		md = self.map.QueryPoint(x,y)
 		self.rend.RenderPointTexture(md)	

  	#-----------------------------------------------
	#cylce through all of the info about this point
	#-----------------------------------------------
	def ButtonZeroEvent(self, x, y):
		md = self.map.QueryPoint(x,y)
		
		if md != None:
			#if this is the same area as last time, speak additional info
			if self.last_info['oid'] == md.GetOID():
				self.last_info['curr_offset'] += 1
				self.rend.RenderPointInfo(md,self.last_info['curr_offset'])
			#if this is a different area, speak the main info
			else:
				self.rend.RenderPointInfo(md,0)
				self.last_info['oid'] = md.GetOID()
				self.last_info['curr_offset'] = 0

  	#-----------------------------------------------
	#call out nearby objects of interest
	#-----------------------------------------------
	def ButtonOneEvent(self, x, y):
		#set the query params for accuracy
		self.map.SetAreaSize(80)
		self.map.SetSampling(2)
		
		#get the ids in the local area
		mds = self.map.QueryArea(x,y)
		
 		if mds != None:
 			self.rend.RenderAreaCallouts(mds)
 			
  	#-----------------------------------------------
	#stop the voice immediately
	#-----------------------------------------------
 	def ButtonTwoEvent(self):
 		self.rend.StopVoice()
 			
  	#-----------------------------------------------
	#cycle backwards through the info about this point
	#-----------------------------------------------
 	def ButtonThreeEvent(self,x,y):
		md = self.map.QueryPoint(x,y)
		
		if md != None:
			#if this is the same area as last time, speak additional info
			if self.last_info['oid'] == md.GetOID():
				self.last_info['curr_offset'] -= 1
				self.rend.RenderPointInfo(md,self.last_info['curr_offset'])
			#if this is a different area, speak the main info
			else:
				self.rend.RenderPointInfo(md,0)
				self.last_info['oid'] = md.GetOID()
				self.last_info['curr_offset'] = 0
				

app = NCApp(0)
app.MainLoop()
