FruitCollectMission = {};
FruitCollectMission.NAME = "fruitCollectMission";
FruitCollectMission.DELETE_SWAD = true; --for debug testing
FruitCollectMission.UI_SETTINGS = true;
FruitCollectMission.MAX_NUM_INSTANCE = 2;
FruitCollectMission.DEFAULT_NUM_INSTANCE = 2;
FruitCollectMission.MAX_NUM_SEASON = {3,0,1}; --0=off or max,is,nextDay
FruitCollectMission.BALANCE = {false,0}; --on/off,is
FruitCollectMission.RANDOM = false; --at random pool
FruitCollectMission.ACCEPTS_FRUITTYPE = {SUGARBEET=true,BEETROOT=true,CARROT=true,PARSNIP=true,POTATO=true};
FruitCollectMission.VEHICLE_VARIANT = {
	["VEGETABLES"] = {SUGARBEET=1,BEETROOT=1,CARROT=1,PARSNIP=1,POTATO=1};
	["GRAIN"] = {SOYBEAN=1,OAT=1};
};

source(AdditionalContracts.modDir.. "missions/fruitCollectMission/FruitCollectChangeSettingsEvent.lua");
source(AdditionalContracts.modDir.. "missions/fruitCollectMission/FruitCollectLoadSettingsEvent.lua");

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

FruitCollectMission.data = {	
	ownTable = {FIELD_SIZE_BIGLARGE=50, SUCCESS_FACTOR=0.95, VEHICLE_USE_COST=120, classOverlay="harvester_other", typOverlay="trailer"}; --big large more vehicles, 4x ? Mod Maps... / make it cheaper vehicle cost
	reward = {dismiss=0, min=0, PER_HA=3200}; 
	jobTypName = g_i18n:getText("contract_field_fruitCollect_title");	
};

FruitCollectMission.metadata = {
	interface = "FS25 ...", --new
	title = "Fruit Collect Contracts",
	notes = "Dieser Mod generiert Ernte/Frucht sammel Aufträge.",
	author = "(by HappyLooser)",
	build = 3,
	datum = "17.08.2025",
	update = "01.11.2025",
	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"
};

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

local FruitCollectMission_mt = Class(FruitCollectMission, AbstractFieldMission)
InitObjectClass(FruitCollectMission, "FruitCollectMission")

function FruitCollectMission.registerXMLPaths(schema, key)
	FruitCollectMission: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 FruitCollectMission.registerSavegameXMLPaths(schema, key)
	FruitCollectMission: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 .. "#finishedSwathSpawning", "If fruit spawning is finished")
	local fruitCollectKey = string.format("%s.fruitCollect", key);
	schema:register(XMLValueType.STRING, fruitCollectKey .. "#fruitType", "Name of the fruit type");
	schema:register(XMLValueType.STRING, fruitCollectKey .. "#fillType", "Name of the fill type");
	schema:register(XMLValueType.FLOAT, fruitCollectKey .. "#expectedLiters", "Expected liters");
	schema:register(XMLValueType.FLOAT, fruitCollectKey .. "#depositedLiters", "Deposited liters");
	schema:register(XMLValueType.STRING, fruitCollectKey .. "#sellingStationPlaceableUniqueId", "Unique id of the selling point");
	schema:register(XMLValueType.INT, fruitCollectKey .. "#unloadingStationIndex", "Index of the unloading station");
end;

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

function FruitCollectMission.new(isServer, isClient, customMt)	
	local title = g_i18n:getText("contract_field_fruitCollect_title");
	local description = g_i18n:getText("contract_field_fruitCollect_description");
	local self = AbstractFieldMission.new(isServer, isClient, title, description, customMt or FruitCollectMission_mt);
	self.finishedSwathSpawning = false;
	self.initializedSwathSpawning = 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;
	return self;
end;

function FruitCollectMission:init(field, sellingStation, fruitTypeIndex, fillTypeIndex)
	self.fruitTypeIndex = fruitTypeIndex;
	self.fillTypeIndex = fillTypeIndex;
	local success = FruitCollectMission:superClass().init(self, field);
	self:setSellingStation(sellingStation);
	self:setMinReward();	
	return success;	
end;

function FruitCollectMission:onSavegameLoaded()
	if self.field == nil then
		Logging.error("Field is not set for fruit collect 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 fruit collect 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 fruit collect mission", self.unloadingStationIndex, sellingStation.configFileName);
				g_missionManager:markMissionForDeletion(self);
				return;
			end;
			self:setSellingStation(unloadingStation);
			if self:getWasStarted() then
				unloadingStation.missions[self] = self;
			end;
		end;
		FruitCollectMission: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 fruit collect mission", fruitTypeName, self.field:getName());
		g_missionManager:markMissionForDeletion(self);
	end;
end;

function FruitCollectMission:delete()
	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;
	FruitCollectMission:superClass().delete(self);
end;

function FruitCollectMission:writeStream(streamId, connection)
	FruitCollectMission: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);
end;

function FruitCollectMission:readStream(streamId, connection)
	FruitCollectMission: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);
end;

function FruitCollectMission:saveToXMLFile(xmlFile, key)	
	xmlFile:setValue(key .. "#spawnedLiters", self.spawnedLiters)
	if not self.finishedSwathSpawning and self.currentSwathSegmentIndex ~= nil then
		xmlFile:setValue(key .. "#swathSegmentIndex", self.currentSwathSegmentIndex);
	end;
	xmlFile:setValue(key .. "#finishedSwathSpawning", self.finishedSwathSpawning);
	local fruitCollectKey = string.format("%s.fruitCollect", key);
	--xmlFile:setValue(fruitCollectKey .. "#fruitType", g_fruitTypeManager.fruitTypes[self.fruitTypeIndex].name);
	xmlFile:setValue(fruitCollectKey .. "#fruitType", g_fruitTypeManager:getFruitTypeNameByIndex(self.fruitTypeIndex)); --patch 1.14 ?
	--xmlFile:setValue(fruitCollectKey .. "#fillType",	g_fillTypeManager.fillTypes[self.fillTypeIndex].name);
	xmlFile:setValue(fruitCollectKey .. "#fillType",	g_fillTypeManager:getFillTypeNameByIndex(self.fillTypeIndex)); --patch 1.14 ?
	xmlFile:setValue(fruitCollectKey .. "#expectedLiters", self.expectedLiters);
	xmlFile:setValue(fruitCollectKey .. "#depositedLiters", self.depositedLiters);	
	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 fruit collect mission \'%s\' ", sellingStationName, fruitCollectKey);
			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 fruit collect mission \'%s\' ", sellingStationName, fruitCollectKey);
			return;
		end;
		xmlFile:setValue(fruitCollectKey .. "#sellingStationPlaceableUniqueId", sellingStationPlaceable:getUniqueId());
		xmlFile:setValue(fruitCollectKey .. "#unloadingStationIndex", unloadingStationIndex);
	end;
	FruitCollectMission:superClass().saveToXMLFile(self, xmlFile, key);
end;

function FruitCollectMission:loadFromXMLFile(xmlFile, key)
	self.spawnedLiters = xmlFile:getValue(key .. "#spawnedLiters", self.spawnedLiters);
	self.currentSwathSegmentIndex = xmlFile:getValue(key .. "#swathSegmentIndex", self.currentSwathSegmentIndex);
	self.finishedSwathSpawning = xmlFile:getValue(key .. "#finishedSwathSpawning", self.finishedSwathSpawning);
	if self.finishedSwathSpawning then
		self.initializedSwathSpawning = true;
	end;
	local fruitCollectKey = string.format("%s.fruitCollect", key);
	local fruitTypeName = xmlFile:getValue(fruitCollectKey.."#fruitType");	
	self.fruitTypeIndex = g_fruitTypeManager:getFruitTypeIndexByName(fruitTypeName);
	local fillTypeName = xmlFile:getValue(fruitCollectKey.."#fillType");
	self.fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName);
	self.expectedLiters = xmlFile:getValue(fruitCollectKey .. "#expectedLiters", self.expectedLiters);
	self.depositedLiters = xmlFile:getValue(fruitCollectKey .. "#depositedLiters", self.depositedLiters);	
	if not FruitCollectMission:superClass().loadFromXMLFile(self, xmlFile, key) then
		return false;
	end;
	if not self:getIsFinished() then
		local sellingStationPlaceableUniqueId = xmlFile:getValue(fruitCollectKey .. "#sellingStationPlaceableUniqueId");
		if sellingStationPlaceableUniqueId == nil then
			Logging.xmlError(xmlFile, "No sellingStationPlaceable uniqueId given for fruit collect mission at \'%s\'", fruitCollectKey);
			return false;
		end;
		local unloadingStationIndex = xmlFile:getValue(fruitCollectKey .. "#unloadingStationIndex");
		if unloadingStationIndex == nil then
			Logging.xmlError(xmlFile, "No unloadting station index given for fruit collect mission at \'%s\'", fruitCollectKey);
			return false;
		end;
		self.sellingStationPlaceableUniqueId = sellingStationPlaceableUniqueId;
		self.unloadingStationIndex = unloadingStationIndex;
	end;
	if FruitCollectMission:canMaxNumSeason() then self:setMaxNumSeason();end;
	return true;
end;

function FruitCollectMission: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);
	if fruitTypeDesc.harvestGroundType ~= nil then
		fieldPreparingTask:setGroundType(fruitTypeDesc.harvestGroundType);
	end;
	return fieldPreparingTask;
end;

function FruitCollectMission:getIsPrepared()
	if FruitCollectMission:superClass().getIsPrepared(self) then
		return self.finishedSwathSpawning;
	else
		return false;
	end;
end;

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

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

function FruitCollectMission:update(dt)
	if self.isServer and (self.status == MissionStatus.PREPARING and (self.fieldPreparingTask == nil or self.fieldPreparingTask:getIsFinished())) and not self.initializedSwathSpawning then
		local fruitTypeDesc = g_fruitTypeManager:getFruitTypeByIndex(self.fruitTypeIndex);
		local litersPerSqm = fruitTypeDesc.literPerSqm;
		local fieldSizeHa = self.field:getAreaHa();
		local minLitersPerSqm = MathUtil.haToSqm(fieldSizeHa) * litersPerSqm;
		local workingWidth = fieldSizeHa > 6 and 12 or 9;
		local x, z = self.field:getCenterOfFieldWorldPosition();
		local fieldCourseSettings = FieldCourseSettings.new();
		fieldCourseSettings.implementWidth = workingWidth;
		fieldCourseSettings.numHeadlands = 2;
		local swathSegmentIndex = 0;
		local function segmentFunc(sx, sz, ex, ez, _, _, _, totalLength)			
			swathSegmentIndex = swathSegmentIndex + 1;
			if self.currentSwathSegmentIndex == nil or swathSegmentIndex > self.currentSwathSegmentIndex then
				local length = MathUtil.vector2Length(ex - sx, ez - sz);
				local fillTypeIndex = self.fillTypeIndex;
				local delta = length * (minLitersPerSqm / totalLength);
				local fillDelta, _ = DensityMapHeightUtil.tipToGroundAroundLine(self, delta, fillTypeIndex, sx, 0, sz, ex, 0, ez, 0.5, 0.5, 0, false, nil, false, true);
				self.spawnedLiters = self.spawnedLiters + fillDelta;
				self.currentSwathSegmentIndex = swathSegmentIndex;
			end;
		end;
		FieldCourseIterator.new(x, z, fieldCourseSettings, segmentFunc, function()			
			self.finishedSwathSpawning = true;
		end)
		self.initializedSwathSpawning = 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 * AbstractMission.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;	
	FruitCollectMission:superClass().update(self, dt);
end;

function FruitCollectMission: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 FruitCollectMission: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 FruitCollectMission: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 FruitCollectMission:addHotspots()
	FruitCollectMission:superClass().addHotspots(self);
	self.addSellingStationHotSpot = true;
	if self.sellingStationMapHotspot ~= nil then
		g_currentMission:addMapHotspot(self.sellingStationMapHotspot);
	end;
end;

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

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

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

function FruitCollectMission:getCompletion()
	local completion;
	if self.expectedLiters > 0 then
		local sellCompletion = self.depositedLiters / self.expectedLiters / FruitCollectMission.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 FruitCollectMission:getRewardPerHa()
	return g_missionManager:getMissionTypeDataByName(FruitCollectMission.NAME).rewardPerHa;
end;

function FruitCollectMission:getReward()
	local data = g_missionManager:getMissionTypeDataByName(FruitCollectMission.NAME);
	local difficultyMultiplier = 1.3 - 0.1 * g_currentMission.missionInfo.economicDifficulty;
	return FruitCollectMission:superClass().getReward(self) + data.rewardPerHa * difficultyMultiplier;
end;

function FruitCollectMission:setMinReward() 
		
end;

function FruitCollectMission:setVehicleVariant() 
	local fillTypeName = g_fillTypeManager:getFillTypeNameByIndex(self.fillTypeIndex);
	self.vehicleVariant = nil;
	for variant, fillTypes in pairs (FruitCollectMission.VEHICLE_VARIANT) do
		if self.vehicleVariant ~= nil then break;end;
		for fillTypesName, variantGroup in pairs (fillTypes) do
			if fillTypesName == fillTypeName then
				self.vehicleVariant = tostring(variant).. "_".. tostring(variantGroup);
				break;
			end;
		end;
	end;
	if self.vehicleVariant == nil then self.vehicleVariant = "GRAIN_1";end;
end;

function FruitCollectMission:getVehicleVariant()
	self:setVehicleVariant();
	if self.field:getAreaHa() >= FruitCollectMission.data.ownTable.FIELD_SIZE_BIGLARGE then return "BIGLARGE_".. self.vehicleVariant;end; --big large multiple vehicles, 4x Mod Maps
	return self.vehicleVariant;
end;

function FruitCollectMission: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 * FruitCollectMission.data.ownTable.VEHICLE_USE_COST * difficultyMultiplier;
end;

function FruitCollectMission:getStealingCosts()
	if self.finishState ~= MissionFinishState.SUCCESS and self.isServer then
		local data = g_missionManager:getMissionTypeDataByName(FruitCollectMission.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;	
		local diff = litersHarvested - self.depositedLiters;
		if litersHarvested * data.failureCostFactor < diff then
			local _, pricePerLiter = g_additionalContractMapData:getSellingStationWithHighestPrice(fillType);
			local dismiss = Utils.getNoNil(self.data.reward.dismiss, 0); --penalty			
			return (diff * data.failureCostOfTotal * pricePerLiter) + dismiss;
		end;
	end;
	return 0;
end;

function FruitCollectMission:getDetails()
	local details = FruitCollectMission: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 fillType = {
		["title"] = g_i18n:getText("contract_details_harvesting_crop");
		["value"] = g_fillTypeManager:getFillTypeTitleByIndex(self.fillTypeIndex);
	};
	table.insert(details, fillType);
	return details;
end;

function FruitCollectMission: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 FruitCollectMission:fillSold(fillDelta)
	local depositedLiters = self.depositedLiters + fillDelta;
	local expectedLiters = self.expectedLiters;
	self.depositedLiters = math.min(depositedLiters, expectedLiters);
	local expected = self.expectedLiters * AbstractMission.SUCCESS_FACTOR;
	if self.sellingStation ~= nil and expected <= self.depositedLiters then
		self.sellingStation.missions[self] = nil;
	end;
	self.lastSellChange = 30;
end;

function FruitCollectMission:getMaxCutLiters()
	return self.spawnedLiters * 0.89;
end;

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

function FruitCollectMission:validate(event)
	if not FruitCollectMission:superClass().validate(self, event) then
		return false;
	end;
	if not self:getIsFinished() then
		if not FruitCollectMission.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 FruitCollectMission:getIsWorkAllowed(farmId, x, z, workAreaType, vehicle)	
	return true;	
end;

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

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

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

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

function FruitCollectMission.tryGenerateMission(addConsole, setMaxNumSeason)
	if FruitCollectMission.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;
		local isAvailableForField, fruitTypeIndex, fillTypeIndex = FruitCollectMission.isAvailableForField(field, nil);
		if not isAvailableForField or fruitTypeIndex == nil or fillTypeIndex == nil then
			if addConsole then printError("Error: No found isAvailableForField or fruitTypeIndex or fillTypeIndex");end;
			return;
		end;
		local sellingStation, highestPrice = g_additionalContractMapData:getSellingStationWithHighestPrice(fillTypeIndex);
		if not g_additionalContractMapData:isAvailableForSellingStation(sellingStation) then
			if addConsole then printError("Error: No found selling station");end;
			return;
		end;
		local data = g_missionManager:getMissionTypeDataByName(FruitCollectMission.NAME);
		local mission = FruitCollectMission.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 FruitCollectMission.isAvailableForField(field, mission)
	if mission == nil then
		local fieldState = field:getFieldState();
		if not fieldState.isValid then
			return false;
		end;
		local fruitTypeIndex = fieldState.fruitTypeIndex;
		if fruitTypeIndex == FruitType.UNKNOWN then 
			return false;
		end;
		local fruitType = g_fruitTypeManager:getFruitTypeByIndex(fruitTypeIndex);		
		if fruitType == nil or fruitType:getIsCatchCrop() then			
			return false;
		end;
		if FruitCollectMission.ACCEPTS_FRUITTYPE[fruitType.name] == nil or not FruitCollectMission.ACCEPTS_FRUITTYPE[fruitType.name] then
			return false;		
		end;		
		if not g_fruitTypeManager:getFruitTypeByIndex(fruitTypeIndex):getIsHarvestReady(fieldState.growthState) then
			return false;
		end;
		return true, fruitTypeIndex, g_fruitTypeManager:getFillTypeIndexByFruitTypeIndex(fruitTypeIndex);
	end;
	return true;
end;

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

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

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

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

function FruitCollectMission:setOnOff(state)	
	
end;

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

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

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

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

function FruitCollectMission:onStartMap(args)
	
end;

function FruitCollectMission: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(FruitCollectMission.NAME);
				for missionId, missionsType in pairs(mdMod.Missions_Display.values.missionsTypes) do
					if missionsType.name == "fruitCollectMission" and missionsType.modName == "FS25_AdditionalContracts" 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 fruitCollectMissionData = {name="fruitCollectMission",jobTypName=FruitCollectMission:getJobTypName(),typId=missionData.typeId,isActive=isActive,changeMaxNumInstance=changeMaxNumInstance, overlay="harvester_other", modName="FS25_AdditionalContracts", view=true, total=0};
					if isUnknownAlReadyType then
						fruitCollectMissionData.view = mdMod.Missions_Display.values.missionsTypes[missionData.typeId].view;				
					end;
					mdMod.Missions_Display.values.missionsTypes[missionData.typeId] = fruitCollectMissionData;
				end;
			end;			
		end;
	end;
end;

function FruitCollectMission:loadInit()	
	if FruitCollectMission.UI_SETTINGS then		
		local isReady = false;
		for key, value in pairs(FruitCollectMission.uiData.settings) do			
			isReady = g_additionalContractTypes:setUISettings(key, value);
			if not isReady then break;end; 
		end;
		if isReady then
			for _, control in ipairs(FruitCollectMission.uiData.controls) do
				g_additionalContractTypes:setUIControls(control);			
			end;
		else
			for key, value in pairs(FruitCollectMission.uiData.settings) do
				g_additionalContractTypes:delUISettings(key);				 
			end;
		end;		
	end;
	local xmlFile = Utils.getFilename("missionVehicles/fruitCollectMissionVehicles.xml", AdditionalContracts.modDir)
	g_missionManager:addPendingMissionVehiclesFile(xmlFile, AdditionalContracts.modDir)
end;

function FruitCollectMission: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(FruitCollectLoadSettingsEvent.new());
	end;
end;

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

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

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

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

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

function FruitCollectMission:getMissionTypes()
	local missionData = g_missionManager:getMissionType(FruitCollectMission.NAME);
	local isActive = function(args)return missionData.classObject:getOnOff(args);end;
	local changeMaxNumInstance = function(args)return missionData.classObject:getMaxNumInstance(args);end;
	local fruitCollectMissionData = {name="fruitCollectMission",jobTypName=FruitCollectMission:getJobTypName(),typId=missionData.typeId,isActive=isActive,changeMaxNumInstance=changeMaxNumInstance, overlay="harvester_other", modName="FS25_AdditionalContracts", view=true};
	return fruitCollectMissionData;
end;

function FruitCollectMission:getClassTypOverlay(args)
	local classOverlay = "harvester_other";
	local typOverlay = "trailer";
	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(FruitCollectMission, FruitCollectMission.NAME, true);

