-- CultivatorSpinners
-- Specialisation for the CultivatorSpinners
--
-- @ Autor  Tobias F. (John Deere 6930)
-- @ Last Edit  31/12/2010

CultivatorSpinners = {};

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

function CultivatorSpinners:load(xmlFile)

	self.groundContactReport = SpecializationUtil.callSpecializationsFunction("groundContactReport");
	
	self.contactReportNodes = {};
	local contactReportNodeFound = false;
	local i=0;
	while true do
		local baseName = string.format("vehicle.contactReportNodes.contactReportNode(%d)", i);
		local index = getXMLString(xmlFile, baseName.. "#index");
		if index == nil then
			break;
		end;
		local node = Utils.indexToObject(self.components, index);
		if node ~= nil then
			local entry = {};
			entry.node = node;
			entry.hasGroundContact = false;

			self.contactReportNodes[node] = entry;
			contactReportNodeFound = true;
		end;
		i = i+1;
	end;
	if not contactReportNodeFound then
		local entry = {};
		entry.node = self.components[1].node;
		entry.hasGroundContact = false;
		self.contactReportNodes[entry.node] = entry;
	end;

	self.groundReferenceThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.groundReferenceNode#threshold"), 0.2);
	self.groundReferenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.groundReferenceNode#index"));

	local numCuttingAreas = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.cuttingAreas#count"), 0);
	for i=1, numCuttingAreas do
		local areanamei = string.format("vehicle.cuttingAreas.cuttingArea%d", i);
		self.cuttingAreas[i].foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, areanamei .. "#foldMinLimit"), 0);
		self.cuttingAreas[i].foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, areanamei .. "#foldMaxLimit"), 1);
	end;

	local cultivatorSound = getXMLString(xmlFile, "vehicle.cultivatorSound#file");
	if cultivatorSound ~= nil and cultivatorSound ~= "" then
		cultivatorSound = Utils.getFilename(cultivatorSound, self.baseDirectory);
		self.cultivatorSound = createSample("cultivatorSound");
		loadSample(self.cultivatorSound, cultivatorSound, false);
		self.cultivatorSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cultivatorSound#pitchOffset"), 0);
		self.cultivatorSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.cultivatorSound#volume"), 1.0);
		self.cultivatorSoundEnabled = false;
	end;
	
	local rotarySound = getXMLString(xmlFile, "vehicle.rotarySound#file");
	if rotarySound ~= nil and rotarySound ~= "" then
		rotarySound = Utils.getFilename(rotarySound, self.baseDirectory);
		self.rotarySound = createSample("rotarySound");
		loadSample(self.rotarySound, rotarySound, false);
		self.rotarySoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotarySound#pitchOffset"), 0);
		self.rotarySoundVolume = 0.0;
		self.rotarySoundVolumeMin = 0.0;
		self.rotarySoundVolumeMax = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotarySound#volume"), 1.0);
		self.rotarySoundEnabled = false;
	end;
  
	self.groundParticleSystems = {};
	local psName = "vehicle.groundParticleSystem";
	Utils.loadParticleSystem(xmlFile, self.groundParticleSystems, psName, self.components, false, nil, self.baseDirectory)
 
	self.newGroundParticleSystems = {};
	local i=0;
	while true do
		local baseName = string.format("vehicle.groundParticleSystems.groundParticleSystem(%d)", i);
		if not hasXMLProperty(xmlFile, baseName) then
			break;
		end;
		local entry = {};
		entry.ps = {};
		Utils.loadParticleSystem(xmlFile, entry.ps, baseName, self.components, false, nil, self.baseDirectory);
		if table.getn(entry.ps) > 0 then
			entry.isActive = false;
			table.insert(self.newGroundParticleSystems, entry);
		end;
		i = i+1;
	end;
	
	self.rotateSpinners = {};
	local i=0;
	while true do
		local baseName = string.format("vehicle.rotateSpinners.node(%d)", i);
		local index = getXMLString(xmlFile, baseName.. "#index");
		local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, baseName.. "#rotationSpeed"));
		local rotationSpeed = {x,y,z};
		local runOutTime = Utils.getNoNil(getXMLFloat(xmlFile, baseName.. "#runOutTime"), 2)*1000;
		if index == nil or rotationSpeed == nil or runOutTime == nil then
			break;
		end;
		local node = Utils.indexToObject(self.components, index);
		if node ~= nil then
			local entry = {};
			entry.node = node;
			entry.runOutTime = runOutTime;
			entry.rotationSpeedMax = rotationSpeed;
			entry.rotationSpeedMin = {0,0,0};
			entry.rotationSpeedCurrent = {0,0,0};
			table.insert(self.rotateSpinners, entry);
		end;
		i = i+1;
	end;
	
	self.onlyActiveWhenLowered = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.onlyActiveWhenLowered#value"), true);
	
	self.aiTerrainDetailChannel1 = g_currentMission.ploughChannel;
	self.aiTerrainDetailChannel2 = g_currentMission.sowingChannel;
	
	self.maxSpeedLevel = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.maxSpeedLevel#value"), 1);
	self.speedViolationMaxTime = 2500;
	self.speedViolationTimer = self.speedViolationMaxTime;
	self.cultivatorContactReportsActive = false;
	self.startActivationTimeout = 2000;
	self.startActivationTime = 0;
 
	self.cultivatorHasGroundContact = false;
 
	self.cultivatorLimitToField = false;
	self.cultivatorForceLimitToField = true;
 
	self.cultivatorGroundContactFlag = self.nextDirtyFlag;
	self.nextDirtyFlag = self.cultivatorGroundContactFlag*2;
 
	self.cuttingAreasSend = {};
	
    self.setIsTurnedOn = SpecializationUtil.callSpecializationsFunction("setIsTurnedOn");
	self.isTurnedOn = false;
end;

function CultivatorSpinners:delete()
	Utils.deleteParticleSystem(self.groundParticleSystems);
	for _, entry in ipairs(self.newGroundParticleSystems) do
		Utils.deleteParticleSystem(entry.ps);
	end;
	CultivatorSpinners.removeContactReports(self);
	if self.cultivatorSound ~= nil then
		delete(self.cultivatorSound);
	end;
	if self.rotarySound ~= nil then
		delete(self.rotarySound);
	end;
end;
 
function CultivatorSpinners:readUpdateStream(streamId, timestamp, connection)
	if connection:getIsServer() then
		self.cultivatorHasGroundContact = streamReadBool(streamId);
	end;
end;

function CultivatorSpinners:writeUpdateStream(streamId, connection, dirtyMask)
	if not connection:getIsServer() then
		streamWriteBool(streamId, self.cultivatorHasGroundContact);
	end;
end;

function SowingMachine:readStream(streamId, connection)
	local turnedOn = streamReadBool(streamId);
	self:setIsTurnedOn(turnedOn, true);
end;

function SowingMachine:writeStream(streamId, connection)
	streamWriteBool(streamId, self.isTurnedOn);
end;

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

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

function CultivatorSpinners:setIsTurnedOn(turnedOn, noEventSend)
	SetTurnedOnEvent.sendEvent(self, turnedOn, noEventSend)
	self.isTurnedOn = turnedOn;
end;

function CultivatorSpinners:update(dt)
	if self:getIsActiveForInput() then
		if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then
			self:setIsTurnedOn(not self.isTurnedOn);
		end;
	end;
end;

function CultivatorSpinners:updateTick(dt)
	if self:getIsActive() then
		local hasGroundContact = false;
		if self.isServer then
			for k, v in pairs(self.contactReportNodes) do
				if v.hasGroundContact then
					hasGroundContact = true;
					break;
				end;
			end;
			
			if not hasGroundContact then
				if self.groundReferenceNode ~= nil then
					local x,y,z = getWorldTranslation(self.groundReferenceNode);
					local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z);
					if terrainHeight+self.groundReferenceThreshold >= y then
						hasGroundContact = true;
					end;
				end;
			end;
			if self.cultivatorHasGroundContact ~= hasGroundContact then
				self:raiseDirtyFlags(self.cultivatorGroundContactFlag);
			end;
			self.cultivatorHasGroundContact = hasGroundContact;
		end;
		local hasGroundContact = self.cultivatorHasGroundContact;
		
		local volume = Utils.getMovedLimitedValues({self.rotarySoundVolume}, {self.rotarySoundVolumeMax}, {self.rotarySoundVolumeMin}, 1, 1200, dt, not self.isTurnedOn);
		self.rotarySoundVolume = volume[1];
		
		if self.isTurnedOn then
			if not self.rotarySoundEnabled and self:getIsActiveForSound() then
				playSample(self.rotarySound, 0, self.rotarySoundVolume, 0);
				setSamplePitch(self.rotarySound, self.rotarySoundPitchOffset);
				self.rotarySoundEnabled = true;
			end;
			if self.rotarySoundVolume < self.rotarySoundVolumeMax then
				setSampleVolume(self.rotarySound, self.rotarySoundVolume);
			end;
			if hasGroundContact and (not self.onlyActiveWhenLowered or self:isLowered(false)) then
				local enableGroundParticleSystems = (self.lastSpeed*3600 > 5);
				if self.isClient then
					if self.cultivatorSound ~= nil then
						if self.lastSpeed*3600 > 3 then
							if not self.cultivatorSoundEnabled and self:getIsActiveForSound() then
								playSample(self.cultivatorSound, 0, self.cultivatorSoundVolume, 0);
								setSamplePitch(self.cultivatorSound, self.cultivatorSoundPitchOffset);
								self.cultivatorSoundEnabled = true;
							end;
						else
							if self.cultivatorSoundEnabled then
								stopSample(self.cultivatorSound);
								self.cultivatorSoundEnabled = false;
							end;
						end;
					end;
					Utils.setEmittingState(self.groundParticleSystems, enableGroundParticleSystems);
					if enableGroundParticleSystems then
						for k, cuttingArea in pairs(self.cuttingAreas) do
							local ps = self.newGroundParticleSystems[k];
							if self:getIsAreaActive(cuttingArea) then
								if ps ~= nil then
									if not ps.isActive then
										ps.isActive = true;
										Utils.setEmittingState(ps.ps, true);
									end;
								end;
							else
								if ps ~= nil and ps.isActive then
									ps.isActive = false;
									Utils.setEmittingState(ps.ps, false);
								end;
							end;
						end;
					else
						for k, ps in pairs(self.newGroundParticleSystems) do
							if ps.isActive then
								ps.isActive = false;
								Utils.setEmittingState(ps.ps, false);
							end;
						end;
					end;
				end;
				
				if self.startActivationTime <= self.time then
					if self.isServer then
						local cuttingAreasSend = {};
						for k, cuttingArea in pairs(self.cuttingAreas) do
							if self:getIsAreaActive(cuttingArea) then
								local x,y,z = getWorldTranslation(cuttingArea.start);
								local x1,y1,z1 = getWorldTranslation(cuttingArea.width);
								local x2,y2,z2 = getWorldTranslation(cuttingArea.height);
								table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});
							end;
						end;
						if table.getn(cuttingAreasSend) > 0 then
							local limitToField = self.cultivatorLimitToField or self.cultivatorForceLimitToField;
							if not g_currentMission.allowClientsCreateFields then
								local owner = self:getOwner();
								if owner ~= nil and not owner:getIsLocal() then
									limitToField = true;
								end;
							end;
							CultivatorAreaEvent.runLocally(cuttingAreasSend, limitToField);
							g_server:broadcastEvent(CultivatorAreaEvent:new(cuttingAreasSend, limitToField));
						end;
					end;
					
					local speedLimit = 20;
					if self.maxSpeedLevel == 2 then
						speedLimit = 30;
					elseif self.maxSpeedLevel == 3 then
						speedLimit = 100;
					end;
					if self:doCheckSpeedLimit() and self.lastSpeed*3600 > speedLimit then
						self.speedViolationTimer = self.speedViolationTimer - dt;
						if self.isServer then
							if self.speedViolationTimer < 0 then
								if self.attacherVehicle then
									self.attacherVehicle:detachImplementByObject(self);
								end;
							end;
						end;
					else
						self.speedViolationTimer = self.speedViolationMaxTime;
					end;
				end;
			else
				self.speedViolationTimer = self.speedViolationMaxTime;
				if self.isClient then
					if self.cultivatorSoundEnabled then
						stopSample(self.cultivatorSound);
						self.cultivatorSoundEnabled = false;
					end;
					Utils.setEmittingState(self.groundParticleSystems, false);
					for k, ps in pairs(self.newGroundParticleSystems) do
						if ps.isActive then
							ps.isActive = false;
							Utils.setEmittingState(ps.ps, false);
						end;
					end;
				end;
			end;
		else
			if self.rotarySoundEnabled then
				if self.rotarySoundVolume <= self.rotarySoundVolumeMin then
					stopSample(self.rotarySound);
					self.rotarySoundEnabled = false;
				else
					setSampleVolume(self.rotarySound, self.rotarySoundVolume);
				end;
			end;
		end;
		for k, spinner in pairs(self.rotateSpinners) do
			local values = Utils.getMovedLimitedValues(spinner.rotationSpeedCurrent, spinner.rotationSpeedMax, spinner.rotationSpeedMin, 3, spinner.runOutTime, dt, not self.isTurnedOn);
			spinner.rotationSpeedCurrent = values;
			rotate(spinner.node, unpack(spinner.rotationSpeedCurrent));
		end;
	end;
end;
 
function CultivatorSpinners:draw()

	if not self.isTurnedOn then	
        g_currentMission:addHelpButtonText(g_i18n:getText("activate"), InputBinding.IMPLEMENT_EXTRA);
	else
        g_currentMission:addHelpButtonText(g_i18n:getText("deactivate"), InputBinding.IMPLEMENT_EXTRA);
	end;

	if math.abs(self.speedViolationTimer - self.speedViolationMaxTime) > 2 then
		local buttonName = InputBinding.SPEED_LEVEL1;
		if self.maxSpeedLevel == 2 then
			buttonName = InputBinding.SPEED_LEVEL2;
		elseif self.maxSpeedLevel == 3 then
			buttonName = InputBinding.SPEED_LEVEL3;
		end;
		g_currentMission:addWarning(g_i18n:getText("Dont_drive_to_fast") .. "\n" .. string.format(g_i18n:getText("Cruise_control_levelN"), tostring(self.maxSpeedLevel), InputBinding.getKeyNamesOfDigitalAction(buttonName)), 0.07+0.022, 0.019+0.029);
	end;
end;

function CultivatorSpinners:onAttach(attacherVehicle)
	CultivatorSpinners.onActivate(self);
	CultivatorSpinners.addContactReports(self);
	self.startActivationTime = self.time + self.startActivationTimeout;
end;

function CultivatorSpinners:onDetach()
	self.cultivatorLimitToField = false;
	if self.deactivateOnDetach then
		CultivatorSpinners.onDeactivate(self);
		CultivatorSpinners.removeContactReports(self);
	else
		CultivatorSpinners.onDeactivateSounds(self);
	end;
end;

function CultivatorSpinners:onEnter(isControlling)
	if isControlling then
		CultivatorSpinners.onActivate(self);
		CultivatorSpinners.addContactReports(self);
	end;
end;

function CultivatorSpinners:onLeave()
	if self.deactivateOnLeave then
		CultivatorSpinners.onDeactivate(self);
		CultivatorSpinners.removeContactReports(self);
	else
		CultivatorSpinners.onDeactivateSounds(self);
	end;
end;
 
function CultivatorSpinners:onActivate()
end;
 
function CultivatorSpinners:onDeactivate()
	self.speedViolationTimer = self.speedViolationMaxTime;
	Utils.setEmittingState(self.groundParticleSystems, false);
	for k, ps in pairs(self.newGroundParticleSystems) do
		if ps.isActive then
			ps.isActive = false;
			Utils.setEmittingState(ps.ps, false);
		end;
	end;
	self:setIsTurnedOn(false, true);
	CultivatorSpinners.onDeactivateSounds(self);
end;
 
function CultivatorSpinners:onDeactivateSounds()
	if self.cultivatorSoundEnabled then
		stopSample(self.cultivatorSound);
		self.cultivatorSoundEnabled = false;
	end;
	if self.rotarySoundEnabled then
		stopSample(self.rotarySound);
		self.rotarySoundEnabled = false;
	end;
end;
 
function CultivatorSpinners:aiTurnOn()
	self.cultivatorLimitToField = true;
end;
 
function CultivatorSpinners:aiTurnOff()
	self.cultivatorLimitToField = false;
end;

function CultivatorSpinners:addContactReports()
	if not self.cultivatorContactReportsActive then
		for k, v in pairs(self.contactReportNodes) do
			addContactReport(v.node, 0.0001, "groundContactReport", self);
		end;
		self.cultivatorContactReportsActive = true;
	end;
end;

function CultivatorSpinners:removeContactReports()
	if self.cultivatorContactReportsActive then
		for k, v in pairs(self.contactReportNodes) do
			removeContactReport(v.node);
			v.hasGroundContact = false;
		end;
		self.cultivatorContactReportsActive = false;
	end;
end;

function CultivatorSpinners:groundContactReport(objectId, otherObjectId, isStart, normalForce, tangentialForce)
	if otherObjectId == g_currentMission.terrainRootNode then
		local entry = self.contactReportNodes[objectId];
		if entry ~= nil then
			entry.hasGroundContact = isStart or normalForce > 0 or tangentialForce > 0;
		end;
	end;
end;