--[[
Interface: 1.3.0.1 b4007

Copyright (C) GtX (Andy), 2018

Author: GtX | Andy
Date: 17.2.2022
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'.)
V 1.0.1.0 @ 29.01.2019 - Fix running lights not turning off on linked trailers (2 or more) when rootVehicle was disconnected.


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
	
	-- We allow many realLights. I am not sure how Giants is implementing 'g_maxNumRealVehicleLights'.
	-- This returns a value of '10'.

	-- 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
			local isMotorStarted = rootVehicle:getIsMotorStarted()
			if isMotorStarted ~= spec.runningLights.active then
				self:setRunningLightsState(isMotorStarted)
			end
		else
			if spec.runningLights.active then
				self:setRunningLightsState(false)
			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





