--[[
Interface: FS19 1.2.0.1

Copyright (C) GtX (Andy), 2018

Author: GtX | Andy
Date: 17.12.2018
Version: 1.0.0.0

Support only at: https://ls-modcompany.com

History:
V 1.0.0.0 @ 17.12.2018 - Release Version for FS19. (Based / Converted from my FS17 'LightExtension.lua'.)


Thankyou:
Sven777b @ http://ls-landtechnik.com	-	Allowing me to use parts of his strobe light code as found in ‘Beleuchtung v3.1.1’.
Inerti and Nicolina		-	FS17 Suggestions, single and multiplayer testing.


Important:
Free for use in other mods - no permission needed.
No changes are to be made to this script without permission from GtX @ https://ls-modcompany.com

frei verwendbar - keine erlaubnis nötig
Modifikationen erst nach Rücksprache!
]]


LightExtension = {};

function LightExtension.prerequisitesPresent(specializations)
	return SpecializationUtil.hasSpecialization(Lights, specializations);
end;

function LightExtension.registerEventListeners(vehicleType)
	SpecializationUtil.registerEventListener(vehicleType, "onLoad", LightExtension);
	SpecializationUtil.registerEventListener(vehicleType, "onDelete", LightExtension);
	SpecializationUtil.registerEventListener(vehicleType, "onUpdate", LightExtension);
	SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", LightExtension);
	SpecializationUtil.registerEventListener(vehicleType, "onStartMotor", LightExtension);
	SpecializationUtil.registerEventListener(vehicleType, "onStopMotor", LightExtension);
	SpecializationUtil.registerEventListener(vehicleType, "onPostDetach", LightExtension);
	SpecializationUtil.registerEventListener(vehicleType, "onBeaconLightsVisibilityChanged", LightExtension);
end;

function LightExtension.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "loadLightExtensionLight", LightExtension.loadLightExtensionLight);
	SpecializationUtil.registerFunction(vehicleType, "setRunningLightsState", LightExtension.setRunningLightsState);
end;

function LightExtension:onLoad(savegame)
	self.spec_lightExtension = {};
	local spec = self.spec_lightExtension;

	-- Strobe Lights (Activate using beacon light key only).
	local strobeLightsKey = "vehicle.lightExtension.strobeLights";
	if hasXMLProperty(self.xmlFile, strobeLightsKey) then

		spec.strobeLights = {};
		spec.strobeLights.strobes = {};

		spec.strobeLights.active = false;
		spec.strobeLights.reset = false;

		spec.strobeLights.realBeaconLights = g_gameSettings:getValue("realBeaconLights");
		spec.strobeLights.realLights = 0;
		spec.strobeLights.maxRealLights = 20;

		-- Only recommended on 'personal mods' if you know your system can handle the extra lights.
		local disableRealLightWarning = Utils.getNoNil(getXMLBool(self.xmlFile, strobeLightsKey .. "#disableRealLightWarning"), false);

		local loadedXmlFiles = {};

		local i = 0;
		while true do
			local key = string.format("%s.strobeLight(%d)", strobeLightsKey, i);

			if not hasXMLProperty(self.xmlFile, key) then
				break;
			end;

			local strobe = {};
			local strobeLightLoaded = false;

			local strobeXmlFilename = getXMLString(self.xmlFile, key .. "#filename")
			if strobeXmlFilename ~= nil then
				strobeXmlFilename = Utils.getFilename(strobeXmlFilename, self.baseDirectory);

				local strobeXmlFile = nil;
				if loadedXmlFiles[strobeXmlFilename] ~= nil then
					strobeXmlFile = loadedXmlFiles[strobeXmlFilename];
				else
					strobeXmlFile = loadXMLFile("strobeLightXML", strobeXmlFilename);
					if strobeXmlFile ~= nil and strobeXmlFile ~= 0 then
						loadedXmlFiles[strobeXmlFilename] = strobeXmlFile;
					end;
				end;

				if strobeXmlFile ~= nil then
					strobeLightLoaded = self:loadLightExtensionLight(strobe, self.xmlFile, key, strobeXmlFile, true);
					if strobe.realLightNode ~= nil then
						spec.strobeLights.realLights = spec.strobeLights.realLights + 1;
					end;
					--delete(strobeXmlFile);
				end;
			else
				strobeLightLoaded = self:loadLightExtensionLight(strobe, self.xmlFile, key, nil, true);
			end;

			if strobeLightLoaded then
				table.insert(spec.strobeLights.strobes, strobe);
			end;

			i = i + 1;
		end;

		if not disableRealLightWarning and spec.strobeLights.realLights > spec.strobeLights.maxRealLights then
			g_logManager:warning("[LightExtension] - More than 20 'real' strobe lights have been found in '%s' this may cause performance issues on some computers.");
		end;

		for filename, file in pairs (loadedXmlFiles) do
			delete(file);
		end;

		if next(spec.strobeLights.strobes) == nil then
			spec.strobeLights = nil;
		end;
	end;

	-- Beacon Sounds (Sirens or beacon rotate motor sounds).
	local soundsKey = "vehicle.lightExtension.beaconSound";
	if hasXMLProperty(self.xmlFile, soundsKey) then
		local beaconSoundSample = g_soundManager:loadSampleFromXML(self.xmlFile, "vehicle.lightExtension", "beaconSound", self.baseDirectory, self.components, 0, AudioGroup.VEHICLE, self.i3dMappings, self);
		if beaconSoundSample ~= nil then
			spec.beaconSound = {};
			spec.beaconSound.sample = beaconSoundSample;
			spec.beaconSound.isActive = false;

		end;
	end;

	-- Fill Warning ('FillUnit Combines' Only).
	local fillWarningKey = "vehicle.lightExtension.combineFillWarning";
	local percent = getXMLFloat(self.xmlFile, fillWarningKey .. "#percent")
	if percent ~= nil then
		if self.spec_combine ~= nil and self.spec_fillUnit ~= nil then
			spec.combineFillWarning = {};
			spec.combineFillWarning.isEnabled = true;	-- This is to allow global scripts using a similar feature to disable us. ;-).
														-- EXAMPLE: if self.spec_lightExtension ~= nil and self.spec_lightExtension.combineFillWarning ~= nil then
														--				self.spec_lightExtension.combineFillWarning.isEnabled = false;
														--			end;
			spec.combineFillWarning.percent = percent;
			spec.combineFillWarning.beaconsSetActive = false;
			spec.combineFillWarning.isActive = false;
			spec.combineFillWarning.turnOnBeacons = Utils.getNoNil(getXMLBool(self.xmlFile, fillWarningKey .. "#turnOnBeacons"), false);
			spec.combineFillWarning.playSound = Utils.getNoNil(getXMLBool(self.xmlFile, fillWarningKey .. "#playWarningSound"), false);

			if spec.combineFillWarning.playSound then
				spec.combineFillWarning.soundLoops = Utils.getNoNil(getXMLInt(self.xmlFile, fillWarningKey .. "#warningSoundLoops"), 3); -- How many times will the sound repeat.
				spec.combineFillWarning.is3DSound = Utils.getNoNil(getXMLBool(self.xmlFile, fillWarningKey .. "#is3DSound"), false); -- Is it only played for the driver or for everyone near the vehicle?
				local inCabAlarmSample = nil;
				if hasXMLProperty(self.xmlFile, fillWarningKey .. ".alarmSound") then
					inCabAlarmSample = g_soundManager:loadSampleFromXML(self.xmlFile, fillWarningKey, "alarmSound", self.baseDirectory, self.components, spec.combineFillWarning.soundLoops, AudioGroup.VEHICLE, self.i3dMappings, self);
				else
					-- Load a default sound file for the alarm if none is given.
					local memXml = '<vehicle> <lightExtension> <combineFillWarning> <alarmSound template="ROLLBELT_ALARM" linkNode="0>" /> </combineFillWarning> </lightExtension> </vehicle>';
					local memXmlFile = loadXMLFileFromMemory("alarmSoundFake", memXml);
					inCabAlarmSample = g_soundManager:loadSampleFromXML(memXmlFile, fillWarningKey, "alarmSound", self.baseDirectory, self.components, spec.combineFillWarning.soundLoops, AudioGroup.VEHICLE, self.i3dMappings, self);
					delete(memXmlFile);
				end;

				spec.combineFillWarning.sample = inCabAlarmSample;
				spec.combineFillWarning.samplePlaying = false;
			end;
		else
			g_logManager:xmlWarning(self.configFileName, "Combine Fill Warning is only for use on vehicles using the 'fillUnit' and 'combine' specialisations.");
		end;
	end;

	-- Running Lights (These are only active when engine is running just as with BMW, Mercedes-Benz, Volvo etc).
	local runningLightsKey = "vehicle.lightExtension.runningLights";
	if hasXMLProperty(self.xmlFile, runningLightsKey) then
		spec.runningLights = {};
		spec.runningLights.lights = {};
		spec.runningLights.active = false;

		spec.runningLights.realLights = 0;
		spec.runningLights.maxRealLights = 20; -- (20) This is a huge limit and more than any mod should use for performance / lag reasons.

		-- Only recommended on 'personal mods' if you know your system can handle the extra lights.
		local disableRealLightWarning = Utils.getNoNil(getXMLBool(self.xmlFile, runningLightsKey .. "#disableRealLightWarning"), false);

		local loadedXmlFiles = {};

		local i = 0;
		while true do
			local key = string.format("%s.runningLight(%d)", runningLightsKey, i);

			if not hasXMLProperty(self.xmlFile, key) then
				break;
			end;

			local runLight = {};
			local runningLightsLoaded = false;

			local lightXmlFilename = getXMLString(self.xmlFile, key .. "#filename")
			if lightXmlFilename ~= nil then
				lightXmlFilename = Utils.getFilename(lightXmlFilename, self.baseDirectory);

				local lightXmlFile = nil;
				if loadedXmlFiles[lightXmlFilename] ~= nil then
					lightXmlFile = loadedXmlFiles[lightXmlFilename];
				else
					lightXmlFile = loadXMLFile("runLightXML", lightXmlFilename);
					if lightXmlFile ~= nil and lightXmlFile ~= 0 then
						loadedXmlFiles[lightXmlFilename] = lightXmlFile;
					end;
				end;

				if lightXmlFile ~= nil then
					runningLightsLoaded = self:loadLightExtensionLight(runLight, self.xmlFile, key, lightXmlFile, false);
					if runLight.realLightNode ~= nil then
						spec.runningLights.realLights = spec.runningLights.realLights + 1;
					end;
					--delete(lightXmlFile);
				end;
			else
				runningLightsLoaded = self:loadLightExtensionLight(runLight, self.xmlFile, key, nil, false);
			end;

			if runningLightsLoaded then
				table.insert(spec.runningLights.lights, runLight);
			end;

			i = i + 1;
		end;

		if not disableRealLightWarning and spec.runningLights.realLights > spec.runningLights.maxRealLights then
			g_logManager:warning("[LightExtension] - More than 20 'real' running lights have been found, this may cause performance issues on some computers.");
		end;

		for filename, file in pairs (loadedXmlFiles) do
			delete(file);
		end;

		if next(spec.runningLights.lights) == nil then
			spec.runningLights = nil;
		end;
	end;
end;

function LightExtension:onDelete()
	local spec = self.spec_lightExtension;

	if spec.strobeLights ~= nil then
		for _, strobeLight in pairs(spec.strobeLights.strobes) do
			if strobeLight.filename ~= nil then
				g_i3DManager:releaseSharedI3DFile(strobeLight.filename, self.baseDirectory, true);
			end;
		end;
	end;

	if spec.beaconSound ~= nil then
		g_soundManager:deleteSample(spec.beaconSound.sample);
	end;

	if spec.combineFillWarning ~= nil and spec.combineFillWarning.sample ~= nil then
		g_soundManager:deleteSample(spec.combineFillWarning.sample);
	end;
end;

function LightExtension:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
	local spec = self.spec_lightExtension;

	if spec.strobeLights ~= nil then
		if spec.strobeLights.active then

			spec.strobeLights.reset = true;
			for _, st in ipairs(spec.strobeLights.strobes) do
				if st.t > st.ns then
					st.ls = not st.ls;

					if spec.strobeLights.realBeaconLights and st.realLightNode ~= nil then
						setVisibility(st.realLightNode, st.ls);
					end;

					if st.lightNode ~= nil then
						setVisibility(st.lightNode, st.ls);
					end;

					if st.lightShaderNode ~= nil then
						local value = 1 * st.intensity;
						if not st.ls then
							value = 0;
						end;
						setShaderParameter(st.lightShaderNode, "lightControl", value, st.shaderParameter[1], st.shaderParameter[2], st.shaderParameter[3], false);
					end;

					st.t = 0;
					if st.rnd then
						if st.ls then
							st.ns = math.random(st.rndnn,st.rndxn);
						else
							st.ns = math.random(st.rndnf,st.rndxf);
						end;
					else
						st.ss = st.ss + 1;
						if st.ss > table.getn(st.sq) then
							st.ss = 1;
						end;
						st.ns = st.sq[st.ss];
					end;
				else
					st.t = st.t + dt;
				end;
			end;
		else
			if spec.strobeLights.reset then
				for _, st in ipairs(spec.strobeLights.strobes) do
					if spec.strobeLights.realBeaconLights and st.realLightNode ~= nil then
						setVisibility(st.realLightNode, false);
					end;

					if st.lightNode ~= nil then
						setVisibility(st.lightNode, false);
					end;

					if st.lightShaderNode ~= nil then
						setShaderParameter(st.lightShaderNode, "lightControl", 0, st.shaderParameter[1], st.shaderParameter[2], st.shaderParameter[3], false);
					end;
				end;
				spec.strobeLights.reset = false;
			end;
		end;
	end;

	if spec.combineFillWarning ~= nil and spec.combineFillWarning.isEnabled and self:getIsActive() and not self.isHired then
		local percent = 100 * self:getFillUnitFillLevelPercentage(self.spec_combine.fillUnitIndex);
		if spec.combineFillWarning.isActive then
			if percent < spec.combineFillWarning.percent then
				if spec.combineFillWarning.beaconsSetActive and spec.combineFillWarning.turnOnBeacons then
					self:setBeaconLightsVisibility(false);
					spec.combineFillWarning.beaconsSetActive = false;
				end;
				spec.combineFillWarning.counter = spec.combineFillWarning.seconds;
				spec.combineFillWarning.isActive = false;

				if spec.combineFillWarning.playSound and spec.combineFillWarning.sample ~= nil and spec.combineFillWarning.samplePlaying then
					g_soundManager:stopSample(spec.combineFillWarning.sample);
					spec.combineFillWarning.samplePlaying = false;
				end;
			end;
		else
			if percent >= spec.combineFillWarning.percent then
				if not self.spec_lights.beaconLightsActive and spec.combineFillWarning.turnOnBeacons then
					self:setBeaconLightsVisibility(true);
					spec.combineFillWarning.beaconsSetActive = true;
				end;
				spec.combineFillWarning.counter = 0;
				spec.combineFillWarning.isActive = true;

				if spec.combineFillWarning.playSound and spec.combineFillWarning.sample ~= nil and not spec.combineFillWarning.samplePlaying then
					if self.spec_enterable.isEntered or spec.combineFillWarning.is3DSound then
						g_soundManager:playSample(spec.combineFillWarning.sample);
						spec.combineFillWarning.samplePlaying = true;
					end;
				end;
			end;
		end;
	end;

	if spec.runningLights ~= nil and self.spec_attachable ~= nil then
		local rootVehicle = self:getRootVehicle()
		if rootVehicle ~= nil and rootVehicle.getIsMotorStarted ~= nil then
			if rootVehicle:getIsMotorStarted() ~= spec.runningLights.active then
				self:setRunningLightsState(rootVehicle:getIsMotorStarted());
			end;
		end;
	end;
end;

function LightExtension:setRunningLightsState(isActive)
	local spec = self.spec_lightExtension;

	if spec.runningLights.lights ~= nil then
		spec.runningLights.active = isActive;

		for _, runLight in ipairs(spec.runningLights.lights) do
			if runLight.realLightNode ~= nil and self:getUseHighProfile() then
				setVisibility(runLight.realLightNode, isActive);
			end;

			if runLight.lightNode ~= nil then
				setVisibility(runLight.lightNode, isActive);
			end;

			if runLight.lightShaderNode ~= nil then
				local intensity = 0;
				if isActive then
					intensity = runLight.intensity;
				end;

				setShaderParameter(runLight.lightShaderNode, "lightControl", intensity, runLight.shaderParameter[1], runLight.shaderParameter[2], runLight.shaderParameter[3], false);
			end;
		end;
	end;
end;

function LightExtension:onPostDetach()
	local spec = self.spec_lightExtension;
	if spec.runningLights ~= nil then
		self:setRunningLightsState(false);
	end;
end;

function LightExtension:onLeaveVehicle()
	local spec = self.spec_lightExtension;
	if spec.combineFillWarning ~= nil and spec.combineFillWarning.isEnabled then
		if spec.combineFillWarning.samplePlaying and not spec.combineFillWarning.is3DSound then
			g_soundManager:stopSample(spec.combineFillWarning.sample);
			spec.combineFillWarning.samplePlaying = false;
		end;
	end;
end;

function LightExtension:onStartMotor()
	if self.spec_lightExtension.runningLights ~= nil then
		self:setRunningLightsState(true);
	end;
end;

function LightExtension:onStopMotor()
	if self.spec_lightExtension.runningLights ~= nil then
		self:setRunningLightsState(false);
	end;
end;

function LightExtension:onBeaconLightsVisibilityChanged(visibility)
	local spec = self.spec_lightExtension;

	if spec.strobeLights ~= nil then
		spec.strobeLights.realBeaconLights = g_gameSettings:getValue("realBeaconLights");
		spec.strobeLights.active = visibility;
	end;

	if spec.beaconSound ~= nil then
		spec.beaconSound.isActive = visibility;

		if spec.beaconSound.isActive then
			g_soundManager:playSample(spec.beaconSound.sample);
		else
			g_soundManager:stopSample(spec.beaconSound.sample);
		end;
	end;
end;

function LightExtension:loadLightExtensionLight(light, xmlFile, key, lightXmlFile, isStrobeLight)
	local spec = self.spec_lightExtension;

	if light ~= nil then
		if isStrobeLight then
			light.ns = 0;
			light.t = 1;
			light.a = false;

			local sequence = getXMLString(xmlFile, key .. "#sequence");
			if sequence ~= nil then
				light.rnd = false;
				light.sq = {StringUtil.getVectorFromString(sequence)};
				light.inv = Utils.getNoNil(getXMLBool(xmlFile, key .. "#invert"), false);
				light.ls = light.inv;
				light.ss = 1;
			else
				light.rnd = true;
				light.ls = false;
				light.rndnn = Utils.getNoNil(getXMLInt(xmlFile, key .. "#minOn"), 100);
				light.rndxn = Utils.getNoNil(getXMLInt(xmlFile, key .. "#maxOn"), 100);
				light.rndnf = Utils.getNoNil(getXMLInt(xmlFile, key .. "#minOff"), 100);
				light.rndxf = Utils.getNoNil(getXMLInt(xmlFile, key .. "#maxOff"), 400);
				math.randomseed(getTime());
				math.random();
			end;
		end;


		if lightXmlFile ~= nil then
			-- Allow shared 'realLight' to be disabled as it is not necessary to have these on all strobes.
			light.useRealLight = Utils.getNoNil(getXMLBool(xmlFile, key .. "#useRealLight"), false);

			local node = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#linkNode"), self.i3dMappings);
			if node ~= nil then
				local sharedKey = "lightExtensionShared.runningLight";
				if isStrobeLight then
					sharedKey = "lightExtensionShared.strobeLight";
				end;

				local i3dFilename = getXMLString(lightXmlFile, sharedKey .. ".filename");
				if i3dFilename ~= nil then
					local i3dNode = g_i3DManager:loadSharedI3DFile(i3dFilename, self.baseDirectory, false, false, false);
					if i3dNode ~= nil and i3dNode ~= 0 then
						local rootNode = I3DUtil.indexToObject(i3dNode, getXMLString(lightXmlFile, sharedKey .. ".rootNode#node"));

						-- Load 'lightNode' that is part of shared XML (e.g Previous generation coronas.)
						local lightNode = I3DUtil.indexToObject(i3dNode, getXMLString(lightXmlFile, sharedKey .. ".light#node"));

						-- Load 'lightShaderNode' that is part of 'shared XML'
						local lightShaderNode = I3DUtil.indexToObject(i3dNode, getXMLString(lightXmlFile, sharedKey .. ".light#shaderNode"));

						if rootNode ~= nil and (lightNode ~= nil or lightShaderNode ~= nil) then
							link(node, rootNode);
							setTranslation(rootNode, 0,0,0);

							light.rootNode = rootNode;
							light.filename = i3dFilename;
							light.intensity = Utils.getNoNil(getXMLFloat(lightXmlFile, sharedKey .. ".light#intensity"), 100);

							if lightNode ~= nil then
								light.lightNode = lightNode;
								setVisibility(lightNode, false);
							end;

							if lightShaderNode ~= nil then
								light.lightShaderNode = lightShaderNode;
								local _, y, z, w = getShaderParameter(lightShaderNode, "lightControl");
								light.shaderParameter = {y, z, w};
								setShaderParameter(lightShaderNode, "lightControl", 0, y, z, w, false);
							end;

							-- Load 'realLight' that is part of 'shared XML'
							local xmlRealLightNode = I3DUtil.indexToObject(i3dNode, getXMLString(lightXmlFile, sharedKey .. ".realLight#node"));
							if xmlRealLightNode ~= nil then
								light.defaultColor = {getLightColor(xmlRealLightNode)};
								setVisibility(xmlRealLightNode, false);

								if light.useRealLight then
									light.realLightNode = xmlRealLightNode;
								end;
							end;

							-- Load 'realLights' that are part of the vehicle.i3d (Only if 'realLight' is not part of 'shared XML'
							if light.realLightNode == nil then
								local realLightNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#realLightNode"), self.i3dMappings);
								if realLightNode ~= nil then
									light.defaultColor = {getLightColor(realLightNode)};
									setVisibility(realLightNode, false);
									light.realLightNode = realLightNode;
								end;
							end;

							return true;
						end;
						delete(i3dNode)
					end;
				end;
			else
				g_logManager:error("[LightExtension] - No link node was found at %s in '%s'",  key, self.configFileName);
			end;
		else
			-- Load 'lightNode' that is part of the 'vehicle i3d' (e.g Previous generation coronas.)
			local lightNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#lightNode"), self.i3dMappings);

			-- Load 'lightShaderNode' that is part of the 'vehicle i3d'
			local lightShaderNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#lightShaderNode"), self.i3dMappings);

			if lightNode ~= nil or lightShaderNode ~= nil then
				light.intensity = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#shaderIntensity"), 100);

				if lightNode ~= nil then
					light.lightNode = lightNode;
					setVisibility(lightNode, false);
				end;

				if lightShaderNode ~= nil then
					light.lightShaderNode = lightShaderNode;
					local _, y, z, w = getShaderParameter(lightShaderNode, "lightControl");
					light.shaderParameter = {y, z, w};
					setShaderParameter(lightShaderNode, "lightControl", 0, y, z, w, false);
				end;

				-- Load 'realLights' that are part of the 'vehicle i3d'
				local realLightNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#realLightNode"), self.i3dMappings);
				if realLightNode ~= nil then
					light.defaultColor = {getLightColor(realLightNode)};
					setVisibility(realLightNode, false);
					light.realLightNode = realLightNode;
				end;
			end;

			return true;
		end;
	end;

	return false;
end;





