--
-- Cultivator
-- Class for all Cultivators
-- 
-- @author  Stefan Geiger
-- @date  04/02/08
--
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
-- EDIT derSchreiner
putrefaction = {};

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

function putrefaction:load(xmlFile)

    self.groundContactReport = SpecializationUtil.callSpecializationsFunction("groundContactReport");

    self.speedRotatingParts = {};
    local i=0;
    while true do
        local baseName = string.format("vehicle.speedRotatingParts.speedRotatingPart(%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.rotationSpeedScale = getXMLFloat(xmlFile, baseName.."#rotationSpeedScale");
            if entry.rotationSpeedScale == nil then
                entry.rotationSpeedScale = 1.0/Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#radius"), 1);
            end;

            entry.rotateOnGroundContact = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#rotateOnGroundContact"), false);

            entry.foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMinLimit"), 0);
            entry.foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMaxLimit"), 1);

            table.insert(self.speedRotatingParts, entry);
        end;
        i = i+1;
    end;

    local drumNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.drum#index"));
    if drumNode ~= nil then
        print("Warning: vehicle.drum is no longer used, use speedRotatingParts\n");
    end;

    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;


    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.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 = {};
end;

function putrefaction:delete()

    Utils.deleteParticleSystem(self.groundParticleSystems);
    
    for _, entry in ipairs(self.newGroundParticleSystems) do
        Utils.deleteParticleSystem(entry.ps);
    end;
    
    putrefaction.removeContactReports(self);

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

end;

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

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

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

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

function putrefaction:update(dt)
end;

function putrefaction: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 rotateRotatingParts = false;

        local foldAnimTime = self.foldAnimTime;
        if hasGroundContact and (not self.onlyActiveWhenLowered or self:isLowered(false)) then
            rotateRotatingParts = true;
            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;

                        putrefactionEvent.runLocally(cuttingAreasSend, limitToField);
                        g_server:broadcastEvent(putrefactionEvent: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;

        if self.isClient then
            local updateWheelRotatingParts = hasGroundContact;
            if not updateWheelRotatingParts then
                for k,v in pairs(self.wheels) do
                    if v.hasGroundContact then
                        updateWheelRotatingParts = true;
                        break;
                    end;
                end;
            end;
            if updateWheelRotatingParts then
                for k,v in pairs(self.speedRotatingParts) do
                    if rotateRotatingParts or v.rotateOnGroundContact then
                        if foldAnimTime == nil or (foldAnimTime <= v.foldMaxLimit and foldAnimTime >= v.foldMinLimit) then
                            rotate(v.node, v.rotationSpeedScale * self.lastSpeedReal * self.movingDirection * dt, 0, 0);
                        end;
                    end;
                end;
            end;
        end;
    end;

end;

function putrefaction:draw()
    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 putrefaction:onAttach(attacherVehicle)
    putrefaction.onActivate(self);
    putrefaction.addContactReports(self);
    self.startActivationTime = self.time + self.startActivationTimeout;
end;

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

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

function putrefaction:onLeave()
    if self.deactivateOnLeave then
        putrefaction.onDeactivate(self);
        putrefaction.removeContactReports(self);
    else
        putrefaction.onDeactivateSounds(self);
    end;
end;

function putrefaction:onActivate()
end;

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

    putrefaction.onDeactivateSounds(self);
end;

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

function putrefaction:aiTurnOn()
    self.cultivatorLimitToField = true;
end;

function putrefaction:aiTurnOff()
    self.cultivatorLimitToField = false;
end;

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