

Missile = inherited("Missile", ActionEntity)

Network.registerClass(Missile)

Missile.variables = {
	sync = {"x", "y", "angle", "engine", "health"},
	new = {"map", "owner", "x", "y", "angle", "dx", "dy", "da", "type"},
}
Missile.structure = {
	sync = {"f","f","r","f","f"},
	new = {"e", "e", "f", "f","f","f","f", "f", "i"},
}

Missile.interpolation = {
	angle = INTERPOLATORS.angle,
}

function Missile:new(map, owner, x, y, angle, dx, dy, da, type)
	local b = instance(self, map, owner, x, y, angle, dx, dy, da, MISSILE_TYPES[type].health)
	
	b.type = type
	b.desiredAmount = 0
	b.desiredAngle = angle
	b.energy = MISSILE_TYPES[type].energy

	b.engine = 0
	b.rudder = 0
	b.engineRudder = 0

	b.ignitionDelay =  MISSILE_TYPES[type].ignition
	b.thrust = 0
	b.time = 0
	b.free = false
	map:addEnt(b)
	return b
end

function Missile:def()
	return MISSILE_TYPES[self.type]
end

function Missile:getMass()
	return self:stat("mass")
end

function Missile:getRadius(angle)
	if self:def().radius then
		return self:stat("radius")
	else
		local angleDiff = math.angleDifference(self.angle, angle or self.angle)
		return math.abs(self:stat("radiusX")*math.cos(angleDiff)) + math.abs(self:stat("radiusY")*math.sin(angleDiff))
	end
end

function Missile:stat(id)
	if MISSILE_TYPES[self.type][id] then
		return MISSILE_TYPES[self.type][id]
	else
		print("stat",id, "missing")
		return 1
	end
end

function Missile:onCollided(normal, impulse)
	self.health = 0
	self.ground = true
end


function Missile:onHit(ent, angle, impulse, counterImpulse)
	if self:def().detonateOnHit then
		self.health = 0
	end
	if ent:canBeHurtBy(self) then
		if ent:hurt(impulse*self:stat("impulseDamageFactor")/ent:getMass()) then
			ent:onKilled()
		end
	end
end	

function Missile:onDestroy()
	self:explode()
end

function Missile:hurt(h)
	local damage = h
	self.health = math.max(0, self.health - damage)
	if self.health <= 0 then
		return true
	end
end

function Missile:isHoming()
	return not self:def().targetBuilding
end

function Missile:step(time)
	if self.ignitionDelay > 0 then
		self.ignitionDelay = self.ignitionDelay - time
	else
		self.thrust = math.approach(self.thrust,1, time * self:stat("engineResponsiveness"))
	end

	if not self:def().minTargetSpeed or math.distance(0,0,self.dx, self.dy) > self:def().minTargetSpeed then
		local bestTarget = nil
		local relX = 0
		local relY = 0
		local relDX = 0
		local relDY = 0
		local targets = 0
		for index, ent in pairs(self.map.ents) do
			if (ent:isTarget() and (not self:def().targetBuilding or ent:isBuilding())) and ent:getOwner() ~= self:getOwner() then

				local distToTarget = self:getDistanceTo(ent)
				if distToTarget < self:stat("targetDistance") then
					local targetFactor = (self:stat("targetDistance") - distToTarget)/self:stat("targetDistance")
					local angToTarget = self:getAngleTo(ent)
					local angDiff = math.abs(math.angleDifference(self.angle, angToTarget))
					if angDiff < self:stat("targetArc")*0.5 then
						targetFactor = targetFactor * (self:stat("targetArc")*0.5 - angDiff) / (self:stat("targetArc")*0.5)
						relX = relX + self:getRelativeX(ent) * targetFactor
						relY = relY + self:getRelativeY(ent) * targetFactor
						targets = targets + targetFactor
						relDX = relDX + (ent.dx or 0) * targetFactor
						relDY = relDY + (ent.dy or 0) * targetFactor
					end
				end
				
			end
		end
		if targets > 0 then
			relX = relX / targets
			relY = relY / targets
			relDX = relDX / targets
			relDY = relDY / targets
			local tdist = math.distance(0,0,relX,relY)
			local tspeed = math.distance(0,0,relDX,relDY)
			local speed = math.distance(0,0,self.dx,self.dy)
			local tAng = math.angleBetweenPoints(0,0,relDX,relDY)

			local aheadFactor = tspeed/(1+speed)*tdist

			local above = self:def().targetAboveFactor and (self:stat("targetAboveFactor")*math.max(0,math.difference(0,relX) -  self:stat("targetAboveThreshold"))) or 0
			self.desiredAngle = math.angleBetweenPoints(0,0, relX + math.cos(tAng)*aheadFactor, relY + math.sin(tAng)*aheadFactor - above)
		end
	end

	local using = false

	local desiredRudder = -math.angleDifference(self.angle, self.desiredAngle)
	self.da = math.approach(self.da, desiredRudder, self:stat("angleThrusters")*time)
	self.rudder = math.clamp(math.approach(self.rudder, desiredRudder, time * self:stat("rudderResponsiveness")), -self:stat("rudderArc"), self:stat("rudderArc"))

	--apply thrust
	if self.energy > 0 then
		self.engine = self.thrust
		if self.engine > 0 then
			self.energy = math.max(0,self.energy - time*self.engine*self:stat("engineEnergyConsumption"))
		end
	else
		self.engine = 0
		if self:def().stepType then
			local sx, sy = self.x + math.cos(self.angle)*self:def().stepOffset, self.y + math.sin(self.angle)*self:def().stepOffset
			local mi = self.map.sim:create(Missile, self.map, self.owner, sx ,sy, self.angle, self.dx, self.dy, self.da, MISSILE_TYPES[self:def().stepType])
			mi.health = mi:def().health * self:getHealthPercent()
			self:remove()
		end
		--self.desiredAngle = self.angle + math.pi*math.sign(self.dx)*0.5
	end
	
	--engine
	self.engineRudder = math.clamp(math.approach(self.engineRudder, desiredRudder, time * self:stat("engineRudderResponsiveness")), -self:stat("engineRudderArc"), self:stat("engineRudderArc"))
	self.da = self.da + self.engineRudder*self.engine*time*self:stat("engineRudderPower")* self:stat("enginePower") * math.abs(math.sin(self.engineRudder))

	local engineAngle = self.angle + self.engineRudder
	self.dx = self.dx + math.cos(engineAngle)*self.engine*time* self:stat("enginePower")
	self.dy = self.dy + math.sin(engineAngle)*self.engine*time* self:stat("enginePower")

	

	local speedAngle = math.angleBetweenPoints(0,0,self.dx, self.dy)
	local speed = math.distance(0,0,self.dx, self.dy)
	local speedEffect = self:getSpeedFactor()
	local rudderEffectFactor = math.cos(math.angleDifference(self.angle + self.rudder, speedAngle))*speedEffect
	
	--anglespeed control
	local airFriction = self.map:getAtmosphere(self.x, self.y)
	local frictionDelta = self.lastFriction and (airFriction - self.lastFriction) or 0
	self.lastFriction = airFriction

	
	self.da = self.da + self.rudder*rudderEffectFactor*time*self:stat("rudderFlowSize")*airFriction

	if frictionDelta > 0 then
		self.dy = self.dy - frictionDelta*10*speedEffect*time*math.pow(math.abs(math.cos(speedAngle)),2)
		self.heat = frictionDelta*2500
		self:hurt(time*self.heat)
	else
		self.heat = 0
	end
	


	local rudderdiff = math.difference(0,self.rudder)
	self.rudder = math.approach(self.rudder, 0, rudderdiff*rudderEffectFactor*time*self:stat("rudderFlowSize")*airFriction*self:stat("rudderNullify"))
	--carving
	local speedAngle = math.angleBetweenPoints(0,0,self.dx, self.dy)
	local speed = math.distance(0,0,self.dx, self.dy)
	local speedEffect = self:getSpeedFactor()


	local speedf = 1-(self:stat("speedThresholdDamping")/(self:stat("speedThresholdDamping")+speed))
	local cf = self:stat("maxCarvingLoss") * speedf + (1-self:stat("maxCarvingLoss"))
	
	local speedAngleDiff = math.abs(math.cos(math.clamp(math.angleDifference(self.angle, speedAngle), -math.pi*0.5, math.pi*0.5)))

	local carveFactor = math.pow(speedAngleDiff,self:stat("carvingExp"))*speedEffect

	local diff = math.abs(math.angleDifference(speedAngle, self.angle))/math.pi*2
	local newSpeedAngle = math.approachAngle(speedAngle, self.angle, cf*diff*time*carveFactor*self:stat("bodyCarving")*airFriction)
	local carvedAmount = math.abs(math.angleDifference(speedAngle, newSpeedAngle))

	
	local carvedAmountFract = math.min(1,carvedAmount/math.pi/0.5)
	local speed = speed*self:stat("carvingSpeedDamping")*carvedAmountFract + (1-carvedAmountFract)*speed
	self.dx = math.cos(newSpeedAngle)*speed
	self.dy = math.sin(newSpeedAngle)*speed


	self.da = math.approach(self.da, 0, carvedAmount*self:stat("carvingAngleDamping"))

	
	local frictionAmount = (1-self:stat("aerodynamicness"))*airFriction + self:stat("aerodynamicness")*math.pow(speedAngleDiff, self:stat("aerodynamicExp"))*airFriction

	--friction
	self.da = math.approach(self.da, math.angleDifference(speedAngle, self.angle), frictionAmount*diff*time*speed*self:stat("angleAdjustment"))

	if self.energy <= 0 then
		self.da = self.da  + time*speedEffect*frictionAmount/200
	end

	--local speed = math.distance(0,0,self.dx, self.dy)
	self.dx = math.approach(self.dx, 0, math.pow(self.dx*self.map:getFriction(),2)*frictionAmount*time*self:stat("airFriction"))
	self.dy = math.approach(self.dy, 0, math.pow(self.dy*self.map:getFriction(),2)*frictionAmount*time*self:stat("airFriction"))
	local daDiff = math.difference(self.da, 0)
	self.da = math.approach(self.da, 0, math.pow(self.da*self.map:getFriction(),2)*frictionAmount*time*self:stat("angleFriction") + daDiff*frictionAmount*time*self:stat("angleForwardFriction")/100)

	--gravity
	self.dy = self.dy + time*self.map:getGravity(self.x, self.y)

	self:super().step(self, time)
	
	self.time = self.time + time

	self:applyHail(time)

	if self.time > self:stat("maxDuration") then
		self:remove()
	end

	if self.ground then
		self.ground = false
	else
		self.free = true
	end
end

function Missile:isBackground()
	return self:def().spawnInGround
end

function Missile:hasCollision()
	if self:def().spawnInGround then
		return self.free
	else
		return true
	end
end

function Missile:hasWarningHud()
	return self:def().warning
end


function Missile:getEnergyPercent()
	return self.energy/self:stat("energy")
end

function Missile:getHealthPercent()
	return self.health/self:stat("health")
end

function Missile:initProxy()
	self:createTrail()
end

function Missile:getTrailOffset()
	return math.cos(self.angle + math.pi)*self:def().trailDist,  math.sin(self.angle + math.pi)*self:def().trailDist
end

function Missile:fakeStep(time)
	self:updateTrail(time, 1)
	--if self.engine > 0 then
	--	if math.random() < time*20*self.engine then
	--		local fx = self.map:addFx(Fx:new(FX_TYPES.smoke, self.map, self.x, self.y, math.cos(self.angle+math.pi)*self:stat("enginePower")*self.engine, math.sin(self.angle+math.pi)*self:stat("enginePower")*self.engine))
	--	end
	--end
end

function Missile:renderAt(x,y,scale,angle,a,r,g,b,interp)
	local angle = angle + self:interp("angle", interp)

	if self.trail then
		self:renderTrailAt(x,y,scale,angle,a,r,g,b,16, interp)
	end

	if self:def().sprites.main then
		video.renderSpriteState(self:def().sprites.main, x, y, scale, angle, a, r, g, b, true)
	else
		for index, of in ipairs(self:def().offsets) do
			local rx,ry = x + math.cos(angle)*of.offset*scale, y + math.sin(angle)*of.offset*scale
			video.renderSpriteState(self:def().sprites[of.sprite], rx, ry, scale, angle + self:def().spriteAngle , a, r, g, b)
		end
	end
	local ox,oy = self:getTrailOffset()
	if self.engine > 0 then
		local amt = self.engine*self:def().flameSize
		local def = FX_TYPES[FX_TYPES.afterburn]

		for index, spr in pairs(def.sprites) do
			local sprscale = scale*amt + math.safeGaussian()*scale*amt*0.5
			video.renderSpriteState(spr,x+ox*scale,y+oy*scale,sprscale,angle, a, 255, 255, 255, true)
		end
	end

	--video.renderLine(x,y,x + math.cos(self.desiredAngle)*200, y + math.sin(self.desiredAngle)*200,255,255,255,255)
end



