--
-- Name: PowerHarrow
-- Description: Specialization for power harrows
-- Version: 1.0
-- Author: Templaer
-- Date: 07/05/2011
--
-- Edit by Fendt413 

source("dataS/scripts/vehicles/specializations/CultivatorAreaEvent.lua");
source("dataS/scripts/vehicles/specializations/SetTurnedOnEvent.lua");

PowerHarrow = {}

function PowerHarrow.prerequisitesPresent(specializations)
	return SpecializationUtil.hasSpecialization(Attachable, specializations);
end;

function PowerHarrow:load(xmlFile)

	-- Register specializations --
	self.groundContactReport = SpecializationUtil.callSpecializationsFunction("groundContactReport");
	self.setIsTurnedOn = SpecializationUtil.callSpecializationsFunction("setIsTurnedOn");
	
	-- AI channels --
	self.aiTerrainDetailChannel1 = g_currentMission.ploughChannel;
	self.aiTerrainDetailChannel2 = g_currentMission.cultivatorChannel;
	
	-- Contact report nodes --
	self.contactReportNodes = {};
	local contactReportNodeFound = false;
	local i = 0;
	while true do
		local baseName = string.format("vehicle.contactReportNodes.contactReportNode(%d)", i);
		if not hasXMLProperty(xmlFile, baseName) then
			break;
		end;
		
		local index = getXMLString(xmlFile, baseName .. "#index");
		if index == nil then
			break;
		end;
		
		local node = Utils.indexToObject(self.components, index);
		if node == nil then
			break;
		end;
		
		local entry = {};
		entry.node = node;
		entry.hasGroundContact = false;

		self.contactReportNodes[node] = entry;
		
		contactReportNodeFound = true;
		
		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.contactReportsActive = false; 
    self.groundContactFlag = self:getNextDirtyFlag();
	self.hasGroundContact = false;
	
	-- Cutting areas --
	local numCultivatorCuttingAreas = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.cultivatorCuttingAreas#count"), 0);
	self.cultivatorCuttingAreas = {}
	for i=1, numCultivatorCuttingAreas do
		self.cultivatorCuttingAreas[i] = {};
		local areanamei = string.format("vehicle.cultivatorCuttingAreas.cultivatorCuttingArea%d", i);
		self.cultivatorCuttingAreas[i].start = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#startIndex"));
		self.cultivatorCuttingAreas[i].width = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#widthIndex"));
		self.cultivatorCuttingAreas[i].height = Utils.indexToObject(self.components, getXMLString(xmlFile, areanamei .. "#heightIndex"));
	end;
 	local numCultivatorCuttingAreas = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.cultivatorCuttingAreas#count"), 0);
	for i=1, numCultivatorCuttingAreas do
		local areanamei = string.format("vehicle.cultivatorCuttingAreas.cultivatorCuttingArea%d", i);
		self.cultivatorCuttingAreas[i].foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, areanamei .. "#foldMinLimit"), 0);
		self.cultivatorCuttingAreas[i].foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, areanamei .. "#foldMaxLimit"), 1);
	end;

	-- Ground reference --
	self.groundReferenceThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.groundReferenceNode#threshold"), 0.2);
    self.groundReferenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.groundReferenceNode#index"));
	
	-- Particle systems --
	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;

	-- Power rotating parts --
	self.powerRotatingParts = {};
	local i = 0;
	while true do
	    local baseName = string.format("vehicle.powerRotatingParts.powerRotatingPart(%d)", i);
		if not hasXMLProperty(xmlFile, baseName) then
			break;
		end;
		
		local index = getXMLString(xmlFile, baseName .. "#index");
		if index == nil then
			break;
		end;
		
		local node = Utils.indexToObject(self.components, index);
		if node == nil then
		    break;
		end;
		
		local entry = {};
		entry.node = node;
		entry.rotationSpeed = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#rotationSpeedScale"), 0.04);
		
		table.insert(self.powerRotatingParts, entry);
		
		i = i + 1;
	end;

	self.spinTime = 0;
	self.spinMaxTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.powerRotatingParts#spinMaxTime"), 2500);
		
	-- Speed rotating parts --
	self.speedRotatingParts = {};
	local i = 0;
	while true do
	    local baseName = string.format("vehicle.speedRotatingParts.speedRotatingPart(%d)", i);
		if not hasXMLProperty(xmlFile, baseName) then
			break;
		end;
		
		local index = getXMLString(xmlFile, baseName .. "#index");
		if index == nil then
			break;
		end;
		
		local node = Utils.indexToObject(self.components, index);
		if node == nil then
		    break;
		end;
		
		local entry = {};
		entry.node = node;
		
		entry.rotationSpeedScale = getXMLFloat(xmlFile, baseName .. "#rotationSpeedScale");
		if entry.rotationSpeedScale == nil then
		    entry.rotationSpeedScale = 1.0 / Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#radius"), 1);
		end;
		
		entry.foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMinLimit"), 0);
		entry.foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMaxLimit"), 1);
		entry.rotateOnGroundContact = Utils.getNoNil(getXMLBool(xmlFile, baseName .. "#rotateOnGroundContact"), false);
		
		table.insert(self.speedRotatingParts, entry);
		
		i = i + 1;
	end;
	
	-- Sounds --
	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 rotorSound = getXMLString(xmlFile, "vehicle.rotorSound#file");
	if rotorSound ~= nil and rotorSound ~= "" then
		rotorSound = Utils.getFilename(rotorSound, self.baseDirectory);
		self.rotorSound = createSample("rotorSound");
		loadSample(self.rotorSound, rotorSound, false);
		self.rotorSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotorSound#pitchOffset"), 0);
		self.rotorSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.rotorSound#volume"), 1.0);
		self.rotorSoundEnabled = false;
	end;
	
	-- Speed violation --
	self.maxSpeedLevel = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.maxSpeedLevel#value"), 1);
    self.speedViolationMaxTime = 2500;
    self.speedViolationTimer = self.speedViolationMaxTime;
	
	-- Other variables --
	self.isTurnedOn = false;

    self.startActivationTimeout = 2000;
    self.startActivationTime = 0;

end;

function PowerHarrow:delete()

	-- Contact report nodes --
	PowerHarrow.removeContactReports(self);

	-- Particle systems --
    Utils.deleteParticleSystem(self.groundParticleSystems);
       
    for _, entry in ipairs(self.newGroundParticleSystems) do
        Utils.deleteParticleSystem(entry.ps);
    end;
	
	-- Sounds --
	if self.cultivatorSound ~= nil then
		delete(self.cultivatorSound);
	end;

	if self.rotorSound ~= nil then
		delete(self.rotorSound);
	end;
end;

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

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

function PowerHarrow:readUpdateStream(streamId, timestamp, connection)
    if connection:getIsServer() then
        self.hasGroundContact = streamReadBool(streamId);
    end;
end;

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

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

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

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

function PowerHarrow:updateTick(dt)
	if self:getIsActive() then
		
		local hasGroundContact = false;
		
		if self.isServer then
		
			-- Ground contact --
			if not hasGroundContact then
				for k, v in pairs(self.contactReportNodes) do
					if v.hasGroundContact then
						hasGroundContact = true;
						break;
					end;
				end;
				
				for k,v in pairs(self.wheels) 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.hasGroundContact ~= hasGroundContact then
					self:raiseDirtyFlags(self.groundContactFlag);
				end;
			end;
			self.hasGroundContact = hasGroundContact;
			
			-- Cutting areas --
			if hasGroundContact and self.isTurnedOn and self:isLowered(false) then
				if self.startActivationTime <= self.time then

					local cuttingAreasSend = {};
					
					for k, cultivatorCuttingArea in pairs(self.cultivatorCuttingAreas) do
						if self:getIsAreaActive(cultivatorCuttingArea) then
							local x,y,z = getWorldTranslation(cultivatorCuttingArea.start);
							local x1,y1,z1 = getWorldTranslation(cultivatorCuttingArea.width);
							local x2,y2,z2 = getWorldTranslation(cultivatorCuttingArea.height);

							table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});
						end;
					end;
					
					if table.getn(cuttingAreasSend) > 0 then
						local limitToField = true;

						CultivatorAreaEvent.runLocally(cuttingAreasSend, limitToField);
						g_server:broadcastEvent(CultivatorAreaEvent:new(cuttingAreasSend, limitToField));
					end;
					
				end;
			end;
			
		end;
		
		if self.isClient then
		
			-- Particle systems --			
			local enableGroundParticleSystems = (self.lastSpeed * 3600 > 5) and hasGroundContact and self.isTurnedOn and self:isLowered(false);
			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;
		
			-- Sounds --
			if self:getIsActiveForSound() then
			
				if self.rotorSound ~= nil then
					if self.isTurnedOn then
						if not self.rotorSoundEnabled then
						
							playSample(self.rotorSound, 0, 0, 0);
							setSamplePitch(self.rotorSound, self.rotorSoundPitchOffset);
							self.rotorSoundEnabled = true;
							
						elseif self.spinTime > self.time then
						
							if getSampleVolume(self.rotorSound) < self.rotorSoundVolume then
								local percentage = math.min((self.spinMaxTime - (self.spinTime - self.time)) / self.spinMaxTime, 1);
								setSampleVolume(self.rotorSound, percentage * self.rotorSoundVolume);								
							end;
							
						end;
					else
						if self.rotorSoundEnabled then
							if getSampleVolume(self.rotorSound) > 0 then
								local percentage = math.max((self.spinTime - self.time) / self.spinMaxTime, 0);
								setSampleVolume(self.rotorSound, percentage * self.rotorSoundVolume);
							else
								stopSample(self.rotorSound);
								self.rotorSoundEnabled = false;
							end;
						end;
					end;
				end;
				
				if self.cultivatorSound ~= nil then
					if hasGroundContact and self.isTurnedOn and self:isLowered(false) and self.lastSpeed * 3600 > 3 then	
						if not self.cultivatorSoundEnabled 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;
				
			end;
		end;
		
		-- Power rotating parts --
		if self.isTurnedOn then
		
			local percentage = math.min((self.spinMaxTime - (self.spinTime - self.time)) / self.spinMaxTime, 1);
			for k,v in pairs(self.powerRotatingParts) do
				if math.mod(k, 2) == 0 then
					rotate(v.node, 0, percentage * v.rotationSpeed * 1 * dt, 0);
				else
					rotate(v.node, 0, percentage * v.rotationSpeed * -1 * dt, 0);
				end;	
			end;
			
		elseif self.spinTime > self.time then
		
			local percentage = math.max((self.spinTime - self.time) / self.spinMaxTime, 0);
			for k,v in pairs(self.powerRotatingParts) do
				if math.mod(k, 2) == 0 then
					rotate(v.node, 0, percentage * v.rotationSpeed * 1 * dt, 0);
				else
					rotate(v.node, 0, percentage * v.rotationSpeed * -1 * dt, 0);
				end;	
			end;
			
		end;
		
		-- Speed rotating parts --
		if self.movingDirection ~= 0 and hasGroundContact then
			for k,v in pairs(self.speedRotatingParts) do
				if self.foldAnimTime == nil or (self.foldAnimTime <= v.foldMaxLimit and self.foldAnimTime >= v.foldMinLimit) then
					rotate(v.node, v.rotationSpeedScale * self.lastSpeedReal * self.movingDirection * dt, 0, 0);
				end;
			end;
		end;
		
		-- Speed violation --
		local speedLimit = 20;
		if self.maxSpeedLevel == 2 then
			speedLimit = 30;
		elseif self.maxSpeedLevel == 3 then
			speedLimit = 100;
		end;
		
		if hasGroundContact and self.isTurnedOn and self:isLowered(false) then	
			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;
		else
			self.speedViolationTimer = self.speedViolationMaxTime;
		end;
	end;	
end;

function PowerHarrow:draw()
	if self.isClient then
	
		if self.isTurnedOn then
			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_off_OBJECT"), self.typeDesc), InputBinding.IMPLEMENT_EXTRA);
		else
			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("turn_on_OBJECT"), self.typeDesc), 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;
end;

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

function PowerHarrow:onDetach()
    if self.deactivateOnDetach then
        PowerHarrow.onDeactivate(self);
        PowerHarrow.removeContactReports(self);
    else
        PowerHarrow.onDeactivateSounds(self);
    end;
end;

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

function PowerHarrow:onLeave()
    if self.deactivateOnLeave then
        PowerHarrow.onDeactivate(self);
        PowerHarrow.removeContactReports(self);
    end;
end;

function PowerHarrow:onActivate()
end;

function PowerHarrow:onDeactivate()
	self.speedViolationTimer = self.speedViolationMaxTime;

    if self.groundParticleSystemActive then
        self.groundParticleSystemActive = false;
        Utils.setEmittingState(self.groundParticleSystems, false);
    end;
 
    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);
	PowerHarrow.onDeactivateSounds(self);
end;

function PowerHarrow:onDeactivateSounds()
	if self.cultivatorSoundEnabled then
		stopSample(self.cultivatorSound);
		self.cultivatorSoundEnabled = false;
	end;
	
	if self.rotorSoundEnabled then
		stopSample(self.rotorSound);
		self.rotorSoundEnabled = false;
	end;
end;

function PowerHarrow:setIsTurnedOn(isTurnedOn, noEventSend)
	SetTurnedOnEvent.sendEvent(self, isTurnedOn, noEventSend)
	self.isTurnedOn = isTurnedOn;
	
	if not noEventSend then
		if self.spinTime < self.time then
			self.spinTime = self.time + self.spinMaxTime;
		else
			self.spinTime = self.time + (self.spinMaxTime - (self.spinTime - self.time));
		end;
	end;
end;

function PowerHarrow:aiTurnOn()
	self.isTurnedOn = true;
	self.spinTime = self.time + self.spinMaxTime;
end;

function PowerHarrow:aiLower()
	self.isTurnedOn = true;
	self.spinTime = self.time + self.spinMaxTime;
end;

function PowerHarrow:aiRaise()
	self.isTurnedOn = false;
	self.spinTime = self.time + self.spinMaxTime;
end;

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

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

function PowerHarrow: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;

