dofile("collision.lua")

PhysicsWrapper = {}

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
function PhysicsWrapper:new()

	local pw = {}
	
	pw.physics = nil
	pw.borders = {}

	pw = setmetatable(pw, self)
	self.__index = self
	return pw
end

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
function PhysicsWrapper:createPhysics(width, height, gravityX, gravityY, resolution, borderWidth)

	if borderWidth then
		self.physics = Physics(-borderWidth * resolution, -borderWidth * resolution, (width + borderWidth) * resolution, (height + borderWidth) * resolution, gravityX, gravityY)
		
		-- create border
		local left = self.physics:createEntity({}, -borderWidth * .5 * resolution, height * .5 * resolution)
		self.borders[1] = self.physics:addBoxShape(left, {x = 0, y = 0, width = borderWidth * resolution, height = height * resolution})
		local right = self.physics:createEntity({}, (width + borderWidth * .5) * resolution, height * .5 * resolution)
		self.borders[2] = self.physics:addBoxShape(right, {x = 0, y = 0, width = borderWidth * resolution, height = height * resolution})
		local top = self.physics:createEntity({}, width * .5 * resolution, -borderWidth * .5 * resolution)
		self.borders[3] = self.physics:addBoxShape(top, {x = 0, y = 0, width = width * resolution, height = borderWidth * resolution})
		local bottom = self.physics:createEntity({}, width * .5 * resolution, (height + borderWidth * .5) * resolution)
		self.borders[4] = self.physics:addBoxShape(bottom, {x = 0, y = 0, width = width * resolution, height = borderWidth * resolution})
		
	else
		self.physics = Physics(0, 0, width * resolution, height * resolution, gravityX, gravityY)
	end
	self.resolution = resolution
	self.bodyLookUp = {}
	
end

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
function PhysicsWrapper:update(time, steps)
	self.physics:update(time, steps or 10)
end

function PhysicsWrapper:applyForce(body, x, y, dx, dy)
	if body then
		self.physics:applyEntityForce(body, x* self.resolution, y* self.resolution, dx* self.resolution, dy* self.resolution)
	end
end
function PhysicsWrapper:applyImpulse(body, x, y, dx, dy)
	if body then
		self.physics:applyEntityImpulse(body, x* self.resolution, y* self.resolution, dx* self.resolution, dy* self.resolution)
	end
end

function PhysicsWrapper:setJointMotor(joint, maxForce, targetSpeed)
	if joint then
		self.physics:setJointMotorEnabled(joint, true)
		self.physics:setJointMotorForce(joint, math.abs(maxForce*self.resolution))
		self.physics:setJointMotorSpeed(joint, targetSpeed * self.resolution)
	end
end

function PhysicsWrapper:getPrismaticTranslation(joint)
	if joint then
		local data = self.physics:getJointData(joint)
		return (data.translation or 0)/self.resolution
	end
end

function PhysicsWrapper:getJointMotorForce(joint)
	if joint then
		return self.physics:getCurrentJointMotorSpeed(joint)/self.resolution
	end
end

function PhysicsWrapper:getBodyPos(body)
	if body then
		local x,y = self.physics:getEntityPosition(body)
		return x/self.resolution, y/self.resolution
	end
end

function PhysicsWrapper:getBodyAngle(body)
	if body then
		return self.physics:getEntityAngle(body)
	end
end

function PhysicsWrapper:deleteBody(body)
	if body then
		if body <= 0 then
			print("oh noh "..body)
		else
			self.physics:deleteEntity(body)
		end
	end
end

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
-- Creates a physics entity and returns the created entity id
-- Position is world coordinates {x, y}
-- Angle is in radians, default is 0
-- Scale is used if the object is resized
-- Definition is the physics object { bodyDef, shapes }
function PhysicsWrapper:createBody(position, angle, scale, definition, overrideCollisionGroup)
	
	if definition.joint and position.body then
		local joint = nil 
		if not position.body[definition.joint.bodyA] or not position.body[definition.joint.bodyB] then
			print("body invalid")
		end
		if definition.joint.type == "prismatic" then
			joint = self.physics:createPrismaticJoint(position.body[definition.joint.bodyA], position.body[definition.joint.bodyB], (position.x + definition.joint.startX)*self.resolution,(position.y + definition.joint.startY)*self.resolution, math.cos(definition.joint.angle + angle) , math.sin(definition.joint.angle + angle), {lowerTranslation = definition.joint.min*self.resolution*scale, upperTranslation = definition.joint.max*self.resolution*scale, enableLimit = true, enableMotor = 1, maxMotorForce = (definition.joint.friction or 0)*self.resolution, motorSpeed = 0})	
		elseif definition.joint.type == "angle" then
			--joint = self.physics:createRevoluteJoint(self.body, actor.body, (self.x + math.cos(self.angle+math.pi*0.5)*dist)*self.map.resolution,(self.y + math.sin(self.angle+math.pi*0.5)*dist)*self.map.resolution, {lowerAngle = definition.joint.min, upperAngle = definition.joint.max, enableLimit = true})	
			joint = self.physics:createRevoluteJoint(position.body[definition.joint.bodyA], position.body[definition.joint.bodyB], (position.x + definition.joint.startX)*self.resolution,(position.y + definition.joint.startY)*self.resolution, {lowerAngle = definition.joint.min, upperAngle = definition.joint.max, enableLimit = true, enableMotor = 1, maxMotorTorque = (definition.joint.friction or 0)*self.resolution, motorSpeed = 0})	
		end
		return joint, true
	end
	
	scale = scale or 1
	angle = angle or 0

	local bodyDef = definition.bodyDef or { allowSleep = true, isBullet = false, fixedRotation = false, linearDamping = 0 }	
	local body = self.physics:createEntity(bodyDef, position.x * self.resolution, position.y * self.resolution)
	
	
	for index, phys in pairs(definition.shapes) do
	
		local filterGroup = phys.filterGroup or -body
		if overrideCollisionGroup then
			filterGroup = overrideCollisionGroup
		end
	
		local shapeSettings = {
			friction = phys.friction or 0,
			density = phys.density or 1,
			isSensor = phys.isSensor or false,
			filterCategory = phys.category or _collisionCategory.static,
			filterMask = phys.mask or _collisionFilter.everything,
			restitution = phys.restitution or 0,
			filterGroup = filterGroup,
			userData = phys.userData,
		}
		
		if phys.shape == "box" then
			shapeSettings.x = phys.ox * scale * self.resolution
			shapeSettings.y = phys.oy * scale * self.resolution
			shapeSettings.width = phys.width * self.resolution * scale
			shapeSettings.height = phys.height * self.resolution * scale
			
			self.physics:addBoxShape(body, shapeSettings)
			
		elseif phys.shape == "circle" then
			shapeSettings.x = phys.ox * scale * self.resolution
			shapeSettings.y = phys.oy * scale * self.resolution
			shapeSettings.radius = phys.radius * scale * self.resolution
			
			self.physics:addCircleShape(body, shapeSettings)
			
		elseif phys.shape == "poly" then

			-- copy and recalculate coordinates
			local x, y = {}, {}
			for i=1, #phys.x do
				x[i] = phys.x[i] * scale * self.resolution
				y[i] = phys.y[i] * scale * self.resolution
			end

			self.physics:addPolygonShape(body, shapeSettings, x, y)	
			
		elseif phys.shape == "edge" then

			shapeSettings.startX = phys.startX * scale * self.resolution
			shapeSettings.startY = phys.startY * scale * self.resolution
			shapeSettings.endX = phys.endX * scale * self.resolution
			shapeSettings.endY = phys.endY * scale * self.resolution

			self.physics:addEdgeShape(body, shapeSettings)	
		end
	end

	self.physics:setEntityAngle(body, angle)
	
	if definition.isActive then
		self.physics:activateEntity(body)
	end
	
	self.bodyLookUp[body] = position
	
	return body
end

function PhysicsWrapper:setAngle(body, angle)
	if body then
		self.physics:setEntityAngle(body, angle)
	end
end

function PhysicsWrapper:setSpeed(body, dx, dy )
	if body then
		self.physics:setEntitySpeed(body, dx*self.resolution, dy*self.resolution)
	end
end

function PhysicsWrapper:getFromLookup(id)
	return self.bodyLookUp[id] or nil
end

function PhysicsWrapper:isBorder(id)
	for i,b in ipairs(self.borders) do
		if b == id then
			return true
		end
	end
	return false
end