import serial
import time, gc
import termios, fcntl, sys, os

gc.collect()

def combine(high, low):
	return (high << 8) | low

def getBit(bit, byte):
	return (byte >> bit) & 0x01

def splitWideInt(num):
	if num >= 0:
		out = num
	else:
		out = (1<<16) + num
	return ( (out >> 8) & 0xff, out & 0xff)


class robot():
	def __init__(self):
		self.port = serial.Serial('/dev/tty.ElementSerial-ElementSe-1', 57600)
		self.restart()
		self.sensors = {}
		self.sendBytes([140, 0, 6, 48, 32, 45, 32])
		time.sleep(0.1)
		self.sendBytes([140, 1, 6, 72, 20, 76, 20, 79, 20])

	def __del__(self):
		print 'destroying'
		self.port.close()

	def sendBytes(self, bytes):
		out_string = ''
		for i in range(len(bytes)):
			out_string += chr(bytes[i])
		self.port.write(out_string)

	def restart(self):
		self.port.setTimeout(0.5)
		self.init()
		time.sleep(0.2)
		self.safeMode()

	def close(self):
		self.port.close()

	def init(self):
		self.sendBytes([128])

	def safeMode(self):
		self.sendBytes([131])

	def drive(self, vel, rad):
		vel = splitWideInt(vel);
		rad = splitWideInt(rad);
		self.sendBytes([137, vel[0], vel[1], rad[0], rad[1]])

	def straight(self, vel):
		self.drive(vel, 32768)

	def waitDistance(self, vel):
		vel = splitWideInt(vel)
		self.sendBytes([156, vel[0], vel[1]])
		
	def waitAngle(self, vel):
		vel = splitWideInt(vel)
		self.sendBytes([157, vel[0], vel[1]])
		
		self.sendBytes
	def turn(self, vel):
		self.drive(vel, 1)

	def stop(self):
		self.drive(0,0)

	def remote(self):
		fd = sys.stdin.fileno()

		oldterm = termios.tcgetattr(fd)
		newattr = termios.tcgetattr(fd)
		newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
		termios.tcsetattr(fd, termios.TCSANOW, newattr)

		oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
		fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)

		try:
			while 1:
				try:
					c = sys.stdin.read(1)
					if c == 'q':
						self.stop()
						return

					speed = 150
					if c == 's':
						self.turn(speed)
					if c == 'f':
						self.turn(-speed)
					if c == 'e':
						self.straight(speed)
					if c == 'd':
						self.straight(-speed)
					if c == ' ':
						self.stop()

				except IOError: pass
		finally:
			termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
			fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)

	def playSong(self, num):
		self.sendBytes([141, num])

	def parseSensorData(self, data):
		try:
			self.sensors['bumpRight'] = getBit(0, data[0])
			self.sensors['bumpLeft'] = getBit(1, data[0])
			self.sensors['dropCaster'] = getBit(4, data[0])
			self.sensors['dropLeft'] = getBit(3, data[0])
			self.sensors['dropRight'] = getBit(2, data[0])
			self.sensors['wall'] = getBit(0, data[1])
			self.sensors['infrared'] = data[10]
			self.sensors['chargingState'] = data[16]
			self.sensors['wallSignal'] = combine(data[26], data[27])
			self.sensors['chargingSource'] = data[39]
			self.sensors['oiMode'] = data[40]
		except:
			print 'Error reading sensors! Data packet size was ' + str(len(data))
				

	def getSensor(self):
		s = 52
		self.sendBytes([142, 6])
		time.sleep(0.1)
		vals = self.port.read(size=s)
		vals = [ord(c) for c in vals]
		self.parseSensorData(vals)

	def inSingleBeam(self, dir):
		if self.sensors['bumpRight'] or self.sensors['bumpLeft']:
			self.backupTryagain()
		else :
			self.drive(100, -300*dir)
			time.sleep(0.1)

	def fastTurnAround(self, orgDir):
		dir = orgDir
		if self.sensors['bumpLeft']:
			dir = -1
		if self.sensors['bumpRight'] or self.sensors['bumpLeft']:
			self.straight(-250*dir)
			time.sleep(0.3)
			self.turn(200*dir)
			time.sleep(0.6)
			self.drive(250, -300*dir)
			time.sleep(0.5)
			self.turn(-200*dir)
			time.sleep(0.8)
			self.drive(150, -300*dir)
			time.sleep(0.2)


	def backupTryagain(self):
		self.straight(-250)
		time.sleep(0.3)
		self.turn(200)
		time.sleep(0.3)
		self.drive(150, -900)

	def backupTurnGoStraight(self, dir):
		self.straight(-250*dir)
		time.sleep(0.4)
		self.turn(200*dir)
		time.sleep(0.3)
		self.drive(150, -900*dir)
		time.sleep(0.2)
		self.turn(-200*dir)
		time.sleep(0.3)

	def singleBeamForce(self, dir):
		self.drive(100, 100*dir)
		time.sleep(0.1)
		if self.sensors['bumpRight'] or self.sensors['bumpLeft']:
			self.backupTurnGoStraight(dir)


	def auto(self):
		self.getSensor()

		self.drive(150, -500)
		self.wasAtWall = False
		self.wasAtRGF = False
		while 1:
			self.getSensor()
			#print self.sensors
			#continue

			if self.sensors['infrared'] == 248:
				print 'red                 :-)'
				self.inSingleBeam(1)
				self.wasAtRGF = False
			elif self.sensors['infrared'] == 244:
				print 'green               :-)'
				self.inSingleBeam(-1)
				self.wasAtRGF = False
			elif self.sensors['infrared'] == 242:
				print 'force'
				if self.wasAtRGF:
					print 'At home!'
					self.stop()
					self.playSong(1)
					return
				if self.sensors['bumpRight'] or self.sensors['bumpLeft']:
					self.backupTryagain()
			elif self.sensors['infrared'] == 250:
				print 'red + force         :-|'
				self.singleBeamForce(1)
				self.wasAtRGF = False
			elif self.sensors['infrared'] == 246:
				print 'green + force       :-|'
				self.singleBeamForce(-1)
				self.wasAtRGF = False
			elif self.sensors['infrared'] == 254:
				print 'red + green + force 8-D'
				self.wasAtRGF = True
				self.straight(150)
			elif self.sensors['infrared'] == 252:
				print 'red and green       :-D'
				self.straight(100)
				self.wasAtRGF = False

			elif self.sensors['wall'] and self.sensors['bumpRight']:
				print 'bump and wall'
				self.drive(100, 300)
				time.sleep(0.1)
				self.drive(150, -1500)
				self.wasAtWall = True
			elif self.sensors['bumpRight'] or self.sensors['bumpLeft']:
				print 'bumped!'
				self.backupTryagain()
			elif self.sensors['wall']:
				print 'at wall :-)'
				time.sleep(0.2)
				self.straight(150)
				self.wasAtWall = True
			elif self.sensors['wall'] == 0 and self.wasAtWall:
				print 'was at wall :-('
				self.drive(150, -500)
				time.sleep(0.2)
				self.wasAtWall = False
			elif self.sensors['wall'] == 0:
				self.wasAtWall = False
				


r=robot()
r.auto()

