--[[ threshingFlowIndicator 

Author: 		HoFFi (modding-welt.com)
Remarks:		Thanks to Zetor6245 for testing and providing his Claas Jaguar 800 Pack as guinea pig
				Thanks to antonis78 who had the initial idea for this script and who gave me the motivation to continue with lua


Description: 	script to visualize the current threshing flow

Version: 		1.0.2.0

Changelog: 		2019-08-08 	- initial release
				2019-08-12 	- added more complex way of showing the current load (indicator bar with 13 lights)
				2019-08-27 	- hud / help window text, has been improved
							- cutter load depends on…: fruit type, current speed, used width of cutter
							- max. speed of each fruit is easily adjustable in below
							- engine dies, warn sound is played and warning text is showed when driving too fast (allowed fruit speed + tolerance)
							- tolerance adjustable via xml (default 2kmh/mph)
							- “hardStop” (engine dies) can be turned off in xml


--------------------------------------------------------------------------------------------------

XML:
	<threshingFlowIndicator indicatorBarNode="name of bar in i3d" showInHud="true" hardStop="true" maxSpeedOffset="2">
		<lights light1="greenstarON01" light2="greenstarON02" light3="greenstarON03" light4="greenstarON04" light5="greenstarON05" light6="greenstarON06" light7="greenstarON07" light8="greenstarON08" light9="greenstarON09" light10="greenstarON10" light11="greenstarON11" light12="greenstarON12" light13="greenstarON13" />
	</threshingFlowIndicator>
	
Moddesc:
	<specializations>
        <specialization name="threshingFlowIndicator" className="threshingFlowIndicator" filename="threshingFlowIndicator.lua"/>
    </specializations>
	
	<vehicleTypes>
		<type name="newVehicleTypeName" parent="...." filename="$dataS/scripts/vehicles/Vehicle.lua">
			....
			<specialization name="threshingFlowIndicator" />
		</type>
	</vehicleTypes>
	
	<l10n>
		<text name="TFIspeed"><en>Max. speed:</en><de>Max. Geschw.:</de></text>
		<text name="TFIcutterLoad"><en>Load (cutter):</en><de>Auslastung (Schneidwerk):</de></text>
		<text name="TFItooFast"><en>You drove too fast. Engine died.</en><de>Du bist zu schnell gefahren. Motor abgesoffen.</de></text>
	</l10n>
	
Explaination:
	indicatorBarNode = visual object (bar) to be scaled
	showInHud = if true, current flow will be displayed in hud (left upper corner)
	lightsRootNode = parent object of light objects 
	hardStop = if true, motor stops if current speed is higher than allowed maximum speed for a fruit + maxSpeedOffset
	maxSpeedOffset = this value is added to the maximum allowed speed per fruit as tolerance before motor stops

]]


threshingFlowIndicator = {};

threshingFlowIndicator.modDir = g_currentModDirectory;
threshingFlowIndicator.currentModName = g_currentModName;

function threshingFlowIndicator.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Combine, specializations); 
end;

function threshingFlowIndicator.registerEventListeners(vehicleType)
	for _, spec in pairs({"onLoad", "onDelete", "onUpdate", "onDraw", "onReadStream", "onWriteStream"}) do
		SpecializationUtil.registerEventListener(vehicleType, spec, threshingFlowIndicator)
	end
end

function threshingFlowIndicator:onLoad(savegame)
	--Please only change the following values
	self.maxSpeedBARLEY = 8
	self.maxSpeedCANOLA = 8
	self.maxSpeedCOTTON = 6
	self.maxSpeedDRYGRASS = 11
	self.maxSpeedGRASS = 11
	self.maxSpeedGRASSWINDROW = 11
	self.maxSpeedMAIZE = 12
	self.maxSpeedOAT = 8
	self.maxSpeedPOPPLAR = 6    --not supported so far
	self.maxSpeedPOTATO = 6    --not tested
	self.maxSPeedSTRAW = 12
	self.maxSpeedSUGARBEET = 6    --not tested
	self.maxSpeedSUGARCANE = 6    --not tested
	self.maxSpeedSUNFLOWER = 11
	self.maxSpeedWHEAT = 8
	self.maxSpeedSOYBEAN = 10
	--Please DO NOT change the following values
	self.currentFruitSpeedLimit = 10
	self.lastFT = FruitType.UNKNOWN

	self.indicatorBarNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator#indicatorBarNode"), self.i3dMappings);
	self.showInHud = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.threshingFlowIndicator#showInHud"), true);
	self.hardStop = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.threshingFlowIndicator#hardStop"), true);
	self.maxSpeedOffset = Utils.getNoNil(getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator#maxSpeedOffset"), "2");
	self.currentFlow = 0
	self.loadWarnSamplePlayed = false
	self.unloadSamplePlayed = false
	self.light1 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light1"), self.i3dMappings);
	self.light2 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light2"), self.i3dMappings);
	self.light3 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light3"), self.i3dMappings);
	self.light4 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light4"), self.i3dMappings);
	self.light5 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light5"), self.i3dMappings);
	self.light6 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light6"), self.i3dMappings);
	self.light7 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light7"), self.i3dMappings);
	self.light8 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light8"), self.i3dMappings);
	self.light9 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light9"), self.i3dMappings);
	self.light10 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light10"), self.i3dMappings);
	self.light11 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light11"), self.i3dMappings);
	self.light12 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light12"), self.i3dMappings);
	self.light13 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light13"), self.i3dMappings);
	self.light14 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light14"), self.i3dMappings);
	self.light15 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light15"), self.i3dMappings);
	self.light16 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light16"), self.i3dMappings);
	self.light17 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light17"), self.i3dMappings);
	self.light18 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light18"), self.i3dMappings);
	self.light19 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#light19"), self.i3dMappings);
	--self.gpsReceiver = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#gpsReceiver"), self.i3dMappings);
	--self.gpsSteering = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#gpsSteering"), self.i3dMappings);
	self.unloadingIcon = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.threshingFlowIndicator.lights#unloadingIcon"), self.i3dMappings);
	self.flowprc = VehicleHudUtils.loadHud(self, self.xmlFile, "flowPercent");
	self.loadprc = VehicleHudUtils.loadHud(self, self.xmlFile, "loadPercent");
	self.sampleWarn = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.threshingFlowIndicator", "warnSound", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self)
end;


function threshingFlowIndicator:onUpdate(dt)
local fileName = Utils.getFilename( "sounds/warnair.ogg", self.baseDirectory )
			threshingFlowIndicator.snapOnSample = createSample("LoadWarning")
			loadSample(threshingFlowIndicator.snapOnSample, fileName, false)
	local fileName = Utils.getFilename( "sounds/unload.ogg", self.baseDirectory )
			threshingFlowIndicator.unloadSample = createSample("unloadWarning")
			loadSample(threshingFlowIndicator.unloadSample, fileName, false)		
--[[local spec = self.spec_globalPositioningSystem
    setVisibility(self.gpsReceiver, false);
	setVisibility(self.gpsSteering, false);
    if self:getIsMotorStarted() then
   	   if self:getHasGuidanceSystem() then
	     setVisibility(self.gpsReceiver, true);
	   end;
	   if spec.guidanceSteeringIsActive then
		 setVisibility(self.gpsSteering, true);
	   end;   
	end;
	]]
    local spec = self.spec_dischargeable
	--setVisibility(self.unloadingIcon, false);
	if self:getIsMotorStarted() then
       if spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT then
		 
		 if not self.unloadSamplePlayed then
                        playSample(threshingFlowIndicator.unloadSample, 1, 0.2, 0, 0, 0);
                        self.unloadSamplePlayed = true
                    end
                else
                    self.unloadSamplePlayed = false
					
                end
		 
		end;
	setVisibility(self.unloadingIcon, spec.currentDischargeState == Dischargeable.DISCHARGE_STATE_OBJECT);
	local spec = self.spec_motorized
				local loadPercentage = spec.motor:getMotorAppliedTorque() / math.max(spec.motor:getMotorAvailableTorque(), 0.0001)
        spec.actualLoadPercentage = loadPercentage
        if spec.actualLoadPercentage > spec.smoothedLoadPercentage then
            spec.smoothedLoadPercentage = 0.95*spec.smoothedLoadPercentage + 0.05*loadPercentage
        else
            spec.smoothedLoadPercentage = 0.98*spec.smoothedLoadPercentage + 0.02*loadPercentage
        end
					if self:getIsMotorStarted() then
            if self.loadprc ~= nil then
				if spec.smoothedLoadPercentage ~= nil then
					
					VehicleHudUtils.setHudValue(self, self.loadprc, spec.smoothedLoadPercentage * 106, 100); 
				end;
			end;
		end;  
								
	self.currentFruitSpeedLimit = 10;
	local spec = self.spec_combine
    if spec ~= nil then
		setVisibility(self.light1, false);
		setVisibility(self.light2, false);
		setVisibility(self.light3, false);
		setVisibility(self.light4, false);
		setVisibility(self.light5, false);
		setVisibility(self.light6, false);
		setVisibility(self.light7, false);
		setVisibility(self.light8, false);
		setVisibility(self.light9, false);
		setVisibility(self.light10, false);
		setVisibility(self.light11, false);
		setVisibility(self.light12, false);
		setVisibility(self.light13, false);
		setVisibility(self.light14, false);
		setVisibility(self.light15, false);
		setVisibility(self.light16, false);
		setVisibility(self.light17, false);
		setVisibility(self.light18, false);
		setVisibility(self.light19, false);
		setScale(self.indicatorBarNode, 1, 1, 0);
        if spec.numAttachedCutters > 0 then
			self.lastFT = spec.lastValidInputFruitType
            for cutter, _ in pairs(spec.attachedCutters) do
                if cutter.getCutterLoad ~= nil then
					if self.lastFT == FillType.BARLEY then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedBARLEY + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedBARLEY
						setVisibility(self.light16, true);
					elseif self.lastFT == FillType.CANOLA then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedCANOLA + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedCANOLA
						setVisibility(self.light18, true);
					elseif self.lastFT == FillType.COTTON then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedCOTTON + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedCOTTON
					elseif self.lastFT == FillType.DRYGRASS then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedDRYGRASS + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedDRYGRASS
					elseif self.lastFT == FillType.GRASS then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedGRASS + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedGRASS
					elseif self.lastFT == FillType.GRASSWINDROW then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedGRASSWINDROW + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedGRASSWINDROW
					elseif self.lastFT == FillType.MAIZE then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedMAIZE + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedMAIZE
						setVisibility(self.light14, true);
					elseif self.lastFT == FillType.OAT then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedOAT + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedOAT
					elseif self.lastFT == FillType.POPPLAR then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedPOPPLAR + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedPOPPLAR
					elseif self.lastFT == FillType.POTATO then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedPOTATO + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedPOTATO
					elseif self.lastFT == FillType.STRAW then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedSTRAW + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedSUGARBEET
					elseif self.lastFT == FillType.SUGARBEET then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedSUGARBEET + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedSUGARBEET
					elseif self.lastFT == FillType.SUGARCANE then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedSUGARCANE + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedSUGARCANE
					elseif self.lastFT == FillType.SUNFLOWER then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedSUNFLOWER + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedSUNFLOWER
						setVisibility(self.light17, true);
					elseif self.lastFT == FillType.WHEAT then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedWHEAT + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedWHEAT
					elseif self.lastFT == FillType.SOYBEAN then
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (self.maxSpeedSOYBEAN + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = self.maxSpeedSOYBEAN	
						setVisibility(self.light19, true);	
					else
						self.currentFlow = cutter:getCutterLoad() * (self:getLastSpeed() / (10 + self.maxSpeedOffset))
						self.currentFruitSpeedLimit = 10
						self.lastFT = FruitType.UNKNOWN
						setVisibility(self.light16, true);
					end
						
					-- scale indicator bar according to current cutter flow
					if self.currentFlow > 1 then
						setScale(self.indicatorBarNode, 1, 1, 1);
					else
						setScale(self.indicatorBarNode, 1, 1, self.currentFlow);
					end
			
					-- turn on idicator LEDs according to current cutter flow
					if self.currentFlow > 0.09 then
						setVisibility(self.light1, true);
					end
					if self.currentFlow >= 0.18 then
						setVisibility(self.light2, true);
					end
					if self.currentFlow >= 0.27 then
						setVisibility(self.light3, true);
					end
					if self.currentFlow >= 0.36 then
						setVisibility(self.light4, true);
					end
					if self.currentFlow >= 0.45 then
						setVisibility(self.light5, true);
					end
					if self.currentFlow >= 0.54 then
						setVisibility(self.light6, true);
					end
					if self.currentFlow >= 0.63 then
						setVisibility(self.light7, true);
					end
					if self.currentFlow >= 0.72 then
						setVisibility(self.light8, true);
					end
					if self.currentFlow >= 0.81 then
						setVisibility(self.light9, true);
					end
					if self.currentFlow >= 0.9 then
						setVisibility(self.light10, true);
					end
					if self.currentFlow >= 0.93 then
						setVisibility(self.light11, true);
					end
					if self.currentFlow >= 0.96 then
						setVisibility(self.light12, true);
					end
					if self.currentFlow >= 0.96 then
						setVisibility(self.light13, true);
					end
				
					  if self:getIsEntered()
			and self:getIsVehicleControlledByPlayer() then

                     if self.currentFlow > 0.96 and self.currentFlow < 0.968 then
                    if not self.loadWarnSamplePlayed then
                        playSample(threshingFlowIndicator.snapOnSample, 1, 0.2, 0, 0, 0);
                        self.loadWarnSamplePlayed = true
                    end
                else
                    self.loadWarnSamplePlayed = false
					end
					end
					if self:getIsMotorStarted() then
            if self.flowprc ~= nil then
				if self.currentFlow ~= nil then
					
					VehicleHudUtils.setHudValue(self, self.flowprc, self.currentFlow * 100, 100);
				end;
			end;
		end;  
					-- stop Motor if flow is too high
					if self.hardStop and self:getIsTurnedOn() then
						if self.currentFruitSpeedLimit ~= nil and self.currentFruitSpeedLimit > 0 then
							if self:getLastSpeed() > self.currentFruitSpeedLimit + self.maxSpeedOffset then
								if self.currentFlow > 1 then -- prevent motor from dieing if no fruit is been processed currently
								if self.getCruiseControlState ~= nil then
										if self:getCruiseControlState() == Drivable.CRUISECONTROL_STATE_ACTIVE then
											local spec = self.spec_drivable
											spec.cruiseControl.speed = self.currentFruitSpeedLimit + self.maxSpeedOffset
										end
									else
									self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF)
									--self:stopMotor()
									end
								    
									--self:stopMotor()
									g_soundManager:playSample(self.spec_honk.sample)
									g_soundManager:stopSample(self.spec_honk.sample)
									g_currentMission:showBlinkingWarning(g_i18n:getText("TFItooFast"), 2000)
								end
							end
						end
					end
                end
            end
        end
    end
end;

function threshingFlowIndicator:onDelete()
end;

function threshingFlowIndicator:onReadStream(streamId, connection)
end;

function threshingFlowIndicator:onWriteStream(streamId, connection)
end;

function threshingFlowIndicator:onDraw(isActiveForInput, isSelected)
	if self.isClient then
		if isSelected then
			if self.showInHud then
				if g_fruitTypeManager:getFruitTypeByIndex(self.lastFT) ~= nil then
					local fruitType = g_fruitTypeManager:getFruitTypeByIndex(self.lastFT)
					text1 = fruitType.fillType.title
				else
					text1 = " "
				end
				g_currentMission:addExtraPrintText(g_i18n:getText("TFIspeed") .. " " .. self.currentFruitSpeedLimit .. "km/h" .. " (" .. text1 .. ")" .. "    " .. g_i18n:getText("TFIcutterLoad") .. " " .. MathUtil.round(self.currentFlow*100, 1) .. "%")
			end
		end
	end
end