KuhnLSB1270 = {};

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

function KuhnLSB1270:load(xmlFile)

    self.moveBalesOut = KuhnLSB1270.moveBalesOut;
    self.setJointLimit = SpecializationUtil.callSpecializationsFunction("setJointLimit");
    self.setTransportMode = SpecializationUtil.callSpecializationsFunction("setTransportMode");
    self.allowPickingUp = KuhnLSB1270.allowPickingUp;
    self.setBaleTime = Baler.setBaleTime;

    self.pickupRotLimit = {0};

    self.balecounterX = 0.8143
    self.balecounterY = 0.81
    self.balecounterW = 0.176
    self.balecounterH = 0.06638
    self.balecounterFontSize = 0.032
    self.balecounterHud = Overlay:new("counterOverlay", Utils.getFilename("Balecounter_hud.dds", self.baseDirectory), self.balecounterX, self.balecounterY, self.balecounterW, self.balecounterH);

    self.baleCount = 0;
    self.lastBaleCount = 0;
    self.moveBalesOutside = false;
    self.transportMode = true;
    self.printWait = false;
    self.allowPick = false;

    self.allowPickingUp = KuhnLSB1270.allowPickingUp;
    --Utils.overwrittenFunction(self.allowPickingUp, KuhnLSB1270.allowPickingUp);
    self.backTimer = 0;

    -- RPM
    self.setVehicleIncreaseRpm = SpecializationUtil.callSpecializationsFunction("setVehicleIncreaseRpm");
    self.saveMinimumRpm = 0;
    --#
    self.actLoad = 0;
    self.actLoad_Int = 0;
    self.actLoad_IntSend = 0;
    self.setActLoad = SpecializationUtil.callSpecializationsFunction("setActLoad");

    -- Particle systems
    self.psActiveState = false
    self.psTimeout = 0
    self.psTurnoffDelayMs = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.particleSystems#turnoffDelayMs"), 1000);
    self.psArray = {}
    local i = 0
    while true do
        local keyPS = string.format("vehicle.particleSystems.particleSystem(%d)", i)
        local psName = getXMLString(xmlFile, keyPS.."#name")
        if psName == nil then
            break
        end
        self.psArray[psName] = {}
        Utils.loadParticleSystem(xmlFile, self.psArray[psName], keyPS, self.components, false, nil, self.baseDirectory);
        i = i + 1
    end
end;

function KuhnLSB1270:delete()
    for _,ps in pairs(self.psArray) do
        Utils.deleteParticleSystem(ps);
    end

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

function KuhnLSB1270:readStream(streamId, connection)
    self.baleCount = streamReadInt32(streamId);
end;

function KuhnLSB1270:writeStream(streamId, connection)
    streamWriteInt32(streamId, self.baleCount);
end;

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

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

function KuhnLSB1270:getSaveAttributesAndNodes(nodeIdent)
    local attributes = 'baleCount="'.. tostring(self.baleCount) ..'"';
    return attributes, nil;
end;

function KuhnLSB1270:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
    if not resetVehicles then
        self.baleCount = Utils.getNoNil(getXMLInt(xmlFile, key.."#baleCount"), 0);
        if self.animationParts[2].isLoading and self.animationParts[2].inputTime > self.animationParts[2].offSet then
            self:setTransportMode(false);
        end;
    end;
    return BaseMission.VEHICLE_LOAD_OK;
end;

function KuhnLSB1270:update(dt)

    if self:getIsActiveForInput() then
        if InputBinding.hasEvent(InputBinding.LOWER_IMPLEMENT) then
            if self.animationParts[1].clipEndTime then
                self:setAnimationTime(1, self.animationParts[1].offSet);
            elseif self.animationParts[1].clipStartTime then
                self:setAnimationTime(1, self.animationParts[1].animDuration);
            end;
        end;
        if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
            self:setTransportMode(not self.transportMode);
        end;
        if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then
            self.baleCount = 0;
        end;
    end;

    if not self.animationParts[2].clipEndTime then
        self.isTurnedOn = false;
    end;

    if self.setAnimationTime ~= nil then
        if self.isTurnedOn then
            self:setAnimationTime(3, 1);
        else
            self:setAnimationTime(3, 0);
        end;
    end;
    -- RPM
    if self.attacherVehicle ~= nil then
        if self.attacherVehicle.motor ~= nil then
            if self.isTurnedOn == true then
                self:setVehicleIncreaseRpm(dt, 200+(1000*self.actLoad_IntSend), true);
                --if self.attacherVehicle.rpmDisplay ~= nil then
                    --self.attacherVehicle.rpmDisplay.dynOffset = 1000*self.actLoad_IntSend;
                --end;
            else
                self:setVehicleIncreaseRpm(nil, 0, false);
                --if self.attacherVehicle.rpmDisplay ~= nil then
                    --self.attacherVehicle.rpmDisplay.dynOffset = 0;
                --end;
            end;
        end;
    end;
end;

function KuhnLSB1270:setTransportMode(transportMode)
    TransportEvent.sendEvent(self, transportMode, noEventSend);
    self.transportMode = transportMode;
    if self.transportMode then
        self.isTurnedOn = false;
        self:setAnimationTime(1, self.animationParts[1].offSet);
    end;
end;

function KuhnLSB1270:updateTick(dt)

    if table.getn(self.bales) > self.lastBaleCount then
        self.baleCount = self.baleCount + 1;
        self.lastBaleCount = self.lastBaleCount + 1;
        self.balerKnotCleaningTime = (self.time-50);
    elseif table.getn(self.bales) < self.lastBaleCount then
        self.lastBaleCount = self.lastBaleCount - 1;
    end;

    if self.transportMode then
        if self.moveBalesOutside then
            self.printWait = false;
            self:setAnimationTime(2, self.animationParts[2].offSet);
        else
            self:moveBalesOut(dt, self);
            self.printWait = true;
        end;
    else
        self.printWait = false;
        self:setAnimationTime(2, self.animationParts[2].animDuration);
        self.moveBalesOutside = false;
    end;

    local newRot = Utils.getMovedLimitedValues(self.pickupRotLimit, {8}, {0}, 1, 2000, dt, not self.animationParts[1].clipEndTime);
    local jointDesc = self.componentJoints[2];
    setJointRotationLimit(jointDesc.jointIndex, 0, true, Utils.degToRad(-newRot[1]), Utils.degToRad(newRot[1]));
    self.pickupRotLimit = newRot;

    if self.isTurnedOn then
        if self.movingDirection == -1 then
            self.backTimer = self.backTimer+dt;
            if self.backTimer > 50 then
                self.allowPick = false;
            end;
        else
            self.allowPick = true;
            self.backTimer = 0;
        end;
    end;

    -- Particle systems
    if self.isClient then
        local activeState = false;
        if self.psTimeout > 0 then
            self.psTimeout = self.psTimeout - dt
            activeState = true
        elseif self.isTurnedOn then
            -- Attempt to detect if baler picks up straw, by comparing previous-fillLevel with current-fillLevel.
            if self.psPrevFillLevel ~= self.fillLevel then
                activeState = true
                self.psTimeout = self.psTurnoffDelayMs -- Keep particle system running for this many milliseconds
            end
        end
        self.psPrevFillLevel = self.fillLevel;
        --
        if activeState ~= self.psActiveState then
            for _,ps in pairs(self.psArray) do
                Utils.setEmittingState(ps, activeState);
            end
            self.psActiveState = activeState
        end
    end;
end;

function KuhnLSB1270:allowPickingUp()
    local allow = false;
    if self.animationParts[1].currentPosition > 300 then
        allow = true;
    end;
    if not self.allowPick then
        allow = false;
    end;
    return allow;
end;

function KuhnLSB1270:moveBalesOut(dt, self)
    local done = true;
    local balesToDrive = 0;
    local drive = true;
    for k, bale in pairs(self.bales) do
        if bale.time > 0.4384 then
            local sendTime = math.min(1, bale.time+dt/10000);
            self.setBaleTime(self, k, sendTime, true);
            done = false;
            balesToDrive = balesToDrive+1;
        end;
    end;
    if balesToDrive == 0 and not self.moveBalesOutside then
        drive = false;
    end;
    if self.attacherVehicle ~= nil then
        local kmh = self.attacherVehicle.lastSpeed*self.attacherVehicle.speedDisplayScale*1000;
        if drive then
            if kmh < 3 then
                self.attacherVehicle.motor:setSpeedLevel(1, true);
            else
                self.attacherVehicle.motor:setSpeedLevel(0, false);
            end;
        elseif not drive and table.getn(self.bales) > 0 then
            self.attacherVehicle.motor:setSpeedLevel(0, false);
        end;
    end;
    self.moveBalesOutside = done;
    return;
end;

function KuhnLSB1270:draw()
    if self.animationParts[1].clipStartTime then
        g_currentMission:addHelpButtonText(g_i18n:getText("KuhnLSB1270_1"), InputBinding.LOWER_IMPLEMENT);
    elseif self.animationParts[1].clipEndTime then
        g_currentMission:addHelpButtonText(g_i18n:getText("KuhnLSB1270_2"), InputBinding.LOWER_IMPLEMENT);
    end;
    if self.animationParts[2].clipStartTime then
        g_currentMission:addHelpButtonText(g_i18n:getText("KuhnLSB1270_3"), InputBinding.IMPLEMENT_EXTRA2);
        g_currentMission:addExtraPrintText(g_i18n:getText("KuhnLSB1270_5"));
    elseif self.animationParts[2].clipEndTime then
        g_currentMission:addHelpButtonText(g_i18n:getText("KuhnLSB1270_4"), InputBinding.IMPLEMENT_EXTRA2);
        g_currentMission:addHelpButtonText(g_i18n:getText("KuhnLSB1270_6"), InputBinding.IMPLEMENT_EXTRA3);
        --
        self.balecounterHud:render();
        setTextAlignment(RenderText.ALIGN_RIGHT);
        setTextBold(true);
        setTextColor(1, 1, 1, 1);
        renderText(self.balecounterX + self.balecounterW - 0.025, self.balecounterY + (self.balecounterH/2 - self.balecounterFontSize/2), self.balecounterFontSize, tostring(self.baleCount));
        setTextAlignment(RenderText.ALIGN_LEFT);
        setTextBold(false);
    end;
    if self.printWait then
        g_currentMission:addWarning(g_i18n:getText("BaleWarning"), 0.040, 0.035);
    end;
    --if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then
    --    local fillType = self.currentFillType;
    --    if fillType == Fillable.FILLTYPE_GRASS then
    --        fillType = Fillable.FILLTYPE_DRYGRASS;
    --    end;
    --end;
end;

function KuhnLSB1270:onAttach(attacherVehicle)
    self.attacherVehicle = attacherVehicle;
    if self.attacherVehicle.motor ~= nil then
        self.saveMinimumRpm = self.attacherVehicle.motor.minRpm;
    else
        if self.attacherVehicle.saveMinRpm ~= nil then
            self.saveMinimumRpm = self.attacherVehicle.saveMinimumRpm;
        else
            self.attacherVehicle.saveMinimumRpm  = 100;
        end;
    end;
end;

function KuhnLSB1270:onDetach()
    for k, steerable in pairs(g_currentMission.steerables) do
        if self.attacherVehicleCopy1 == steerable then
            steerable.motor.minRpm = self.saveMinimumRpm;
            self.attacherVehicleCopy1 = nil;
        end;
    end;
end;

function KuhnLSB1270:setVehicleIncreaseRpm(dt, increase, isActive)
    if self.attacherVehicle ~= nil and self.saveMinimumRpm ~= 0 and
        self.attacherVehicle.motor ~= nil then
        if dt ~= nil then
            if isActive == true then
                self.attacherVehicle.motor.minRpm = math.max(self.attacherVehicle.motor.minRpm-(dt*2), -increase);
            else
                self.attacherVehicle.motor.minRpm = math.min(self.attacherVehicle.motor.minRpm+(dt*5), self.saveMinimumRpm);
            end;
        else
            self.attacherVehicle.motor.minRpm = self.saveMinimumRpm;
        end;
        if self.attacherVehicle.isMotorStarted then
            local fuelUsed = 0.0000012*math.abs(self.attacherVehicle.motor.minRpm);
            self.attacherVehicle:setFuelFillLevel(self.attacherVehicle.fuelFillLevel-fuelUsed);
            g_currentMission.missionStats.fuelUsageTotal = g_currentMission.missionStats.fuelUsageTotal + fuelUsed;
            g_currentMission.missionStats.fuelUsageSession = g_currentMission.missionStats.fuelUsageSession + fuelUsed;
        end;
    end;
end;

function KuhnLSB1270:setActLoad(load, noEventSend)
    SetActLoadEvent.sendEvent(self, load, noEventSend);
    self.actLoad_IntSend = load;
end;

--
--
--
--
SetActLoadEvent = {};
SetActLoadEvent_mt = Class(SetActLoadEvent, Event);

InitEventClass(SetActLoadEvent, "SetActLoadEvent");

function SetActLoadEvent:emptyNew()
    local self = Event:new(SetActLoadEvent_mt);
    self.className="SetActLoadEvent";
    return self;
end;

function SetActLoadEvent:new(vehicle, state)
    local self = SetActLoadEvent:emptyNew()
    self.vehicle = vehicle;
    self.state = state;
    return self;
end;

function SetActLoadEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
    self.vehicle = networkGetObject(id);
    self.state = streamReadFloat32(streamId);
    self:run(connection);
end;

function SetActLoadEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
    streamWriteFloat32(streamId, self.state);
end;

function SetActLoadEvent:run(connection)
    self.vehicle:setActLoad(self.state, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(SetActLoadEvent:new(self.vehicle, self.state), nil, connection, self.vehicle);
    end;
end;

function SetActLoadEvent.sendEvent(vehicle, state, noEventSend)
    if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(SetActLoadEvent:new(vehicle, state), nil, nil, vehicle);
        else
            g_client:getServerConnection():sendEvent(SetActLoadEvent:new(vehicle, state));
        end;
    end;
end;