--
-- VariableTip
-- This specialization allows to 
--	- toggle between trailers, to select one specific 
--	- toggle through the tips of the active trailer
-- 
--
-- @author:		fruktor (wwww.modding-society.de)
-- @version:	v0.1
-- @date:		08/12/10
-- @history:	v0.1 - inital implementation
--
-- Copyright (C) 
--

VariableTip = {};

function VariableTip.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Trailer, specializations) and 
		SpecializationUtil.hasSpecialization(Attachable, specializations);
end;

function VariableTip:load(xmlFile)

	self.setTipSide = SpecializationUtil.callSpecializationsFunction("setTipSide");
	self.setActiveTrailer = SpecializationUtil.callSpecializationsFunction("setActiveTrailer");
	self.onEnterChargeTrigger = SpecializationUtil.callSpecializationsFunction("onEnterChargeTrigger");
	self.setUnloadingState = SpecializationUtil.callSpecializationsFunction("setUnloadingState");

	self.varTip = {};
	self.varTip.tipCount = 1;
	self.varTip.activeTipIdx = 1;
	self.varTip.isMaster = 1;
	self.varTip.trailerNr = 1;
	self.varTip.trailerCount = 1;
	self.varTip.activeTrailerIdx = 1;	
	self.varTip.canUnload = false;
	self.varTip.doUnload = false;
	self.varTip.unloadDone = true;
	self.varTip.enableOvercharge = false;
	self.isUnloading = false;
	
	-- load all data into self.varTip.tips
	self.varTip.tips = {};

	local i=0; 
	while true do 

		local node = {};
		local path = string.format("vehicle.variableTip.node(%d)", i);
		
		local tipSide = getXMLString(xmlFile, path .. "#tipSide");
		if tipSide == nil then
			--print("no more side");
			break;
		end;
		node.tipSide	= tipSide;

		node.tipIndex 	= Utils.indexToObject(self.components, getXMLString(xmlFile, path.."#tipIndex"));
		if node.tipIndex == nil then
			print("!ERROR! VariableTip:load(xmlFile):: Entry nr.:"..i.." has no tipIndex .... ABORTING!");
			return false;
		end;
			
		--#### animation ####--
		
		node.animIndex 	= Utils.indexToObject(self.components, getXMLString(xmlFile, path.."#animIndex"));
		if node.animIndex == nil or node.animIndex == 0 then
			print("!ERROR! VariableTip:load(xmlFile):: Entry nr.:"..i.." has no animIndex or it is zero .... ABORTING!");
			return false;
		end;
		
		node.animCharSet = getAnimCharacterSet(node.animIndex);
		if node.animCharSet == 0 or node.animCharSet == nil then
			print("!ERROR! VariableTip:load(xmlFile):: Entry nr.:"..i.." has no or invalid animCharSet .... ABORTING!");
			return false;
		end;
		--print("node.animCharSet="..tostring(node.animCharSet));
		
		node.animClip 	= getXMLString(xmlFile, path .. "#animClip");
		if node.animClip == nil then
			print("!ERROR! VariableTip:load(xmlFile):: Entry nr.:"..i.." has no animClip .... ABORTING!");
			return false;
		end;

		node.animClipIdx = getAnimClipIndex(node.animCharSet, getXMLString(xmlFile, path.."#animClip"));	
		if node.animClipIdx == nil or node.animClipIdx == -1 then
			print("!ERROR! VariableTip:load(xmlFile):: Entry nr.:"..i.." has no or invalid animClipIdx .... ABORTING!");
			return false;
		end;
		
		node.animSpeedScale 	= getXMLFloat(xmlFile, path .. "#animSpeedScale");
		if node.animSpeedScale == nil then
			node.animSpeedScale = 1;
		end;
		
		node.animTipDuration = getAnimClipDuration(node.animCharSet, node.animClipIdx);
		if node.animTipDuration == nil or node.animTipDuration == 0 then
			node.animTipDuration = self.animTipDuration;
			if node.animTipDuration == 0 then
				print("!ERROR! VariableTip:load(xmlFile):: Entry nr.:"..i.." has no or invalid animTipDuration .... ABORTING!");
				return false;
			end;
		end;
			
		node.animTipDischargeEndTime = getXMLFloat(xmlFile, path .. "#animTipDischargeEndTime");
		if node.animTipDischargeEndTime == nil then
			node.animTipDischargeEndTime = node.animTipDuration*2.0;
		end;		
			
			
		--#### particlesystem ####--
		--MISSING: rotation and position offset
		
		node.dischargePS = {};
		local j=0;

		while true do
			local psPath = string.format("vehicle.variableTip.node(%d).dischargeParticleSystem(%d)", i, j);
			
			local psType = getXMLString(xmlFile, psPath .. "#type");	
			if psType == nil then
				--print("Entry nr."..i.." has no type for particleSystem nr.:"..j.." .... !");
				break;
			end;
			
			local lsType = FruitUtil.fruitTypes[psType];
			if lsType == nil then
				--print("!ERROR! VariableTip:load(xmlFile):: Entry nr."..i.." has an invalid type of particleSystem nr.:"..j.." .... !");
				break;
			end;
			
			local fillType = FruitUtil.fruitTypeToFillType[lsType.index]
			
			local currentPS = {};
			local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, psPath, self.components, false, "$data/vehicles/particleSystems/trailerDischargeParticleSystem.i3d", self.baseDirectory);
			node.dischargePS[fillType] = currentPS; 
			--table.insert(node.dischargePS, fillType, currentPS);

			j = j + 1;
		end; 
			
			
		--####
		node.parts = {};
			
		if string.match("charge", node.tipSide) or 
			string.match("overcharge", node.tipSide) then
						
			local k=1;
			local str = getXMLString(xmlFile, string.format("vehicle.variableTip.node(%d).parts#index%d", i, k));

			while str ~= nil do 
				local tmpIdx = Utils.indexToObject(self.components, str);
				k=k+1;
				table.insert(node.parts, tmpIdx);
				str = getXMLString(xmlFile, string.format("vehicle.variableTip.node(%d).parts#index%d", i, k));
			end;
			node.partCount = k-1;
			
			--<extraPlane type="fertilizer" node="0>2|1|0|0|8|0"/>
			local k=1;
			local typ = getXMLString(xmlFile, string.format("vehicle.variableTip.node(%d).extraPlane(%d)#type", i, k));
			node.extraPlane = {};
			while typ ~= nil do 
				local root = Utils.indexToObject( self.components, getXMLString(xmlFile, string.format("vehicle.variableTip.node(%d).extraPlane(%d)#node", i, k)) );
				local lsType = FruitUtil.fruitTypes[typ];
				local fillType = FruitUtil.fruitTypeToFillType[lsType.index]
				node.extraPlane[fillType] = root;
				k=k+1;
				typ = getXMLString(xmlFile, string.format("vehicle.variableTip.node(%d).extraPlane(%d)#type", i, k));	
			end;
			
			node.extraPS = {};
			local j=0;

			while true do
				local psPath = string.format("vehicle.variableTip.node(%d).extraParticleSystem(%d)", i, j);
				
				local psType = getXMLString(xmlFile, psPath .. "#type");	
				if psType == nil then
					--print("Entry nr."..i.." has no type for particleSystem nr.:"..j.." .... !");
					break;
				end;
				
				local lsType = FruitUtil.fruitTypes[psType];
				if lsType == nil then
					--print("!ERROR! VariableTip:load(xmlFile):: Entry nr."..i.." has an invalid type of particleSystem nr.:"..j.." .... !");
					break;
				end;
				
				local fillType = FruitUtil.fruitTypeToFillType[lsType.index]
				
				local currentPS = {};
				local particleNode = Utils.loadParticleSystem(xmlFile, currentPS, psPath, self.components, false, "$data/vehicles/particleSystems/trailerDischargeParticleSystem.i3d", self.baseDirectory);
				node.extraPS[fillType] = currentPS; 
				--table.insert(node.dischargePS, fillType, currentPS);

				j = j + 1;
			end; 			
			
		end;

		node.nonParts = {};
		if string.match("overcharge", node.tipSide) then
						
			local k=1;
			local str = getXMLString(xmlFile, string.format("vehicle.variableTip.node(%d).nonparts#index%d", i, k));

			while str ~= nil do 
				local tmpIdx = Utils.indexToObject(self.components, str);
				k=k+1;
				table.insert(node.nonParts, tmpIdx);
				str = getXMLString(xmlFile, string.format("vehicle.variableTip.node(%d).nonparts#index%d", i, k));
			end;
			node.nonPartCount = k-1;
		end;
		
			
		if string.match("charge", node.tipSide) then
		
			local file = Utils.getFilename("overlays/overlay.png", self.baseDirectory);
			self.hudDischarge = Overlay:new("hudDischarge", file, 0.935, 0.21, 0.06, 0.06 * (4 / 3));			
				
			self.varTip.triggerNode = {};
			self.varTip.triggerNode.index = node.tipIndex; 
			self.varTip.triggerNode.distance = Utils.getNoNil(getXMLInt(xmlFile, string.format("vehicle.variableTip.node(%d).unloading#maxDistance", i)), 1);
			self.varTip.triggerNode.capacity = Utils.getNoNil(getXMLInt(xmlFile, string.format("vehicle.variableTip.node(%d).unloading#capacity", i)), 1);
			self.varTip.unloadingTipTrigger = nil;					
				
			--## second animation for/before enabling charge
			node.anim2Index 	= Utils.indexToObject(self.components, getXMLString(xmlFile, path..".chargeAnim#animIndex"));
			--if node.anim2Index == nil or node.anim2Index == 0 then
			--	print("!Warning! VariableTip:load(xmlFile):: Entry nr.:"..i.." has no anim2Index or it is zero .... ABORTING!");
			--	return false;
			--end;
			if node.anim2Index ~= nil then
				node.anim2CharSet = getAnimCharacterSet(node.anim2Index);
				node.anim2Clip 	= getXMLString(xmlFile, path .. ".chargeAnim#animClip");
				node.anim2ClipIdx = getAnimClipIndex(node.anim2CharSet, getXMLString(xmlFile, path..".chargeAnim#animClip"));	
				node.anim2SpeedScale 	= getXMLFloat(xmlFile, path .. ".chargeAnim#animSpeedScale");
				node.tipAnim2Duration = getAnimClipDuration(node.tipAnim2CharSet, node.anim2ClipIdx);
			end;
				
		end;
		
		--#### finally insert tip into table
		table.insert(self.varTip.tips, node);
		
		i = i + 1;			
		self.varTip.tipCount = i;
		
	end;
	--print("-----------> self.varTip.tipCount = "..self.varTip.tipCount.." table.getn(self.varTip.tips)"..table.getn(self.varTip.tips));
	if self.unloadingCapacity == nil then
		self.unloadingCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.unloadingCapacity"), 100);
	end;

	
	self.varTip.activeTipIdx = 1;
	self:setTipSide(self.varTip.activeTipIdx, true);

	self:setActiveTrailer(self.varTip.activeTrailerIdx, true);
end;

function VariableTip:delete()
	for k,v in pairs(self.varTip.tips) do
		if k ~= self.varTip.activeTipIdx then
			for i,j in pairs(v.dischargePS) do
				Utils.deleteParticleSystem(j);
			end;
		end;
	end;
	for i,j in pairs(self.varTip.tips) do
		if string.match(self.varTip.tips[i].tipSide, "charge") then
			removeTrigger(self.varTip.tips[i].tipIndex, "onEnterChargeTrigger", self);
		end;
	end;	
end;

function VariableTip:readStream(streamId, connection)
	self.varTip.tipCount = streamReadInt8(streamId);
	self.varTip.activeTipIdx = streamReadInt8(streamId);
	self.varTip.isMaster = streamReadInt8(streamId);
	self.varTip.trailerNr = streamReadInt8(streamId);
	self.varTip.trailerCount = streamReadInt8(streamId);
	self.varTip.activeTrailerIdx = streamReadInt8(streamId);
	
	self:setTipSide(self.varTip.activeTipIdx, true);
	self:setActiveTrailer(self.varTip.activeTrailerIdx, true);
end;

function VariableTip:writeStream(streamId, connection)
	streamWriteInt8(streamId, self.varTip.tipCount);
	streamWriteInt8(streamId, self.varTip.activeTipIdx);
	streamWriteInt8(streamId, self.varTip.isMaster);
	streamWriteInt8(streamId, self.varTip.trailerNr);
	streamWriteInt8(streamId, self.varTip.trailerCount);
	streamWriteInt8(streamId, self.varTip.activeTrailerIdx);
end;


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

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

function VariableTip:update(dt)

	if self:getIsActiveForInput() then
	
		if InputBinding.hasEvent(InputBinding.VARIABLETIP_changeVarTipTrailer) and
			self.varTip.trailerCount > 1 then
			local idx = self.varTip.activeTrailerIdx + 1;
			if idx > self.varTip.trailerCount then
				idx = 1;
			end;
			self:setActiveTrailer(idx);
		end;

		if self.overchargeDone ~= nil then
			if self.overchargeDone == false then
				return;
			end;
		end;
		
		if self:getIsActive() and self.tipState == Trailer.TIPSTATE_CLOSED and 
			self.varTip.unloadDone then
			if InputBinding.hasEvent(InputBinding.VARIABLETIP_changeVarTipMode) then
				if self.varTip.activeTrailerIdx == self.varTip.trailerNr and self.varTip.tipCount > 1 then
					local tipIdx = self.varTip.activeTipIdx + 1;
					if tipIdx  > self.varTip.tipCount then
						tipIdx = 1;
					end;
					-- don't allow overcharge on non last tariler(s)
					--[[ if self.varTip.tips[tipIdx].partCount ~= nil and 
						self.varTip.trailerNr < self.varTip.trailerCount and
						self.varTip.trailerCount > 1
						then
						tipIdx = tipIdx + 1;
					end; ]]--
					if tipIdx  > self.varTip.tipCount then
						tipIdx = 1;
					end;
					self:setTipSide(tipIdx);
				end;
			end;
		end;	
		
		if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) and
			self.varTip.trailerNr == self.varTip.activeTrailerIdx and
			(self.varTip.trailerInTrigger ~= nil or self.varTip.unloadingTipTrigger ~= nil)
			then
			if self.varTip.doUnload == false then
				self:setUnloadingState(true);
			else
				self:setUnloadingState(false);
			end
		end;
		
	end;
	
end;

function VariableTip:updateTick(dt)

	if self.varTip ~= nil then
	
		--# check if a trailer/sowing-machine is in range
		if self.varTip.trailerInTrigger ~= nil then		
			local trailer = self.varTip.trailerInTrigger;
			_=[[ if trailer.setSeedIndex then
				if trailer:allowFillType(Fillable.fillTypeNameToInt["seeds"], true) or not trailer.allowFillFromAir then
					if FruitUtil.fruitTypeToFillType[trailer.seeds[trailer.currentSeed] ] == self.currentFillType or trailer.fillLevel <=0 then
						if self.fillLevel == 0 or trailer.fillLevel == trailer.capacity then
							self.varTip.canUnload = false;
						else
							self.varTip.canUnload = true;
						end;
					else
						self.varTip.canUnload = false;
					end;
				else
					self.varTip.canUnload = false;
				end;
			else
				self.varTip.canUnload = false;
			end; ]]
			self.varTip.canUnload = false;
			if ( trailer:allowFillType(Fillable.fillTypeNameToInt["seeds"], true) or trailer:allowFillType(self.currentFillType, true) ) 
				and	self.fillLevel > 0 and trailer.fillLevel < trailer.capacity then
				self.varTip.canUnload = true;
			end;
			
		else
			--# check if a tip-trigger is in range

			self.varTip.canUnload = false;			
			self.varTip.unloadingTipTrigger = nil;
			
			if self.fillLevel > 0 and string.match(self.varTip.tips[self.varTip.activeTipIdx].tipSide, "charge") and
				self.varTip.triggerNode ~= nil and self.varTip.canUnload == false then
				for k, tipTrigger in pairs(g_currentMission.tipTriggers) do
					local trailerX, trailerY, trailerZ = getWorldTranslation(self.varTip.triggerNode.index);
					local triggerX, triggerY, triggerZ = getWorldTranslation(tipTrigger.triggerId);
					local distance = Utils.vector3Length(trailerX-triggerX, trailerY-triggerY, trailerZ-triggerZ);
					if distance < self.varTip.triggerNode.distance then
						self.varTip.unloadingTipTrigger = tipTrigger;
						self.varTip.canUnload = true;
						break;
					end;
				end;
			end;		
			
			if self.varTip.canUnload == false then 
				self.varTip.doUnload = false;	
			end;
		end;

		
		if self.varTip.canUnload == true and self.varTip.doUnload == true then
				
			local curTime=0;
			local duration=0;
			local wait = false;
				
			if self.varTip.tips[self.varTip.activeTipIdx].anim2Index ~= nil then
				curTime = getAnimTrackTime(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0);
				duration = getAnimClipDuration(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0);
				if curTime < 0.5*duration then
					assignAnimTrackClip(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0, getAnimClipIndex(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, self.varTip.tips[self.varTip.activeTipIdx].anim2Clip)); 
					setAnimTrackLoopState(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0, false);
					enableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0);
					setAnimTrackSpeedScale(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0, self.varTip.tips[self.varTip.activeTipIdx].anim2SpeedScale);
					wait = true;
				else
					disableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0);
					setAnimTrackSpeedScale(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0, 0);
				end;
			end;
								
			if wait == false then
			
				local trailer = self.varTip.trailerInTrigger;								
				local deltaFillLevel = self.unloadingCapacity*dt/1000.0;
				
				if trailer ~= nil then
					trailer.fillLevel = math.min(trailer.fillLevel+deltaFillLevel, trailer.capacity);
					self.fillLevel = math.max(self.fillLevel-deltaFillLevel, 0);
					self:setFillLevel(self.fillLevel, self.currentFillType);
					
					if trailer:allowFillType(Fillable.fillTypeNameToInt["seeds"]) then
						trailer:setFillLevel(trailer.fillLevel, Fillable.fillTypeNameToInt["seeds"]);
						trailer:setSeedFruitType(FruitUtil.fillTypeToFruitType[self.currentFillType]);	
					else
						trailer:setFillLevel(trailer.fillLevel, self.currentFillType);
					end;
					
				elseif self.varTip.unloadingTipTrigger ~= nil then
					local tipTrigger = self.varTip.unloadingTipTrigger;
					local fruitType = FruitUtil.fillTypeToFruitType[self.currentFillType];
					local fruitAccept = tipTrigger.acceptedFruitTypes[fruitType];
					if tipTrigger.isFarmTrigger and fruitAccept then
						if self.isUnloading then
							g_currentMission.missionStats.farmSiloAmounts[self.currentFillType] = g_currentMission.missionStats.farmSiloAmounts[self.currentFillType] + deltaFillLevel;
							self.fillLevel = math.max(self.fillLevel-deltaFillLevel, 0);
							self:setFillLevel(self.fillLevel, self.currentFillType);	
						else
							renderOverlay = true;
						end;
					elseif fruitAccept then
						if self.isUnloading then
							if self.isServer then
								local priceMultiplier = tipTrigger.priceMultipliers[fruitType];
								if priceMultiplier > 0 then
									local difficultyMultiplier = math.max(3 * (3 - g_currentMission.missionStats.difficulty), 1);
									local money = FruitUtil.fruitIndexToDesc[fruitType].pricePerLiter * priceMultiplier * difficultyMultiplier * deltaFillLevel;
									g_currentMission:addSharedMoney(money);
									self.fillLevel = math.max(self.fillLevel-deltaFillLevel, 0);
									self:setFillLevel(self.fillLevel, self.currentFillType);
								else
									self.fillLevel = math.max(self.fillLevel-deltaFillLevel, 0);
									self:setFillLevel(self.fillLevel, self.currentFillType);
									self.varTip.unloadingTipTrigger:updateMoving(deltaFillLevel);
								end;
							end;
								
						else
							renderOverlay = true;
						end;
					else
						if FruitUtil.fruitIndexToDesc[fruitType].name ~= nil then
							g_currentMission:addWarning(g_i18n:getText(FruitUtil.fruitIndexToDesc[fruitType].name) .. g_i18n:getText("notAcceptedHere"), 0.018, 0.033);
						end;
					end;
				end;
				

				local pss = self.varTip.tips[self.varTip.activeTipIdx].dischargePS;
				for k,v in pairs(pss) do
					if k == self.currentFillType then
						Utils.setEmittingState(v, true);
					else
						Utils.setEmittingState(v, false);
					end;
				end;		
								
				curTime = getAnimTrackTime(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
				duration = getAnimClipDuration(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);				
			
				assignAnimTrackClip(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, getAnimClipIndex(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, self.varTip.tips[self.varTip.activeTipIdx].animClip)); 
				setAnimTrackLoopState(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, false);

				if curTime < 0.0 then
					setAnimTrackTime(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, 0.0);
				end;
				setAnimTrackSpeedScale(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, self.varTip.tips[self.varTip.activeTipIdx].animSpeedScale);
				
				if (1-self.fillLevel/self.capacity) < curTime/duration or
					curTime > 0.4*duration then
					disableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
				else
					enableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
				end;
				
			end;				
			
		--### 
		elseif string.match(self.varTip.tips[self.varTip.activeTipIdx].tipSide, "overcharge") then
		
			local curTime=0;
			local duration=0;
			
			if self.isUnloading == true then
				if self.varTip.tips[self.varTip.activeTipIdx].animIndex ~= nil then
					curTime = getAnimTrackTime(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
					duration = getAnimClipDuration(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);

					if (1-self.fillLevel/self.capacity) < curTime/duration or
						curTime > 0.4*duration then
						disableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
					else
						assignAnimTrackClip(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, getAnimClipIndex(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, self.varTip.tips[self.varTip.activeTipIdx].animClip)); 
						setAnimTrackLoopState(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, false);
						setAnimTrackSpeedScale(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, self.varTip.tips[self.varTip.activeTipIdx].animSpeedScale);
						enableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
					end;					
				end;
				if self.varTip.tips[self.varTip.activeTipIdx].extraPlane ~= nil then
					local plane = self.varTip.tips[self.varTip.activeTipIdx].extraPlane[self.currentFillType];
					if plane ~= nil then
						setVisibility(plane, true);
						setShaderParameter(plane, "movingDirection", -1.4,0,0,0,false);
					end;
				end;
				if self.varTip.tips[self.varTip.activeTipIdx].extraPS ~= nil then
					if self.varTip.tips[self.varTip.activeTipIdx].extraPS[self.currentFillType] ~= nil then
						Utils.setEmittingState(self.varTip.tips[self.varTip.activeTipIdx].extraPS[self.currentFillType], true);
					end;
				end;
			else
				curTime = getAnimTrackTime(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
				if curTime ~= nil then 
					if curTime > 0 then
						assignAnimTrackClip(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, getAnimClipIndex(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, self.varTip.tips[self.varTip.activeTipIdx].animClip)); 
						setAnimTrackSpeedScale(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, -self.varTip.tips[self.varTip.activeTipIdx].animSpeedScale);
						enableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);	
					else
						disableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);	
						self.varTip.unloadDone = true;	
						self.varTip.doUnload = false;	
						self.isUnloading = false;
					end;
				end;
				if self.varTip.tips[self.varTip.activeTipIdx].extraPlane ~= nil then
					local plane = self.varTip.tips[self.varTip.activeTipIdx].extraPlane[self.currentFillType];
					if plane ~= nil then
						setVisibility(plane, false);
						setShaderParameter(plane, "movingDirection", 0,0,0,0,false);
					end;
				end;
				if self.varTip.tips[self.varTip.activeTipIdx].extraPS ~= nil then
					if self.varTip.tips[self.varTip.activeTipIdx].extraPS[self.currentFillType] ~= nil then
						Utils.setEmittingState(self.varTip.tips[self.varTip.activeTipIdx].extraPS[self.currentFillType], false);
					end;
				end;					
			end;
		
		else
		
			if self.tipState ~= Trailer.TIPSTATE_OPENING and self.tipState ~= Trailer.TIPSTATE_OPEN then
				local curTime;
				local wait = false;
				if self.varTip.tips[self.varTip.activeTipIdx].anim2Index ~= nil then
					curTime = getAnimTrackTime(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0);
					if curTime ~= nil then
						if curTime > 0 then
							assignAnimTrackClip(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0, getAnimClipIndex(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, self.varTip.tips[self.varTip.activeTipIdx].anim2Clip)); 
							enableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0);
							setAnimTrackSpeedScale(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0, -self.varTip.tips[self.varTip.activeTipIdx].anim2SpeedScale);			
							wait = true;
						else
							disableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].anim2CharSet, 0);
							wait = false;
						end;
					end;
				end;
				
				if wait == false then
					curTime = getAnimTrackTime(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);
					if curTime ~= nil then 
						if curTime > 0 then
							assignAnimTrackClip(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, getAnimClipIndex(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, self.varTip.tips[self.varTip.activeTipIdx].animClip)); 
							setAnimTrackSpeedScale(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0, -self.varTip.tips[self.varTip.activeTipIdx].animSpeedScale);
							enableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);	
						else
							disableAnimTrack(self.varTip.tips[self.varTip.activeTipIdx].animCharSet, 0);		
							self.varTip.unloadDone = true;		
							self.varTip.doUnload = false;		
							self.isUnloading = false;						
						end;
					end;
				end;
			end;
		end;	
		
	end;	
						
end;

function VariableTip:draw()

	if self:getIsActive() then
	
		if self.varTip.activeTrailerIdx == self.varTip.trailerNr and
			self.varTip.trailerCount > 1 then
			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("VARIABLETIP_displayVarTipTrailer"),
					self.varTip.activeTrailerIdx, 
					self.varTip.trailerCount), 
				InputBinding.VARIABLETIP_changeVarTipTrailer);
		end;

		if self.overchargeDone ~= nil then
			if self.overchargeDone == false then
				return;
			end;
		end;

		if self.varTip.activeTrailerIdx == self.varTip.trailerNr and
			self.varTip.tipCount > 1 and self.tipState == Trailer.TIPSTATE_CLOSED and
			self.varTip.unloadDone then
				g_currentMission:addHelpButtonText( 
					string.format(g_i18n:getText("VARIABLETIP_displayVarTipMode"), 
						g_i18n:getText(self.varTip.tips[self.varTip.activeTipIdx].tipSide)), 
					InputBinding.VARIABLETIP_changeVarTipMode);
		end;

		if self.varTip.trailerNr == self.varTip.trailerCount and
			self.varTip.trailerNr == self.varTip.activeTrailerIdx then 
			if self.varTip.trailerInTrigger ~= nil or 
				self.varTip.unloadingTipTrigger ~= nil then
				if string.match("charge", self.varTip.tips[self.varTip.activeTipIdx].tipSide) then
					if self.hudDischarge ~= nil and 
						self.varTip.canUnload == true and self.varTip.doUnload == false and self.isUnloading ~= true then
						g_currentMission:addHelpButtonText(g_i18n:getText("VARIABLETIP_chargeTrailer"), InputBinding.IMPLEMENT_EXTRA2);
						self.hudDischarge:render();
					end;
				end;
			end;
		end;
		
	end;

end;


function VariableTip:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	if not resetVehicles then
		local idx = Utils.getNoNil(getXMLInt(xmlFile,key.."#varTip.activeTipIdx"),1);
		if idx > table.getn(self.varTip.tips) then
			idx = 1;
		end;
		self.varTip.isMaster = Utils.getNoNil(getXMLInt(xmlFile,key.."#varTip.isMaster"),1);
		self.varTip.trailerNr = Utils.getNoNil(getXMLInt(xmlFile,key.."#varTip.trailerNr"),1);
		self.varTip.trailerCount = Utils.getNoNil(getXMLInt(xmlFile,key.."#varTip.trailerCount"),1);
		self.varTip.activeTrailerIdx = Utils.getNoNil(getXMLInt(xmlFile,key.."#varTip.activeTrailerIdx"),1);
		self:setTipSide(idx, true);
	end;
    return BaseMission.VEHICLE_LOAD_OK;
end;

function VariableTip:getSaveAttributesAndNodes(nodeIdent)

    local attributes = ' varTip.activeTipIdx="'..tostring(self.varTip.activeTipIdx)..'"'..
		' varTip.isMaster="'..tostring(self.varTip.isMaster)..'"'..
		' varTip.trailerNr="'..tostring(self.varTip.trailerNr)..'"'..
		' varTip.trailerCount="'..tostring(self.varTip.trailerCount)..'"'..
		' varTip.activeTrailerIdx="'..tostring(self.varTip.activeTrailerIdx)..'"';
    local node = nil;
    return attributes, node;

end;



function VariableTip:onAttach()
	local trailerT = {};

		
	-- iterate to master
	local trailer = self;
	while trailer.attacherVehicle ~= nil do
		if SpecializationUtil.hasSpecialization(Steerable, trailer.attacherVehicle.specializations) then
			break;
		end;
		trailer = trailer.attacherVehicle;
	end;
	local trailerMaster;
	if trailer ~= nil then
		trailerMaster = trailer;
	else
		trailerMaster = self;
	end;

	-- find last trailer 
	local tmp = trailerMaster;
	table.insert(trailerT, trailerMaster);
	
	for i,v in pairs(trailerT) do
		for a,b in pairs(g_currentMission.vehicles) do
			if b.attacherVehicle == v then
				table.insert(trailerT, b);
				tmp = v;
				break;
			end;
		end;
	end;

	for i=1, table.getn(trailerT) do
		if trailerT[i].varTip ~= nil then
			trailerT[i].varTip.isMaster = 0;
			trailerT[i].varTip.trailerNr = i;
			trailerT[i].varTip.trailerCount = table.getn(trailerT);
			trailerT[i].varTip.activeTrailerIdx = 1;
		end;
	end;
	if trailerT[1].varTip ~= nil then
		trailerT[1].varTip.isMaster = 1; 
	end;
	
end;

function VariableTip:onDetach()

	if self.varTip ~= nil then
		self.varTip.isMaster = 1;
		self.varTip.trailerNr = 1;
		self.varTip.trailerCount = 1;
		self.varTip.activeTrailerIdx = 1;	
	end;

end;


function VariableTip:attachImplement(implement)
	local sideIndex = self.varTip.activeTipIdx;
	if string.match(self.varTip.tips[sideIndex].tipSide, "charge") or
		string.match(self.varTip.tips[sideIndex].tipSide, "overcharge") then
		sideIndex = sideIndex + 1;
		if sideIndex > self.varTip.tipCount then
			sideIndex = 1;
		end;
	end;	
	self:setTipSide(sideIndex);	
end;

function VariableTip:detachImplement(implementIndex)

	local trailerT = {};
	
		-- iterate to master
		local trailer = self;
		while trailer.attacherVehicle ~= nil do
			if SpecializationUtil.hasSpecialization(Steerable, trailer.attacherVehicle.specializations) then
				break;
			end;
			trailer = trailer.attacherVehicle;
		end;
		local trailerMaster;
		if trailer ~= nil then
			trailerMaster = trailer;
		else
			trailerMaster = self;
		end;

		-- find last trailer 
		local tmp = trailerMaster;
		table.insert(trailerT, trailerMaster);
		
		for i,v in pairs(trailerT) do
			for a,b in pairs(g_currentMission.vehicles) do
				if b.attacherVehicle == v then
					table.insert(trailerT, b);
					tmp = v;
					break;
				end;
			end;
		end;

		for i=1, table.getn(trailerT)-1 do							-- -1 !!
			if trailerT[i].varTip ~= nil then
				trailerT[i].varTip.isMaster = 0;
				trailerT[i].varTip.trailerNr = i;
				trailerT[i].varTip.trailerCount = table.getn(trailerT)-1;
				trailerT[i].varTip.activeTrailerIdx = 1;
			end;
		end;
		if trailerT[1].varTip ~= nil then
			trailerT[1].varTip.isMaster = 1; 	
		end;
	

	
end;

function VariableTip:setTipSide(sideIndex, noEventSend)

		
		
	VariableTipEvent.sendEvent(self, sideIndex, noEventSend)	
	
	if sideIndex > 0 then 
		--self.varTip.activeTipIdx = sideIndex;
		
		if self.attachedImplements[1] ~= nil then
			if string.match(self.varTip.tips[sideIndex].tipSide, "charge") or
				string.match(self.varTip.tips[sideIndex].tipSide, "overcharge") then
				sideIndex = sideIndex + 1;
				if sideIndex > self.varTip.tipCount then
					sideIndex = 1;
				end;
			end;
		end;
		
		if self.varBody ~= nil then
			if self.varBody.nodes[self.varBody.activeIdx] ~= nil then
				if self.varBody.nodes[self.varBody.activeIdx].tipsL ~= nil then				
					local iil = false;
					while iil == false do
						for i,j in pairs(self.varBody.nodes[self.varBody.activeIdx].tipsL) do
							if j == self.varTip.tips[sideIndex].tipSide then
								iil = true;
								break;
							end;
						end;
						if iil == false then 
							sideIndex = sideIndex + 1;
							if sideIndex > self.varTip.tipCount then
								sideIndex = 1;
							end						
						end;
					end;
				end;
			end;
		end;
			
		self.varTip.activeTipIdx = sideIndex; 
			
		
		--### Update properties: reference point for trigger, particle system and animation
		if string.match(self.varTip.tips[sideIndex].tipSide, "charge") then
			self.tipReferencePoint = nil;
			self.triggerPlacement = self.varTip.tips[3].tipIndex;
			self.isTriggerPlaceDefined = true;
		else
			self.tipReferencePoint = self.varTip.tips[sideIndex].tipIndex;
			self.triggerPlacement = self.tipReferencePoint;
			self.isTriggerPlaceDefined = true;
		end;
		
		for k,v in pairs(self.varTip.tips) do
			for i,j in pairs(v.dischargePS) do
				Utils.setEmittingState(j,false);
			end;
		end;
		self.dischargeParticleSystems = self.varTip.tips[sideIndex].dischargePS;
			
		if not string.match(self.varTip.tips[sideIndex].tipSide, "overcharge") then 
		
			local tipTable = self.varTip.tips[sideIndex];	
			if tipTable.animCharSet ~= 0 and tipTable.animCharSet ~= nil then
				self.tipAnimCharSet = tipTable.animCharSet;
				self.tipAnimSpeedScale = tipTable.animSpeedScale;
				self.tipAnimDuration = tipTable.animTipDuration;
				self.tipDischargeEndTime = tipTable.animTipDischargeEndTime;
				
				assignAnimTrackClip(tipTable.animCharSet, 0, getAnimClipIndex(tipTable.animCharSet, tipTable.animClip)); 
				setAnimTrackLoopState(tipTable.animCharSet, 0, false);
			end;
			self.varTip.enableOvercharge = false;
		else
			self.varTip.enableOvercharge = true;
		end;
		
		if string.match(self.varTip.tips[sideIndex].tipSide, "charge") and
			not string.match(self.varTip.tips[sideIndex].tipSide, "overcharge") 
			then
			addTrigger(self.varTip.tips[sideIndex].tipIndex, "onEnterChargeTrigger", self);
			if self.varTip.tips[sideIndex].parts ~= nil then
				for i,j in pairs(self.varTip.tips[sideIndex].parts) do
					setVisibility(j, true);
				end;
			end;
		else
			removeTrigger(self.varTip.tips[sideIndex].tipIndex, "onEnterChargeTrigger", self);
			for i,j in pairs(self.varTip.tips) do
				if j.parts ~= nil and string.match("charge", j.tipSide) then
					for k,l in pairs(j.parts) do
						setVisibility(l, false);
					end;
				end;
			end;
		end;
		
		if string.match(self.varTip.tips[sideIndex].tipSide, "overcharge") then
			if self.varTip.tips[sideIndex].parts ~= nil then
				for i,j in pairs(self.varTip.tips[sideIndex].parts) do
					setVisibility(j, true);
				end;
			end;
			if self.varTip.tips[sideIndex].nonParts ~= nil then
				for i,j in pairs(self.varTip.tips[sideIndex].nonParts) do
					setVisibility(j, false);
				end;
			end;
		else
			for i,j in pairs(self.varTip.tips) do
				if j.parts ~= nil and string.match(j.tipSide, "overcharge") then
					for k,l in pairs(j.parts) do
						setVisibility(l, false);
					end;
				end;
				if j.nonParts ~= nil and string.match(j.tipSide, "overcharge") then
					for k,l in pairs(j.nonParts) do
						setVisibility(l, true);
					end;
				end;
			end;
		end;

		
		if string.match(self.varTip.tips[sideIndex].tipSide, "charge") then
			--self.allowTipDischarge = false;
		else
			--self.allowTipDischarge = true;
		end;
	end;

	
		
end;

function VariableTip:setActiveTrailer(trailerIdx, noEventSend)

	VariableTrailerEvent.sendEvent(self, trailerIdx, noEventSend);
	
	if trailerIdx > 0 then
		self.varTip.activeTrailerIdx = trailerIdx;
	end;
	
end;


function VariableTip:setUnloadingState(status, noEventSend)
	SetUnloadingEvent.sendEvent(self, status, noEventSend);
	
	if status == false then
		self.varTip.doUnload = false;
		self.isUnloading = false;
	else
		self.varTip.doUnload = true;
		self.isUnloading = true;
		self.varTip.unloadDone = false;
	end;
	
end;

function VariableTip:onEnterChargeTrigger(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)

	if onEnter then 
		local vehicle = g_currentMission.nodeToVehicle[otherShapeId];
		if vehicle ~= nil then
			if vehicle ~= self and vehicle.setFillLevel then
				self.varTip.trailerInTrigger = vehicle;
				self.trailerFoundId = otherId;
			end;
		end;
	end;

	if onLeave then
		self.varTip.trailerInTrigger = nil;
	end;

	if onStay then
		if self.varTip.trailerInTrigger ~= nil then
		end;		
	end;
end;