--[[************************************************************************************************************************************************************
	
	mrFallingWheels v 1.2.1
	
	07/2014 © Saty MODS (Saty / http://forum.lsczech.cz)
	
****************************************************************************************************************************************************************
	
	(EN) INFO
	
	Free for use on mods. Modifications only with my permission !
	
	This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (Attribution / NonCommercial / NoDerivatives)
	http://creativecommons.org/licenses/by-nc-nd/4.0/deed.en
	
****************************************************************************************************************************************************************
	
	(CZ) INFO
	
	Pro volne pouziti v modech. ZAKAZ modifikaci bez meho souhlasu !
	
	Toto dilo podleha licenci : Creative Commons 4.0 International - Uvedte autora / Neuzivejte dilo komercne / Nezasahujte do dila 
	http://creativecommons.org/licenses/by-nc-nd/4.0/deed.cs
	
	--------------------------------------------------------------------------------------------------------------------------------------------------------	
	 
	Zapis do XML souboru stroje :
	*****************************
	
	<wheels autoRotateBackSpeed="1">
		<wheel (zde originalni hodnoty...) componentJointIndex="2" />	(Toto je zakladni nutny zapis, zbytek skript umi doplnit sam...)
		<wheel (zde originalni hodnoty...) componentJointIndex="2" realWheelComponent="1" furrowDepth="0.2" fallSpeed="0.5" tireWidth="0.25" correctionX="0.1" dualWheelNode="1>1|0" dualTireWidth="0.25" dualCorrectionX="0.1" />
	</wheels>
	
	<aiSlipFalling>false</aiSlipFalling>
	
	<movingParts>
		<movingPart index="#" referencePoint="#" referenceFrame="#" isActiveDirty="true" isActiveFW="true" />
	<movingParts>
	
	
	Vysvetlivky k funkcim:
	
	componentJointIndex		- index komponentu kola (musi byt stejny jako u REPR, napr. pri repr="2>1" je componentJointIndex="2")
	realWheelComponent		- hlavni komponent ke kteremu se zpetne pridava hmotnost kola (pri nezadani defaultne hodnota "1" = pokud se nejedna napr. o kloubovy traktor tak masi byt vzdy 1)
	furrowDepth				- hloubka zapadnuti kola v metrech (pri nezadani defaultne hodnota "0.2" = nedavat zbytecne moc, za deste se zdvojnasobi + stejna hloubka se pripocitava pri zahrabani)
	fallSpeed				- rychlost zapdnuti/vynoreni z brazdy (pri nezadani defaultne hodnota "0.5")
	tireWidth				- sirka pneumatiky (pri nezadani defaultne hodnota "0.25")
	correctionX				- korekce pozice vyhledavani brazdy a stopy zahrabavani v ose X oproti driveNode (pri nezadani defaultne hodnota "0" = lza zadat +/- hodnotu)
	dualWheelNode			- index zdvojeneho kola, ktere se da skryt pres klavesu (pri viditelnosti prida stopu prokluzu a upravi vlastnosti kola jako valivy odpor a zabrr kola)
	dualTireWidth			- sirka pneumatiky zdvojeneho kola(pri nezadani defaultne hodnota prevzata z "tireWidth")
	dualCorrectionX			- korekce pozice stopy zahrabavani v ose X oproti dualWheelNode (pri nezadani defaultne hodnota "0" = lza zadat +/- hodnotu)
	
	aiSlipFalling			- funkce zahrabavani pri pouziti pomocnika (pri nezadani defaultne hodnota "true" = zahrabava se)
	
	isActiveFW				- urceno pro tahla rizeni atd., ktera jsou umistena nebo maji refPoint na oddelenem komponentu (aktivni pouze s isActiveDirty="true")
	
	
	V pripade nezadani hodnoty "componentJointIndex" bude skript fungovat pouze v rezimu vytvareni stop prokluzu !!!
	
	--------------------------------------------------------------------------------------------------------------------------------------------------------	
	
	Zapis do souboru modDesc.xml :
	******************************
	
	vsadit do <specializations> :
		<specialization name="mrFallingWheels" className="mrFallingWheels" filename="scripts/mrFallingWheels.lua"/> 
	
	vsadit do <vehicleTypes> :
		<specialization name="mrFallingWheels"/>
	
************************************************************************************************************************************************************]]--

mrFallingWheels = {};

source("dataS/scripts/vehicles/specializations/PloughRotationEvent.lua");
source("dataS/scripts/vehicles/specializations/PloughLimitToFieldEvent.lua");
source("dataS/scripts/vehicles/specializations/PloughAreaEvent.lua");

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

function mrFallingWheels:load(xmlFile)
	local i = 0;
	while true do
		local wheelnamei = string.format("vehicle.wheels.wheel(%d)", i);
		if self.wheels[i+1] == nil then
			break;
		end;
		
		local tireWidth = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#tireWidth"), 0.25) / 2;
		local correctionX = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#correctionX"), 0);
		self.wheels[i+1].tireWidth = tireWidth;
		self.wheels[i+1].correctionX = correctionX;
		
		self.wheels[i+1].dualWheel = Utils.indexToObject(self.components, getXMLString(xmlFile, wheelnamei .. "#dualWheelNode"));
		if self.wheels[i+1].dualWheel ~= nil then
			local dualTireWidth = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#dualTireWidth"), tireWidth) / 2;
			local dualCorrectionX = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#dualCorrectionX"), 0);
			self.wheels[i+1].dualTireWidth = dualTireWidth;
			self.wheels[i+1].dualCorrectionX = dualCorrectionX;
		end;
		self.wheels[i+1].dualWheelActive = false;
		
		if self.isServer and self.motor ~= nil then
			local area = {};
			area.s = createTransformGroup(string.format("ploughS%d", i+1));
			area.w = createTransformGroup(string.format("ploughW%d", i+1));
			area.h = createTransformGroup(string.format("ploughH%d", i+1));
			link(self.wheels[i+1].driveNode, area.s);
			link(self.wheels[i+1].driveNode, area.w);
			link(self.wheels[i+1].driveNode, area.h);
			setTranslation(area.s, (correctionX - tireWidth), -1, -tireWidth);
			setTranslation(area.w, (correctionX + tireWidth), -1, -tireWidth);
			setTranslation(area.h, (correctionX - tireWidth),  1,  tireWidth);
			self.wheels[i+1].area = area;
			if self.wheels[i+1].dualWheel ~= nil then
				local areaDual = {};
				areaDual.s = createTransformGroup(string.format("ploughS%d", i+1));
				areaDual.w = createTransformGroup(string.format("ploughW%d", i+1));
				areaDual.h = createTransformGroup(string.format("ploughH%d", i+1));
				link(self.wheels[i+1].dualWheel, areaDual.s);
				link(self.wheels[i+1].dualWheel, areaDual.w);
				link(self.wheels[i+1].dualWheel, areaDual.h);
				setTranslation(areaDual.s, (self.wheels[i+1].dualCorrectionX - self.wheels[i+1].dualTireWidth), -1, -self.wheels[i+1].dualTireWidth);
				setTranslation(areaDual.w, (self.wheels[i+1].dualCorrectionX + self.wheels[i+1].dualTireWidth), -1, -self.wheels[i+1].dualTireWidth);
				setTranslation(areaDual.h, (self.wheels[i+1].dualCorrectionX - self.wheels[i+1].dualTireWidth),  1,  self.wheels[i+1].dualTireWidth);
				self.wheels[i+1].areaDual = areaDual;
			end;
		end;
		
		local jointIndex = getXMLInt(xmlFile, wheelnamei .. "#componentJointIndex");
		if jointIndex ~= nil then
			if self.componentJoints[jointIndex] ~= nil then
				self.wheels[i+1].isComponent = true;
				local x,y,z = {};
				self.wheels[i+1].jointIndex = jointIndex;
				self.wheels[i+1].furrowDepth = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#furrowDepth"), 0.2);
				self.wheels[i+1].slowFurrow = 0;
				self.wheels[i+1].rainFurrow = 0;
				self.wheels[i+1].slipFurrow = 0;
				self.wheels[i+1].fallSpeed = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#fallSpeed"), 0.5) * 1000;
				x,y,z = getTranslation(self.componentJoints[jointIndex].jointNode);
				self.wheels[i+1].jointNodeDefault = y;
				x,y,z = getTranslation(self.wheels[i+1].repr);
				self.wheels[i+1].wheelDefault = y;
				self.wheels[i+1].bounce = false;
				self.wheels[i+1].bounceTime = 0;
				self.wheels[i+1].positionDiff = 0;
				self.wheels[i+1].jointNodeBounce = 0;
				
				local realWheelComponent = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#realWheelComponent"), 1);
				if realWheelComponent ~= nil then
					if self.components[realWheelComponent] ~= nil then
						if self.components[realWheelComponent].realWheels == nil then
							self.components[realWheelComponent].realWheels = {};
						end;
						table.insert(self.components[realWheelComponent].realWheels, self.wheels[i+1]);
						
						if self.realComponentsWithWheels[realWheelComponent] == nil then
							self.realComponentsWithWheels[realWheelComponent] = self.components[realWheelComponent];
						end;
					end;
				end;
				
				local innerNode = createTransformGroup("innerNode");
				link(self.wheels[i+1].node, innerNode);
				setTranslation(innerNode, x - 1, y, z);
				local outerNode = createTransformGroup("outerNode");
				link(self.wheels[i+1].node, outerNode);
				setTranslation(outerNode, x + 1, y, z);
				local forwardNode = createTransformGroup("forwardNode");
				link(self.wheels[i+1].node, forwardNode);
				setTranslation(forwardNode, x, y, z - 1);
				local backwardNode = createTransformGroup("backwardNode");
				link(self.wheels[i+1].node, backwardNode);
				setTranslation(backwardNode, x, y, z + 1);
				self.wheels[i+1].innerNode = innerNode;
				self.wheels[i+1].outerNode = outerNode;
				self.wheels[i+1].forwardNode = forwardNode;
				self.wheels[i+1].backwardNode = backwardNode;
			else
				print("mrFallingWheels Error: invalid componentJointIndex " .. jointIndex .. " for wheel(" .. i .. ")");
			end;
		end;
		i = i + 1;
	end;
	
	self.aiSlipFalling = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.aiSlipFalling"), true);
	
	self.realVehicleFlotationFxBackup = self.realVehicleFlotationFx;
	self.realRollingResistanceBackup = self.realRollingResistance;
	
	self.activeFWDirtyUpdateTime = 100;
end;

function mrFallingWheels:postLoad(xmlFile)
	self.activeFWDirtyMovingParts = {};
	local i = 0;
	while true do
		local baseName = string.format("vehicle.movingParts.movingPart(%d)", i);
		if not hasXMLProperty(xmlFile, baseName) then
			break;
		end;
		
		local node = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#index"));
		if Utils.getNoNil(getXMLBool(xmlFile, baseName.."#isActiveFW"), false) then
			for i , part in pairs(self.activeDirtyMovingParts) do
				if part.node == node then
					table.insert(self.activeFWDirtyMovingParts, part);
					table.remove(self.activeDirtyMovingParts, i);
				end;
			end;
		end;
		i = i + 1;
	end;
end;

function mrFallingWheels:readUpdateStream(streamId, timestamp, connection)
	if connection:getIsServer() then
		for _,wheel in pairs(self.wheels) do
			if wheel.isComponent then
				wheel.positionDiff = streamReadFloat32(streamId);
			end;
		end;
	end;
end;

function mrFallingWheels:writeUpdateStream(streamId, connection, dirtyMask)
	if not connection:getIsServer() then
		for _,wheel in pairs(self.wheels) do
			if wheel.isComponent then
				streamWriteFloat32(streamId, wheel.positionDiff);
			end;
		end;
	end;
end;

function mrFallingWheels:updateTick(dt)
	if self:getIsActive() then
		self.activeFWDirtyUpdateTime = 100;
	end;
	
	if self.activeFWDirtyUpdateTime > 0 then
		self.activeFWDirtyUpdateTime = self.activeFWDirtyUpdateTime - 1
		
		for _, part in ipairs(self.activeFWDirtyMovingParts) do
			Cylindered.updateMovingPart(self, part, false);
		end;
	end;
end;

function mrFallingWheels:update(dt)
	if self.activeFWDirtyUpdateTime > 0 then
		
		local realVehicleFlotationFx = 0;
		local realRollingResistance = 0;
		for _,wheel in pairs(self.wheels) do
			local dualWheelActive = getVisibility(wheel.dualWheel)
			if dualWheelActive and self.wheelsSwitchMode == nil then
				realVehicleFlotationFx = realVehicleFlotationFx + 0.06;
				realRollingResistance = realRollingResistance + 0.005;
			end;
			wheel.dualWheelActive = dualWheelActive;
		end;
		
		if not self.aiSlipFalling and self.isHired then
			realVehicleFlotationFx = realVehicleFlotationFx + 0.1;
		end;
		
		self.realVehicleFlotationFx = self.realVehicleFlotationFxBackup + realVehicleFlotationFx;
		self.realRollingResistance = self.realRollingResistanceBackup + realRollingResistance;
		
		local implementLoweredForExhaust = 0;
		for i=1, table.getn(self.attachedImplements) do
			if self.attachedImplements[i].object:isLowered(false) then
				implementLoweredForExhaust = implementLoweredForExhaust + 0.1;
			end;
		end;
		
		local densActiveForExhaust = 0;
		for _,wheel in pairs(self.wheels) do
			if self.isServer and self.realIsMotorized then 
				if self.realLastMotorFx > 0.5 and self.realGroundSpeed < 1 and wheel.realLastSlip > 0.3 then
					local cuttingAreasSend = {};
					if self:getIsAreaActive(wheel.area) then
						local x,_,z = getWorldTranslation(wheel.area.s);
						local x1,_,z1 = getWorldTranslation(wheel.area.w);
						local x2,_,z2 = getWorldTranslation(wheel.area.h);
						table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});
					end;
					if wheel.dualWheelActive and wheel.areaDual ~= nil then
						if self:getIsAreaActive(wheel.areaDual) then
							local x,_,z = getWorldTranslation(wheel.areaDual.s);
							local x1,_,z1 = getWorldTranslation(wheel.areaDual.w);
							local x2,_,z2 = getWorldTranslation(wheel.areaDual.h);
							table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});
						end;
					end;
					if (table.getn(cuttingAreasSend) > 0) then
						local dx,_,dz = localDirectionToWorld(wheel.repr, 0, 0, 1);
						local angle = Utils.convertToDensityMapAngle(Utils.getYRotationFromDirection(dx, dz), g_currentMission.terrainDetailAngleMaxValue);
						local realArea = PloughAreaEvent.runLocally(cuttingAreasSend, true, true, angle);
						g_server:broadcastEvent(PloughAreaEvent:new(cuttingAreasSend, true, true, angle));
					end;
				end;
			end;
			
			if wheel.isComponent then
				local xC, yC, zC = getWorldTranslation(wheel.driveNode);
				local xI, yI, zI = getWorldTranslation(wheel.innerNode);
				local xO, yO, zO = getWorldTranslation(wheel.outerNode);
				local xF, yF, zF = getWorldTranslation(wheel.forwardNode);
				local xB, yB, zB = getWorldTranslation(wheel.backwardNode);
				local densC = Utils.getDensity(g_currentMission.terrainDetailId, g_currentMission.ploughChannel, xC + wheel.correctionX + wheel.tireWidth, zC + wheel.tireWidth, xC + wheel.correctionX - wheel.tireWidth, zC + wheel.tireWidth, xC + wheel.correctionX + wheel.tireWidth, zC - wheel.tireWidth);
				local densI = Utils.getDensity(g_currentMission.terrainDetailId, g_currentMission.ploughChannel, xI + 0.1, zI + 0.1, xI - 0.1, zI + 0.1, xI + 0.1, zI - 0.1);
				local densO = Utils.getDensity(g_currentMission.terrainDetailId, g_currentMission.ploughChannel, xO + 0.1, zO + 0.1, xO - 0.1, zO + 0.1, xO + 0.1, zO - 0.1);
				local densF = Utils.getDensity(g_currentMission.terrainDetailId, g_currentMission.ploughChannel, xF + 0.1, zF + 0.1, xF - 0.1, zF + 0.1, xF + 0.1, zF - 0.1);
				local densB = Utils.getDensity(g_currentMission.terrainDetailId, g_currentMission.ploughChannel, xB + 0.1, zB + 0.1, xB - 0.1, zB + 0.1, xB + 0.1, zB - 0.1);
				local densActive = densC > 0;
				local densInner = densI > 0;
				local densOuter = densO > 0;
				local densForward = densF > 0;
				local densBackward = densB > 0;
				
				if densActive and ((not densInner and not densOuter) or (not densForward and not densBackward)) and wheel.slipFurrow == 0 then
					densActive = false;
				end;
				
				if densActive then
					densActiveForExhaust = densActiveForExhaust + 1;
				end;
				
				local furrowDepth = wheel.furrowDepth;
				
				if not self.aiSlipFalling and self.isHired then
					furrowDepth = furrowDepth / 2;
				end;
				
				if wheel.dualWheelActive then
					furrowDepth = furrowDepth / 2;
				end;
				if self.realFillVolume ~= nil then
					local fx = (furrowDepth * 0.33) / self.realBaseCapacity;
					furrowDepth = (furrowDepth * 0.66) + (self.realFillVolume * fx);
				end;
				
				if densActive then
					if wheel.slowFurrow ~= furrowDepth then
						wheel.slowFurrow = math.min(furrowDepth, wheel.slowFurrow + (dt * 0.001));
					end;
				else
					if wheel.slowFurrow ~= 0 then
						wheel.slowFurrow = math.max(0, wheel.slowFurrow - (dt * 0.001));
					end;
				end;
				
				if g_currentMission.environment.lastRainScale > 0 then
					if wheel.rainFurrow ~= furrowDepth / 2 then
						wheel.rainFurrow = math.min(furrowDepth / 2, wheel.rainFurrow + (dt * 0.000005));
					end;
				else
					if wheel.rainFurrow ~= 0 and (self.realGroundSpeed > 0.3 or not densActive) then
						wheel.rainFurrow = math.max(0, wheel.rainFurrow - (dt * 0.000001));
					end;
				end;
				
				if self.aiSlipFalling or (not self.aiSlipFalling and not self.isHired) then
					if wheel.slipFurrow ~= furrowDepth and wheel.hasGroundContact and self.realGroundSpeed < 1 and wheel.realLastSlip > 0.3 then
						wheel.slipFurrow = math.min(furrowDepth , wheel.slipFurrow + (dt * (wheel.realLastSlip * 0.00001)));
					end;
					if wheel.slipFurrow ~= 0 and self.realGroundSpeed > 1 and wheel.realLastSlip < 0.3 then
						wheel.slipFurrow = math.max(0, wheel.slipFurrow - (dt * 0.0001));
					end;
				end;
				
				local fallSpeed = math.min(wheel.fallSpeed * 1.5, wheel.fallSpeed + ((furrowDepth - wheel.rainFurrow) * 1000));
				
				local x,y,z = getTranslation(self.componentJoints[wheel.jointIndex].jointNode);
				local trans = {x, y, z};
				local jointNodeMax = {x, wheel.jointNodeDefault - wheel.slowFurrow - wheel.rainFurrow - wheel.slipFurrow, z};
				
				if not densActive then
					wheel.jointNodeBounce = math.min(wheel.slowFurrow, wheel.jointNodeBounce + (fallSpeed * 0.000001));
				else
					wheel.jointNodeBounce = math.max(wheel.slowFurrow / 2, wheel.jointNodeBounce - (fallSpeed * 0.000001));
				end;
				
				local jointNodeBounce = {x, wheel.jointNodeDefault - (wheel.slowFurrow - wheel.jointNodeBounce) - wheel.rainFurrow - wheel.slipFurrow, z};
				
				if self.realGroundSpeed > 0.3 then
					wheel.bounceTime = wheel.bounceTime - dt;
					if wheel.bounceTime <= 0 then
						wheel.bounce = not wheel.bounce;
						wheel.bounceTime = math.random(500, 2000) / (self.realGroundSpeed * 0.5);
					end;
				end;
				
				newTrans = Utils.getMovedLimitedValues(trans, jointNodeMax, jointNodeBounce, 3, fallSpeed, dt, not wheel.bounce);
				setTranslation(self.componentJoints[wheel.jointIndex].jointNode, unpack(newTrans));
				
				if self.isServer then
					local x,y,z = getTranslation(self.componentJoints[wheel.jointIndex].jointNode);
					wheel.positionDiff = wheel.jointNodeDefault + y;
				end;
				setTranslation(wheel.repr, 0, wheel.wheelDefault + wheel.positionDiff, 0);
				
				if self.componentJoints[wheel.jointIndex].jointIndex ~= nil then
					setJointFrame(self.componentJoints[wheel.jointIndex].jointIndex, 1 ,self.componentJoints[wheel.jointIndex].jointNode);
				end;
			end;
		end;
		
		local workExhaustAlpha = 0;
		if self.realLastMotorFx ~= nil then
			if self.realLastMotorFx > 0.3 then
				workExhaustAlpha = workExhaustAlpha + implementLoweredForExhaust + (densActiveForExhaust * 0.05);
			end;
		end;
		self.workExhaustAlpha = workExhaustAlpha;
	end;
end;

function mrFallingWheels:delete()
end;

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

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

function mrFallingWheels:draw()
end;