collisions = {}

function collisions.update(entity)
	local colliders = {}
	local collisionHandlers = {}
	collisions.findEntitiesWithSpriteCorners(entity, colliders)
	collisions.findEntitiesWithCollisionHandlers(entity, collisionHandlers)
	for i1,c1 in ipairs(collisionHandlers) do
		for i2,c2 in ipairs(colliders) do
			if c1 ~= c2 and collisions.areCollidersIntersecting(c1["__collision_temp"], c2["__collision_temp"]) then
				setmetatable(_G, {__index = c1, __newindex = c1})
				c1.onCollision(c2)
			end
		end
	end
end

function collisions.findEntitiesWithCollisionHandlers(entity, result)
	if entity.onCollision then
		table.insert(result, entity)
	end
	for k,v in pairs(entity) do
		if type(v) == "table" then
			collisions.findEntitiesWithCollisionHandlers(v, result)
		end
	end
end

function collisions.findEntitiesWithSpriteCorners(entity, result)
	if type(entity) == "table" then
		if entity["__spritecorners"] then
			table.insert(result, entity)
			local corners = entity["__spritecorners"]
			local temp = {
				a = Vector:c(corners[1].x, corners[1].y),
				b = Vector:c(corners[2].x, corners[2].y),
				c = Vector:c(corners[3].x, corners[3].y),
				d = Vector:c(corners[4].x, corners[4].y),
			}
			temp.left = math.min(temp.a.x, temp.b.x, temp.c.x, temp.d.x)
			temp.right = math.max(temp.a.x, temp.b.x, temp.c.x, temp.d.x)
			temp.top = math.max(temp.a.y, temp.b.y, temp.c.y, temp.d.y)
			temp.bottom = math.min(temp.a.y, temp.b.y, temp.c.y, temp.d.y)
			entity["__collision_temp"] = temp
		end
		for k,v in pairs(entity) do
			collisions.findEntitiesWithSpriteCorners(v, result)
		end
	end
end

function collisions.areCollidersIntersecting(c0, c1)
	if  c0.right < c1.left or
		c0.left > c1.right or
		c0.bottom > c1.top or
		c0.top < c1.bottom then 
			return false 
	end
	if collisions.isPointInsideCollider(c0.a, c1) then return true end
	if collisions.isPointInsideCollider(c0.b, c1) then return true end
	if collisions.isPointInsideCollider(c0.c, c1) then return true end
	if collisions.isPointInsideCollider(c0.d, c1) then return true end
	return false
end

function collisions.isPointInsideCollider(p, collider)
	local dotPB = p:sub(collider.a):dot(collider.b:sub(collider.a))
	local dotPD = p:sub(collider.a):dot(collider.d:sub(collider.a))
	--print("Area sizes: " .. areaA .. " " .. areaB .. " " .. collider.area)
	if collider.dotBB == nil then
		collider.dotBB = collider.b:sub(collider.a):dot(collider.b:sub(collider.a))
		collider.dotDD = collider.d:sub(collider.a):dot(collider.d:sub(collider.a))
	end
	return (dotPB  > 0 and dotPB < collider.dotBB) and (dotPD > 0 and dotPD < collider.dotDD)
end

return collisions