AdditionalContractSpawnManager = {};
AdditionalContractSpawnManager.TEST_HEIGHT = 70;
local AdditionalContractSpawnManager_mt = Class(AdditionalContractSpawnManager);

function AdditionalContractSpawnManager.new()	
	local self = {};
	setmetatable(self, AdditionalContractSpawnManager_mt);	
	return self;
end;

function AdditionalContractSpawnManager:getObjectSize(object)
	local sw, sl, sh = Vehicle.DEFAULT_SIZE.width, Vehicle.DEFAULT_SIZE.length, Vehicle.DEFAULT_SIZE.height;
	if object ~= nil and object.size ~= nil then
		sw, sl, sh = object.size.width or sw, object.size.length or sl, object.size.height or sh;		
	end;
	return sw, sl, sh;
end;

function AdditionalContractSpawnManager:getObjectConfigSize(object, sw, sl, sh)
	sw, sl, sh = sw or 0, sl or 0, sh or 0;
	if object ~= nil and object.size ~= nil then
		local oSw, oSl, oSh = unpack(object.size);
		sw, sl, sh = oSw or sw, oSl or sl, oSh or sh;	
	end;
	return sw, sl, sh;
end;

function AdditionalContractSpawnManager:getObjectConfigRotation(object, rx, ry, rz)
	rx, ry, rz = rx or 0, ry or 0, rz or 0;
	if object ~= nil and object.rotation ~= nil then
		local oRx, oRy, oRz = unpack(object.rotation);
		rx, ry, rz = oRx or rx, oRy or ry, oRz or rz;	
	end;
	return rx, ry, rz;
end;

function AdditionalContractSpawnManager:getObjectPositionAndLookYaw(object)
	if object ~= nil then
		local currentNode = object.rootNode or object.nodeId;	
		if currentNode ~= nil then
			local dirX, _, dirZ = localDirectionToWorld(currentNode, 0, 0, -1);
			local ry = MathUtil.getYRotationFromDirection(dirX, dirZ);					
			local x, y, z = getWorldTranslation(currentNode);
			return x, y, z, 0, ry, 0, dirX, dirZ;
		end;
	end;
	return nil;
end;

function AdditionalContractSpawnManager:getDistancePositionFromObject(object, distance, setSize)	
	if object ~= nil then
		distance = distance or 5;
		local x, y, z, rx, ry, rz, dirX, dirZ = self:getObjectPositionAndLookYaw(object);
		if x ~= nil and z ~= nil then
			if setSize ~= nil and setSize then 
				local _, sl, _ = self:getObjectSize(object);
				distance = distance + sl; 
			end;
			local distanceX = x + dirX * distance;
			local distanceZ = z + dirZ * distance;
			return distanceX, y, distanceZ, rx or 0, ry or 0, rz or 0, dirX, dirZ;
		end;
	end;
	return nil;
end;

function AdditionalContractSpawnManager:getDistanceTool(mission, numTool)
	local tool = nil;
	if mission.distanceTools ~= nil then
		if numTool == nil then tool = mission.distanceTools[#mission.distanceTools];else tool = mission.distanceTools[numTool];end;		
	end;
	if tool ~= nil and (tool.rootNode == nil or not entityExists(tool.rootNode)) then tool = nil;end;
	return tool;
end;

function AdditionalContractSpawnManager:getObjectDistanceSpawnPosition(trigger, checkEmptyTrigger, objectConfig, testHeight, fallingHeight)
	local objectsSpawnPosition = {};
	local x, y, z, rx, ry, rz, dirX, dirZ = unpack(trigger);
	if x == nil or z == nil then return false, {};end;
	local sizeX, _, sizeZ = unpack(objectConfig.size);
	rx, ry, rz = self:getObjectConfigRotation(objectConfig, rx, ry, rz);	
	if (checkEmptyTrigger == nil or checkEmptyTrigger) and not self:isTriggerEmpty(x, y, z, rx, ry, rz, sizeX, sizeZ, testHeight) then
		return false, objectsSpawnPosition;
	end;
	local terrainHeightY = getTerrainHeightAtWorldPos(g_terrainNode, x, 0, z);
	if y == nil or y < terrainHeightY then y = terrainHeightY;end;
	if fallingHeight ~= nil and y ~= nil then y = y + fallingHeight;end;
	table.insert(objectsSpawnPosition, {x, y, z, rx, ry, rz, dirX, dirZ});
	return true, objectsSpawnPosition;
end;

function AdditionalContractSpawnManager:getDistancePositionFromPosition(position, distance)
	if position ~= nil and position[1] ~= nil and position[3] ~= nil then
		distance = tonumber(distance) or 10;
		local x, y, z, rx, ry, rz, dirX, dirZ = unpack(position);
		if dirX == nil or dirZ == nil then
			dirX, dirZ = MathUtil.getDirectionFromYRotation(ry);
		else
			dirX, dirZ = tonumber(dirX) or 1, tonumber(dirZ) or 0;
		end;		
		distanceX = x + dirX * distance;
		distanceZ = z + dirZ * distance;
		if y == nil then y = getTerrainHeightAtWorldPos(g_terrainNode, x, 0, z);end;
		return {distanceX, y, distanceZ, rx, ry, rz, dirX, dirZ};		
	end;
	return {};
end;

function AdditionalContractSpawnManager:getLastToolSpawnPosition(mission, distance, setSize)
	local tool = self:getDistanceTool(mission);	
	if tool ~= nil then return self:getDistancePositionFromObject(tool, distance, setSize);end;
	return nil;
end;

function AdditionalContractSpawnManager:getObjectsSpawnPosition(trigger, checkEmptyTrigger, numObjects, objectConfig, testHeight, fallingHeight) --old
	local canSpawn = 0;
	local objectsSpawnPosition = {};
	local sizeX, _, sizeZ = unpack(objectConfig.size);	
	local offX, _, offZ = unpack(objectConfig.offset);	
	local x, y, z;
	local rx, ry, rz;
	if trigger.triggerId ~= nil then
		x, y, z = getWorldTranslation(trigger.triggerId);
		rx, ry, rz = getWorldRotation(trigger.triggerId);
	else
		x, y, z = trigger.x, trigger.y, trigger.z;
		rx, ry, rz = trigger.rx, trigger.ry, trigger.rz;
	end;
	rx, ry, rz = self:getObjectConfigRotation(objectConfig, rx, ry ,rz);
	local terrainHeightY = getTerrainHeightAtWorldPos(g_terrainNode, x, 0, z);
	if y == nil or y < terrainHeightY then y = terrainHeightY;end;
	if fallingHeight ~= nil then y = y + fallingHeight;end;
	local rowOffset = sizeZ / 2 + 0.3;
	local xCellOffset = sizeX + 0.1;
	local dirX, dirZ = MathUtil.getDirectionFromYRotation(ry);
	if trigger.dirX ~= nil and trigger.dirZ ~= nil then dirX = trigger.dirX;dirZ=trigger.dirZ;end;	
	local theta = math.atan2(dirZ, dirX);
	local rcos = math.cos(theta);
	local rsin = math.sin(theta);
	if (checkEmptyTrigger == nil or checkEmptyTrigger) and not self:isTriggerEmpty(x, y, z, rx, ry, rz, sizeX, sizeZ, testHeight) then
		return false, objectsSpawnPosition;
	end;
	
	for i=1, numObjects do
		local dx = 0;
		if i >= 5 then
			dx = -xCellOffset;
		elseif i >= 3 then
			dx = xCellOffset;
		end;
		local dz = rowOffset;
		if i % 2 == 0 then
			dz = -rowOffset;		
		end;
		dx = rcos * dx - rsin * dz;
		dz = rsin * dx + rcos * dz;		
		table.insert(objectsSpawnPosition, {x+dx,y,z+dz,rx,ry,rz});
		canSpawn = canSpawn + 1;			
	end;
	return canSpawn == numObjects, objectsSpawnPosition;
end;

function AdditionalContractSpawnManager:isTriggerEmpty(x, y, z, rx, ry, rz, objectSizeX, objectSizeZ, testHeight)
	local testHeight = testHeight or AdditionalContractSpawnManager.TEST_HEIGHT;	
	self.tempHasCollision = false;
	local mask = CollisionFlag.DYNAMIC_OBJECT + CollisionFlag.VEHICLE + CollisionFlag.PLAYER + CollisionFlag.TREE;
	overlapBox(x, y, z, rx, ry, rz, 3 * (objectSizeX + 0.1), testHeight * 0.5, 2 * (objectSizeZ + 0.1), "collisionTestCallback", self, mask);
	return not self.tempHasCollision;
end;

function AdditionalContractSpawnManager:collisionTestCallback(transformId)
	if g_currentMission.nodeToObject[transformId] ~= nil or (g_currentMission.players[transformId] ~= nil or g_currentMission:getNodeObject(transformId) ~= nil) then
		self.tempHasCollision = true;
	end;
end;

g_additionalContractSpawnManager = AdditionalContractSpawnManager.new();