--
-- PressureWasher
-- Specialization for PressureWasher
--
-- @author  	Manuel Leithner (SFM-Modding)
-- @version 	v2.0
-- @date  		19/10/10
-- @history:	v1.0 - Initial version
--				v2.0 - converted to 2011 and some bugfixes
--

PressureWasher = {};

function PressureWasher.prerequisitesPresent(specializations)
    return true;
end;

function PressureWasher:load(xmlFile)

	self.washVehicles = SpecializationUtil.callSpecializationsFunction("washVehicles");
	self.getPlayerInRange = PressureWasher.getPlayerInRange;
	self.getWashableInRange = SpecializationUtil.callSpecializationsFunction("getWashableInRange");
	self.setActiveState = SpecializationUtil.callSpecializationsFunction("setActiveState");
	self.setLanceActive = SpecializationUtil.callSpecializationsFunction("setLanceActive");
	
	self.kaercher = {};
	self.kaercher.actionDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.kaercher#actionDistance"), 10);
	self.kaercher.washDistance = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.kaercher#washDistance"), 8);
	self.kaercher.pipe = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.kaercher#pipe"));
	self.kaercher.lance = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.kaercher#lance"));
	local x,y,z = getTranslation(self.kaercher.lance);	
	self.kaercher.lanceTrans = {x,y,z};
	x,y,z = getRotation(self.kaercher.lance);
	self.kaercher.lanceRot = {x,y,z};
	self.kaercher.pressureWasher = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.kaercher#pressureWasher"));	
	
    self.kaercher.particleSystems = {};
    local psName = "vehicle.waterParticelSystem";
    Utils.loadParticleSystem(xmlFile, self.kaercher.particleSystems, psName, self.components, false, nil, self.baseDirectory);
	Utils.setEmittingState(self.kaercher.particleSystems, false);
	
	local compressorSound = getXMLString(xmlFile, "vehicle.sounds#compressor");	
	if compressorSound ~= nil and compressorSound ~= "" then
		compressorSound = Utils.getFilename(compressorSound, self.baseDirectory); 
		self.compressorSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.sounds#volume"), 1.0);
        self.compressorSoundRadius = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.sounds#radius"), 50);
        self.compressorSoundInnerRadius = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.sounds#innerRadius"), 10);
        self.compressorSound = createAudioSource("compressorSound", compressorSound, self.compressorSoundRadius, self.compressorSoundInnerRadius, self.compressorSoundVolume, 0);
        link(self.components[1].node, self.compressorSound);
        setVisibility(self.compressorSound, false);
	end;
	
	local washerSound = getXMLString(xmlFile, "vehicle.sounds#washer");
    if washerSound ~= nil and washerSound ~= "" then
        washerSound = Utils.getFilename(washerSound, self.baseDirectory); 
        self.washerSound = createSample("washerSound");
        self.washerSoundEnabled = false;
        loadSample(self.washerSound, washerSound, false);
        self.washerSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.sounds#washerPitchOffset"), 1);
        self.washerSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.sounds#washerVolume"), 1);
    end;
	
	self.oldToBeWashed = {};
	self.toBeWashed = {};
	self.isPlayerInRange = false;
	self.washablesInRange = {};
	self.controllingPlayer = nil;
	self.isLanceActive = false;
	self.lastInRangePosition = {0,0,0};
	self.playerIdBackup = nil;
	self.hasPlayerQuit = false;
end;

function PressureWasher:delete()
	self:setActiveState(nil, true);	
	if self.kaercher.particleSystems ~= nil then
		Utils.deleteParticleSystem(self.kaercher.particleSystems);
	end;
    if self.washerSound ~= nil then
        delete(self.washerSound);
    end;
end;

function PressureWasher:readStream(streamId, connection)
	local id = streamReadInt32(streamId);
	if id ~= -1 then
		self.playerIdBackup = id;
	end;	
end;

function PressureWasher:writeStream(streamId, connection)
	local idToWrite = -1;
	if self.controllingPlayer ~= nil then
		idToWrite = networkGetObjectId(self.controllingPlayer);
	end;
	streamWriteInt32(streamId, idToWrite);	
end;

function PressureWasher:mouseEvent(posX, posY, isDown, isUp, button)
end;

function PressureWasher:keyEvent(unicode, sym, modifier, isDown)
end;

function PressureWasher:update(dt)

	if g_gui.currentGui ~= nil and self.playerIdBackup ~= nil then	
		self:setActiveState(networkGetObject(self.playerIdBackup), true);
		self.playerIdBackup = nil;		
	end;

	if self.controllingPlayer == g_currentMission.player or (self.controllingPlayer == nil and self.isPlayerInRange) then
		if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then
			if self.controllingPlayer == g_currentMission.player then
				self:setActiveState(nil);
			else
				self:setActiveState(g_currentMission.player);
			end;
		end;	
		
		if self.controllingPlayer ~= nil and self.controllingPlayer == g_currentMission.player then
			local x,y,z = getWorldTranslation(self.controllingPlayer.cameraId);
			setTranslation(self.kaercher.lance, x,y - 0.8,z);
			local x,y,z = getWorldRotation(self.controllingPlayer.cameraId);
			setRotation(self.kaercher.lance, x,y,z);
			
			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_off_ROHR"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA);
			g_currentMission:addExtraPrintText(g_i18n:getText("pressure_washer_spray"));
			if Input.isMouseButtonPressed(Input.MOUSE_BUTTON_LEFT) and g_gui.currentGui == nil then
				if not self.isLanceActive then
					self:setLanceActive(true);
				end;
			else
				if self.isLanceActive then
					self:setLanceActive(false);
				end;
			end;
		elseif self.controllingPlayer == nil and self.isPlayerInRange then
			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_on_ROHR"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA);	
		end;
	else
		if self.controllingPlayer ~= nil then	
			if g_currentMission:getIsServer() then
				if g_currentMission.connectionsToPlayer[self.controllingPlayer.creatorConnection] == nil then
					self.hasPlayerQuit = true;
					self:setActiveState(nil);
				end;
			end;
			-- do a second check to make sure that player didn't disconnect
			if self.controllingPlayer ~= nil then
				local x,y,z = getWorldTranslation(self.controllingPlayer.rootNode);
				setTranslation(self.kaercher.lance, x+0.1,y-0.5,z);
				local x,y,z = getRotation(self.controllingPlayer.graphicsRootNode);
				setRotation(self.kaercher.lance, x,y,z);
			end;
		end;
	end;	
	if self.controllingPlayer ~= nil then
		-- if player joins a vehicle disable washer
		if not self.controllingPlayer.isControlled and not self.controllingPlayer.isEntered then
			self:setActiveState(nil);
		end;
	end;
end;

function PressureWasher:updateTick(dt)

	if self.controllingPlayer == nil then
		if g_currentMission.currentVehicle == nil then
			self.isPlayerInRange = PressureWasher.getPlayerInRange(self, g_currentMission.player);
		end;
		if self.washerSoundEnabled then
			stopSample(self.washerSound);
			self.washerSoundEnabled = false;
		end;
	else
		local isPlayerInRange = PressureWasher.getPlayerInRange(self, self.controllingPlayer);
		if isPlayerInRange then
			self.lastInRangePosition = {getTranslation(self.controllingPlayer.rootNode)};
		else
			local x,y,z = unpack(self.lastInRangePosition);
			self.controllingPlayer:moveTo(x, 0.05, z);
		end;
		if self.isLanceActive then			
			if self.controllingPlayer == g_currentMission.player then
				self.toBeWashed = {};
				if not self.washerSoundEnabled then
					setSamplePitch(self.washerSound, self.washerSoundPitchOffset);
					playSample(self.washerSound, 0, self.washerSoundVolume, 0);
					self.washerSoundEnabled = true;
				end;
				self:getWashableInRange();
				if table.getn(self.washablesInRange) > 0 then
					for _, washable in pairs(self.washablesInRange) do
						for _, component in pairs(washable.components) do
							local x,y,z = getWorldTranslation(component.node);
							x,y,z = project(x,y,z);
							if x >= 0.2 and x <= 0.8 then
								if y >= 0.2 and y <= 0.8 then
									if z <= 1 then
										table.insert(self.toBeWashed, washable);	
										break;
									end;
								end;
							end;
						end;
					end;
					
				end;
			end;
		else
			if self.controllingPlayer == g_currentMission.player then
				self.toBeWashed = {};
			end;
			if self.washerSoundEnabled then
				stopSample(self.washerSound);
				self.washerSoundEnabled = false;
			end;
		end;
		if table.getn(self.oldToBeWashed) ~= table.getn(self.toBeWashed) then
			self:washVehicles(self.toBeWashed);
		end;
		self.oldToBeWashed = self.toBeWashed;	
	end;	
	for _, washable in pairs(self.toBeWashed) do
		washable:decreaseDirt(dt);
	end;	
end;

function PressureWasher:draw()	
end;

function PressureWasher:setActiveState(player, noEventSend)	
	PressureWasherEnableEvent.sendEvent(self, player, noEventSend);

	self.controllingPlayer = player;	
	if player ~= nil then	
		link(getRootNode(), self.kaercher.lance);
		setVisibility(self.kaercher.pipe, false);
		setVisibility(self.compressorSound, true);
	else
		link(self.kaercher.pressureWasher, self.kaercher.lance);
		setVisibility(self.kaercher.lance, true);
		setVisibility(self.kaercher.pipe, true);
		setRotation(self.kaercher.lance, unpack(self.kaercher.lanceRot));
		setTranslation(self.kaercher.lance, unpack(self.kaercher.lanceTrans));
		setVisibility(self.compressorSound, false);
		Utils.setEmittingState(self.kaercher.particleSystems, false);
	end;
	self.hasPlayerQuit = false;
end;

function PressureWasher:setLanceActive(isActive, noEventSend)
	PressureWasherLanceEvent.sendEvent(self, isActive, noEventSend);
	Utils.setEmittingState(self.kaercher.particleSystems, isActive);
	self.isLanceActive = isActive;
end;

function PressureWasher:washVehicles(washables, noEventSend)
	PressureWasherEvent.sendEvent(self, washables, noEventSend);
	self.toBeWashed = washables;	
end;

function PressureWasher:getPlayerInRange(player)
	local isPlayerInRange = false; 
	if player ~= nil then
		local px, py, pz = getWorldTranslation(self.rootNode);
		local vx, vy, vz = getWorldTranslation(player.rootNode);
		local distance = Utils.vector2Length(px-vx, pz-vz);
		if distance < self.kaercher.actionDistance then		
			isPlayerInRange = true;
		end; 
	end;
	
	return isPlayerInRange;
end;

function PressureWasher:getWashableInRange()
	self.washablesInRange = {}; 
	local nearestDistance = self.kaercher.washDistance;
	local px, py, pz = getWorldTranslation(g_currentMission.player.rootNode);
	for k,v in pairs(g_currentMission.vehicles) do
		if v.decreaseDirt ~= nil then
			local vx, vy, vz = getWorldTranslation(v.rootNode);
			local distance = Utils.vector2Length(px-vx, pz-vz);
			if distance < nearestDistance then
				table.insert(self.washablesInRange, v);
			end; 
		end;
    end;
end;