/Richter/Actor.py
from Sprite import *

class Actor:
	def __init__(self, surfaces = None, position = [0,0]):
		self.states = {}
		self.currentstatename = None
		self.currentstate = None
		self.surfaces = surfaces
		self.position = position
		self.hitbox = None
		self.colliding = set()

	
	def defineState(self, statename, sprite):
		if self.states.has_key(statename):
			print "WARNING: Redefining state", statename
		self.states[statename] = sprite


	def setState(self, statename):
		self.currentstatename = statename
		self.currentstate = self.states[statename]
		self.currentstate.reset()
		self.currentstate.play()
		self.currentstate.setPosition(self.position[0], self.position[1])
		self.hitbox = self.currentstate.hitbox


	def setPosition(self, x, y):
		self.position = [x,y]
		self.currentstate.setPosition(x, y)


	def collideDelta(self, delta):
		if not self.surfaces:
			return False
		s = self.currentstate
		new_hitbox = s.hitbox.move(delta[0], delta[1])
		if self.surfaces.collideRect(new_hitbox):
			return True
		else:   
			return False


	def moveDelta(self, delta):
		collided = False
		s = self.currentstate
		d = [0,0]
		if self.surfaces.collideRect(s.hitbox) or (delta[0] == 0 and delta[1] == 0):
			return (True, (0,0))
		if abs(delta[0]) > abs(delta[1]):
			dx = delta[0] / abs(delta[0])
			dy = float(delta[1]) / float(abs(delta[0]))
			xrange = abs(int(delta[0]))
			while xrange > 0:
				d[0] += dx
				d[1] += dy
				nrect = s.hitbox.move(d[0],d[1])
				if self.surfaces.collideRect(nrect):
					# Slide along surfaces if possible
					nrect = s.hitbox.move(d[0],d[1]-dy)
					if not self.surfaces.collideRect(nrect):
						d[1] -= dy
						xrange -= 1
						continue
					nrect = s.hitbox.move(d[0]-dx,d[1])
					if dy != 0 and not self.surfaces.collideRect(nrect):
						d[0] -= dx
						xrange -= 1
						continue
					d[0] -= dx
					d[1] -= dy
					collided = True
					break
				xrange -= 1
			d[1] = int(d[1])
		else:
			dy = delta[1] / abs(delta[1])
			dx = float(delta[0]) / float(abs(delta[1]))
			yrange = abs(int(delta[1]))
			while yrange > 0:
				d[1] += dy
				d[0] += dx
				nrect = s.hitbox.move(d[0],d[1])
				if self.surfaces.collideRect(nrect):
					# Slide along surfaces if possible
					nrect = s.hitbox.move(d[0]-dx,d[1])
					if not self.surfaces.collideRect(nrect):
						d[0] -= dx
						yrange -= 1
						continue
					nrect = s.hitbox.move(d[0],d[1]-dy)
					if dx != 0 and not self.surfaces.collideRect(nrect):
						d[1] -= dy
						yrange -= 1
						continue
					d[1] -= dy
					d[0] -= dx
					collided = True
					break
				yrange -= 1
			d[0] = int(d[0])
		return (collided, d)


	def move(self, delta):
		(collided, delta) = self.moveDelta(delta)
		self.position[0] += delta[0]
		self.position[1] += delta[1]
		self.currentstate.setPosition(self.position[0], self.position[1])
		if collided:
			self.hitWall()
		return collided


	def collideCheck(self, objs):
		"""Test whether another object in objs is colliding with us,
		and if so, trigger a collision event"""

		if not self.surfaces: return

		remove = []
		for o in self.colliding:
			if not self.hitbox.colliderect(o.hitbox):
				remove.append(o)
		for o in remove:
			self.colliding.remove(o)
			o.colliding.remove(self)
			self.collideEnd(o)
			o.collideEnd(self)

		#for o in objs.collideRectWalk(self.hitbox):
		for i in self.hitbox.collidelistall(filter(None, [o.hitbox for o in objs])):
			o = objs[i]
			if o is self: continue
			if o not in self.colliding:
				self.colliding.add(o)
				o.colliding.add(self)
				self.collideStart(o)
				o.collideStart(self)


	def hitWall(self):
		"Called when this object hits a wall after attempting to move"
		pass


	def collideStart(self, obj):
		"Called when this object begins colliding with another"
		pass


	def collideEnd(self, obj):
		"Called when this object stops colliding with another"
		pass


	def update(self):
		"Override this function. Called once per frame."
		pass


	def draw(self):
		self.currentstate.draw()