AdditionalContractsLoading = {};

local AdditionalContractsLoading_mt = Class(AdditionalContractsLoading);

function AdditionalContractsLoading.new()	
	local self = {};
	setmetatable(self, AdditionalContractsLoading_mt);	
	self.pendingLoadingData = {};
	self.vehiclesLoad = {};
	self.vehiclesToLoading = {};	
	return self;
end;

function AdditionalContractsLoading:spawnAbstractObject(mission, spawnPosition, trigger, objectConfig, saveTable, numObject, multiTable)
	local x, y, z, rx, ry, rz = unpack(spawnPosition);
	local data = VehicleLoadingData.new();
	data:setFilename(objectConfig.filename);
	
	if data.isValid then	
		if objectConfig.configuration ~= nil then
			data:setConfigurations(objectConfig.configuration);
		end;
		if objectConfig.storeSpawn ~= nil and objectConfig.storeSpawn then
			data:setLoadingPlace(g_currentMission.storeSpawnPlaces, g_currentMission.usedStorePlaces);
		else
			data:setPosition(x, y, z);			
			local oRx, oRy, oRz = g_additionalContractSpawnManager:getObjectConfigRotation(objectConfig, rx, ry, rz);
			data:setRotation(oRx or 0, oRy or 0, oRz or 0);
		end;
		if objectConfig.propertyState ~= nil then
			data:setPropertyState(objectConfig.propertyState);
		else
			data:setPropertyState(VehiclePropertyState.MISSION);
		end;
		if objectConfig.access ~= nil and objectConfig.access then
			data:setOwnerFarmId(mission.farmId); 
		else		
			data:setOwnerFarmId(AccessHandler.NOBODY);
		end;	
		
		data.mission = mission;	
		
		table.insert(self.pendingLoadingData, data)
		data:load(self.onLoadSpawnedVehicle, self, {
			["loadingData"] = data,
			["objectConfig"] = objectConfig,
			["numObject"] = numObject or 1,
			["multiTable"] = multiTable or false,
			["mission"] = mission,
			["saveTable"] = saveTable
		})
	end;	
end;

function AdditionalContractsLoading:spawnObject(mission, spawnPosition, trigger, objectConfig, saveTable, numObject, multiTable)
	local x, y, z, rx, ry, rz = unpack(spawnPosition);
	local data = VehicleLoadingData.new();
	data:setFilename(objectConfig.filename);
	
	if data.isValid then	
		if objectConfig.configuration ~= nil then
			data:setConfigurations(objectConfig.configuration);
		end;
		if objectConfig.storeSpawn ~= nil and objectConfig.storeSpawn then
			data:setLoadingPlace(g_currentMission.storeSpawnPlaces, g_currentMission.usedStorePlaces);
		else
			data:setPosition(x, y, z);
			data:setRotation(trigger.rx or rx or 0, trigger.rotY or ry or 0, trigger.rz or rz or 0);
		end;
		if objectConfig.propertyState ~= nil then
			data:setPropertyState(objectConfig.propertyState);
		else
			data:setPropertyState(VehiclePropertyState.MISSION);
		end;
		if objectConfig.access ~= nil and objectConfig.access then
			data:setOwnerFarmId(mission.farmId); 
		else		
			data:setOwnerFarmId(AccessHandler.NOBODY);
		end;	
		
		data.mission = mission;	
		
		table.insert(self.pendingLoadingData, data)
		data:load(self.onLoadSpawnedVehicle, self, {
			["loadingData"] = data,
			["objectConfig"] = objectConfig,
			["numObject"] = numObject or 1,
			["multiTable"] = multiTable or false,
			["mission"] = mission,
			["saveTable"] = saveTable
		})
	end;	
end;

function AdditionalContractsLoading:onLoadSpawnedVehicle(vehicles, vehicleLoadState, loadingInfo)
	table.removeElement(self.pendingLoadingData, loadingInfo.loadingData)
	if self.failedToLoadVehicles then
		for _, vehicle in ipairs(vehicles) do
			vehicle:delete();
		end;
		return;
	elseif vehicleLoadState == VehicleLoadingState.OK then
		local objectConfig = loadingInfo.objectConfig;
		local mission = loadingInfo.mission;	
		local numObject = loadingInfo.numObject or 1;
		local multiTable = loadingInfo.multiTable or false;
		for _, vehicle in ipairs(vehicles) do
			self:setWearPercent(objectConfig, vehicle);			
			
			self:setDamagePercent(objectConfig, vehicle);
			
			self:setDirtPercent(objectConfig, vehicle);
			
			self:setMaxSpeed(objectConfig, vehicle);
			
			self:setCanBeReset(objectConfig, vehicle);
			
			self:setCanBeSold(objectConfig, vehicle);
			
			self:setOperatingTime(objectConfig, vehicle);
			
			self:setMapHotspotAvailable(objectConfig, vehicle);
			
			self:setFuelPercent(objectConfig, vehicle);
			
			self:setFillPercent(objectConfig, vehicle);			
			
			local name = "";
			if vehicle.getCluster ~= nil and vehicle.spec_rideable ~= nil and objectConfig.animalData ~= nil and #objectConfig.animalData > 0 then
				if objectConfig.animalData[numObject] == nil then numObject = 1;end;				
				name = self:setAnimalData(objectConfig.animalData[numObject], vehicle);							
			end;
			--self:setIgnoreFarmlandTyp(objectConfig, vehicle);
			
			table.insert(self.vehiclesLoad, vehicle);
			local nodeId = vehicle.rootNode or vehicle.nodeId;			
			if loadingInfo.saveTable ~= nil then
				if loadingInfo.multiTable then					
					table.insert(loadingInfo.saveTable, {object=vehicle});
				else
					loadingInfo.saveTable[nodeId] = vehicle;
				end;
			else
				if loadingInfo.multiTable then					
					local objectTable = {
						object = vehicle;
						state = false;
						name = name;
					}
					table.insert(mission.objects, objectTable);
				else
					mission.objects[nodeId] = vehicle;
				end;				
			end;
			if mission.distanceTools ~= nil and objectConfig.distanceTool ~= nil and true then
				table.insert(mission.distanceTools, vehicle);				
			end;
		end;
	else
		self.failedToLoadVehicles = true;
		for _, vehicle in ipairs(vehicles) do
			vehicle:delete();
		end;
		for _, data in ipairs(self.pendingLoadingData) do
			data:cancelLoading();
		end;
		table.clear(self.pendingLoadingData);
		table.clear(self.vehiclesToLoading);
		self.spawnedVehicles = false;
		for _, vehicle in ipairs(self.vehiclesLoad) do
			vehicle:delete();
		end;
		table.clear(self.vehiclesLoad);
	end;
end;

function AdditionalContractsLoading:setWearPercent(objectConfig, vehicle)
	if objectConfig.wearPercent ~= nil then 
		local wearPercent = g_additionalContractUtils:getIntegerToVector(objectConfig.wearPercent)/100;
		vehicle:addWearAmount(wearPercent, true);
	end;
end;

function AdditionalContractsLoading:setDamagePercent(objectConfig, vehicle)
	if objectConfig.damagePercent ~= nil then 
		local damagePercent = g_additionalContractUtils:getIntegerToVector(objectConfig.damagePercent)/100;
		vehicle:addDamageAmount(damagePercent, true);
	end;
end;

function AdditionalContractsLoading:setDirtPercent(objectConfig, vehicle)
	if objectConfig.dirtPercent ~= nil then 
		local dirtPercent = g_additionalContractUtils:getIntegerToVector(objectConfig.dirtPercent)/100;
		vehicle:addDirtAmount(dirtPercent, true);
	end;
end;

function AdditionalContractsLoading:setMaxSpeed(objectConfig, vehicle)
	if objectConfig.maxSpeed ~= nil and vehicle.getMotor ~= nil then 
		local maxSpeed = g_additionalContractUtils:getIntegerToVector(objectConfig.maxSpeed);
		vehicle:getMotor().maxForwardSpeed = maxSpeed;vehicle:getMotor().maxBackwardSpeed = maxSpeed;
	end;
end;

function AdditionalContractsLoading:setCanBeReset(objectConfig, vehicle, state)
	if objectConfig ~= nil then
		vehicle.canBeReset = objectConfig.canBeReset or false;
	else
		vehicle.canBeReset = state or false;
	end;
end;

function AdditionalContractsLoading:setCanBeSold(objectConfig, vehicle, state)
	if objectConfig.canBeSold ~= nil then
		vehicle.canBeSold = objectConfig.canBeSold;	
	end;
end;

function AdditionalContractsLoading:setOperatingTime(objectConfig, vehicle)
	if objectConfig.setOperatingTime == nil or objectConfig.setOperatingTime then vehicle:setOperatingTime(3600000 * (math.random() * 40 + 30));end;
end;

function AdditionalContractsLoading:setMapHotspotAvailable(objectConfig, vehicle)
	if objectConfig.mapHotspotAvailable == nil or objectConfig.mapHotspotAvailable then
		vehicle.mapHotspotAvailable = true;
		if objectConfig.mapHotspotType == nil or VehicleHotspot.TYPE[objectConfig.mapHotspotType] == nil then
			vehicle.mapHotspotType = VehicleHotspot.TYPE.OTHER;
		else
			vehicle.mapHotspotType = VehicleHotspot.TYPE[objectConfig.mapHotspotType];
		end;
	end;
end;

function AdditionalContractsLoading:setFuelPercent(objectConfig, vehicle)
	if objectConfig.fuelPercent ~= nil and vehicle.getConsumerFillUnitIndex ~= nil then				
		local fuelUnitIndex = vehicle:getConsumerFillUnitIndex(FillType.DIESEL) or (vehicle:getConsumerFillUnitIndex(FillType.ELECTRICCHARGE) or vehicle:getConsumerFillUnitIndex(FillType.METHANE));
		if fuelUnitIndex ~= nil then						
			local capacity = vehicle:getFillUnitCapacity(fuelUnitIndex);					
			local fuelPercent = g_additionalContractUtils:getIntegerToVector(objectConfig.fuelPercent);					
			local fillDelta = (capacity*fuelPercent)/100 - vehicle:getFillUnitFillLevel(fuelUnitIndex);						
			vehicle:addFillUnitFillLevel(vehicle:getOwnerFarmId(), fuelUnitIndex, fillDelta, vehicle:getFillUnitFirstSupportedFillType(fuelUnitIndex), ToolType.UNDEFINED, nil);
		end;			
	end;
end;

function AdditionalContractsLoading:setFillPercent(objectConfig, vehicle)
	if objectConfig.fillPercent ~= nil and objectConfig.fillType ~= nil then										
		local fillType = nil;
		if objectConfig.fillType:upper() == "RANDOM" then 
			local randomFillUnits = {};
			for _, fillUnit in ipairs(vehicle:getFillUnits()) do
				local fillTypes = vehicle:getFillUnitSupportedFillTypes(fillUnit.fillUnitIndex);				
				for index, available in pairs(fillTypes) do
					local consumerFillUnit = vehicle.getConsumerFillUnitIndex ~= nil and vehicle:getConsumerFillUnitIndex(index);
					if (consumerFillUnit == nil or not consumerFillUnit) and available then
						table.insert(randomFillUnits, index);
					end;
				end;
			end;
			local fillTyp = table.getRandomElement(randomFillUnits);			
			fillType = g_fillTypeManager:getFillTypeByIndex(fillTyp);
			if fillType == nil or fillType.index == FillType.UNKNOWN then fillType = g_fillTypeManager:getFillTypeByIndex(2);end; --change
		else 
			fillType = g_fillTypeManager:getFillTypeByName(objectConfig.fillType);
		end;		
		if fillType ~= nil and fillType.index ~= FillType.UNKNOWN then					
			local fillPercent = g_additionalContractUtils:getIntegerToVector(objectConfig.fillPercent);					
			for _, fillUnit in ipairs(vehicle:getFillUnits()) do				
				if vehicle:getFillUnitSupportsFillType(fillUnit.fillUnitIndex, fillType.index) then
					local capacity = vehicle:getFillUnitCapacity(fillUnit.fillUnitIndex);
					local fillDelta = (capacity*fillPercent)/100 - vehicle:getFillUnitFillLevel(fillUnit.fillUnitIndex);
					vehicle:addFillUnitFillLevel(vehicle:getOwnerFarmId(), fillUnit.fillUnitIndex, fillDelta, fillType.index, ToolType.UNDEFINED, nil);
					break;
				end;						
			end;				
		end;
		
	end;
end;

function AdditionalContractsLoading:setAnimalData(animalData, vehicle)
	if animalData ~= nil then
		local subType = g_currentMission.animalSystem:getSubTypeByName(animalData.subType)		
		local animalCluster = g_currentMission.animalSystem:createClusterFromSubTypeIndex(subType.subTypeIndex)		
		local name = "";
		local hasPrefix = animalData.prefix ~= nil;
		if animalData.name ~= nil then 
			name = tostring(animalData.name);
			if name:upper() == "RANDOM" then name = g_currentMission.animalNameSystem:getRandomName();end;			
			if hasPrefix then name = tostring(animalData.prefix).. name;end;
			animalCluster.name = name;
		end;		
		if animalData.age ~= nil then
			animalCluster.age = g_additionalContractUtils:getIntegerToVector(animalData.age);
		end;
		if animalData.ridingPercent ~= nil then
			animalCluster.riding = g_additionalContractUtils:getIntegerToVector(animalData.ridingPercent);
		end;
		if animalData.dirtPercent ~= nil then
			animalCluster.dirt = g_additionalContractUtils:getIntegerToVector(animalData.dirtPercent)/100;	
		end;
		if animalData.fitnessPercent ~= nil then
			animalCluster.fitness = g_additionalContractUtils:getIntegerToVector(animalData.fitnessPercent)/100;	
		end;
		if animalData.healthPercent ~= nil then
			animalCluster.health = g_additionalContractUtils:getIntegerToVector(animalData.healthPercent)/100;
		end;
		if animalData.canBeSold ~= nil then
			animalCluster.canBeSold = animalData.canBeSold;
		end;
		
		---realisticLivestock mod has problems with default giants vehicleloadingdata animals---
		-- print(DebugUtil.printTableRecursively(animalCluster, "before animalCluster ", 0, 1))
		-- local warning = false;
		-- local class = nil;
		-- local clusterByMod = nil;
		-- if FS25_RealisticLivestockRM ~= nil and FS25_RealisticLivestockRM.Animal ~= nil and FS25_RealisticLivestockRM.Animal.new ~= nil then 
			-- class = FS25_RealisticLivestockRM.Animal;
			-- warning = true;
		-- end;
		-- if class ~= nil then
			-- clusterByMod = class.new(animalCluster);
			-- if clusterByMod ~= nil then animalCluster = clusterByMod;end;
			-- if animalCluster ~= nil and animalCluster.maxNumAnimals ~= 2 ^ AnimalCluster.NUM_BITS_NUM_ANIMALS - 1 then animalCluster.maxNumAnimals = 2 ^ AnimalCluster.NUM_BITS_NUM_ANIMALS - 1;end;
		-- end;
		-- if warning then Logging.info("** Generate/Create Animal, AnimalDealer-Contracts have problems with mod FS25_RealisticLivestockRM, better abort this contract  **");end;
		-- ---
		-- print(DebugUtil.printTableRecursively(animalCluster, "after animalCluster ", 0, 1))
		---realisticLivestock mod has problems with default giants vehicleloadingdata animals---
		animalCluster.numAnimals = animalData.numAnimals;
		vehicle:setCluster(animalCluster);
		return name;
	end;	
end;

function AdditionalContractsLoading:setIgnoreFarmlandTyp(objectConfig, vehicle)
	
end;

function AdditionalContractsLoading:spawnHeap(mission, vehicle, spawnPosition, data, objectConfig)
	if spawnPosition == nil then return nil, "spawnPosition not found";end;
	local fillTypeName = nil;
	local fillTypeIndex = nil;	
	local amount = 0;
	local length = 1;
	local rows = 1;
	local spacing = 3;
	if data ~= nil then		
		fillTypeName = data.fillTypeName;
		fillTypeIndex = data.fillTypeIndex;
		amount = Utils.getNoNil(tonumber(data.amount), amount);
		length = Utils.getNoNil(tonumber(data.length), length);
		rows = Utils.getNoNil(tonumber(data.rows), rows);
		spacing = Utils.getNoNil(tonumber(data.spacing), spacing);
	end;
	if tonumber(amount) <= 0 then return nil, "no amount given or 0";end;
	if objectConfig ~= nil then		
	
	end;	
	if fillTypeIndex == nil and fillTypeName ~= nil then fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName);end; 
	if fillTypeName == nil and fillTypeIndex ~= nil then fillTypeName = g_fillTypeManager:getFillTypeNameByIndex(fillTypeIndex);end; 
	
	if fillTypeName == nil and fillTypeIndex == nil then return nil, "fillTypeName and fillTypeIndex not found";end;
	
	local heightType = g_densityMapHeightManager:getDensityMapHeightTypeByFillTypeIndex(fillTypeIndex);
	if heightType == nil then return nil, "heightType not found";end;	
	
	local x, y, z, dirX, dirZ;
	if vehicle ~= nil then
		x, y, z = getWorldTranslation(vehicle.rootNode);		
		dirX, _, dirZ = localDirectionToWorld(vehicle.rootNode, 0, 0, 1);
	else
		x, y, z, _, _, _, dirX, dirZ = unpack(spawnPosition);
	end;
	if x == nil or y == nil or z == nil then return nil, "x,y,z not found";end;
	if dirX == nil then dirX = 1;end;
	if dirZ == nil then dirZ = 0;end;
	
	
	local spawnedLiters = 0
	local initialOffset = (rows - 1) * spacing * -0.5
	local i = 0
	local height = rows - 1
	for i = i, height do
		local offset = initialOffset + i * spacing
		local lx = x + offset * dirZ
		local lz = z + offset * -dirX
		spawnedLiters = spawnedLiters + DensityMapHeightUtil.tipToGroundAroundLine(vehicle, amount, fillTypeIndex, lx, y, lz, lx + length * dirX, y, lz + length * dirZ, 10, 40, nil, nil, nil, nil)
	end
	if vehicle ~= nil and vehicle.resetPositionToTerrainHeight ~= nil then
		--local height = DensityMapHeightUtil.getHeightAtWorldPos(x, y, z);
		--if height > y+1 then vehicle:resetPositionToTerrainHeight();end;		
	end;
	return spawnedLiters, "loading true !";
end;