-- author: rafftnix, fruktor
-- date: 03.12.2012

-- Änderungen am Skript nur mit meiner Zustimmung!
-- Modification only with my permission!

ChainSaw = {};

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

function ChainSaw:load(xmlFile)
	self.startMotorSaw 				= SpecializationUtil.callSpecializationsFunction("startMotorSaw");
	self.stopMotorSaw 				= SpecializationUtil.callSpecializationsFunction("stopMotorSaw");
	self.setSawIsRunning 			= SpecializationUtil.callSpecializationsFunction("setSawIsRunning");
	self.setSawIsTurning 			= SpecializationUtil.callSpecializationsFunction("setSawIsTurning"); 
	self.setSawFuelFillLevel 		= SpecializationUtil.callSpecializationsFunction("setSawFuelFillLevel"); 
	self.refuelMotorSaw 			= SpecializationUtil.callSpecializationsFunction("refuelMotorSaw"); 
	self.chainSawDraw 				= SpecializationUtil.callSpecializationsFunction("chainSawDraw"); 
	self.playerTriggerCallback 		= ChainSaw.playerTriggerCallback;
	self.chainSawTreeCallback 		= ChainSaw.chainSawTreeCallback;
	self.chainSawBranchTreeCallback = ChainSaw.chainSawBranchTreeCallback;
	self.idleTimer 					= ChainSaw.idleTimer;
	self.stopStartSound				= ChainSaw.stopStartSound;
	self.stopStopSound				= ChainSaw.stopStopSound;
	self.addHelpButtonTextWithCorrectMouseButtons = ChainSaw.addHelpButtonTextWithCorrectMouseButtons;
	
	self.setCuttingPS				= SpecializationUtil.callSpecializationsFunction("setCuttingPS");
	self.setSawXRotation			= SpecializationUtil.callSpecializationsFunction("setSawXRotation");
	
	self.playerTriggerId = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.playerTrigger#index"));
	addTrigger(self.playerTriggerId, "playerTriggerCallback", self);
	
	self.playerInTrigger = false;
	self.firstRunDone = false;
	self.controller = nil;
	
	-- chain saw
	self.saw = {}
	self.saw.isActive = false;
	self.saw.isRunning = false;
	self.saw.isTurning = false;
	self.saw.showContactTimer = false;
	self.saw.branchCutActivatePs = false;
	self.saw.treeContactTimer = 0;
	self.saw.treeContactTimerMax = 0;   
	self.saw.currentTrunks = {};
	self.saw.currentDamageVehicle = nil;
	self.saw.cutRange = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chainSaw.node#cutRange"), 0.5);
	self.saw.bladeProtection = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.bladeProtection#index")); 
	self.saw.cutPoint = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.node#cutPointIndex"));
	self.saw.cutDurationFactor = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chainSaw.cutDurationFactor"), 1);
	self.saw.nodeId = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.node#index"));
	self.saw.treeTriggerNodeId = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.node#treeTriggerIndex"));
	addTrigger(self.saw.treeTriggerNodeId, "chainSawTreeCallback", self);
	
	self.saw.branchTreeTriggerNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.branch#treeTriggerIndex"));
	addTrigger(self.saw.branchTreeTriggerNode, "chainSawBranchTreeCallback", self);
	self.saw.currentBranchTrunkNodeId = nil;
	self.saw.currentBranchTrunks = {};
	
	self.saw.posOnPallet = { getTranslation(self.saw.nodeId) }
	self.saw.rotOnPallet = { getRotation(self.saw.nodeId) }
	
	local x, y, z = Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.chainSaw.workPos#rotation"));
	self.saw.workRot = { Utils.getNoNil(math.rad(x), 0), Utils.getNoNil(math.rad(y), 0), Utils.getNoNil(math.rad(z), 0)}
	self.saw.workTrans = { Utils.getVectorFromString(getXMLString(xmlFile, "vehicle.chainSaw.workPos#translation")) }
	self.saw.xRotFactor = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chainSaw.workPos#xRotFactor"), 0);
	
	self.saw.fuelCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chainSaw.fuel#capacity"), 5);
	self.saw.fuelUsage = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chainSaw.fuel#usage"), 0.005);
	self.saw.fuelPrice = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chainSaw.fuel#price"), 1);
	self.saw.fuelFillLevel = self.saw.fuelCapacity;
	self.saw.lastSentFuelFillLevel = self.saw.fuelCapacity;
	
	self.saw.turnAnimName = getXMLString(xmlFile, "vehicle.chainSaw.turn#animName");
	
	self.saw.chain = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.chain#index"));
	self.saw.chainSpeed = 0;
	self.saw.chainShaderMaxSpeed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.chainSaw.chain#shaderMaxSpeed"), 0.001);
	self.saw.chainShaderStep = getXMLString(xmlFile, "vehicle.chainSaw.chain#chainShaderStep");
	
	-- particle systems
	local psNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.woodShavingParticleSystems#nodeIndex"));
	self.woodShavings = {}
	Utils.loadParticleSystem(xmlFile, self.woodShavings, "vehicle.chainSaw.woodShavingParticleSystems", self.components, false, "machinery/StihlMS660_toolbox/woodShavingPS.i3d", self.baseDirectory, psNode); 
	
	local psNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.chainSaw.exhaustParticleSystems#nodeIndex"));
	self.exhaustParticles = {}
	Utils.loadParticleSystem(xmlFile, self.exhaustParticles, "vehicle.chainSaw.exhaustParticleSystems", self.components, false, "machinery/StihlMS660_toolbox/smokeParticleSystem.i3d", self.baseDirectory, psNode); 

	-- sounds
	self.saw.sounds = {}
	
	local startSoundFile = getXMLString(xmlFile, "vehicle.chainSaw.sounds.start#file");
	if startSoundFile ~= nil then
		self.saw.startSound = {}
		local sound = self.saw.startSound;
		local key = "vehicle.chainSaw.sounds.start";
		
		local filename = Utils.getFilename(startSoundFile, self.baseDirectory);
		local radius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#radius"), 10);
		local innerRadius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#innerRadius"), 1);
		local volume = Utils.getNoNil(getXMLFloat(xmlFile, key.."#volume"), 1);
		local pitch = Utils.getNoNil(getXMLFloat(xmlFile, key.."#pitch"), 1);
		
		sound.source = createAudioSource("chainSawStartMotorSound", filename, radius, innerRadius, volume, 1);
		sound.sample = getAudioSourceSample(sound.source);
		setSamplePitch(sound.sample, pitch);
		link(self.saw.nodeId, sound.source);
		
		sound.volume = volume;
		sound.pitch = pitch;
		
		setVisibility(sound.source, false);
	end;
	
	local stopSoundFile = getXMLString(xmlFile, "vehicle.chainSaw.sounds.stop#file");
	if stopSoundFile ~= nil then
		self.saw.stopSound = {}
		local sound = self.saw.stopSound;
		local key = "vehicle.chainSaw.sounds.stop";
		
		local filename = Utils.getFilename(stopSoundFile, self.baseDirectory);
		local radius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#radius"), 10);
		local innerRadius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#innerRadius"), 1);
		local volume = Utils.getNoNil(getXMLFloat(xmlFile, key.."#volume"), 1);
		local pitch = Utils.getNoNil(getXMLFloat(xmlFile, key.."#pitch"), 1);
		sound.source = createAudioSource("chainSawStopMotorSound", filename, radius, innerRadius, volume, 1);
		sound.sample = getAudioSourceSample(sound.source);
		setSamplePitch(sound.sample, pitch);
		link(self.saw.nodeId, sound.source);
		
		sound.volume = volume;
		sound.pitch = pitch;
		
		setVisibility(sound.source, false);
	end;
	
	local idleSoundFile = getXMLString(xmlFile, "vehicle.chainSaw.sounds.idle#file");
	if idleSoundFile ~= nil then
		self.saw.idleSound = {}
		local sound = self.saw.idleSound;
		local key = "vehicle.chainSaw.sounds.idle";
		
		local filename = Utils.getFilename(idleSoundFile, self.baseDirectory);
		local radius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#radius"), 10);
		local innerRadius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#innerRadius"), 1);
		local volume = Utils.getNoNil(getXMLFloat(xmlFile, key.."#volume"), 1);
		local pitch = Utils.getNoNil(getXMLFloat(xmlFile, key.."#pitch"), 1);
		
		sound.source = createAudioSource("chainSawMotorIdleSound", filename, radius, innerRadius, volume, 0);
		sound.sample = getAudioSourceSample(sound.source);
		setSamplePitch(sound.sample, pitch);
		link(self.saw.nodeId, sound.source);
		
		sound.volume = volume;
		sound.pitch = pitch;
		
		setVisibility(sound.source, false);
	end;
	
	local runSoundFile = getXMLString(xmlFile, "vehicle.chainSaw.sounds.run#file");
	if runSoundFile ~= nil then
		self.saw.runSound = {}
		local sound = self.saw.runSound;
		
		local key = "vehicle.chainSaw.sounds.run";
		
		local filename = Utils.getFilename(runSoundFile, self.baseDirectory);
		local radius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#radius"), 10);
		local innerRadius = Utils.getNoNil(getXMLFloat(xmlFile, key.."#innerRadius"), 1);
		local volume = Utils.getNoNil(getXMLFloat(xmlFile, key.."#volume"), 1);
		local pitch = Utils.getNoNil(getXMLFloat(xmlFile, key.."#pitch"), 1);
		local maxPitch = Utils.getNoNil(getXMLFloat(xmlFile, key.."#maxPitch"), 1);
		local minPitch = Utils.getNoNil(getXMLFloat(xmlFile, key.."#minPitch"), 1);
				
		sound.source = createAudioSource("chainSawMotorRunSound", filename, radius, innerRadius, volume, 0);
		sound.sample = getAudioSourceSample(sound.source);
		setSamplePitch(sound.sample, pitch);
		link(self.saw.nodeId, sound.source);
		
		sound.volume = volume;
		sound.pitch = pitch;
		sound.maxPitch = maxPitch;
		sound.minPitch = minPitch;
		sound.isRunning = false;
		
		setVisibility(sound.source, false);
	end;
	
	-- gui
	self.saw.gui = {}
	self.saw.gui.x = 0.875;
	self.saw.gui.y = 0.2;
	self.saw.gui.w = 0.075;
	self.saw.gui.h = 0.1;
	
	self.saw.gui.fuel = Overlay:new("fuel", Utils.getFilename(getXMLString(xmlFile, "vehicle.chainSaw.gui.fuelImg#fileName"), self.baseDirectory), self.saw.gui.x, self.saw.gui.y, self.saw.gui.w, self.saw.gui.h);
	self.saw.gui.saw = Overlay:new("saw", Utils.getFilename(getXMLString(xmlFile, "vehicle.chainSaw.gui.sawImg#fileName"), self.baseDirectory), self.saw.gui.x, self.saw.gui.y-1.25*self.saw.gui.h, self.saw.gui.w, self.saw.gui.h);
	self.saw.gui.redBar = Overlay:new("redBar", Utils.getFilename(getXMLString(xmlFile, "vehicle.chainSaw.gui.redBar#fileName"), self.baseDirectory), self.saw.gui.x, self.saw.gui.y, self.saw.gui.w, self.saw.gui.h);
	self.saw.gui.yellowBar = Overlay:new("yellowBar", Utils.getFilename(getXMLString(xmlFile, "vehicle.chainSaw.gui.yellowBar#fileName"), self.baseDirectory), self.saw.gui.x, self.saw.gui.y, self.saw.gui.w, self.saw.gui.h);
	self.saw.gui.greenBar = Overlay:new("greenBar", Utils.getFilename(getXMLString(xmlFile, "vehicle.chainSaw.gui.greenBar#fileName"), self.baseDirectory), self.saw.gui.x, self.saw.gui.y, self.saw.gui.w, self.saw.gui.h);
	self.saw.gui.greenBar2 = Overlay:new("greenBar2", Utils.getFilename(getXMLString(xmlFile, "vehicle.chainSaw.gui.greenBar#fileName"), self.baseDirectory), self.saw.gui.x, self.saw.gui.y-1.25*self.saw.gui.h, self.saw.gui.w, self.saw.gui.h);
end;

function ChainSaw:delete()
	self:stopMotorSaw(true, true);

	removeTrigger(self.playerTriggerId);	
	removeTrigger(self.saw.treeTriggerNodeId);
	removeTrigger(self.saw.branchTreeTriggerNode);
	
	if self.woodShavings ~= nil then
		Utils.deleteParticleSystem(self.woodShavings);
	end;
	if self.exhaustParticles ~= nil then
		Utils.deleteParticleSystem(self.exhaustParticles);
	end;
end;

function ChainSaw:readStream(streamId, connection)
	if streamReadBool(streamId) then
		self.controllerToActivate = streamReadInt32(streamId);
	end;
	
	local isRunning = streamReadBool(streamId);	
	self:setSawIsRunning(isRunning, true);
	
	local val = streamReadUInt8(streamId, 8);
	val = val / 255;
	self.saw.fuelFillLevel = val * self.saw.fuelCapacity;
end;

function ChainSaw:writeStream(streamId, connection)
	streamWriteBool(streamId, self.saw.isActive);
	if self.saw.isActive then
		streamWriteInt32(streamId, networkGetObjectId(self.controller));
	end;
	
	streamWriteBool(streamId, self.saw.isRunning);
	
	local val = self.saw.fuelFillLevel / self.saw.fuelCapacity;
	val = math.ceil( val * 255 );
	streamWriteUInt8(streamId, val, 8);
end;

function ChainSaw:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	local level = getXMLFloat(xmlFile, key.."#sawFuelFillLevel");
	if level ~= nil then
		self.saw.fuelFillLevel = math.max(math.min(level, self.saw.fuelCapacity), 0);
	end;
	return BaseMission.VEHICLE_LOAD_OK;
end;

function ChainSaw:getSaveAttributesAndNodes(nodeIdent)
	local attributes = 'sawFuelFillLevel="'..self.saw.fuelFillLevel..'"';
	return attributes, nil;
end;

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

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

function ChainSaw:update(dt)
	if self.varBody.activeIdx == 4 then
		if self.playerInTrigger then
			if self.saw.isActive then
				if self.controller == g_currentMission.player then
					if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then
						self:stopMotorSaw();
					end;  
					g_currentMission:addHelpButtonText(g_i18n:getText("put_back_motorSaw"), InputBinding.IMPLEMENT_EXTRA3);
					-- refuel
					if self.saw.fuelFillLevel ~= self.saw.fuelCapacity then
						if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
							self:refuelMotorSaw();
						end;
						g_currentMission:addHelpButtonText(g_i18n:getText("fill_motorSaw"), InputBinding.IMPLEMENT_EXTRA2);
					end;
				end;
			elseif self.controller == nil then
				if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) and not g_currentMission.player.hasHandTool then
					self:startMotorSaw(g_currentMission.player);
				end;
				g_currentMission:addHelpButtonText(g_i18n:getText("take_motorSaw"), InputBinding.IMPLEMENT_EXTRA3);
			end;
		end;

		if self.saw.isActive and self.controller == g_currentMission.player and g_gui.currentGui == nil then
			if self.saw.fuelFillLevel > 0 then
				if InputBinding.isPressed(InputBinding.SETSAWRUNNING) then
					if not self.saw.isRunning then
						self:setSawIsRunning(true);
					end;
				else
					self:addHelpButtonTextWithCorrectMouseButtons("SETSAWRUNNING", InputBinding.SETSAWRUNNING, "set_saw_running");
					if self.saw.isRunning then
						self:setSawIsRunning(false);
					end;
				end;
			else
				g_currentMission:addExtraPrintText(g_i18n:getText("fillSawInfo"));
			end;
			
			if InputBinding.isPressed(InputBinding.SETSAWTURNING) then
				if not self.saw.isTurning then
					self:setSawIsTurning(true);
				end;
			else
				self:addHelpButtonTextWithCorrectMouseButtons("SETSAWTURNING", InputBinding.SETSAWTURNING, "turn_saw");
			
				if self.saw.isTurning then
					self:setSawIsTurning(false);
				end;
			end;
		end;
	else
		self.saw.isActive = false;
	end;
end;

function ChainSaw:updateTick(dt)
	if not self.firstRunDone then
		self.firstRunDone = true;
		if self.controllerToActivate ~= nil then
			local controller = networkGetObject(self.controllerToActivate);
			self:startMotorSaw(controller, true);
			self.controllerToActivate = nil;
		end;
	end;
	
	if self.controller ~= nil and self.controller == g_currentMission.player then
		if not self.controller.isEntered and self.saw.isActive then
			self:stopMotorSaw();
		end;
	end;
	
	if self.saw.currentDamageVehicle ~= nil and self.saw.isRunning and self.isServer then
		self.saw.currentDamageVehicle:setDamageLevel(self.saw.currentDamageVehicle.damageLevel + 0.5);
	end;

	if self.saw.isActive and self.isServer and self.saw.fuelFillLevel > 0 then 
		
		local activeCuttingPS = false;
		
		if table.getn(self.saw.currentTrunks) > 0 and self.saw.isRunning then 
			-- get the trunks' joint
			self.saw.currentJointToCut = nil;
			activeCuttingPS = true;
			local x, y, z = getWorldTranslation(self.saw.cutPoint);
			local nearestTrunkDist = math.huge;
			local nearestJointTableIndex;
			local nearestTrunkTrigger;
			for index, trunk in pairs(self.saw.currentTrunks) do
				for k, joint in pairs(trunk.jointsToOtherTrunks) do
					local jx, jy, jz = getWorldTranslation(joint.jointNode);
					local dist = Utils.vector3Length(x-jx, y-jy, z-jz);					
					if dist < self.saw.cutRange and dist < nearestTrunkDist then 
						nearestTrunkDist = dist;
						nearestJointTableIndex = k;
						nearestTrunkTrigger = index;
					end;
				end;
			end;
			if nearestJointTableIndex ~= nil then	
				self.saw.currentJointToCut = self.saw.currentTrunks[nearestTrunkTrigger].jointsToOtherTrunks[nearestJointTableIndex];
				if self.saw.currentJointToCut ~= self.saw.lastJointToCut then
					self.saw.treeContactTimer = self.saw.currentTrunks[nearestTrunkTrigger].jointsToOtherTrunks[nearestJointTableIndex].cutDuration * self.saw.cutDurationFactor;		
					self.saw.treeContactTimerMax = self.saw.treeContactTimer;
					self.saw.lastJointToCut = self.saw.currentJointToCut;
				end;				
			else
				self.saw.currentJointToCut = nil;
				self.saw.lastJointToCut = nil;
			end;			
			
			if self.saw.currentJointToCut ~= nil then
				local val = math.max(0, self.saw.treeContactTimer - dt);
				if self.saw.treeContactTimer ~= val then	
					self.saw.treeContactTimer = val;
					g_server:broadcastEvent(SetSawContactTimerEvent:new(self, val, self.saw.treeContactTimerMax), nil, nil, self); 
				end;
				activeCuttingPS = true;
			end;
			
			-- cut the joint 
			if self.saw.currentJointToCut ~= nil and self.saw.treeContactTimer == 0 then
				local joint = self.saw.currentJointToCut;
				
				local dx,dy,dz = 0,0,0;
				local isRoot = joint.trunk1.isRoot or joint.trunk2.isRoot;
				if isRoot then 
					dx,dy,dz = localDirectionToWorld(self.controller.graphicsRootNode, 1, 0, 0); 			
				end;				
				joint.trunk1:removeTrunkJoint(self.saw.currentTrunks[nearestTrunkTrigger].jointsToOtherTrunks[nearestJointTableIndex].jointTableIndex, dx,dy,dz);
				
				self.saw.currentJointToCut = nil;
				table.remove(self.saw.currentTrunks, nearestTrunkTrigger);
				
				if g_currentMission.treeManager.cuttedTreeJointsForTransportTutorial ~= nil then
					g_currentMission.treeManager.cuttedTreeJointsForTransportTutorial = g_currentMission.treeManager.cuttedTreeJointsForTransportTutorial + 1;
					if g_currentMission.treeManager.cuttedTreeJointsForTransportTutorial > 10 then
						if g_currentMission.treeManager.forrestModTutorial:checkShowTutorial("woodCrane") then
							table.insert(g_currentMission.treeManager.tutorialGuisToOpen, "transport");
						end;
					end;
				end;
				g_currentMission.treeManager.forrestModTutorial:checkShowTutorial("cutBranchesAndSplitTrees");
			end;
		end;
		
		if self.saw.isActive and self.saw.isRunning then
			if self.saw.currentBranchTrunks ~= nil then
				for i,trunk in pairs(self.saw.currentBranchTrunks) do
					local cutX, cutY, cutZ = getWorldTranslation(self.saw.cutPoint);
					if trunk.branches ~= nil then
						for branchNum, nodeId in pairs(trunk.branches) do
							local x, y, z = getWorldTranslation(nodeId);
							local dist = Utils.vector3Length(cutX-x, cutY-y, cutZ-z);
							if dist < trunk.maxBranchCutDist then
								trunk:setBranchInvisible(branchNum);
								activeCuttingPS = true;							
							end;
						end;
					end;
				end;
			end;
		end;	

		if activeCuttingPS ~= self.saw.activeCuttingPS then
			self:setCuttingPS(activeCuttingPS);
		end;
	end;
	
	if self.saw.fuelFillLevel <= 0 then -- switch of everything that needs fuel
		if self.saw.activeCuttingPS then
			self:setCuttingPS(false);
		end;
		if self.saw.isRunning then
			self:setSawIsRunning(false);
		end;
	end;

	if self.isServer and self.saw.isActive then
		if self.saw.isRunning then
			local newLevel = math.max(self.saw.fuelFillLevel-(dt*(self.saw.fuelUsage/1000)), 0);
			if newLevel ~= self.saw.fuelFillLevel then
				self:setSawFuelFillLevel(newLevel);
				if self.saw.fuelFillLevel == 0 then
					self:setSawIsRunning(false);
				end;
			end;
		end;
	end;	
	
	if self.saw.isRunning then
		self.saw.chainSpeed = math.min(self.saw.chainSpeed + self.saw.chainShaderStep, 1);
	else
		self.saw.chainSpeed = math.max(self.saw.chainSpeed-self.saw.chainShaderStep, 0);
	end;

	if self.saw.chainSpeed > 0 then 
		self.saw.lastChainSpeed = self.saw.chainSpeed;
		if not self.saw.runSound.isRunning then
			self.saw.runSound.isRunning = true;
			setVisibility(self.saw.runSound.source, true);
			setVisibility(self.saw.idleSound.source, false);
			playSample(self.saw.runSound.sample, 0, self.saw.runSound.volume, 0);
		end;
		setSamplePitch(self.saw.runSound.sample, math.max(self.saw.runSound.minPitch, math.min(self.saw.runSound.maxPitch, self.saw.chainSpeed*self.saw.runSound.pitch)));
		setShaderParameter(self.saw.chain, "uvScrollSpeed", math.min(self.saw.chainSpeed, self.saw.chainShaderMaxSpeed), 0, 0, 0, false);
	else
		if self.saw.runSound.isRunning then
			self.saw.runSound.isRunning = false;
			stopSample(self.saw.runSound.sample);
			setVisibility(self.saw.runSound.source, false);
			setVisibility(self.saw.idleSound.source, true);
			setShaderParameter(self.saw.chain, "directions", self.saw.chainSpeed, 0, 0, 0, false);
		end;
	end;
	
	-- sync saw x rotation
	if self.saw.isActive and self.controller == g_currentMission.player and g_gui.currentGui == nil then
		local rx,ry,rz = getRotation(g_currentMission.player.cameraId);
		self:setSawXRotation(rx);
	end;	
	
	--# check if 'growing tree' is in range and replace it with a harvestable tree
	if self.isServer and g_currentMission.treeManager ~= nil and self.saw.isActive and self.saw.isTurning then
		local x1,y1,z1 = getWorldTranslation(self.saw.nodeId);
		for i,gt in pairs(g_currentMission.treeManager.growingTrees) do
			if gt.growingTime == gt.growTime then
				local x2,y2,z2 = getWorldTranslation(gt.col);
				local d = Utils.vector3Length(x1-x2,y1-y2,z1-z2);
				if d < 3 then
					g_currentMission.treeManager:removeGrowingTree(i);
					break;					-- replace only one tree in one time step
				end;
			end;
		end;
	end;
end;

function ChainSaw:draw()

end;

function ChainSaw:chainSawDraw()
	self.saw.gui.fuel:render();
	local fillFactor = self.saw.fuelFillLevel / self.saw.fuelCapacity;
	if fillFactor > 0.25 then
		self.saw.gui.greenBar:render();
	elseif fillFactor > 0.1 then
		self.saw.gui.yellowBar:render();
	elseif fillFactor > 0 then
		self.saw.gui.redBar:render();
	end;
	
	if table.getn(self.saw.currentTrunks) > 0 and self.saw.treeContactTimer > 0 then
		self.saw.gui.saw:render();
		setTextColor(0.803, 0.803, 0.803, 1);
		renderText(0.875, 0.10, 0.045, tostring(math.floor(((self.saw.treeContactTimer / self.saw.treeContactTimerMax)*100))).."%"); 
		setTextColor(1, 1, 1, 1);
	end;
end;

function ChainSaw:addHelpButtonTextWithCorrectMouseButtons(inputBindingName, inputBinding, text)
	local inputBindingActionIndex = InputBinding[inputBindingName];
	local isMouseButton = false;
	local buttonName = nil;
	
	if inputBindingActionIndex ~= nil then
		if InputBinding.actions[inputBindingActionIndex] ~= nil then
			local buttonIndex = InputBinding.actions[inputBindingActionIndex].mouseButtons[1];
			if buttonIndex ~= nil then
				if buttonIndex == 1 then
					isMouseButton = true;
					buttonName = g_i18n:getText("leftMouseButton");
				end;
				if buttonIndex == 2 then
					isMouseButton = true;
					buttonName = g_i18n:getText("middleMouseButton");
				end;
				if buttonIndex == 3 then
					isMouseButton = true;
					buttonName = g_i18n:getText("rightMouseButton");
				end;
			end;
		end;
	end;
	
	if isMouseButton then
		g_currentMission:addExtraPrintText(buttonName..": "..g_i18n:getText(text));
	else
		g_currentMission:addHelpButtonText(g_i18n:getText(text), inputBinding);
	end;
end;	

function ChainSaw:playerTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
	if self.firstRunDone and g_currentMission.player ~= nil then
		if otherId == g_currentMission.player.rootNode then
			if onEnter then
				self.playerInTrigger = true;
			end;
			if onLeave then
				self.playerInTrigger = false;
			end;
		end;
	end;
end;

function ChainSaw:chainSawTreeCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
	local trunk = g_currentMission.treeManager.nodeIdToTrunk[otherId];
	if trunk ~= nil then
		if onEnter then
			local enter = true;
			for i,t in pairs(self.saw.currentTrunks) do
				if t == trunk then
					enter = false;
					break;
				end;
			end;
			if enter then
				table.insert(self.saw.currentTrunks, trunk);
			end;
		elseif onLeave then
			local rem = false;
			local idx;
			for i,t in pairs(self.saw.currentTrunks) do
				if t == trunk then
					rem = true;
					idx = i;
					break;
				end;
			end;
			if rem then
				table.remove(self.saw.currentTrunks, idx);
			end;				
		end;
	end;
		
	if self.isServer then		
		local damageVehicle = g_currentMission.nodeToVehicle[otherId];
		if damageVehicle ~= nil and damageVehicle.setDamageLevel ~= nil then
			if onEnter then
				if not damageVehicle.dontAllowDamage then
					self.saw.currentDamageVehicle = damageVehicle;
				end;
			elseif onLeave then
				if self.saw.currentDamageVehicle == damageVehicle then
					self.saw.currentDamageVehicle = nil;
				end;
			end;
		end;
	end;
end;

function ChainSaw:startMotorSaw(player, noEventSend)
	if self.saw.isActive then
		return;
	end;
	
	if player == g_currentMission.player then
		g_currentMission.treeManager.chainSawNeedsDrawing = self;
		addTimer(getSampleDuration(self.saw.startSound.sample)*0.9, "idleTimer", self);
	end;
	
	if self.saw.startSound ~= nil and self.saw.fuelFillLevel > 0 then
		playSample(self.saw.startSound.sample, 1, self.saw.startSound.volume, 0);
		addTimer(getSampleDuration(self.saw.startSound.sample), "stopStartSound", self);
	end;
  
	self.saw.isActive = true;
	player.hasHandTool = true;
	player.walkingSpeed = 0.003;
	player.currentSaw = self;
	self.controller = player;
	
	link(player.graphicsRootNode, self.saw.nodeId);
	
	setTranslation(self.saw.nodeId, unpack(self.saw.workTrans)); 
	setRotation(self.saw.nodeId, unpack(self.saw.workRot)); 
	setVisibility(self.saw.bladeProtection, false);
	
	if not noEventSend then
		StartMotorSawEvent.sendEvent(self, player, noEventSend);
	end;
end;

function ChainSaw:stopMotorSaw(noEventSend, delete)
	if self.controller == g_currentMission.player then
		g_currentMission.treeManager.chainSawNeedsDrawing = nil;
	end;

	self:setSawIsRunning(false);
	self:stopAnimation(self.saw.turnAnimName);
	self:setAnimationTime(self.saw.turnAnimName, 0, true);

	if self.saw.stopSound ~= nil and not delete and self.saw.fuelFillLevel > 0 then
		setVisibility(self.saw.stopSound.source, true);
		playSample(self.saw.stopSound.sample, 1, self.saw.stopSound.volume, 0);
		addTimer(getSampleDuration(self.saw.stopSound.sample), "stopStopSound", self);
	end;
	
	stopSample(self.saw.idleSound.sample);
	setVisibility(self.saw.idleSound.source, false);
	
	self.saw.currentDamageVehicle = nil;
	self.saw.isActive = false;
	if self.controller ~= nil then
		self.controller.hasHandTool = false;	
		self.controller.walkingSpeed = 0.004;
		self.controller.currentSaw = nil;
		removeContactReport(self.saw.nodeId);
		link(self.components[1].node, self.saw.nodeId);
		setTranslation(self.saw.nodeId, unpack(self.saw.posOnPallet));
		setRotation(self.saw.nodeId, unpack(self.saw.rotOnPallet));
		setVisibility(self.saw.bladeProtection, true);
	end;

	self.controller = nil;
	
	if not noEventSend then
		StopMotorSawEvent.sendEvent(self, noEventSend);
	end;
end;

function ChainSaw:setSawIsRunning(isRunning, noEventSend)
	SetSawIsRunningEvent.sendEvent(self, isRunning, noEventSend);
	self.saw.isRunning = isRunning; 
	
	Utils.setEmittingState(self.exhaustParticles, self.saw.isRunning);
end;

function ChainSaw:setSawIsTurning(isTurning, noEventSend)
	self.saw.isTurning = isTurning;
	if isTurning then
		self:playAnimation(self.saw.turnAnimName, 1, nil);
	else
		self:playAnimation(self.saw.turnAnimName, -1, nil);
	end;
	if not noEventSend then
		SetSawIsTurningEvent.sendEvent(self, self.saw.isTurning, noEventSend);
	end;
end;

function ChainSaw:setSawFuelFillLevel(newFuelFillLEvel)
	self.saw.fuelFillLevel = math.max(math.min(newFuelFillLEvel, self.saw.fuelCapacity), 0);
	if self.isServer then 
		local newSentLevel = self.saw.fuelFillLevel;
		if self.saw.lastSentFuelFillLevel ~= newSentLevel then
			g_server:broadcastEvent(SetSawFuelFillLevelEvent:new(self, newSentLevel), nil, nil, self); 
			self.saw.lastSentFuelFillLevel = newSentLevel;
		end;
	end;
end;

function ChainSaw:refuelMotorSaw(noEventSend)
	if self.isServer then
		local fuelAmount = self.saw.fuelCapacity - self.saw.fuelFillLevel;
		local price = self.saw.fuelPrice * fuelAmount;
		g_currentMission:addSharedMoney(-price, "other");
		self:setSawFuelFillLevel(self.saw.fuelCapacity);
	else
		RefuelMotorSawEvent.sendEvent(self, noEventSend);
	end;
end;

function ChainSaw:chainSawBranchTreeCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
	if self.isServer then
		if onEnter then
			local trunk = g_currentMission.treeManager.nodeIdToTrunk[otherId];
			if trunk ~= nil then
				local insert = true;
				for i,t in pairs(self.saw.currentBranchTrunks) do
					if t == trunk then
						insert = false;
					end;
				end;
				if insert then
					table.insert(self.saw.currentBranchTrunks, trunk);
				end;
			end;
		elseif onLeave then
			local trunk = g_currentMission.treeManager.nodeIdToTrunk[otherId];
			if trunk ~= nil then
				for i,t in pairs(self.saw.currentBranchTrunks) do
					if t == trunk then
						table.remove(self.saw.currentBranchTrunks, i);
						break;
					end;
				end;
			end;
		end;
	end;
end;

function ChainSaw:idleTimer()
	if self.saw.isActive then
		setVisibility(self.saw.idleSound.source, true);
		playSample(self.saw.idleSound.sample, 0, self.saw.idleSound.volume, 0);
	end;
	return false;
end;

function ChainSaw:stopStartSound()
	setVisibility(self.saw.startSound.source, false);
	return false;
end;

function ChainSaw:stopStopSound()
	setVisibility(self.saw.stopSound.source, false);
	return false;
end;

function ChainSaw:setCuttingPS(state, noEventSend)
	SetCuttingPSEvent.sendEvent(self, state, noEventSend);
	self.saw.activeCuttingPS = state;
	Utils.setEmittingState( self.woodShavings, state );
end;

function ChainSaw:setSawXRotation(xRot, noEventSend)
	SetSawXRotationEvent.sendEvent(self, xRot, noEventSend);
	if set ~= false then
		setRotation(self.saw.nodeId, xRot,math.rad(20),math.rad(-10));
		b =  math.asin( math.max(-1.0, math.min(1.0, xRot)) ) * 1.0;
		setTranslation(self.saw.nodeId, 0.45, 0.0+math.max(0, b/2), -0.6-math.min(0, b/2));
	end;	
end;

local oldPlayerDelete = Player.delete;
Player.delete = function(self)
	if self.isServer then
		if self.currentSaw ~= nil then
			self.currentSaw:stopMotorSaw();
		end;
	end;
	oldPlayerDelete(self);
end;