# Travis Scrimshaw
# classes.py - Module containing all classes
# Notes: turnSpeed is measured in strange units, most of everything are 0 - 1,
#      : foodPercent is how much eating one unit of food affects hunger

import functions as F
import math as M
import random as Rand

hungerType = "constant"

class Vector:
	def __init__(self, x, y, z):
		self.vec = (x, y, z)
	# __init__

	def __repr__(self):
		return "Vector type: " + `self.vec[0]` + ' ' + `self.vec[1]` + ' ' + `self.vec[2]`
	# __repr__

	def Len(self):
		return M.sqrt(self.vec[0]**2 + self.vec[1]**2 + self.vec[2]**2)
	# Len()

	def Dist(self, vec2):
		return (self - vec2).Len()

	def Norm(self):
		return self / self.Len()
	# Norm()

	def Cross(self, vec2):
		return Vector(self.vec[1]*vec2.vec[2] - self.vec[2]*vec2.vec[1], self.vec[2]*vec2.vec[0] \
			 - self.vec[0]*vec2.vec[2], self.vec[0]*vec2.vec[1] - self.vec[1]*vec2.vec[0])
	# Cross()

	def Dot(self, vec2):
		return self.vec[0]*vec2.vec[0] + self.vec[1]*vec2.vec[1] + self.vec[2]*vec2.vec[2]
	# Dot()

	def __add__(self, rhs):
		return Vector(self.vec[0] + rhs.vec[0], self.vec[1] + rhs.vec[1], self.vec[2] + rhs.vec[2])
	# __add__

	def __sub__(self, rhs):
		return Vector(self.vec[0] - rhs.vec[0], self.vec[1] - rhs.vec[1], self.vec[2] - rhs.vec[2])
	# __subtract__

	def __mul__(self, scalar):
		return Vector(self.vec[0]*scalar, self.vec[1]*scalar, self.vec[2]*scalar)
	# __multiply__

	def __div__(self, scalar):
		return Vector(self.vec[0] / scalar, self.vec[1] / scalar, self.vec[2] / scalar)
	# __div__
# Vector

class Bird:
	def __init__(self, idnum, f, ip, iv, ts=0.3, ih=.0, fp=.0, ie=.0, ms=.0, si=.0, s=.0, a=.0, i=.0, ifri=[]):
		self.ID = idnum
		self.feeder = f
		self.pos = ip
		self.vel = iv
		self.speed = iv.Len()
		self.turnSpeed = ts
		self.isFeeding = False
		self.foodPercent = fp
		self.health = 100.0

		self.size = si
		self.hunger = ih
		self.energy = ie
		self.strength = s
		self.aggression = a
		self.intellect = i
		self.friends = ifri
		self.numFriends = len(ifri)
		self.maxSpeed = ms
		self.avoid = Vector(0, 0, 0)
		self.lag = 0
	# __init__

	def __repr__(self):
		return "Percent hunger = " + `self.hunger*100.0` + "%"
	# __repr__

	def eval(self, timestep, birdList):
		if self.isFeeding == True:
			ammount = self.feeder.feeding(timestep)
			self.hunger -= self.foodPercent*ammount
			self.energy += self.foodPercent*ammount
			if self.energy > 1:
				self.energy = 1
			# If

			# If full, leave
			if self.hunger <= 0:
				#print "Leaving feeder"
				self.feeder.birdLeft(self)
				self.hunger = 0
				self.isFeeding = False
				self.lag = 0
			else:
				self.vel = Vector(0, 0, 0)
				return True
			# If-Else
		elif (self.feeder.pos - self.pos).Len() < self.feeder.size + self.size and self.hunger > 0:
			#print "Landing on feeder"
			self.isFeeding = True
			self.feeder.birdLanded(self)
			self.vel = Vector(0, 0, 0)
			return True
		# If-Else

		if hungerType == "variable" and self.lag > 5 / timestep:
			self.hunger += .01
			if self.hunger >= 1:
				self.hunger = 1
			# If
		# If

		# Choose and move towards the current goal
		target, targetDir = self.evalGoal(birdList)

		avoidDir = Vector(0, 0, 0)
		for bird in birdList:
			if bird != self and bird.isFeeding == False and bird != target: # Don't include ourselves!!!
				# If in contact with another bird
				if (self.pos - bird.pos).Len() <= self.size + bird.size:
					# print "Attacking bird"
					damage = F.Pos((bird.strength - self.strength)*F.Rand())
					self.energy -= damage
					self.health -= damage

					# If no health, the bird has just been killed
					if self.health <= 0:
						# print "I am now dead."
						return False
					# If
				# If

				# Avoidence of birds
				avoidDir += self.Avoidence(bird)
			# If
		# For
		if self.hunger <= 0:
			avoidDir += (self.pos - self.feeder.pos) / (.01*(self.pos - self.feeder.pos).Len())
		# If
		#self.avoid = avoidDir
		#self.target = targetDir

		dir = targetDir + avoidDir
		if dir.Len() < self.turnSpeed:
			newVel = self.vel + dir
		else:
			newVel = self.vel + dir.Norm()*self.turnSpeed
		# If-Else

		if newVel.Len() > self.maxSpeed:
			newVel = newVel.Norm()*self.maxSpeed

		self.newVel = newVel

		self.pos += newVel*timestep
		self.vel = newVel
		self.lag += 1
		return True
	# eval()

	def Avoidence(self, bird):
		VecBirdSelf = self.pos - bird.pos
		dir = VecBirdSelf / ((1+4*self.aggression)*(VecBirdSelf.Len() - bird.size*.6))
		# dir = VecBirdSelf / (VecBirdSelf.Len() - bird.size*.6)
		return dir
	# Avoidence()

	def evalGoal(self, birdList):
		minDist = 0.0
		attack = 1.0
		for bird in birdList:
			dist = self.pos.Dist(bird.pos)
			# bird.energy bird.strength self.strength self.energy self.hunger
		# For

		if attack < self.aggression:
			dir = target.pos - self.pos
		elif self.feeder.full == False and self.hunger > 0:
			dir = (self.feeder.pos - self.pos) * (.5 + self.hunger)
			target = self.feeder
		else:
			tempVec = self.feeder.pos - self.pos
			#if self.vel.Len() == 0:
			#	self.vel = Vector(0.5-F.Rand(), 0.5-F.Rand(), 0.5-F.Rand())
			#print "\nTempVec : planeNorm : dir"
			#print tempVec
			#planeNorm = tempVec.Cross(self.vel).Norm()
			#print planeNorm
			#dir = tempVec.Cross(planeNorm).Norm()
			#print dir
			if tempVec.Len() < self.feeder.orbitSize:
				dir = tempVec*(tempVec.Len()- self.feeder.orbitSize)
			else:
				dir = tempVec*(tempVec.Len() - self.feeder.orbitSize)
			target = -1
			# If-Else
		# If-Else

		return target, dir
	# evalGoal()

	def setPosVel(self, newPosVel):
		self.pos = newPosVel[0]
		self.vel = newPosVel[1]
	# setPosVel()
# Bird

class Feeder:
	def __init__(self, np, s, os, f, mf, x, y, z):
		self.pos = Vector(x, y, z)
		self.size = s
		self.orbitSize = os
		self.numPerches = np
		self.numInUse = 0
		self.full = False
		self.food = f
		self.maxFood = mf
	# __init__

	def regenerage(self, type, timestep, numBirds):
		if type == "infinite":
			self.food = numBirds
		else:
			self.food += self.maxFood*type(timestep)
		# If-Else

		if self.food > self.maxFood:
			self.food = self.maxFood
	# Regenerage()

	def feeding(self, ammount):
		ammount *= .1
		if(ammount > self.food):
			self.food -= ammount
			return ammount
		# If
		ammount = self.food
		self.food = 0
		return ammount
	# Feeding()

	def birdLanded(self, bird):
		if self.full == True:
			return False
		# If
		self.numInUse += 1
		if self.numInUse == self.numPerches:
			self.full = True
		# If
		return True
	# BirdLanded()

	def birdLeft(self, bird):
		self.numInUse -= 1
		self.full = False
	# birdLeft

	def __repr__(self):
		return "There are " + `numInUse` + " of " + `numPerches` + " being used."
	# __repr__
# Feeder
