--[[************************************************************************************************************************************************************
    
    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(savegame)
    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;