--
-- SiloTrailer
-- Specialization for a Silotrailer
--
-- Source: Realloading.lua by Face; 
--         Trailer.lua by Heady;
--
-- @author:     Mofi (BM-Modding)
-- @version:    v2.0
-- @date:       26/04/2013
-- @history:    v1.0 - Initial version
--              v1.1 - some bugfixes
--              v2.0 - converted to LS2013
-- 

SiloTrailer = {};

function SiloTrailer.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Trailer, specializations);
end;

function SiloTrailer:load(xmlFile)

	self.modDir_SiloTrailer = self.baseDirectory;
	
	self.setFillLevel = Utils.overwrittenFunction(self.setFillLevel, SiloTrailer.setFillLevel);
	self.updateTipping = Utils.overwrittenFunction(self.updateTipping, SiloTrailer.updateTipping);
	self.setVehicleIncreaseRpm = SpecializationUtil.callSpecializationsFunction("setVehicleIncreaseRpm");
	
	if self.isClient then
        self.unloadingSoundEnabled = false;
        local unloadingSound = getXMLString(xmlFile, "vehicle.unloadingSound#file");
        if unloadingSound ~= nil and unloadingSound ~= "" then
            unloadingSound  = Utils.getFilename(unloadingSound, self.baseDirectory);
            self.unloadingSound = createSample("unloadingSound");
            loadSample(self.unloadingSound, unloadingSound, false);
            self.unloadingSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingSound#pitchOffset"), 1);
            self.unloadingSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingSound#Volume"), 2.0);
        end;
    end;
	
	self.realUnloading = {};
	self.realUnloading.trailerEnd = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.realUnloading#trailerEnd"));
	local x,y,z = getTranslation(self.realUnloading.trailerEnd);
	self.realUnloading.maxDistance = math.abs(z);
	self.realUnloading.maxZ = z;
	self.realUnloading.minZ = 0;
	self.realUnloading.unloadingTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.realUnloading#unloadingTime"), 10)*1000;
	self.lastFillPlane = nil;
	self.emptyTime = 1000;
	self.setTime = true;
	self.tipDischargeEndTime = self.realUnloading.unloadingTime;
	self.lastFillLevelTime = 1;
	self.saveMinimumRpm = 0;

end;

function SiloTrailer:delete()
end;

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

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

function SiloTrailer:update(dt)
end;

function SiloTrailer:updateTick(dt)
end;

function SiloTrailer:draw()
end;

local TrailerUpdateTick = Trailer.updateTick;
Trailer.updateTick = function(self, dt)

	if self.modDir_SiloTrailer == self.baseDirectory then
	    if self:getIsActive() then
            if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) then
                if self.attacherVehicle  ~= nil then
                    if self.attacherVehicle.motor ~= nil then
                        self:setVehicleIncreaseRpm(dt, true);
                    end;
                end;
            elseif (self.tipState == Trailer.TIPSTATE_CLOSING or self.tipState == Trailer.TIPSTATE_CLOSED) then
                self:setVehicleIncreaseRpm(nil, false);
            end;
        end;
        if self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN then
  
            if self.isServer then
                self:updateTipping(dt);
            end;
  
            if self.fillLevel <= 0 then
                self:onEndTip(true);
            end;
  
            if self.tipState == Trailer.TIPSTATE_OPENING then
                if self:getCurrentTipAnimationTime() >= self:getCurrentTipAnimationDuration() then
                    self.tipState = Trailer.TIPSTATE_OPEN;
  
                    if self.hydraulicSoundEnabled then
                        stopSample(self.hydraulicSound);
                        self.hydraulicSoundEnabled = false;
                    end;
                end;
            end;
  
            if self.isClient then
                if self.tipState == Trailer.TIPSTATE_OPENING then
                    if not self.hydraulicSoundEnabled and self.hydraulicSound ~= nil and self:getIsActiveForSound() then
                        playSample(self.hydraulicSound, 0, self.hydraulicSoundVolume, 0);
                        self.hydraulicSoundEnabled = true;
                    end;
                end;
  
                -- check if we still are in opening/open state. Maybe we ended tipping before.
                if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) and self.fillLevel > 0 then
                    if not self.fillSoundEnabled and self.fillSound ~= nil and self:getIsActiveForSound() then
                        playSample(self.fillSound, 0, self.fillSoundVolume, 0);
                        self.fillSoundEnabled = true;
                    end;
                else
                    if self.fillSoundEnabled then
                        stopSample(self.fillSound);
                        self.fillSoundEnabled = false;
                    end;
                end;
            end;
  
        elseif self.tipState == Trailer.TIPSTATE_CLOSING then
  
            if self.isClient then
                if not self.hydraulicSoundEnabled and self.hydraulicSound ~= nil and self:getIsActiveForSound() then
                    playSample(self.hydraulicSound, 0, self.hydraulicSoundVolume, 0);
                    self.hydraulicSoundEnabled = true;
                end;
            end;
  
            if self:getCurrentTipAnimationTime() <= 0.0 then
                if self.hydraulicSoundEnabled then
                    stopSample(self.hydraulicSound);
                    self.hydraulicSoundEnabled = false;
                end;
                self:disableCurrentTipAnimation();
                self.tipState = Trailer.TIPSTATE_CLOSED;
                for _, scroller in pairs(self.tipScrollers) do
                    setShaderParameter(scroller.node, scroller.shaderParameter, 0, 0, 0, 0, false);
                end;
            end;
        end;
  
            if self.isClient then
                if self.currentTipReferencePointIndex ~= nil then
                    local tipAnimation = self.tipAnimations[self.currentTipReferencePointIndex];
                    if tipAnimation ~= nil then
                        if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) and self.fillLevel > 0 and self:getCurrentTipAnimationTime() >= tipAnimation.dischargeStartTime then
                            Utils.setEmittingState(tipAnimation.dischargeParticleSystems[self.currentFillType], true);
                        else
                            Utils.setEmittingState(tipAnimation.dischargeParticleSystems[self.currentFillType], false);
                        end;
                    end;
                end;
            end;
  
            if self.tipState ~= Trailer.TIPSTATE_CLOSED then
                for _, node in pairs(self.tipRotationNodes) do
                    rotate(node.node, -dt*node.rotSpeed, 0, 0);
                end;
            end;				
	else
		TrailerUpdateTick(self, dt);		
	end;	
end;

function SiloTrailer:updateTipping(superFunc, dt)

        if not self.isServer then
            return;
        end;

        if self:getCanTip() then
            local fillType = self.currentFillType;
            if self.fillLevel > 0 then
                self.lastFillType = self.currentFillType;
            end;

            self.isReadyForUnloading = false;
            local isEmpty = false;

            if self.fillLevel > 0 then
                if self.tipState == Trailer.TIPSTATE_OPEN then
                    local grainPlane;
                    if self.currentFillType ~= Fillable.FILLTYPE_UNKNOW then
                        if self.currentFillType == Fillable.FILLTYPE_CHAFF then
                            grainPlane = self.fillPlanes["chaff_unload"];
                            self.currentFillPlane = grainPlane;
                        elseif self.currentFillType == Fillable.FILLTYPE_GRASS_WINDROW or Fillable.FILLTYPE_GRASS then -- grass
                            --grainPlane = self.fillPlanes[Fillable.fillTypeIntToName[self.currentFillType]];
                            grainPlane = self.fillPlanes["grass_windrow_unload"];
                            self.currentFillPlane = grainPlane;
                        end;
                        local t = self.fillLevel/self.capacity;
                        for _, node in ipairs(grainPlane.nodes) do
                            local x,y,z, rx,ry,rz, sx,sy,sz = node.animCurve:get(t);

                            --setTranslation(node.node, x, y, z);
                            setRotation(node.node, rx, ry, rz);
                            setScale(node.node, sx, sy, sz);
                            setVisibility(node.node, self.fillLevel > 0);
                        end;
                    end;
                    if grainPlane == nil then
                        grainPlane = self.defaultFillPlane;
                    end;
                    self.lastFillPlane = grainPlane;
                    self.lastFillType = self.currentFillType;

                    local x,y,z = getTranslation(grainPlane.nodes[1].node);
                    local currentPosition = (self.realUnloading.maxDistance - math.abs(z)) / self.realUnloading.maxDistance;
                    local xScale,yScale,zScale = getScale(grainPlane.nodes[1].node);
                    if currentPosition <= zScale then
                        self.isReadyForUnloading = true;
                    end;

                    local curX,curY,curZ = getTranslation(grainPlane.nodes[1].node);
                    local newZ = Utils.getMovedLimitedValues({curZ}, {self.realUnloading.maxZ}, {self.realUnloading.minZ}, 1, self.realUnloading.unloadingTime, dt, false);
                    setTranslation(grainPlane.nodes[1].node, curX,curY, newZ[1]);
                end;
            else
                self.isReadyForUnloading = true;
                isEmpty = true;
            end;

            local fillDelta = -self.fillLevel;
            if self.currentTipReferencePointIndex ~= nil then
                local animation = self.tipAnimations[self.currentTipReferencePointIndex];
                if animation ~= nil and self.tipDischargeEndTime > animation.dischargeStartTime then
                    if self:getCurrentTipAnimationTime() >= animation.dischargeStartTime then
                        local m = self.capacity/((self.tipDischargeEndTime-animation.dischargeStartTime)/animation.animationOpenSpeedScale);
                        fillDelta = -m * dt;
                    else
                        fillDelta = 0;
                    end;
                end;
            end;

            if fillDelta < 0 then
                local curFill = self.fillLevel;
                self:setFillLevel(self.fillLevel + fillDelta, fillType);
                local fillDelta = self.fillLevel - curFill;
                self.lastFillDelta = fillDelta;

                if self.currentTipTrigger ~= nil then
                    if self.currentTipTrigger.updateTrailerTipping ~= nil then
                        self.currentTipTrigger:updateTrailerTipping(self, fillDelta*self.fillLevelToTippedFillLevel, fillType);
                    end;
                elseif self:getCanTipToGround() then
                    if fillDelta < 0 then
                        local x, _,z = getWorldTranslation(self.groundDropArea.start);
                        local x1,_,z1 = getWorldTranslation(self.groundDropArea.width);
                        local x2,_,z2 = getWorldTranslation(self.groundDropArea.height);
                        local area = {x,z,x1,z1,x2,z2, -fillDelta*self.fillLevelToTippedFillLevel};
                        local area, bitType, valueAccumulation = TrailerTipAreaEvent.runLocally(area, fillType, self.groundAmountAccumulation)
                        if area ~= nil then
                            g_server:broadcastEvent(TrailerTipAreaEvent:new(area, bitType, fillType));
                        end;
                        if valueAccumulation ~= nil then
                            self.groundAmountAccumulation = valueAccumulation;
                        end;
                    end;
                end;
            else
                if isEmpty then
                    self.emptyTime = self.emptyTime - dt;
                end;
                if self.emptyTime <= 0 then
                    self.setTime = true;
                    self:onEndTip();
                end;
            end;
        end;
        
        superFunc(self, dt);
end;    

function SiloTrailer:setFillLevel(superFunc, fillLevel, fillType, force)
    superFunc(self, fillLevel, fillType);

    if (force == nil or force == false) and not self:allowFillType(fillType, false) then
        return
    end;

	if self.fillLevel <= 0 then
		if self.setTime then
			self.emptyTime = 1000;
			self.setTime = false;
		end;
	end;

    self.currentFillType = fillType;
	local emptying = false;
	if fillLevel < self.fillLevel then
		emtying = true;
	end;
    self.fillLevel = fillLevel;
    if self.fillLevel > self.capacity then
        self.fillLevel = self.capacity;
    end;
    if self.isClient then
        -- disable particle system if we switch the type or if the trailer is empty
        if (self.currentFillType ~= fillType or fillLevel <= 0) and self.currentTipReferencePointIndex ~= nil then
            local tipAnimation = self.tipAnimations[self.currentTipReferencePointIndex];
            if tipAnimation ~= nil then
                Utils.setEmittingState(tipAnimation.dischargeParticleSystems[self.currentFillType], false);
            end;
        end;
    end;
    if self.fillLevel <= 0 then
        self.fillLevel = 0;
        self.currentFillType = Fillable.FILLTYPE_UNKNOWN;
    end;


    if self.isClient then
        if self.currentFillPlane ~= nil then
            for _, node in ipairs(self.currentFillPlane.nodes) do
                setVisibility(node.node, false);
            end;
            self.currentFillPlane = nil;
        end;
        if self.fillPlanes ~= nil and self.defaultFillPlane ~= nil and fillType ~= Fillable.FILLTYPE_UNKNOWN then
            local fillTypeName = Fillable.fillTypeIntToName[fillType];
			if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN or self.tipState == Trailer.TIPSTATE_CLOSING ) or emptying then
				if (self.currentFillType == Fillable.FILLTYPE_CHAFF) then
					fillTypeName = "chaff_unload";
				elseif self.currentFillType == Fillable.FILLTYPE_GRASS_WINDROW or Fillable.FILLTYPE_GRASS then
					fillTypeName = "grass_windrow_unload";
				end;
			end;
            local fillPlane = self.fillPlanes[fillTypeName];
            if fillPlane == nil then
                fillPlane = self.defaultFillPlane;
            end;
            local t = self.fillLevel/self.capacity;
            for _, node in ipairs(fillPlane.nodes) do
                local x,y,z, rx,ry,rz, sx,sy,sz = node.animCurve:get(t);

				if self.lastFillLevel ~= nil then
				    if self.fillLevel > self.lastFillLevel then
					   setTranslation(node.node, x, y, z);
				    end;
				end;
                setRotation(node.node, rx, ry, rz);
				setScale(node.node, sx, sy, sz);
                setVisibility(node.node, self.fillLevel > 0);
            end;
            self.currentFillPlane = fillPlane;
        end;
    end;

	self.lastFillLevel = self.fillLevel;
end;

function SiloTrailer:onAttach(attacherVehicle)
	self.attacherVehicle = attacherVehicle;
	if self.attacherVehicleCopy1 == nil then
		self.attacherVehicleCopy1 = self.attacherVehicle;
	end;
	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 SiloTrailer: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 SiloTrailer:setVehicleIncreaseRpm(dt, 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), -1200);
			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;

