--[[************************************************************************************************************************************************************
	
	mrFallingWheels v 1.0.5 BETA
	
	11/2013 by Saty / http://forum.lsczech.cz
	
****************************************************************************************************************************************************************
	
	(EN) INFO
	
	Free for use on mods. Modifications only with my permission !
	
****************************************************************************************************************************************************************
	
	(CZ) INFO
	
	Pro volne pouziti v modech. ZAKAZ modifikaci bez meho souhlasu !
	
************************************************************************************************************************************************************]]--

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 jointIndex = getXMLInt(xmlFile, wheelnamei .. "#componentJointIndex");
		if jointIndex ~= nil then
			if self.componentJoints[jointIndex] ~= nil then
				local x,y,z = {};
				self.wheels[i+1].jointIndex = jointIndex;
				self.wheels[i+1].dualWheel = Utils.indexToObject(self.components, getXMLString(xmlFile, wheelnamei .. "#dualWheelNode"));
				self.wheels[i+1].dualWheelActive = false;
				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.4) * 1000;
				self.wheels[i+1].tireWidth = Utils.getNoNil(getXMLFloat(xmlFile, wheelnamei .. "#tireWidth"), 0.25) / 2;
				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;
				
				local realWheelComponent = getXMLFloat(xmlFile, wheelnamei .. "#realWheelComponent");
				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;
				
				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].node, area.s);
					link(self.wheels[i+1].node, area.w);
					link(self.wheels[i+1].node, area.h);
					setTranslation(area.s, -self.wheels[i+1].tireWidth * 2, 0.1, -self.wheels[i+1].tireWidth * 2);
					setTranslation(area.w,	self.wheels[i+1].tireWidth * 2, 0.1, -self.wheels[i+1].tireWidth * 2);
					setTranslation(area.h, -self.wheels[i+1].tireWidth * 2, 0.1,  self.wheels[i+1].tireWidth * 2);
					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].tireWidth * 2, 0.1, -self.wheels[i+1].tireWidth * 2);
						setTranslation(areaDual.w,  self.wheels[i+1].tireWidth * 2, 0.1, -self.wheels[i+1].tireWidth * 2);
						setTranslation(areaDual.h, -self.wheels[i+1].tireWidth * 2, 0.1,  self.wheels[i+1].tireWidth * 2);
						self.wheels[i+1].areaDual = areaDual;
					end;
				end;
			else
				print("mrFallingWheels Error: invalid componentJointIndex " .. jointIndex .. " for wheel(" .. i .. ")");
			end;
		end;
		i = i + 1;
	end;
	
	local test = 0;
	for _,wheel in pairs(self.wheels) do
		if wheel.jointIndex ~= nil then
			test = test + 1;
		end;
	end;
	if test == 0 then
		print("mrFallingWheels Error: no data for wheels (none or bad writing format)");
	end
	
	self.aiSlipFalling = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.aiSlipFalling"), true);
	
	self.realVehicleFlotationFxBackup = self.realVehicleFlotationFx;
	self.realRollingResistanceBackup = self.realRollingResistance;
end;

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

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

function mrFallingWheels:update(dt)
	local realVehicleFlotationFx = 0;
	local realRollingResistance = 0;
	for _,wheel in pairs(self.wheels) do
		local dualWheelActive = getVisibility(wheel.dualWheel)
		if dualWheelActive then
			realVehicleFlotationFx = realVehicleFlotationFx + 0.06;
			realRollingResistance = realRollingResistance + 0.005;
		end;
		wheel.dualWheelActive = dualWheelActive;
	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 wheel.jointIndex ~= nil then
			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;
			
			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.tireWidth, zC + wheel.tireWidth, xC - wheel.tireWidth, zC + wheel.tireWidth, xC + 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 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};
			local jointNodeBounce = {x, wheel.jointNodeDefault - (wheel.slowFurrow / 2) - wheel.rainFurrow - wheel.slipFurrow, z};
			
			if not densActive then
				jointNodeBounce = jointNodeMax;
			end;
			
			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, 1000) / (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;

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;