INTERPOLATORS = {}

INTERPOLATORS.angle = function(self, from, to, interp)
	local diff = math.abs(math.angleDifference(from,to))
	return math.approachAngle(from, to, diff*interp)
end

INTERPOLATORS.bool = function(self, from, to, interp)
	return to
end


INTERPOLATORS.xpos = function(self, from, to, interp, var)
	local fromD = self["lastd"..var]
	local toD = self["d"..var]

	local diff = math.abs(math.angleDifference(from,to))
	local ddiff = toD-fromD

	return math.cubicInterpolation(interp, from, from +fromD*self:getStepFrequency(), to, to+toD*self:getStepFrequency())
end

INTERPOLATORS.ypos = function(self, from, to, interp, var)
	local fromD = self["lastd"..var]
	local toD = self["d"..var]

	local diff = math.abs(math.angleDifference(from,to))
	local ddiff = toD-fromD

	return math.cubicInterpolation(interp, from, from +fromD*self:getStepFrequency(), to, to+toD*self:getStepFrequency())
end


SimEntity = class("SimEntity")

Network.registerClass(SimEntity)

SimEntity.variables = {
	sync = {},
	new = {},
}
SimEntity.structure = {
	sync = {},
	new = {}
}

function SimEntity:new()
	local se = instance(self)
	return se
end

function SimEntity:getName()
	return "SimEntity"
end

function SimEntity:onFakeEvent()

end

function SimEntity:getAngleTo(ent, xf, yf)
	local xdiff = math.normalize(ent.x - self.x, -self.map.width*0.5, self.map.width*0.5)
	local ydiff = ent.y - self.y
	return math.angleBetweenPoints(0,0,xdiff/(xf or 1),ydiff/(yf or 1))
end

function SimEntity:getDistanceTo(ent)
	local xdiff = math.normalize(ent.x - self.x, -self.map.width*0.5, self.map.width*0.5)
	local ydiff = ent.y - self.y
	return math.distance(0,0,xdiff,ydiff)
end

function SimEntity:isInside(ent)
	if ent.getRadius then
		local dist = self:getDistanceTo(ent)
		if dist < ent:getRadius() then
			return true
		end
	end
	return false
end

function SimEntity:getRelativeX(ent)
	return math.normalize(ent.x - self.x, -self.map.width*0.5, self.map.width*0.5)
end

function SimEntity:getRelativeXPosition(x)
	return math.normalize(x - self.x, -self.map.width*0.5, self.map.width*0.5)
end

function SimEntity:getRelativeY(ent)
	return ent.y - self.y
end


function SimEntity:getRelativeYPosition(y)
	return y - self.y
end

function SimEntity:getVariableValues(subset, sim)
	local vars = self:getVariables(subset)
	local struc = self:getVariablesStructure(subset)
	local vals = {}
	for index, var in ipairs(vars) do
		local str = struc[index]
		if self[var] ~= nil then
			if str == "e" then
				table.insert(vals, sim:getProxy(self[var]))
			else
				table.insert(vals, self[var])
			end
		else
			print(var, " was not set in ent,", getClassNameOf(self), " failed to get proxy values")
		end
	end
	return unpack(vals)
end

function SimEntity:getOwner()
	return self
end

function SimEntity:getVariables(subset)
	return self.variables[subset]
end

function SimEntity:getVariablesStructure(subset)
	return self.structure[subset]
end

function SimEntity:getVariablesHandling(subset)
	return self.handling
end


function SimEntity:isBackground()
	return false
end

function SimEntity:isBuilding()
	return false
end

function SimEntity:hasWarningHud()
	return false
end

function SimEntity:isProtective()
	return false
end

function SimEntity:isHoming()
	return false
end

function SimEntity:isTarget()
	return false
end

function SimEntity:isResource()
	return false
end

function SimEntity:hasTargetHud()
	return false
end

function SimEntity:canCollideWith(ent)
	return true
end

function SimEntity:isCarrier()
	return false
end

function SimEntity:getVariableInterpolation()
	return self.interpolation
end

function SimEntity:initProxy()
end


function SimEntity:newProxy(sim)
	local proxy = getClassTableOf(self):new(self:getVariableValues("new", sim))--SimEntity:new()
	if proxy then
		proxy:initProxy()
		return proxy
	else
		print("new proxy failed")
		print(getClassNameOf(self))
		printtable(self:getVariableValues("new", sim))
	end
end

function SimEntity:getRenderRadius()
	return 64
end

function SimEntity:getInterpolation(time)
	if self.becameTime then
		return math.min(2,(time - self.becameTime)/(self.becameStep or self:getStepFrequency()))
	end
end

function SimEntity:saveVariables(subset, interp)
	for index, var in ipairs(self:getVariables(subset)) do
		if self[var] then
			if interp then
				self["last"..var] = self:interp(var, interp)
			else
				self["last"..var] = self[var]
			end
		end
	end
end

function SimEntity:fetchVariables(subset, from)
	local handling = self:getVariablesHandling()
	for index, var in ipairs(self:getVariables(subset)) do
		if from[var] ~= nil then
			if handling and handling[var] then
				handling[var](self, self[var], from[var])
			end
			self[var] = from[var]
		end
	end
end

function SimEntity:getDiffingAmount(subset)
	local diffs = 0
	for index, var in ipairs(self:getVariables(subset)) do
		if self[var] ~= (self["last"..var]) then
			diffs = math.max(diffs,index)
		end
	end
	return diffs
end

function SimEntity:interp(var, amt)
	if amt then
		local str = self:getVariableInterpolation()
		local last = self["last"..var] or self[var]
		if last then
			if str and str[var] then
				return str[var](self, last, self[var], amt, var)
			else
				return last + (self[var] - last) * amt
			end
		else
			return self[var]
		end
	else
		return self[var]
	end
end

function SimEntity:fakeStep(time)
end

function SimEntity:emitSound(id, vol, mod)
	if self.map then
		self.map:emitSoundAt(id, self.x, self.y, vol or 1, mod or 1)
	end
end

function SimEntity:emitLoop(id, vol, mod)
	if self.map then
		if not self.loops then
			self.loops = {}
		end
		self.map:emitLoopAt(id, self.x, self.y, vol or 1, mod or 1, self.loops)
	end
end

function SimEntity:getStepFrequency()
	return APP.tickTime
end

function SimEntity:onKilled()
end

function SimEntity:step(time)
end

function SimEntity:canBeHurtBy(ent)
	return true
end

function SimEntity:become(idol, time, interp)
	self:saveVariables("sync", interp)
	self:fetchVariables("sync", idol)
	self.lastBecameTime = self.becameTime
	self.becameTime = time
	if self.lastBecameTime then
		self.becameStep = math.max(APP.stepTime, self.becameTime - self.lastBecameTime)
	else
		self.becameStep  = self:getStepFrequency()
	end
end

function SimEntity:renderAt(x,y,scale,angle,a,r,g,b)
end

dofile("gameDefs.lua")
dofile("effects.lua")
dofile("ships.lua")
dofile("projectiles.lua")
dofile("missiles.lua")

local PARTICLES = {
}

Particle = inherited("Particle", SimEntity)

Network.registerClass(Particle)

Particle.variables = {
	sync = {"x", "y", "dx", "dy"},
	new = {"map", "x", "y", "dx", "dy"},
}
Particle.structure = {
	sync = {"f", "f", "f" , "f"},
	new = {"e", "f", "f", "f", "f"},
}

function Particle:new(map, x,y, dx, dy)
	local part = instance(self)
	part.map = map
	part.x = x
	part.y = y
	part.dx = dx or 0
	part.dy = dy or 0
	part.time = 0
	map:addEnt(part)
	return part
end

function Particle:remove()
	self.map:removeEnt(self)
end

function Particle:step(time)
	self.x = self.x + self.dx*time
	self.y = self.y + self.dy*time
	self.time = self.time + time
	if self.time > 3 then
		self:remove()
	end
end

function Particle:renderAt(x,y,scale,angle,a,r,g,b)
	video.renderSpriteState(SPRITES.circle, x, y, scale/16, angle, a, r, g, b)
end
dofile("resource.lua")
dofile("buildings.lua")
dofile("team.lua")
dofile("asteroid.lua")