FieldHeapMission = {};
FieldHeapMission.NAME = "fieldHeapMission";
FieldHeapMission.MISSIONS_XML = {};
FieldHeapMission.DELETE_HEAP = true; --for debug testing
FieldHeapMission.UI_SETTINGS = true;
FieldHeapMission.MAX_NUM_INSTANCE = 2;
FieldHeapMission.DEFAULT_NUM_INSTANCE = 2;
FieldHeapMission.MAX_NUM_SEASON = {2,0,1}; --0=off or max,is,nextDay
FieldHeapMission.BALANCE = {false,0}; --on/off,is
FieldHeapMission.RANDOM = false; --at random pool
FieldHeapMission.ACCEPTS_FRUITTYPE = {STATE=false,SUGARBEET=true,BEETROOT=true,CARROT=true,PARSNIP=true,POTATO=true,ONION=true};
FieldHeapMission.NOACCEPTS_FRUITTYPE = {COTTON=false,OAT=false,MAIZE=false,SOYBEAN=false,SUNFLOWER=false,CANOLA=false,SORGHUM=false};
FieldHeapMission.ACCEPTS_VARIANT = {[1]={ROOTCROP_BELT=true,ROOT_CROP=true,VEGETABLES=false},[2]={COMBINE=false},[3]={SHOVEL=false}};
FieldHeapMission.VEHICLE_VARIANT = {
	[1] = "VEGETABLES";
	[2] = "GRAIN";
	[3] = "SHOVEL";	
};

source(AdditionalContracts.modDir.. "missions/fieldHeapMission/FieldHeapChangeSettingsEvent.lua");
source(AdditionalContracts.modDir.. "missions/fieldHeapMission/FieldHeapLoadSettingsEvent.lua");

FieldHeapMission.uiData = {
	settings = {
		fieldHeapMission_minMax = FieldHeapMission.DEFAULT_NUM_INSTANCE;		
	};
	controls = {
		{ typName="fieldHeapMission", name="fieldHeapMission_minMax", autoBind=true, min=0, max=FieldHeapMission.MAX_NUM_INSTANCE, step=1, unit="/ ".. tostring(FieldHeapMission.MAX_NUM_INSTANCE), nillable=false }; --min 0 = off
	};
};

FieldHeapMission.data = {	
	ownTable = {MINMAX_HA={2,40}, SUCCESS_FACTOR=0.92, BIG_HEAP=120000, VEHICLE_USE_COST=120, classOverlay="tractor", typOverlay="conveyorBelt"}; --big large more vehicles, 4x ? Mod Maps... / make it cheaper vehicle cost
	reward = {dismiss=1000, min=4000, BONUS=2000, PER_HA=1, PER_LITER=0.1, PER_METER=2.3}; 
	jobTypName = g_i18n:getText("contract_field_heap_title");	
};

FieldHeapMission.metadata = {
	interface = "FS25 ...", --new
	title = "Field Heap Contracts",
	notes = "Dieser Mod generiert Feldhaufen Aufträge.",
	author = "(by HappyLooser)",
	build = 3,
	datum = "06.02.2026",
	update = "02.03.2026",
	debugPrint = false, 
	discord = "HappyLooser Modding",
	info = " Link Freigabe,Änderungen,Kopien oder Code Benutzung ist ohne meine Zustimmung nicht erlaubt",
	"##Orginal Link Freigabe: https://www.farming-simulator.com/mods.php"
};

local function debugPrint(text, ...)
	if FieldHeapMission.metadata.debugPrint then
		Logging.info(text,...);
	end;
end;

local FieldHeapMission_mt = Class(FieldHeapMission, AbstractFieldMission)
InitObjectClass(FieldHeapMission, "FieldHeapMission")

function FieldHeapMission.registerXMLPaths(schema, key)
	FieldHeapMission:superClass().registerXMLPaths(schema, key)
	schema:register(XMLValueType.INT, key .. "#rewardPerHa", "Reward per ha")
	schema:register(XMLValueType.FLOAT, key .. "#failureCostFactor", "Failure cost factor");
	schema:register(XMLValueType.FLOAT, key .. "#failureCostOfTotal", "Failure cost of total");
end

function FieldHeapMission.registerSavegameXMLPaths(schema, key)
	FieldHeapMission:superClass().registerSavegameXMLPaths(schema, key)
	schema:register(XMLValueType.INT, key .. "#spawnedLiters", "Spawned fruit liters")
	schema:register(XMLValueType.INT, key .. "#swathSegmentIndex", "Current swath segment index")
	schema:register(XMLValueType.BOOL, key .. "#finishedHeapSpawning", "If fruit spawning is finished")
	local fieldHeapKey = string.format("%s.fieldHeap", key);
	schema:register(XMLValueType.STRING, fieldHeapKey .. "#missionConfig", "missionConfig name");
	schema:register(XMLValueType.INT, fieldHeapKey .. "#objectConfig", "objectConfig index");
	schema:register(XMLValueType.INT, fieldHeapKey .. "#numObjects", "Num objects spawned");
	schema:register(XMLValueType.STRING, fieldHeapKey .. ".object(?)#uniqueId", "UniqueId of created object");
	schema:register(XMLValueType.STRING, fieldHeapKey .. ".tool(?)#uniqueId", "UniqueId of created tool");	
	schema:register(XMLValueType.STRING, fieldHeapKey .. "#fruitType", "Name of the fruit type");
	schema:register(XMLValueType.STRING, fieldHeapKey .. "#fillType", "Name of the fill type");
	schema:register(XMLValueType.FLOAT, fieldHeapKey .. "#expectedLiters", "Expected liters");
	schema:register(XMLValueType.FLOAT, fieldHeapKey .. "#depositedLiters", "Deposited liters");
	schema:register(XMLValueType.FLOAT, fieldHeapKey .. "#distanceToSellingStation", "Distance to selling station");
	schema:register(XMLValueType.STRING, fieldHeapKey .. "#sellingStationPlaceableUniqueId", "Unique id of the selling point");
	schema:register(XMLValueType.INT, fieldHeapKey .. "#unloadingStationIndex", "Index of the unloading station");
end;

function FieldHeapMission.registerMetaXMLPaths(schema, key)
	FieldHeapMission:superClass().registerMetaXMLPaths(schema, key);
	schema:register(XMLValueType.INT, key .. "#nextDay", "Earliest day a new mission can spawn");	
end;

function FieldHeapMission.new(isServer, isClient, customMt)	
	local title = g_i18n:getText("contract_field_heap_title");
	local description = g_i18n:getText("contract_field_heap_description");
	local self = AbstractFieldMission.new(isServer, isClient, title, description, customMt or FieldHeapMission_mt);
	
	self.workAreaTypes = {
		[WorkAreaType.DEFAULT] = true;
	};
	self.loadingObjects = AdditionalContractsLoading.new();	
	self.tools = {};
	self.distanceTools = {};
	self.spawnManager = AdditionalContractSpawnManager.new();
	self.numTools = 0;	
	self.objects = {};
	self.objectsAtTrigger = {};
	self.numFinished = 0;
	self.distanceToSellingStation = 0;
	self.finishedHeapSpawning = false;
	self.initializedHeapSpawning = false;
	self.finishedToolsSpawning = false;
	self.currentSwathSegmentIndex = nil;
	self.spawnedLiters = 0;
	self.pendingSellingStationId = nil;
	self.sellingStation = nil;
	self.fruitTypeIndex = nil;
	self.fillTypeIndex = nil;
	self.depositedLiters = 0;
	self.expectedLiters = 0;
	self.lastSellChange = -1;
	self.startTime = 0;
	self.variant = nil;
	return self;
end;

function FieldHeapMission:getOwnData()
	return self.data;
end;

function FieldHeapMission:getNumObjects()
	return Utils.getNoNil(self.numObjects, #self.objects);
end;

function FieldHeapMission:getNumTools()
	return Utils.getNoNil(self.numTools, #self.tools);
end;

function FieldHeapMission:init(field, sellingStation, fruitTypeIndex, fillTypeIndex)
	
	if self.variant == nil then return false;end;	
	local fieldHeapMission = g_additionalContractUtils:getRandomXmlMissionByVariant(FieldHeapMission.MISSIONS_XML, self.variant[1]); --table.getRandomElement(FieldHeapMission.MISSIONS_XML);
	if fieldHeapMission == nil then return false;end;	
	if fieldHeapMission.tools ~= nil then
		self.numTools = #fieldHeapMission.tools;
	end;
	
	
	local object = g_additionalContractUtils:getRandomElement(fieldHeapMission.objects);
	if object == nil then return false;end;
	self.numObjects = g_additionalContractUtils:getIntegerToVector(object.minMax);	
	
	if not g_additionalContractUtils:canAddLimitedObjects(object, nil, self.numObjects) then return false;end;
			
	self.missionConfig = fieldHeapMission;
	self.objectConfig = object;		
	
	--self:setLeasingVehicles();	
	
	self.fruitTypeIndex = fruitTypeIndex;
	self.fillTypeIndex = fillTypeIndex;
	local success = FieldHeapMission:superClass().init(self, field);
	self:setSellingStation(sellingStation);
	self:setTypDescription();
	self.reward = self:calculateReward();
	self:setMinReward();	
	return success;	
end;

function FieldHeapMission:onSavegameLoaded()
	if self.field == nil then
		Logging.error("Field is not set for field heap mission");
		g_missionManager:markMissionForDeletion(self);
		return;
	elseif self.field:getFieldState().fruitTypeIndex == self.fruitTypeIndex then
		if not self:getIsFinished() then
			local sellingStation = g_currentMission.placeableSystem:getPlaceableByUniqueId(self.sellingStationPlaceableUniqueId);
			if sellingStation == nil then
				Logging.error("Selling station placeable with uniqueId \'%s\' not available for field heap mission", self.sellingStationPlaceableUniqueId);
				g_missionManager:markMissionForDeletion(self);
				return;
			end;
			local unloadingStation = g_currentMission.storageSystem:getPlaceableUnloadingStation(sellingStation, self.unloadingStationIndex);
			if unloadingStation == nil then
				Logging.error("Unable to retrieve unloadingStation %d for placeable %s for field heap mission", self.unloadingStationIndex, sellingStation.configFileName);
				g_missionManager:markMissionForDeletion(self);
				return;
			end;
			self:setSellingStation(unloadingStation);
			if self:getWasStarted() then
				unloadingStation.missions[self] = self;
			end;			
		end;
		
		if self.pendingLoadingData ~= nil then
			for _, uniqueId in ipairs(self.pendingLoadingData.objects) do
				local vehicle = g_currentMission.vehicleSystem:getVehicleByUniqueId(uniqueId);
				if vehicle ~= nil then
					self.loadingObjects:setCanBeReset(self.objectConfig, vehicle); --replace can be reset
					self.objects[vehicle.rootNode] = vehicle;
				end;
			end;
			for _, uniqueId in ipairs(self.pendingLoadingData.tools) do
				local vehicle = g_currentMission.vehicleSystem:getVehicleByUniqueId(uniqueId);
				if vehicle ~= nil then
					self.loadingObjects:setCanBeReset(nil, vehicle); --replace can be reset
					self.tools[vehicle.rootNode] = vehicle;
				end;
			end;
		end;
		self:setTypDescription();		
		self:setVariantCategory(self.fillTypeIndex);
		FieldHeapMission:superClass().onSavegameLoaded(self);
	else
		local fruitTypeName = g_fruitTypeManager:getFruitTypeNameByIndex(self.fruitTypeIndex); --g_fruitTypeManager:getFruitTypeByIndex(self.fruitTypeIndex); --patch 1.14 ?
		Logging.error("FruitType \'%s\' is not present on field \'%s\' for field heap mission", fruitTypeName, self.field:getName());
		g_missionManager:markMissionForDeletion(self);
	end;
end;

function FieldHeapMission:delete()
	for _, object in pairs(self.objects) do
		self:deleteObject(object);
	end;
	self.objects = {};
	for _, tool in pairs(self.tools) do
		self:deleteTools(tool);
	end;
	self.tools = {};
	if self.sellingStation ~= nil then
		self.sellingStation.missions[self] = nil;
		self.sellingStation = nil;
	end;
	if self.sellingStationMapHotspot ~= nil then
		table.removeElement(self.mapHotspots, self.sellingStationMapHotspot);
		g_currentMission:removeMapHotspot(self.sellingStationMapHotspot);
		self.sellingStationMapHotspot:delete();
		self.sellingStationMapHotspot = nil;
	end;
	FieldHeapMission:superClass().delete(self);
end;

function FieldHeapMission:deleteObject(object)
	if object ~= nil and object.rootNode ~= nil and entityExists(object.rootNode) then		
		g_currentMission.nodeToObject[object.rootNode]:delete();		
	end;
end;

function FieldHeapMission:deleteTools(object)
	if object ~= nil and object.rootNode ~= nil and entityExists(object.rootNode) then		
		g_currentMission.nodeToObject[object.rootNode]:delete();		
	end;
end;

function FieldHeapMission:writeStream(streamId, connection)
	FieldHeapMission:superClass().writeStream(self, streamId, connection);
	NetworkUtil.writeNodeObject(streamId, self.sellingStation);	
	streamWriteUIntN(streamId, self.fruitTypeIndex or 0, FruitTypeManager.SEND_NUM_BITS);
	streamWriteUIntN(streamId, self.fillTypeIndex, FillTypeManager.SEND_NUM_BITS);
	
	streamWriteUInt8(streamId, self.numObjects);
	streamWriteUInt8(streamId, self.missionConfig.id);
	streamWriteUInt8(streamId, self.objectConfig.index);
end;

function FieldHeapMission:readStream(streamId, connection)
	FieldHeapMission:superClass().readStream(self, streamId, connection);
	self.pendingSellingStationId = NetworkUtil.readNodeObjectId(streamId);	
	self.fruitTypeIndex = streamReadUIntN(streamId, FruitTypeManager.SEND_NUM_BITS);
	self.fillTypeIndex = streamReadUIntN(streamId, FillTypeManager.SEND_NUM_BITS);
	
	self.numObjects = streamReadUInt8(streamId);	
	self.missionConfig = self:getFieldHeapMissionConfigById(streamReadUInt8(streamId));
	self.objectConfig = self:getFieldHeapMissionObjectConfigByIndex(streamReadUInt8(streamId));
	self:setTypDescription();
	self:setVariantCategory(self.fillTypeIndex);
	--self:setLeasingVehicles();
end;

function FieldHeapMission:saveToXMLFile(xmlFile, key)	
	xmlFile:setValue(key .. "#spawnedLiters", self.spawnedLiters)
	if not self.finishedHeapSpawning and self.currentSwathSegmentIndex ~= nil then
		xmlFile:setValue(key .. "#swathSegmentIndex", self.currentSwathSegmentIndex);
	end;
	xmlFile:setValue(key .. "#finishedHeapSpawning", self.finishedHeapSpawning);
	local fieldHeapKey = string.format("%s.fieldHeap", key);
	xmlFile:setValue(fieldHeapKey .. "#missionConfig", self.missionConfig.name);
	xmlFile:setValue(fieldHeapKey .. "#objectConfig", self.objectConfig.index);	
	xmlFile:setValue(fieldHeapKey .. "#fruitType", g_fruitTypeManager:getFruitTypeNameByIndex(self.fruitTypeIndex)); --patch 1.14 ?
	xmlFile:setValue(fieldHeapKey .. "#fillType",	g_fillTypeManager:getFillTypeNameByIndex(self.fillTypeIndex)); --patch 1.14 ?
	xmlFile:setValue(fieldHeapKey .. "#expectedLiters", self.expectedLiters);
	xmlFile:setValue(fieldHeapKey .. "#depositedLiters", self.depositedLiters);
	xmlFile:setValue(fieldHeapKey .. "#numObjects", self.numObjects);	
	xmlFile:setValue(fieldHeapKey .. "#distanceToSellingStation", self.distanceToSellingStation);
	
	local int = 0
	for _, object in pairs(self.objects) do
		local uniqueId = object:getUniqueId();		
		local objectKey = string.format("%s.object(%d)", fieldHeapKey, int);
		xmlFile:setValue(objectKey .. "#uniqueId", uniqueId);		
		int = int + 1;
	end;
	int = 0
	for _, tool in pairs(self.tools) do
		local uniqueId = tool:getUniqueId();		
		local toolKey = string.format("%s.tool(%d)", fieldHeapKey, int);
		xmlFile:setValue(toolKey .. "#uniqueId", uniqueId);		
		int = int + 1;
	end;
	if self.sellingStation ~= nil then
		local sellingStationPlaceable = self.sellingStation.owningPlaceable;
		if sellingStationPlaceable == nil then
			local sellingStationName = self.sellingStation.getName and self.sellingStation:getName() or "unknown";
			Logging.xmlWarning(xmlFile, "Unable to retrieve placeable of sellingStation \'%s\' for saving field heap mission \'%s\' ", sellingStationName, fieldHeapKey);
			return;
		end;
		local unloadingStationIndex = g_currentMission.storageSystem:getPlaceableUnloadingStationIndex(sellingStationPlaceable, self.sellingStation);
		if unloadingStationIndex == nil then
			local sellingStationName = self.sellingStation.getName and self.sellingStation:getName() or (sellingStationPlaceable.getName and sellingStationPlaceable:getName() or "unknown");
			Logging.xmlWarning(xmlFile, "Unable to retrieve unloading station index of sellingStation \'%s\' for saving field heap mission \'%s\' ", sellingStationName, fieldHeapKey);
			return;
		end;
		xmlFile:setValue(fieldHeapKey .. "#sellingStationPlaceableUniqueId", sellingStationPlaceable:getUniqueId());
		xmlFile:setValue(fieldHeapKey .. "#unloadingStationIndex", unloadingStationIndex);
	end;
	FieldHeapMission:superClass().saveToXMLFile(self, xmlFile, key);
end;

function FieldHeapMission:loadFromXMLFile(xmlFile, key)
	self.spawnedLiters = xmlFile:getValue(key .. "#spawnedLiters", self.spawnedLiters);
	self.currentSwathSegmentIndex = xmlFile:getValue(key .. "#swathSegmentIndex", self.currentSwathSegmentIndex);
	self.finishedHeapSpawning = xmlFile:getValue(key .. "#finishedHeapSpawning", self.finishedHeapSpawning);
	if self.finishedHeapSpawning then
		self.initializedHeapSpawning = true;
	end;
	local fieldHeapKey = string.format("%s.fieldHeap", key);
	local fruitTypeName = xmlFile:getValue(fieldHeapKey.."#fruitType");	
	self.fruitTypeIndex = g_fruitTypeManager:getFruitTypeIndexByName(fruitTypeName);
	local fillTypeName = xmlFile:getValue(fieldHeapKey.."#fillType");
	self.fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName);
	self.expectedLiters = xmlFile:getValue(fieldHeapKey .. "#expectedLiters", self.expectedLiters);
	self.depositedLiters = xmlFile:getValue(fieldHeapKey .. "#depositedLiters", self.depositedLiters);	
	self.distanceToSellingStation = xmlFile:getValue(fieldHeapKey .. "#distanceToSellingStation", self.distanceToSellingStation);	
	
	local missionConfig;
	local objectConfig;
	if xmlFile:hasProperty(fieldHeapKey .. "#missionConfig") then 
		missionConfig = xmlFile:getValue(fieldHeapKey .. "#missionConfig");	
		self.missionConfig = self:getFieldHeapMissionConfigByName(missionConfig);
		if self.missionConfig == nil then			
			return false;
		end;
		self.numTools = 0;
		if self.missionConfig.tools ~= nil and #self.missionConfig.tools > 0 then self.numTools = #self.missionConfig.tools;end;		
		objectConfig = xmlFile:getValue(fieldHeapKey .. "#objectConfig");
		self.objectConfig = self:getFieldHeapMissionObjectConfigByIndex(objectConfig);
		if self.objectConfig == nil then
			return false;
		end;
		self.numObjects = xmlFile:getValue(fieldHeapKey .. "#numObjects", 0);
	end;
	self:setVariantCategory(self.fillTypeIndex);
	self.pendingLoadingData = {
		["objects"] = {};
		["tools"] = {};
	};
	--if self.status == MissionStatus.RUNNING then
		local int = 0;
		while true do
			local objectKey = string.format("%s.object(%d)", fieldHeapKey, int);
			if not xmlFile:hasProperty(objectKey .. "#uniqueId") then
				break;
			end;
			local uniqueId = xmlFile:getValue(objectKey .. "#uniqueId");
			table.insert(self.pendingLoadingData.objects, uniqueId);			
			int = int + 1;			
		end;
		int = 0;
		while true do
			local toolKey = string.format("%s.tool(%d)", fieldHeapKey, int);
			if not xmlFile:hasProperty(toolKey .. "#uniqueId") then
				break;
			end;
			local uniqueId = xmlFile:getValue(toolKey .. "#uniqueId");
			table.insert(self.pendingLoadingData.tools, uniqueId);			
			int = int + 1;			
		end;
	--end
	
	if not FieldHeapMission:superClass().loadFromXMLFile(self, xmlFile, key) then
		return false;
	end;
	if not self:getIsFinished() then
		local sellingStationPlaceableUniqueId = xmlFile:getValue(fieldHeapKey .. "#sellingStationPlaceableUniqueId");
		if sellingStationPlaceableUniqueId == nil then
			Logging.xmlError(xmlFile, "No sellingStationPlaceable uniqueId given for field heap mission at \'%s\'", fieldHeapKey);
			return false;
		end;
		local unloadingStationIndex = xmlFile:getValue(fieldHeapKey .. "#unloadingStationIndex");
		if unloadingStationIndex == nil then
			Logging.xmlError(xmlFile, "No unloadting station index given for field heap mission at \'%s\'", fieldHeapKey);
			return false;
		end;
		self.sellingStationPlaceableUniqueId = sellingStationPlaceableUniqueId;
		self.unloadingStationIndex = unloadingStationIndex;
	end;
	if FieldHeapMission:canMaxNumSeason() then self:setMaxNumSeason();end;
	return true;
end;

function FieldHeapMission:getFieldPreparingTask()
	local fruitType = self.fruitTypeIndex;
	local fruitTypeDesc = g_fruitTypeManager:getFruitTypeByIndex(fruitType);
	local fieldPreparingTask = FieldUpdateTask.new();
	fieldPreparingTask:setArea(self.field:getDensityMapPolygon());
	fieldPreparingTask:setField(self.field);
	fieldPreparingTask:setFruit(fruitType, fruitTypeDesc.cutState);
	fieldPreparingTask:setWeedState(0);
	if fruitTypeDesc.harvestGroundType ~= nil then
		fieldPreparingTask:setGroundType(fruitTypeDesc.harvestGroundType);
	end;
	return fieldPreparingTask;
end;

function FieldHeapMission:getIsPrepared()
	if FieldHeapMission:superClass().getIsPrepared(self) then
		return self.finishedHeapSpawning;
	else
		return false;
	end;
end;

function FieldHeapMission:finishedPreparing()
	FieldHeapMission:superClass().finishedPreparing(self);		
	self.expectedLiters = self:getMaxCutLiters();
	if self.expectedLiters <= 0 then
		self:finish(MissionFinishState.FAILED);
	end;
end;

function FieldHeapMission:getFieldFinishTask()
	local finishTask = FieldHeapMission:superClass().getFieldFinishTask(self);
	if finishTask ~= nil then
		--testing--
		if FieldHeapMission.DELETE_HEAP then finishTask:clearHeight();end;
		--testing--
	end;
	return finishTask;
end;

function FieldHeapMission:update(dt)
	if self.isServer and (self.status == MissionStatus.PREPARING and (self.fieldPreparingTask == nil or self.fieldPreparingTask:getIsFinished())) and not self.initializedHeapSpawning then
		local fruitTypeDesc = g_fruitTypeManager:getFruitTypeByIndex(self.fruitTypeIndex);
		local litersPerSqm = fruitTypeDesc.literPerSqm;
		local fieldSizeHa = self.field:getAreaHa();
		local scaleMuliplier = self.field:getFieldState():getHarvestScaleMultiplier();
		local minLitersPerSqm = MathUtil.haToSqm(fieldSizeHa) * litersPerSqm * scaleMuliplier;
		local workingWidth = 7; 
		local x, z = self.field:getCenterOfFieldWorldPosition();
		local dirX, dirZ = 1,0;
		local angle = self.field:getAngle();	
		if angle ~= nil then dirX, dirZ = MathUtil.getDirectionFromYRotation(angle);end;
		local spawnPosition = {x, getTerrainHeightAtWorldPos(g_terrainNode, x, 0, z), z, nil, nil ,nil, dirX or 1, dirZ or 0};		
		
		local fieldCourseSettings = FieldCourseSettings.new();
		fieldCourseSettings.implementWidth = workingWidth; 
		fieldCourseSettings.numHeadlands = 2;		
		local swathSegmentIndex = 0;		
		local function segmentFunc(sx, sz, ex, ez, _, headlandIndex, _, totalLength)			
			swathSegmentIndex = swathSegmentIndex + 1;
			if self.currentSwathSegmentIndex == nil or swathSegmentIndex > self.currentSwathSegmentIndex then	--headlandIndex == nil	
				local length = MathUtil.vector2Length(ex - sx, ez - sz);				
				local fillDelta = length * (minLitersPerSqm / totalLength);								
				self.spawnedLiters = self.spawnedLiters + (fillDelta or 0);
				self.currentSwathSegmentIndex = swathSegmentIndex;				
			end;
		end;
		FieldCourseIterator.new(x, z, fieldCourseSettings, segmentFunc, function(success)			
			self.finishedHeapSpawning = true;			
			if success then 
				local spec = nil;
				if self.missionConfig.spec ~= nil then spec = "spec_".. tostring(self.missionConfig.spec);end;
				local object = self:loadHeaps(spawnPosition, spec);
				self:loadTools(object, spawnPosition, 5);
			end;			
		end)
		self.initializedHeapSpawning = true;			
	end	
	if self.pendingSellingStationId ~= nil then
		self:tryToResolveSellingStation();
	end;
	if self.lastSellChange > 0 then
		self.lastSellChange = self.lastSellChange - 1;
		if self.lastSellChange == 0 then
			local expected = self.expectedLiters * FieldHeapMission.data.ownTable.SUCCESS_FACTOR;
			local depositedLiters = self.depositedLiters / expected * 100;
			local percentage = math.floor(depositedLiters);
			g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_OK, string.format(g_i18n:getText("contract_field_harvest_progress_transporting_forField"), percentage, self.field.farmland:getId()));
		end;
	end;	
	FieldHeapMission:superClass().update(self, dt);
end;

function FieldHeapMission:createModifier()
	local heightType = g_densityMapHeightManager:getDensityMapHeightTypeByFillTypeIndex(self.fillTypeIndex);
	if heightType ~= nil then
		self.completionModifier = DensityMapModifier.new(DensityMapHeightUtil.terrainDetailHeightId, DensityMapHeightUtil.heightFirstChannel, DensityMapHeightUtil.heightNumChannels);
		self.completionFilter = DensityMapFilter.new(DensityMapHeightUtil.terrainDetailHeightId, DensityMapHeightUtil.typeFirstChannel, DensityMapHeightUtil.typeNumChannels);
		self.completionFilter:setValueCompareParams(DensityValueCompareType.EQUAL, heightType.index);
	end;
end;

function FieldHeapMission:tryToResolveSellingStation()
	if self.pendingSellingStationId ~= nil and self.sellingStation == nil then
		local sellingStation = NetworkUtil.getObject(self.pendingSellingStationId);
		if sellingStation ~= nil then
			self:setSellingStation(sellingStation);
		end;
	end;
end;

function FieldHeapMission:setSellingStation(sellingStation)
	if sellingStation ~= nil then
		self.pendingSellingStationId = nil;
		self.sellingStation = sellingStation;
		local placeable = sellingStation.owningPlaceable;
		if placeable ~= nil and placeable.getHotspot ~= nil then
			local mapHotspot = placeable:getHotspot();
			if mapHotspot ~= nil then
				self.sellingStationMapHotspot = HarvestMissionHotspot.new();
				self.sellingStationMapHotspot:setWorldPosition(mapHotspot:getWorldPosition());
				table.addElement(self.mapHotspots, self.sellingStationMapHotspot);
				if self.addSellingStationHotSpot then
					g_currentMission:addMapHotspot(self.sellingStationMapHotspot);
				end;
			end;
		end;
	end;
end;

function FieldHeapMission:addHotspots()
	FieldHeapMission:superClass().addHotspots(self);
	self.addSellingStationHotSpot = true;
	if self.sellingStationMapHotspot ~= nil then
		g_currentMission:addMapHotspot(self.sellingStationMapHotspot);
	end;
end;

function FieldHeapMission:removeHotspot()
	FieldHeapMission:superClass().removeHotspot(self);
	if self.sellingStationMapHotspot ~= nil then
		g_currentMission:removeMapHotspot(self.sellingStationMapHotspot);
	end;
	self.addSellingStationHotSpot = false;
end;

function FieldHeapMission:start(spawnVehicles)
	if not self:loadObjects() then
		return false;
	end;
	if self.pendingSellingStationId ~= nil then
		self:tryToResolveSellingStation();
	end;
	if self.sellingStation == nil then
		return false;
	end;
	self.sellingStation.missions[self] = self;
	return FieldHeapMission:superClass().start(self, spawnVehicles);
end;

function FieldHeapMission:loadHeaps(spawnPosition, spec)
	local vehicle = self.spawnManager:getDistanceTool(self, 1);
	if vehicle == nil and spec ~= nil then
		for _, object in pairs(self.objects) do
			if object ~= nil and object.rootNode ~= nil and entityExists(object.rootNode) then
				if object[spec] ~= nil then vehicle = object;break;end;
			end;
		end;
	end;
	local rows = 4;
	if self.spawnedLiters <= 25000 then rows = 2;elseif self.spawnedLiters > 25000 and self.spawnedLiters < 40000 then rows =3;end;
	local spawnedLiters = self.loadingObjects:spawnHeap(self, vehicle, spawnPosition, {fillTypeIndex=self.fillTypeIndex, amount=self.spawnedLiters/rows, length=20, rows=rows}, nil);
	if spawnedLiters ~= nil and spawnedLiters < self.spawnedLiters then self.spawnedLiters = spawnedLiters;end;
	return vehicle;
end;

function FieldHeapMission:loadObjects()	
	local objectConfig = self.objectConfig;	
	if objectConfig == nil then
		return false;
	end;	
	local x, z = self.field:getCenterOfFieldWorldPosition();
	--local angle = self.field:getAngle();	
	--local dirX, dirZ = MathUtil.getDirectionFromYRotation(angle);	
	local trigger = {x=x, y=0, z=z, rx=0, ry=0, rz=0};	
	local canSpawn, objectsSpawnPosition = self.spawnManager:getObjectsSpawnPosition(trigger, nil, self.numObjects, objectConfig);
	if not canSpawn or #objectsSpawnPosition <= 0 then return false;end;
	for o=1, #objectsSpawnPosition do		
		self.loadingObjects:spawnObject(self, objectsSpawnPosition[o], {}, objectConfig);		
	end;	
	return true;
end;

function FieldHeapMission:loadTools(object, spawnPosition, startDistance)	
	if self.missionConfig.tools ~= nil and #self.missionConfig.tools > 0 then
		local x, y, z, rx, ry, rz, dirX, dirZ;
		if object ~= nil then x, y, z, rx, ry, rz, dirX, dirZ = self.spawnManager:getDistancePositionFromObject(object, startDistance, true);end;
		if x == nil or z == nil and spawnPosition ~= nil then x, y, z, rx, ry, rz, dirX, dirZ = unpack(spawnPosition);end;		
		if x ~= nil and z ~= nil then
			if y == nil then y = getTerrainHeightAtWorldPos(g_terrainNode, x, 0, z);end;
			local distancePosition = nil;
			local toolsSpawn = 0;		
			local trigger = {x, y, z, rx or 0, ry or 0, rz or 0, dirX, dirZ};
			if object == nil then distancePosition = self.spawnManager:getDistancePositionFromPosition(trigger, startDistance);end;
			if distancePosition ~= nil and distancePosition[1] ~= nil then trigger = distancePosition;end;
			for _, toolConfig in ipairs(self.missionConfig.tools) do
				if toolConfig ~= nil then					
					local canSpawn, objectsSpawnPosition = self.spawnManager:getObjectDistanceSpawnPosition(trigger, false, toolConfig);				
					if canSpawn and #objectsSpawnPosition > 0 then 
						toolsSpawn = toolsSpawn + 1;
						for o=1, #objectsSpawnPosition do
							self.loadingObjects:spawnAbstractObject(self, objectsSpawnPosition[o], {}, toolConfig, self.tools);
						end;
						x, y, z, rx, ry, rz, dirX, dirZ = self.spawnManager:getLastToolSpawnPosition(self, 5, true);
						function setChangeSpawn() --spawn change (load error)
							local _, sl, _ = self.spawnManager:getObjectSize(self.spawnManager:getDistanceTool(self));
							distancePosition = self.spawnManager:getDistancePositionFromPosition(trigger, 5+(sl or 8));
							return distancePosition ~= nil and distancePosition[1] ~= nil;
						end;
						if x ~= nil and z ~= nil then 
							trigger = {x, y or getTerrainHeightAtWorldPos(g_terrainNode, x, 0, z), z, rx or 0, ry or 0, rz or 0, dirX, dirZ};
							if objectsSpawnPosition[#objectsSpawnPosition][1] == x and objectsSpawnPosition[#objectsSpawnPosition][3] == z then --check already exist spawn with ...
								if not setChangeSpawn() then break;end;
								trigger = distancePosition;
							end;
						else							
							if not setChangeSpawn() then break;end; --load error							
							trigger = distancePosition;
						end;						
					end;									
				end;
			end;
			self.distanceTools = nil;
		end;
	end;
end;

function FieldHeapMission:getFieldCompletion()
	FieldHeapMission:superClass().getFieldCompletion(self);
	local sumPixels = 0;
	local allCalculated = true;
	for _, partitionPercentage in ipairs(self.completionPartitions) do
		if not partitionPercentage.wasCalculated then
			allCalculated = false;
		end;
		sumPixels = sumPixels + partitionPercentage.sumPixels;
	end;
	local liters = 1 - sumPixels * g_densityMapHeightManager:getMinValidLiterValue(self.fillTypeIndex) / self.spawnedLiters;
	if allCalculated then		
		self.fieldPercentageDone = math.clamp(liters, 0, 1);
	end
	return self.fieldPercentageDone;
end

function FieldHeapMission:getCompletion()
	local completion;
	if self.expectedLiters > 0 then
		local sellCompletion = self.depositedLiters / self.expectedLiters / FieldHeapMission.data.ownTable.SUCCESS_FACTOR;
		completion = math.min(sellCompletion, 1);
	else
		completion = 1;
	end;
	local fieldCompletion = self:getFieldCompletion() / AbstractMission.SUCCESS_FACTOR;
	local collectCompletion = 0.8 * math.min(fieldCompletion, 1) + 0.2 * completion;
	return math.min(1, collectCompletion);
end;

function FieldHeapMission:calculateSpawnLiters()
	local fruitTypeDesc = g_fruitTypeManager:getFruitTypeByIndex(self.fruitTypeIndex);
	local litersPerSqm = fruitTypeDesc.literPerSqm;
	local fieldSizeHa = self.field:getAreaHa();
	local scaleMuliplier = self.field:getFieldState():getHarvestScaleMultiplier();
	return MathUtil.haToSqm(fieldSizeHa) * litersPerSqm * scaleMuliplier;
end;

function FieldHeapMission:calculateDistanceReward()
	local x, z = self.field:getCenterOfFieldWorldPosition();
	local sX, sZ = nil, nil;
	if self.sellingStation ~= nil and self.sellingStation.rootNode ~= nil then
		sx, _, sz = getWorldTranslation(self.sellingStation.rootNode);
	elseif self.distanceToSellingStation ~= nil and self.distanceToSellingStation > 0 then
		return FieldHeapMission.data.reward.PER_METER * self.distanceToSellingStation; --manipulate savegame xml or map objects ?
	else
		return FieldHeapMission.data.reward.PER_METER * 300; --manipulate, bad luck and less profit
	end;
	local distance = g_additionalContractUtils:getObjectDistance( {x=sX, z=sZ}, {x=x, z=z} );	
	self.distanceToSellingStation = distance;
	return FieldHeapMission.data.reward.PER_METER * distance;	
end;

function FieldHeapMission:calculateReward()
	local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);
	local rewardScale = self.missionConfig.rewardScale or 1;
	local distanceReward = self:calculateDistanceReward();
	local spawnLiterReward = self:calculateSpawnLiters() * FieldHeapMission.data.reward.PER_LITER;	
	return distanceReward + spawnLiterReward * rewardScale;
end;

function FieldHeapMission:getRewardPerHa()
	return g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME).rewardPerHa;
end;

function FieldHeapMission:getReward()
	if self.reward <= 1 or self.reward == nil then self.reward = self:calculateReward();end;
	return self.reward;	
end;

function FieldHeapMission:setMinReward() 
	if FieldHeapMission.data.reward.min == nil or FieldHeapMission.data.reward.min <= 0 then return;end;	
	if self.reward < FieldHeapMission.data.reward.min then self.reward = FieldHeapMission.data.reward.min;end;	
end;

--function FieldHeapMission:hasLeasableVehicles() --no leasing vehicles on testing
--    return false;
--end;

function FieldHeapMission:getVehicleVariant()
	local vehicleVariant = "GRAIN";
	if self.variant ~= nil then vehicleVariant = FieldHeapMission.VEHICLE_VARIANT[self.variant[1]] or "GRAIN";end;	
	if vehicleVariant ~= "SHOVEL" and self.spawnedLiters ~= nil and self.spawnedLiters >= FieldHeapMission.data.ownTable.BIG_HEAP then vehicleVariant = "BIGHEAP_".. vehicleVariant;end;	
	return vehicleVariant;
end;

function FieldHeapMission:getVehicleCosts()
	if self.vehiclesToLoad == nil then
		return 0;
	end;
	local numVehicles = #self.vehiclesToLoad;
	local difficultyMultiplier = 0.7 + 0.3 * g_currentMission.missionInfo.economicDifficulty;
	return numVehicles * FieldHeapMission.data.ownTable.VEHICLE_USE_COST * difficultyMultiplier;
end;

function FieldHeapMission:calculateStealingCost()	
	if self.finishState ~= MissionFinishState.SUCCESS and self.isServer then		
		local penalty = Utils.getNoNil(FieldHeapMission.data.reward.dismiss, 0); --penalty;
		local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);
		local fillType = self.fillTypeIndex;
		local litersHarvested = self.expectedLiters * self:getFieldCompletion();
		for _, vehicle in pairs(self.vehicles) do
			if vehicle.spec_fillUnit ~= nil then
				for fillTypeIndex, _ in pairs(vehicle:getFillUnits()) do
					if vehicle:getFillUnitFillType(fillTypeIndex) == self.fillType then
						litersHarvested = litersHarvested - vehicle:getFillUnitFillLevel(fillTypeIndex);
					end;
				end;
			end;
		end;
		if self:getNumTools() > 0 then
			for _, tool in pairs(self.tools) do
				if tool ~= nil and tool.rootNode ~= nil and entityExists(tool.rootNode) then
					if tool.spec_fillUnit ~= nil then
						for fillTypeIndex, _ in pairs(tool:getFillUnits()) do
							if tool:getFillUnitFillType(fillTypeIndex) == self.fillType then
								litersHarvested = litersHarvested - tool:getFillUnitFillLevel(fillTypeIndex);
							end;
						end;
					end;
				end;
			end;
		end;
		if self:getNumObjects() > 0 then
			for _, object in pairs(self.objects) do
				if object ~= nil and object.rootNode ~= nil and entityExists(object.rootNode) then
					if object.spec_fillUnit ~= nil then
						for fillTypeIndex, _ in pairs(object:getFillUnits()) do
							if object:getFillUnitFillType(fillTypeIndex) == self.fillType then
								litersHarvested = litersHarvested - object:getFillUnitFillLevel(fillTypeIndex);
							end;
						end;
					end;
				end;
			end;
		end;		
		local diff = litersHarvested - self.depositedLiters;
		if litersHarvested * data.failureCostFactor < diff then
			local _, pricePerLiter = g_additionalContractMapData:getMissionSafeSellingStationWithHighestPrice(fillType, false, {requireBulk=true});						
			
			penalty = penalty + (diff * data.failureCostOfTotal * pricePerLiter);
		end;
		self.stealingCost = penalty;
	end;
	return self.stealingCost or 0;
end;

function FieldHeapMission:getStealingCosts()	
	if self.stealingCost == 0 then
        self:calculateStealingCost();
    end;
    return self.stealingCost or 0;	
end;

function FieldHeapMission:getDetails()
	local details = FieldHeapMission:superClass().getDetails(self);	
	local stationName = nil;
	if self.pendingSellingStationId ~= nil then
		self:tryToResolveSellingStation();
	end;
	if self.sellingStation ~= nil then
		stationName = self.sellingStation:getName();
	end;
	if stationName ~= nil then
		local sellPoint = {
			["title"] = g_i18n:getText("contract_details_harvesting_sellingStation");
			["value"] = stationName;
		};
		table.insert(details, sellPoint);
	end;
	local variant = {
		["title"] = g_i18n:getText("infohud_type");
		["value"] = g_i18n:getText("action_collectibleCollect").. "/".. g_i18n:getText("ai_jobTitleDeliver");
	};
	table.insert(details, variant);	
	local fillType = {
		["title"] = g_i18n:getText("contract_details_harvesting_crop");
		["value"] = g_fillTypeManager:getFillTypeTitleByIndex(self.fillTypeIndex);
	};
	table.insert(details, fillType);
	if self.finishState ~= MissionFinishState.SUCCESS and self.status ~= MissionStatus.RUNNING then
		if FieldHeapMission.data.reward.dismiss ~= nil and FieldHeapMission.data.reward.dismiss > 0 then
			local penalty = {
				["title"] = g_i18n:getText("contract_details_penalty");
				["value"] = g_i18n:formatMoney(FieldHeapMission.data.reward.dismiss, 0, true, true);
			};
			table.insert(details, penalty);
		end;
	end;
	return details;
end;

function FieldHeapMission:getFinishedDetails()
	local finishedDetails = FieldHeapMission:superClass().getFinishedDetails(self);
	if FieldHeapMission.data.reward.dismiss ~= nil and FieldHeapMission.data.reward.dismiss > 0 then
		if self.finishState ~= MissionFinishState.SUCCESS then			
			for _, v in pairs(finishedDetails) do
				if v ~= nil and v.title ~= nil and v.title == g_i18n:getText("contract_stealing") then
					v.title = v.title.. " + ".. g_i18n:getText("contract_details_penalty");					
				end;
			end;			
		end;		
	end;
	return finishedDetails;
end;

function FieldHeapMission:getExtraProgressText()
	local stationName = "Unknown";
	if self.pendingSellingStationId ~= nil then
		self:tryToResolveSellingStation();
	end;
	if self.sellingStation ~= nil then
		stationName = self.sellingStation:getName();
	end;
	return string.format(g_i18n:getText("contract_field_harvest_nextUnloadDesc"), g_fillTypeManager:getFillTypeTitleByIndex(self.fillTypeIndex), stationName);
end;

function FieldHeapMission:fillSold(fillDelta)
	local depositedLiters = self.depositedLiters + fillDelta;
	local expectedLiters = self.expectedLiters;
	self.depositedLiters = math.min(depositedLiters, expectedLiters);
	local expected = self.expectedLiters * FieldHeapMission.data.ownTable.SUCCESS_FACTOR;
	if self.sellingStation ~= nil and expected <= self.depositedLiters then
		self.sellingStation.missions[self] = nil;
	end;
	self.lastSellChange = 30;
end;

function FieldHeapMission:getFieldHeapMissionConfigById(id)
	for _, fieldHeapMission in pairs(FieldHeapMission.MISSIONS_XML) do
		if fieldHeapMission.id == id then
			return fieldHeapMission;
		end;
	end;
	return nil;
end;

function FieldHeapMission:getFieldHeapMissionConfigByName(name)
	for _, fieldHeapMission in pairs(FieldHeapMission.MISSIONS_XML) do
		if fieldHeapMission.name == name then
			return fieldHeapMission;
		end;
	end;
	return nil;
end;

function FieldHeapMission:getFieldHeapMissionObjectConfigByIndex(index)
	for _, object in pairs(self.missionConfig.objects) do
		if object.index == index then
			return object;
		end;
	end;	
end;

function FieldHeapMission:getMaxCutLiters()	
	return self.spawnedLiters * 0.92;
end;

function FieldHeapMission:getMissionTypeName()
	return FieldHeapMission.NAME;
end;

function FieldHeapMission:validate(event)
	if not FieldHeapMission:superClass().validate(self, event) then
		return false;
	end;
	if not self:getIsFinished() then
		if not FieldHeapMission.isAvailableForField(self.field, self) then
			return false;
		end;
		if self.sellingStation ~= nil and not self.sellingStation.isRegistered then
			return false;
		end;
	end;
	return true;	
end;

--function FieldHeapMission:getIsWorkAllowed(farmId, x, z, workAreaType, vehicle)	
	
--	local workAreaTypeState = FieldHeapMission:superClass().getIsWorkAllowed(self, farmId, x, z, workAreaType, vehicle);
--	return true;	
--end;


function FieldHeapMission:onDeleteSellingStation(sellingStation)
	if sellingStation == self.sellingStation and (self.isServer and (self.status == MissionStatus.RUNNING and not g_currentMission.isExitingGame)) then
		Logging.warning("Finish field heap mission because selling station was removed");
		self:finish(MissionFinishState.FAILED);
	end;
end;

function FieldHeapMission.loadMapData(xmlFile, key, baseDirectory)	
	local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);
	data.rewardPerHa = xmlFile:getFloat(key .. "#rewardPerHa", FieldHeapMission.data.reward.PER_HA);
	data.failureCostFactor = xmlFile:getFloat(key .. "#failureCostFactor", 0.1);
	data.failureCostOfTotal = xmlFile:getFloat(key .. "#failureCostOfTotal", 0.95);
	return true;
end;

function FieldHeapMission.loadMetaDataFromXMLFile(xmlFile, key)
	if FieldHeapMission:canMaxNumSeason() then
		g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME).nextMissionDay = xmlFile:getValue(key .. "#nextDay");		
	end;
end;

function FieldHeapMission.saveMetaDataToXMLFile(xmlFile, key)
	local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);	
	if data.nextMissionDay ~= nil then
		xmlFile:setValue(key .. "#nextDay", data.nextMissionDay);
	end;	
end;

function FieldHeapMission.tryGenerateMission(addConsole, setMaxNumSeason)
	if FieldHeapMission.canRun(addConsole) then
		local field = g_fieldManager:getFieldForMission();
		if field == nil then
			if addConsole then printError("Error: No found mission field");end;
			return;
		end;
		if field.currentMission ~= nil then
			if addConsole then printError("Error: Field currentMission already");end;
			return;
		end;
		if addConsole then
		
		else
			local areaHa = field:getAreaHa();
			if areaHa < FieldHeapMission.data.ownTable.MINMAX_HA[1] or areaHa > FieldHeapMission.data.ownTable.MINMAX_HA[2] then return;end;
		end;
		local isAvailableForField, fruitTypeIndex, fillTypeIndex, infoMsg = FieldHeapMission.isAvailableForField(field, nil, addConsole);
		if not isAvailableForField or fruitTypeIndex == nil or fillTypeIndex == nil then
			if addConsole then printError("Error: No found isAvailableForField or fruitTypeIndex or fillTypeIndex ".. tostring(infoMsg));end;
			return;
		end;
		local sellingStation, highestPrice = g_additionalContractMapData:getMissionSafeSellingStationWithHighestPrice(fillTypeIndex, false, {requireBulk=true});
		if not g_additionalContractMapData:isAvailableForSellingStation(sellingStation) then
			if addConsole then printError("Error: No found selling station");end;
			return;
		end;
		local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);
		local mission = FieldHeapMission.new(true, g_client ~= nil);
		if mission:init(field, sellingStation, fruitTypeIndex, fillTypeIndex) then
			mission:setDefaultEndDate();
			if (addConsole == nil or (addConsole and setMaxNumSeason ~= nil)) and mission:canMaxNumSeason() then 
				mission:setMaxNumSeason();
				if mission:isMaxNumSeason() then					
					data.nextMissionDay = g_currentMission.environment.currentMonotonicDay + mission:getNextMissionDay();
					mission:setMaxNumSeason(0);
				end;
			end;
			return mission;
		end;
		if addConsole then printError("Error: Mission init not true");end;
		mission:delete();
	end;
	if addConsole then printError("Error: Mission type not active or disabled");end;
	return nil;
end;

function FieldHeapMission.isAvailableForField(field, mission, addConsole)
	if mission == nil then
		local fieldState = field:getFieldState();
		if not fieldState.isValid then
			return false, nil, nil, "(not isValid)";
		end;
		local fruitTypeIndex = fieldState.fruitTypeIndex;
		if fruitTypeIndex == FruitType.UNKNOWN then 
			return false, nil, nil, "(fruitTypeIndex unknown)";
		end;
		local fruitType = g_fruitTypeManager:getFruitTypeByIndex(fruitTypeIndex);		
		if fruitType == nil or fruitType:getIsCatchCrop() then			
			return false, nil, nil, "(fruitType nil or getIsCatchGrop)";
		end;
		local fillTypeIndex = g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(fruitTypeIndex);
		if FieldHeapMission.ACCEPTS_FRUITTYPE.STATE then
			if FieldHeapMission.ACCEPTS_FRUITTYPE[fruitType.name:upper()] == nil or not FieldHeapMission.ACCEPTS_FRUITTYPE[fruitType.name:upper()] then return false, nil, nil, "(ACCEPTS_FRUITTYPE false)";end;
		else
			if FieldHeapMission.NOACCEPTS_FRUITTYPE[fruitType.name:upper()] ~= nil and not FieldHeapMission.NOACCEPTS_FRUITTYPE[fruitType.name:upper()] then return false, nil, nil, "(NOACCEPTS_FRUITTYPE false)";end;
		end;
		local foundVariant, categoryName = FieldHeapMission:setVariantCategory(fillTypeIndex);
		if not foundVariant then return false, nil, nil, "(ACCEPTS_VARIANT false)";end;
		if addConsole == nil and not g_fruitTypeManager:getFruitTypeByIndex(fruitTypeIndex):getIsHarvestReady(fieldState.growthState) then
			return false, nil, nil, "(not getIsHarvestReady)";
		end;
		return true, fruitTypeIndex, fillTypeIndex;
	end;
	return true;
end;

function FieldHeapMission:setVariantCategory(fillTypeIndex)	
	if fillTypeIndex ~= nil then
		for v=1, #FieldHeapMission.ACCEPTS_VARIANT do
			for categoryName, accepts in pairs(FieldHeapMission.ACCEPTS_VARIANT[v]) do
				if accepts and g_fillTypeManager:getIsFillTypeInCategory(fillTypeIndex, categoryName) then 
					self.variant = {v, tostring(categoryName)};
					return true, categoryName;
				end;					
			end;
		end;
	end;
	return false;			
end;

function FieldHeapMission.canRun(addConsole)
	if not FieldHeapMission:getOnOff() then return false;end;
	if addConsole then return true;end;
	local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);
	if data.numInstances >= data.maxNumInstances or data.numInstances >= g_additionalContractTypes.settings[FieldHeapMission.NAME.. "_minMax"] then
		return false;
	else
		return not FieldHeapMission:canMaxNumSeason() or data.nextMissionDay == nil or g_currentMission.environment.currentMonotonicDay >= data.nextMissionDay;		
	end;
end;

function FieldHeapMission:setMaxNumInstance(maxNumInstance)
	self.uiData.settings[FieldHeapMission.NAME.. "_minMax"] = maxNumInstance;
	g_additionalContractTypes:replaceUISettings(FieldHeapMission.NAME.. "_minMax", maxNumInstance);
	local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);
	if data ~= nil then data.maxNumInstances = maxNumInstance;end;
end;

function FieldHeapMission:getMaxNumInstance()
	local data = g_missionManager:getMissionTypeDataByName(FieldHeapMission.NAME);
	if data == nil then 
		return g_additionalContractTypes.settings[FieldHeapMission.NAME.. "_minMax"];
	end;
	return data.maxNumInstances;
end;

function FieldHeapMission:getOnOff()	
	return g_additionalContractTypes.settings[FieldHeapMission.NAME.. "_minMax"] > 0;
end;

function FieldHeapMission:setOnOff(state)	
	
end;

function FieldHeapMission:canMaxNumSeason()
	return FieldHeapMission.MAX_NUM_SEASON[1] > 0;
end;

function FieldHeapMission:isMaxNumSeason(canMaxNumSeason)
	if canMaxNumSeason ~= nil and canMaxNumSeason then return FieldHeapMission:canMaxNumSeason() and FieldHeapMission.MAX_NUM_SEASON[2] >= FieldHeapMission.MAX_NUM_SEASON[1];end;
	return FieldHeapMission.MAX_NUM_SEASON[2] >= FieldHeapMission.MAX_NUM_SEASON[1];
end;

function FieldHeapMission:setMaxNumSeason(state)
	FieldHeapMission.MAX_NUM_SEASON[2] = state or FieldHeapMission.MAX_NUM_SEASON[2] + 1;	
end;

function FieldHeapMission:getNextMissionDay()
	return FieldHeapMission.MAX_NUM_SEASON[3];
end;

function FieldHeapMission:onStartMap(args)
	
end;

function FieldHeapMission:isOnStartMap(args)
	if not args.isDetiServer and g_currentMission.hlUtils.modLoaded ~= nil and g_currentMission.hlUtils.modLoaded["FS25_MissionsDisplay"] ~= nil then
		if g_currentMission.hlUtils.globalFunction["FS25_MissionsDisplay"].getBuildVersion == nil or g_currentMission.hlUtils.globalFunction["FS25_MissionsDisplay"].getBuildVersion() <= 28 then
			local isAlReadyType = false;
			local isUnknownAlReadyType = false;
			local mdMod = getfenv(0)["FS25_MissionsDisplay"];
			if mdMod ~= nil and mdMod.Missions_Display ~= nil and mdMod.Missions_Display.values ~= nil and mdMod.Missions_Display.values.missionsTypes ~= nil then
				local missionData = g_missionManager:getMissionType(FieldHeapMission.NAME);
				for missionId, missionsType in pairs(mdMod.Missions_Display.values.missionsTypes) do
					if missionsType.name == "fieldHeapMission" and missionsType.modName == "FS25_AdditionalContracts_TFU" then isAlReadyType = true;break;end;
					if missionData ~= nil and missionData.typeId ~= nil and missionData.typeId == missionsType.typId then isUnknownAlReadyType = true;break;end;					
				end;				
				if isUnknownAlReadyType or not isAlReadyType then								
					local isActive = function(args)return missionData.classObject:getOnOff(args);end;
					local changeMaxNumInstance = function(args)return missionData.classObject:getMaxNumInstance(args);end;
					local fieldHeapMissionData = {name="fieldHeapMission",jobTypName=FieldHeapMission:getJobTypName(),typId=missionData.typeId,isActive=isActive,changeMaxNumInstance=changeMaxNumInstance, overlay="conveyorBelt", modName="FS25_AdditionalContracts_TFU", view=true, total=0};
					if isUnknownAlReadyType then
						fieldHeapMissionData.view = mdMod.Missions_Display.values.missionsTypes[missionData.typeId].view;				
					end;
					mdMod.Missions_Display.values.missionsTypes[missionData.typeId] = fieldHeapMissionData;
				end;
			end;			
		end;
	end;
end;

function FieldHeapMission:loadInit()	
	if FieldHeapMission.UI_SETTINGS then		
		local isReady = false;
		for key, value in pairs(FieldHeapMission.uiData.settings) do			
			isReady = g_additionalContractTypes:setUISettings(key, value);
			if not isReady then break;end; 
		end;
		if isReady then
			for _, control in ipairs(FieldHeapMission.uiData.controls) do
				g_additionalContractTypes:setUIControls(control);			
			end;
		else
			for key, value in pairs(FieldHeapMission.uiData.settings) do
				g_additionalContractTypes:delUISettings(key);				 
			end;
		end;		
	end;
	local xmlFilename = Utils.getFilename("missions/fieldHeapMission/missions.xml", AdditionalContracts.modDir);
	if xmlFilename ~= nil and xmlFilename ~= "" then FieldHeapMission.MISSIONS_XML = g_additionalContractUtils:loadMissionsXml(xmlFilename);end;	
	xmlFile = Utils.getFilename("missionVehicles/fieldHeapMissionVehicles.xml", AdditionalContracts.modDir)
	g_missionManager:addPendingMissionVehiclesFile(xmlFile, AdditionalContracts.modDir)
end;

function FieldHeapMission:loadSettingsEvent(mission, connection, x, y, z, viewDistanceCoeff)
	if g_currentMission ~= nil and g_currentMission.missionDynamicInfo ~= nil and g_currentMission.missionDynamicInfo.isMultiplayer then
		g_client:getServerConnection():sendEvent(FieldHeapLoadSettingsEvent.new());
	end;
end;

function FieldHeapMission:changeSettingsEvent(settingsId, state)
	if g_currentMission ~= nil and g_currentMission.missionDynamicInfo ~= nil and g_currentMission.missionDynamicInfo.isMultiplayer then
		g_client:getServerConnection():sendEvent(FieldHeapChangeSettingsEvent.new(settingsId, state));
	else
		self:onChangeSettings(settingsId, state);
	end;
end;

function FieldHeapMission:onChangeSettings(settingsId, state)		
	if settingsId == "fieldHeapMission_minMax" then		
		self:setMaxNumInstance(state);		
	end;	
end;

function FieldHeapMission:loadSettingsXML(xmlFile, prefix)
	if not xmlFile:hasProperty(prefix.. ".".. FieldHeapMission.NAME.. ".minMax") then return;end;	--first start
	local minMax = xmlFile:getInt(prefix.. ".".. FieldHeapMission.NAME.. ".minMax");
	local control = g_additionalContractTypes:getUIControls(FieldHeapMission.NAME.. "_minMax");
	if minMax > control.max then minMax = control.max;elseif minMax < control.min then minMax = control.min;end;	
	self:setMaxNumInstance(minMax);
end;

function FieldHeapMission:saveSettingsXML(xmlFile, prefix)
	xmlFile:setInt(prefix.. ".".. FieldHeapMission.NAME.. ".minMax", g_additionalContractTypes.settings[FieldHeapMission.NAME.. "_minMax"]);	
end;

function FieldHeapMission:getTypDescription()

end;

function FieldHeapMission:setTypDescription()
	local xmlTypDescription = g_i18n:getText(self.missionConfig.description);
	if xmlTypDescription == nil or xmlTypDescription:len() <= 0 or string.find(xmlTypDescription:lower(), "missing") then return;end;
	
	
	if self:getNumTools() + self:getNumObjects() > 0 then
		xmlTypDescription = xmlTypDescription.. g_i18n:getText("contract_field_heap_description_tools");
		local missionTyp = tostring(self.missionConfig.typ);
		if missionTyp ~= nil and missionTyp:len() > 0 then			
			if string.find(missionTyp, "packingAndDeliver") then missionTyp = "packing";end;
			local toolsDescription = g_i18n:getText("contract_field_heap_description_tools_".. tostring(missionTyp));
			if toolsDescription ~= nil and not string.find(toolsDescription:lower(), "missing") then
				local toolsDescriptionMultiple = g_i18n:getText("contract_field_heap_description_tools_".. tostring(missionTyp).. "_multiple");
				if toolsDescriptionMultiple ~= nil and not string.find(toolsDescriptionMultiple:lower(), "missing") then 
					xmlTypDescription = xmlTypDescription.. toolsDescriptionMultiple;
				else
					xmlTypDescription = xmlTypDescription.. toolsDescription;
				end;
			end;
		end;
		
	end;
	self.description = self.description.. " ".. xmlTypDescription;	
end;

function FieldHeapMission:getJobTypName()
	return self.data.jobTypName;
end;

function FieldHeapMission:getMissionTypes()
	local missionData = g_missionManager:getMissionType(FieldHeapMission.NAME);
	local isActive = function(args)return missionData.classObject:getOnOff(args);end;
	local changeMaxNumInstance = function(args)return missionData.classObject:getMaxNumInstance(args);end;
	local fieldHeapMissionData = {name="fieldHeapMission",jobTypName=FieldHeapMission:getJobTypName(),typId=missionData.typeId,isActive=isActive,changeMaxNumInstance=changeMaxNumInstance, overlay="conveyorBelt", modName="FS25_AdditionalContracts_TFU", view=true};
	return fieldHeapMissionData;
end;

function FieldHeapMission:getClassTypOverlay(args)
	local classOverlay = "tractor";
	local typOverlay = "conveyorBelt";
	if self.data.ownTable.classOverlay ~= nil then classOverlay = self.data.ownTable.classOverlay;end;
	if self.data.ownTable.typOverlay ~= nil then typOverlay = self.data.ownTable.typOverlay;end;	
	return classOverlay, typOverlay;
end;

g_additionalContractTypes:registerTyp(FieldHeapMission, FieldHeapMission.NAME, true);

