--
-- Trailer; Abladescript für Ladewagen
--
-- Source: Realloading.lua by Face; Pöttinger Jumbo SFM-Modding 
--
-- M@D Author & Support: Heady
-- M@D date: 12.12.2010
--
-- > Copyright (C) Heady - www.planet-ls.de < --
--

source("dataS/scripts/vehicles/specializations/TrailerToggleTipEvent.lua");

Trailer = {};

Trailer.TIPSTATE_CLOSED = 0;
Trailer.TIPSTATE_OPENING = 1;
Trailer.TIPSTATE_OPEN = 2;
Trailer.TIPSTATE_CLOSING = 3;

function Trailer.prerequisitesPresent(specializations)
    if not SpecializationUtil.hasSpecialization(Fillable, specializations) then
        print("Warning: Specialization trailer now needs the specialization fillable");
    end;
    return SpecializationUtil.hasSpecialization(Attachable, specializations) and SpecializationUtil.hasSpecialization(Fillable, specializations);
end;

function Trailer:load(xmlFile)

    self.toggleTipState = SpecializationUtil.callSpecializationsFunction("toggleTipState");
    self.onStartTip = SpecializationUtil.callSpecializationsFunction("onStartTip");
    self.onEndTip = SpecializationUtil.callSpecializationsFunction("onEndTip");
    --self.allowFillType = Trailer.allowFillType;
    --self.setFillLevel = Utils.prependedFunction(self.setFillLevel, Trailer.setFillLevel);
    self.setFillLevel = Utils.overwrittenFunction(self.setFillLevel, Trailer.setFillLevel);
    self.getCurrentFruitType = Trailer.getCurrentFruitType;
    --self.setFillLevel = SpecializationUtil.callSpecializationsFunction("setFillLevel");


    self.lastFillDelta = 0;


    self.tipDischargeEndTime = getXMLFloat(xmlFile, "vehicle.tipDischargeEndTime#value");

    local tipAnimRootNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.tipAnimation#rootNode"));
    self.tipAnimCharSet = 0;
    if tipAnimRootNode ~= nil and tipAnimRootNode ~= 0 then
        self.tipAnimCharSet = getAnimCharacterSet(tipAnimRootNode);
        if self.tipAnimCharSet ~= 0 then
            local clip = getAnimClipIndex(self.tipAnimCharSet, getXMLString(xmlFile, "vehicle.tipAnimation#clip"));
            assignAnimTrackClip(self.tipAnimCharSet, 0, clip);
            setAnimTrackLoopState(self.tipAnimCharSet, 0, false);
            self.tipAnimSpeedScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.tipAnimation#speedScale"), 1);
            self.tipAnimDuration = getAnimClipDuration(self.tipAnimCharSet, clip);
            if self.tipDischargeEndTime == nil then
                self.tipDischargeEndTime = self.tipAnimDuration*2.0;
            end;
        end;
    end;
    self.tipState = Trailer.TIPSTATE_CLOSED;

    self.tipReferencePoint = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.tipReferencePoint#index"));
    if self.tipReferencePoint == nil then
        self.tipReferencePoint = self.components[1].node;
    end;

    if self.isClient then
        self.dischargeParticleSystems = {};
        --Utils.loadParticleSystem(xmlFile, self.dischargeParticleSystems, "vehicle.dischargeParticleSystem", self.components, false, nil, self.baseDirectory)
        --Utils.loadParticleSystem(xmlFile, self.chopperParticleSystems, "vehicle.chopperParticleSystem", self.components, false, "$data/vehicles/particleSystems/threshingChopperParticleSystem.i3d", self.baseDirectory);
        local i = 0;
        while true do
            local key = string.format("vehicle.dischargeParticleSystems.dischargeParticleSystem(%d)", i);
            local t = getXMLString(xmlFile, key .. "#type");
            if t == nil then
                break;
            end;
            local desc = FruitUtil.fruitTypes[t];
            if desc ~= nil then
                local fillType = FruitUtil.fruitTypeToFillType[desc.index];
                local currentPS = {};

                local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, key, self.components, false, "$data/vehicles/particleSystems/trailerDischargeParticleSystem.i3d", self.baseDirectory);
                self.dischargeParticleSystems[fillType] = currentPS;
                if self.defaultdischargeParticleSystems == nil then
                    self.defaultdischargeParticleSystems = currentPS;
                end;
            end;
            i = i + 1;
        end;

        self.hydraulicSoundEnabled = false;
        local hydraulicSound = getXMLString(xmlFile, "vehicle.hydraulicSound#file");
        if hydraulicSound ~= nil and hydraulicSound ~= "" then
            hydraulicSound  = Utils.getFilename(hydraulicSound, self.baseDirectory);
            self.hydraulicSound = createSample("hydraulicSound");
            loadSample(self.hydraulicSound, hydraulicSound, false);
            self.hydraulicSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.hydraulicSound#pitchOffset"), 1);
            self.hydraulicSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.hydraulicSound#pitchMax"), 2.0);
        end;

        self.fillSoundEnabled = false;
        local fillSound = getXMLString(xmlFile, "vehicle.fillSound#file");
        if fillSound ~= nil and fillSound ~= "" then
            fillSound  = Utils.getFilename(fillSound, self.baseDirectory);
            self.fillSound = createSample("fillSound");
            loadSample(self.fillSound, fillSound, false);
            self.fillSoundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillSound#pitchOffset"), 1);
            self.fillSoundVolume = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.fillSound#pitchMax"), 2.0);
        end;
    end;
	
	self.realLoading = {};
	self.realLoading.trailerEnd = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.realLoading#trailerEnd"));
	local x,y,z = getTranslation(self.realLoading.trailerEnd);
	self.realLoading.maxDistance = math.abs(z);
	self.realLoading.maxZ = z;
	self.realLoading.minZ = 0;
	self.realLoading.unloadingTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.realLoading#unloadingTime"), 10)*1000;
	self.lastFillPlane = nil;
	self.emptyTime = 3000;
	self.setTime = true;
	self.lastFillType = Fillable.FILLTYPE_UNKNOWN;
	
	self.tipDischargeEndTime = self.realLoading.unloadingTime;

    self.allowTipDischarge = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.allowTipDischarge#value"), true);
end;

function Trailer:delete()
    for _, particleSystem in pairs(self.dischargeParticleSystems) do
        Utils.deleteParticleSystem(particleSystem);
    end;

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

end;

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

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

function Trailer:readStream(streamId, connection)
    if connection:getIsServer() then
        --local fillLevel = streamReadFloat32(streamId);
        --local fillType = streamReadInt8(streamId);
        local tipState = streamReadUIntN(streamId, 2);
        --self:setFillLevel(fillLevel, fillType);
        if tipState ~= Trailer.TIPSTATE_CLOSED and self.tipAnimCharSet ~= 0 then
            local animTime = streamReadFloat32(streamId);

            Trailer.setAnimTime(self, animTime);
            if tipState ~= Trailer.TIPSTATE_CLOSING then
                self:onStartTip(nil, true);
            else
                self:onEndTip(true);
            end;
        else
            Trailer.setAnimTime(self, 0);
        end;
        self.tipState = tipState;
    end;
end;

function Trailer:writeStream(streamId, connection)
    if not connection:getIsServer() then
        --streamWriteFloat32(streamId, self.fillLevel);
        --streamWriteInt8(streamId, self.fillType);
        assert(self.tipState >= 0 and self.tipState <= 3);
        streamWriteUIntN(streamId, self.tipState, 2);
        if self.tipState ~= Trailer.TIPSTATE_CLOSED and self.tipAnimCharSet ~= 0 then
            local animTime = getAnimTrackTime(self.tipAnimCharSet, 0);
            streamWriteFloat32(streamId, animTime);
        end;
    end;
end;

--[[function Trailer:readUpdateStream(streamId, timestamp, connection)
end;

function Trailer:writeUpdateStream(streamId, connection, dirtyMask)
end;]]

function Trailer:update(dt)
end;

function Trailer:updateTick(dt)

    if self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN then
	
		
		self.isReadyForUnloading = false;
		local isEmpty = false;
		
		if self.tipState == Trailer.TIPSTATE_OPEN then
			if self.fillLevel > 0 then
				local grainPlane;
				if self.currentFillType ~= Fillable.FILLTYPE_UNKNOW then
					grainPlane = self.fillPlanes[Fillable.fillTypeIntToName[self.currentFillType]];
				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 = 0.76*(self.realLoading.maxDistance - math.abs(z)) / self.realLoading.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.realLoading.maxZ}, {self.realLoading.minZ}, 1, self.realLoading.unloadingTime, dt, false);
				setTranslation(grainPlane.nodes[1].node, curX,curY, newZ[1]);	
			else
				self.isReadyForUnloading = true;
				isEmpty = true;
			end;
		
		end;
		
		if self.isReadyForUnloading then	

			if self.isServer then	

				if self.currentFillType ~= Fillable.FILLTYPE_UNKNOW and self.currentTipTrigger ~= nil and g_currentMission:getIsTrailerInTipRange(self, self.currentTipTrigger) then
					local fillType = self.currentFillType;
					local m = self.capacity/(self.tipDischargeEndTime/self.tipAnimSpeedScale);
					local curFill = self.fillLevel;
					self:setFillLevel(self.fillLevel - (m* dt), self.currentFillType);
					local fillDelta = self.fillLevel - curFill;
					self.lastFillDelta = fillDelta;

					if self.currentTipTrigger.isFarmTrigger then
						-- put load into storage
						local siloFillType = fillType;
						if fillType == Fillable.FILLTYPE_DRYGRASS then
							siloFillType = Fillable.FILLTYPE_GRASS
						end;
						
						g_currentMission:setSiloAmount(siloFillType, g_currentMission:getSiloAmount(siloFillType)- fillDelta);

					else
						-- increase money according to price of current fill type
						local fruitType = FruitUtil.fillTypeToFruitType[fillType];
						if fruitType ~= nil then
							local priceMultiplier = self.currentTipTrigger.priceMultipliers[fruitType];

							--local difficultyMultiplier = 2 ^ (3 - g_currentMission.missionStats.difficulty);
							local difficultyMultiplier = math.max(3 * (3 - g_currentMission.missionStats.difficulty), 1); -- 1  3  6
							local money = FruitUtil.fruitIndexToDesc[fruitType].pricePerLiter * priceMultiplier * difficultyMultiplier * -fillDelta;
							--g_currentMission.missionStats.money = g_currentMission.missionStats.money + money;
							g_currentMission:addSharedMoney(money);
						end;
					end;

					if fillDelta < 0 then
						self.currentTipTrigger:updateMoving(-fillDelta);
					end;
				else
					self:onEndTip();
				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);
						setSamplePitch(self.hydraulicSound, self.hydraulicSoundPitchOffset-0.4);
						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;

			if self.tipState == Trailer.TIPSTATE_OPENING then
				if getAnimTrackTime(self.tipAnimCharSet, 0) > self.tipAnimDuration then
					self.tipState = Trailer.TIPSTATE_OPEN;

					if self.hydraulicSoundEnabled then
						stopSample(self.hydraulicSound);
						self.hydraulicSoundEnabled = false;
					end;
				end;
			else
				if isEmpty then
					self.emptyTime = self.emptyTime - dt;
				end;	
				if self.emptyTime <= 0 then
					self.setTime = true;
					self:onEndTip();
				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);
                setSamplePitch(self.hydraulicSound, self.hydraulicSoundPitchOffset);
                self.hydraulicSoundEnabled = true;
            end;
        end;

        if getAnimTrackTime(self.tipAnimCharSet, 0) <= 0.0 then
            if self.hydraulicSoundEnabled then
                stopSample(self.hydraulicSound);
                self.hydraulicSoundEnabled = false;
            end;
            disableAnimTrack(self.tipAnimCharSet, 0);
            self.tipState = Trailer.TIPSTATE_CLOSED;
        end;
    end;
	
	if self.isClient then
        if (self.tipState == Trailer.TIPSTATE_OPENING or self.tipState == Trailer.TIPSTATE_OPEN) and self.fillLevel > 0 and self.isReadyForUnloading then
            Utils.setEmittingState(self.dischargeParticleSystems[self.lastFillType], true);
        else
            Utils.setEmittingState(self.dischargeParticleSystems[self.lastFillType], false);
        end;
    end;


end;

function Trailer:draw()
    if self.isClient then
        if self.currentFillType ~= Fillable.FILLTYPE_UNKNOWN then
            local fruitType = FruitUtil.fillTypeToFruitType[self.currentFillType];
            if fruitType ~= nil then
                g_currentMission:setFruitOverlayFruitType(fruitType);
            end;
        end;
    end;
end;

function Trailer:toggleTipState(currentTipTrigger)

    if self.tipState == Trailer.TIPSTATE_CLOSED or self.tipState == Trailer.TIPSTATE_CLOSING then
        self:onStartTip(currentTipTrigger);
    else
        self:onEndTip();
    end;
end;


function Trailer:onStartTip(currentTipTrigger, noEventSend)

    if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(TrailerToggleTipEvent:new(self, true, currentTipTrigger));
        else
            g_client:getServerConnection():sendEvent(TrailerToggleTipEvent:new(self, true, currentTipTrigger));
        end;
    end;

    self.currentTipTrigger = currentTipTrigger;
    if self.tipAnimCharSet ~= 0 then
        if getAnimTrackTime(self.tipAnimCharSet, 0) < 0.0 then
            setAnimTrackTime(self.tipAnimCharSet, 0, 0.0);
        end;
        setAnimTrackSpeedScale(self.tipAnimCharSet, 0, self.tipAnimSpeedScale);
        enableAnimTrack(self.tipAnimCharSet, 0);
    end;
    self.tipState = Trailer.TIPSTATE_OPENING;
end;

function Trailer:onEndTip(noEventSend)

    if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(TrailerToggleTipEvent:new(self, false), nil, nil, self);
        else
            g_client:getServerConnection():sendEvent(TrailerToggleTipEvent:new(self, false), nil, nil, self);
        end;
    end;

    self.currentTipTrigger = nil;
    if self.tipAnimCharSet ~= 0 then
        if getAnimTrackTime(self.tipAnimCharSet, 0) > self.tipAnimDuration then
            setAnimTrackTime(self.tipAnimCharSet, 0, self.tipAnimDuration);
        end;
        setAnimTrackSpeedScale(self.tipAnimCharSet, 0, -self.tipAnimSpeedScale);
        enableAnimTrack(self.tipAnimCharSet, 0);
    end;

    if self.fillSoundEnabled then
        stopSample(self.fillSound);
        self.fillSoundEnabled = false;
    end;

    self.tipState = Trailer.TIPSTATE_CLOSING;
	
	if self.lastFillPlane ~= nil then
		setTranslation(self.lastFillPlane.nodes[1].node, 0,0,0);
	end;
end;

--client
--[[function Trailer:allowFillType(fillType, allowEmptying)
    local allowed = false;

    if self.fillTypes[fillType] then
        if self.currentFillType ~= FruitUtil.FRUITTYPE_UNKNOWN then
            if self.currentFillType ~= fillType then
                if self.fillLevel / self.capacity <= self.minThreshold then
                    allowed = true; -- fill level is low enough to be overridden
                    if allowEmptying then
                        self.fillLevel = 0; -- empty the trailer
                    end;
                end;
            else
                allowed = true; -- fill type is the same as the trailer's current fill type
            end;
        else
            allowed = true; -- trailer is empty --> FruitUtil.FRUITTYPE_UNKNOWN
        end;
    end;

    return allowed;
end;]]

function Trailer:getCurrentFruitType()
    local fruitType = FruitUtil.fillTypeToFruitType[self.currentFillType];
    if fruitType == nil then
        fruitType = FruitUtil.FRUITTYPE_UNKNOWN;
    end;
    return fruitType;
end;

function Trailer:setFillLevel(superFunc, fillLevel, fillType, force)
    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 = 3000;
			self.setTime = false;
		end;
	end;

    self.currentFillType = fillType;
    self.fillLevel = fillLevel;
    if self.fillLevel > self.capacity then
        self.fillLevel = self.capacity;
    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];
            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);

                --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;
            self.currentFillPlane = fillPlane;
        end;
    end;
		
end;

function Trailer:onAttach(attacherVehicle)
end;

function Trailer:onDetach()
    if self.deactivateOnDetach then
        Trailer.onDeactivate(self);
    else
        Trailer.onDeactivateSounds(self);
    end;
end;

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

function Trailer:onDeactivate()
    Trailer.onDeactivateSounds(self);
end;

function Trailer:onDeactivateSounds()
    if self.fillSoundEnabled then
        stopSample(self.fillSound);
        self.fillSoundEnabled = false;
    end;
    if self.hydraulicSoundEnabled then
        stopSample(self.hydraulicSound);
        self.hydraulicSoundEnabled = false;
    end;
end;

function Trailer.setAnimTime(self, animTime)
    if self.tipAnimCharSet ~= 0 then
        enableAnimTrack(self.tipAnimCharSet, 0);
        setAnimTrackTime(self.tipAnimCharSet, 0, animTime, true);
        disableAnimTrack(self.tipAnimCharSet, 0);
    end;
end;