/keyhandler.py
from bmevent import *
from keyfile import *
from pygame.locals import *
from constants import *

class KeyHandler:
	keyfile = None
	li = None

	keymap = None
	jsbuttonmap = None
	jsaxismap = None
	timings = None

	def __init__(self,gameconfig,keyfile):
		self.keyfile = keyfile
		self.li = BMEListIter(keyfile.bmelist)
		self.lastdataref = [0,0,0,0,0,0,0,0]
		self.li.goto(0)
		for b in self.li.window(1.0):
			if b.type == BME_TEMPO:
				self.bpm = b.dataref
				self.adjust_window()
				break
		self.keymap = gameconfig['keymap']
		self.jsbuttonmap = gameconfig['jsbuttonmap']
		self.jsaxismap = gameconfig['jsaxismap']
		self.timings = gameconfig['timings']

	def decodekey(self,event):
		try: 
			if event.type == KEYDOWN:
				return (self.keymap[event.key],1)
			elif event.type == KEYUP:
				return (self.keymap[event.key],0)
			elif event.type == JOYBUTTONDOWN:
				return (self.jsbuttonmap[event.button],1)
			elif event.type == JOYBUTTONUP:
				return (self.jsbuttonmap[event.button],0)
			elif event.type == JOYAXISMOTION:
				if event.value == 0.0:
					return (self.jsaxismap[(event.axis,event.value)],0)
				else:
					return (self.jsaxismap[(event.axis,event.value)],1)
			else:
				return (None,-1)
		except KeyError:
			return (None,-1)

	def adjust_window(self):
		self.ms_per_beat = 60000.0 / self.bpm
		self.tstart = 200.0 / self.ms_per_beat
		self.tlength = 2000.0 / self.ms_per_beat

	def setproperties(self,properties):
		try:
			self.bpm = properties['bpm']
			self.adjust_window()
		except KeyError:
			pass

	def handle(self,event,t):
		b = self.keyfile.eval_beatfunc(t)
		properties = {}
		(k,ud) = self.decodekey(event)
		if k == None:
			return
		properties['key'] = k
		properties['ud'] = ud
		if ud == 0:
			return (None, properties)
		self.li.goto(b - self.tstart)
		bmelist = filter(lambda x: x.key == k, self.li.window(self.tlength,BME_NOTE1))
		if not bmelist:
			properties['judgement'] = JUDGE_NA
			return (BMEvent(b,BME_HIT,k,self.lastdataref[k]), properties)

		# Find the closest bme to our event
		sd = self.tstart
		cbme = None
		for bme in bmelist:
			d = abs(b - bme.beat)
			if d < sd:
				sd = d
				cbme = bme
		# Convert back to ms
		sd = sd * self.ms_per_beat

		if cbme:
			for bt in self.timings:
				if sd < bt[0]:
					properties['judgement'] = bt[1]
					cbme.type = BME_HIT
					break
			self.lastdataref[k] = cbme.dataref
		else:
			properties['judgement'] = JUDGE_NA
			return (BMEvent(b,BME_HIT,k,self.lastdataref[k]),properties)
		properties['accuracy'] = (b - cbme.beat) * self.ms_per_beat
		return (cbme, properties)