-- Quadrant
-- Specialisation for the Fendt 615 LSA
--
-- by fruktor
-- www.eifok-team.de
-- 
--	changelog:	- baleCounter, 
--
-- 

Quadrant = {};


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

function Quadrant:load(xmlFile)	

	--#
	self.maxSpeedLevel = 2;
	self.wasToFast = false;
	self.speedViolationTimer = 0;
	self.speedViolationMaxTime = 500;
	self.setIsBlocked = SpecializationUtil.callSpecializationsFunction("setIsBlocked");
	self.isBlocked = false;
	self.blockTimer = 0;
	self.blockMaxTime = 1500;	
	local name = getXMLString(xmlFile, "vehicle.balerBlockSound#file");
    self.blockSound = createSample("blockSound");
    loadSample(self.blockSound, Utils.getFilename(name, self.baseDirectory), false);		
	self.blockSoundVolume = getXMLFloat(xmlFile, "vehicle.balerBlockSound#volume");
	self.blockSoundPitch = getXMLFloat(xmlFile, "vehicle.balerBlockSound#pitchOffset");
	
	--#
	self.allowPickingUp = Quadrant.allowPickingUp;
	--Utils.overwrittenFunction(self.allowPickingUp, Quadrant.allowPickingUp);
	self.backTimer = 0;
	
	--#
	self.baleCount = 0;
	self.lastBaleCount = 0;
	self.lastBales = {};
	self.resetBaleCount = SpecializationUtil.callSpecializationsFunction("resetBaleCount");
	
	--#
	self.isFirstRun = true;
	self.lastDeltaFillMean = 0;
	self.lastDeltaFill = 0;
	self.lastDeltaFillRun = 0;
	self.lastDeltaFillRunMax = 24;	-- ? 24 ~ 1 sec @ 1/40ms
	self.lastDeltaFillArray = {0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0};	-- 24 ?
	self.isFirstRun2 = true;
	self.lastDeltaFillMean2 = 0;
	self.lastDeltaFill2 = 0;
	self.lastDeltaFillRun2 = 0;
	self.lastDeltaFillRunMax2 = 24;	-- ? 24 ~ 1 sec @ 1/40ms
	self.lastDeltaFillArray2 = {0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0, 0,0,0};	-- 24 ?
	self.lastFillChangeTime = 0;
	
	self.lastFillDeltas = {};
	
	--#
	self.actLoad = 0;
	self.actLoad_Int = 0;
	self.actLoad_IntSend = 0;
	self.setActLoad = SpecializationUtil.callSpecializationsFunction("setActLoad");
	
	
	--#
	self.emptyBaler = SpecializationUtil.callSpecializationsFunction("emptyBaler");
	self.bEmptyBaler = false;

	--# Pick-Up
	--# <pickup componentJointIndex="0" minRotX="0" maxRotX="30"/>
	self.releasePickUp = SpecializationUtil.callSpecializationsFunction("releasePickUp");
	self.pu = {};
	self.pu.isUp = false;
	self.pu.isDown = false;
	self.pu.bDown = false;
	self.pu.jntIdx = getXMLInt(xmlFile, "vehicle.quadrant.pickup#componentJointIndex");
	self.pu.minRot = { Utils.degToRad( getXMLFloat(xmlFile, "vehicle.quadrant.pickup#minRotX") ) };
	self.pu.maxRot = { Utils.degToRad( getXMLFloat(xmlFile, "vehicle.quadrant.pickup#maxRotX") ) };
	self.pu.curRot = self.pu.minRot;
	self.pu.cleanNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.quadrant.pickup#cleanRefNode"));
	
	--# 
	self.pu.dirt = {};
	self.pu.dirt.grass = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.quadrant.dirt#grass"));
	self.pu.dirt.wheat = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.quadrant.dirt#wheat"));
	self.cleaningEndTime = 0;
	setVisibility( self.pu.dirt.grass, false );
	setVisibility( self.pu.dirt.wheat, false );
	
	--#
	self.rakes = {};
	local i=0;
	while true do
		local str = getXMLString(xmlFile, string.format("vehicle.quadrant.rake(%d)#index", i));
		if str == nil then
			break;
		end;
		local e = {};
		e.node = Utils.indexToObject(self.components, str);
		e.speed = Utils.degToRad( getXMLFloat(xmlFile, string.format("vehicle.quadrant.rake(%d)#speed", i)) ) / 1000;
		table.insert(self.rakes, e);
		i = i + 1;
	end
	
	--# Bale-Slide
	--# <baleslide animIndex="0>9" animClip="RutscheclipSource" />
	self.openSlide = SpecializationUtil.callSpecializationsFunction("openSlide");
	self.sl = {};
	self.sl.isOpen = false;
	self.sl.isClosed = false;
	self.sl.bOpen = false;
	self.sl.anim = {};
	self.sl.anim.idx = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.quadrant.baleslide#animIndex"));
	self.sl.anim.charset = getAnimCharacterSet(self.sl.anim.idx);
	self.sl.anim.clipIdx = getAnimClipIndex(self.sl.anim.charset, getXMLString(xmlFile, "vehicle.quadrant.baleslide#animClip"));
	assignAnimTrackClip(self.sl.anim.charset, 0, self.sl.anim.clipIdx);
	setAnimTrackLoopState(self.sl.anim.charset, 0, false);
	self.sl.anim.duration = getAnimClipDuration(self.sl.anim.charset, self.sl.anim.clipIdx);	
	self.sl.openNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.quadrant.baleslide#openRefNode"));

	--#
	local psPath = string.format("vehicle.quadrant.collectParticle");
	local collectPS = {};
	local particleNode = Utils.loadParticleSystem(xmlFile, collectPS, psPath, self.components, false, "$data/vehicles/particleSystems/trailerDischargeParticleSystem.i3d", self.baseDirectory);
	self.collectPS = collectPS;
	
	psPath = string.format("vehicle.quadrant.extraParticle");
	local extraPS = {};
	particleNode = Utils.loadParticleSystem(xmlFile, extraPS, psPath, self.components, false, "$data/vehicles/particleSystems/trailerDischargeParticleSystem.i3d", self.baseDirectory);
	self.extraPS = extraPS;	
	self.activateCollectPS = false;	
	self.collectLastFillLevel = 0;
	self.collectLastFillLevel2 = 0;
	
	psPath = string.format("vehicle.quadrant.cleaningParticle");
	local cleanPS = {};
	particleNode = Utils.loadParticleSystem(xmlFile, cleanPS, psPath, self.components, false, "$data/vehicles/particleSystems/trailerDischargeParticleSystem.i3d", self.baseDirectory);
	self.cleanPS = cleanPS;	
	
	--#
    self.setVehicleIncreaseRpm = SpecializationUtil.callSpecializationsFunction("setVehicleIncreaseRpm");
    self.saveMinimumRpm = 0;	
	
	--#
	self.hud = {};
	self.hud.xPos = 0.84;
	self.hud.yPos = 0.85;
	self.hud.overlayBg = Overlay:new("hudQuadrantBackground", Utils.getFilename("Balecounter_hud1.png", self.baseDirectory), self.hud.xPos, self.hud.yPos, 0.11, 0.04);
		
	self.hud2 = {};
	self.hud2.xPos = 0.84;
	self.hud2.yPos = 0.82;
	self.hud2.hudWidth = 0.16;
	self.hud2.overlay_hud 	= Overlay:new("loadHUD_hud", 	Utils.getFilename("loadHUD_hud.dds", self.baseDirectory), self.hud2.xPos, self.hud2.yPos, 			0.108, 0.039525);
	self.hud2.overlay_bg 	= Overlay:new("loadHUD_bg", 	Utils.getFilename("loadHUD_bg.dds", self.baseDirectory), self.hud2.xPos, self.hud2.yPos, 			0.109, 0.039525);
	self.hud2.overlay_bar 	= Overlay:new("loadHUD_bar", 	Utils.getFilename("loadHUD_barY.dds", self.baseDirectory), self.hud2.xPos+0.025, self.hud2.yPos+0.01, 0.16, 0.02190);
	
	
end;


function Quadrant:postLoad(xmlFile)

end;


function Quadrant:delete()
	Utils.deleteParticleSystem(self.collectPS);
	Utils.deleteParticleSystem(self.extraPS);
	Utils.deleteParticleSystem(self.cleanPS);
	self.hud.overlayBg:delete();
end;

--function Quadrant:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)		-- gets never ever called ?!
--print("function Quadrant:loadFromAttributesAndNodes("..tostring(xmlFile)..", "..tostring(key)..", "..tostring(resetVehicles));
--	if resetVehicles then
--		self.baleCount = 0;
--		self:setIsBlocked(false);
--	end;
--end;

function Quadrant:readStream(streamId, connection)

	self.bEmptyBaler = streamReadBool(streamId);
	
	self.sl.bOpen = streamReadBool(streamId);
	self.sl.isOpen = streamReadBool(streamId);
	self.sl.isClosed = streamReadBool(streamId);
	local tt = streamReadFloat32(streamId);
	enableAnimTrack(self.sl.anim.charset, 0);
	setAnimTrackTime(self.sl.anim.charset, 0, tt, true);
	disableAnimTrack(self.sl.anim.charset, 0);
	
	self.pu.bDown = streamReadBool(streamId);
	self.pu.isDown = streamReadBool(streamId);
	self.pu.isUp = streamReadBool(streamId);
	self.pu.curRot[1] = streamReadFloat32(streamId);
	if self.isServer then	-- ?never ever
		setJointRotationLimit(self.componentJoints[self.pu.jntIdx].jointIndex , 0, true, -self.pu.curRot[1], self.pu.curRot[1]);
	end	
	
	self.baleCount = streamReadFloat32(streamId);
	self.isBlocked = streamReadBool(streamId);
	
end;

function Quadrant:writeStream(streamId, connection)	

	streamWriteBool(streamId, self.bEmptyBaler);

	streamWriteBool(streamId, self.sl.bOpen);
	streamWriteBool(streamId, self.sl.isOpen);
	streamWriteBool(streamId, self.sl.isClosed);
	local tt = getAnimTrackTime(self.sl.anim.charset, 0);
	streamWriteFloat32(streamId, tt);

	streamWriteBool(streamId, self.pu.bDown);
	streamWriteBool(streamId, self.pu.isDown);
	streamWriteBool(streamId, self.pu.isUp);	
	streamWriteFloat32(streamId, self.pu.curRot[1]);
	
	streamWriteFloat32(streamId, self.baleCount);
	streamWriteBool(streamId, self.isBlocked);

end;

function Quadrant:readUpdateStream(streamId, connection, dirtyMask)
end;

function Quadrant:writeUpdateStream(streamId, connection, dirtyMask)
end;

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

function Quadrant:keyEvent(unicode, sym, modifier, isDown)

end;

function Quadrant:update(dt)

	--#
	if self.sl.bOpen == true and self.sl.isOpen == false then
		self.sl.isOpen = false;
		self.sl.isClosed = false;
		if self.isServer then
			--setJointFrame(self.componentJoints[3].jointIndex, 0, self.componentJoints[3].jointNode);
		end
		enableAnimTrack(self.sl.anim.charset, 0);		
		setAnimTrackSpeedScale(self.sl.anim.charset, 0, -2.0);		
		local tt = getAnimTrackTime(self.sl.anim.charset, 0);
		if tt <= 0 then
			self.sl.isOpen = true;
			setAnimTrackTime(self.sl.anim.charset, 0, 0.0, true);
			disableAnimTrack(self.sl.anim.charset, 0);
		end
	end;

	if self.sl.bOpen == false and self.sl.isClosed == false then
		self.sl.isOpen = false;
		self.sl.isClosed = false;
		if self.isServer then
			--setJointFrame(self.componentJoints[3].jointIndex, 0, self.componentJoints[3].jointNode);
		end
		enableAnimTrack(self.sl.anim.charset, 0);	
		setAnimTrackSpeedScale(self.sl.anim.charset, 0, 2.0);	
		local tt = getAnimTrackTime(self.sl.anim.charset, 0);
		if tt >= self.sl.anim.duration then
			self.sl.isClosed = true;
			setAnimTrackTime(self.sl.anim.charset, 0, self.sl.anim.duration, true);
			disableAnimTrack(self.sl.anim.charset, 0);
		end
	end;
	
	
	--#
	if self:getIsActive() then
		
		--#
		if self.isClient and self:getIsActiveForInput(true) and not self:hasInputConflictWithSelection() then
			if InputBinding.hasEvent(InputBinding.LOWER_IMPLEMENT) then
				self:releasePickUp(not self.pu.bDown);
			end			
			if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
				--print(tostring(self.sl.isOpen).." "..tostring(self.isTurnedOn).." "..tostring(self.lastBaleCount>1));
				if self.sl.isOpen and self.isTurnedOn and self.lastBaleCount > 1 then
					self:emptyBaler(not self.bEmptyBaler);
				end
			end
			if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then
				self:resetBaleCount();
			end					
		end
		
	end;
	
	if (self.isTurnedOn and not self.sl.isOpen) or self.isBlocked or self.PTOId then
		self.isTurnedOn = false;
	end;
	
	--#
	if g_currentMission.player ~= nil then
		local pvx, pvy, pvz = getWorldTranslation(g_currentMission.player.rootNode);
		local px, py, pz = getWorldTranslation(self.sl.openNode);
		local distance = Utils.vector3Length(px-pvx, py-pvy, pz-pvz);
		if distance < 2 then
			g_currentMission:addHelpButtonText( g_i18n:getText("QUADRANT_BALESLIDE"), InputBinding.IMPLEMENT_EXTRA );
			if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then
				if self.lastBaleCount < 3 then
					self:openSlide(not self.sl.bOpen);
				end		
			end;
		end;

		local px, py, pz = getWorldTranslation(self.pu.cleanNode);
		local distance = Utils.vector3Length(px-pvx, py-pvy, pz-pvz);
		if distance < 2 then
			if self.isBlocked then
				g_currentMission:addHelpButtonText( g_i18n:getText("QUADRANT_CLEAN"), InputBinding.IMPLEMENT_EXTRA );
				if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA) then
					self:setIsBlocked(false);
				end;
			end;
		end;
	end;	
	
	
	--#	
	if self.attacherVehicle ~= nil then
		if self.attacherVehicle.motor ~= nil then
			if self.isTurnedOn == true then
				self:setVehicleIncreaseRpm(dt, 200+(800*self.actLoad_IntSend), true);
				if self.attacherVehicle.rpmDisplay ~= nil then
					self.attacherVehicle.rpmDisplay.dynOffset = 800*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;
	
	
	--#
	self.lastBaleCount = table.getn(self.bales);
	
	if self.lastBale == nil and table.getn(self.bales) > 0 then
		self.lastBale = self.bales[1];
	else
		if self.bales[1] ~= self.lastBale then
			self.baleCount = self.baleCount + 1;
			self.lastBale = self.bales[1];
		end;		
	end;

end;

function Quadrant:updateTick(dt)
--print("function Quadrant:updateTick("..tostring(dt));
	
	if not self.pu.isDown then
		self.wasToFast = false;
	end;

	--# speed control
	-- for client
	local deltaFill = 0;
	if self.isClient then
		if self.collectLastFillLevel ~= self.fillLevel then
			if self.fillLevel > self.collectLastFillLevel then
				deltaFill = self.fillLevel - self.collectLastFillLevel;
			else
				deltaFill = self.lastDeltaFill;
			end;			
			self.collectLastFillLevel = self.fillLevel;
			self.lastFillChangeTime = self.time;
		end	
	end;	
	
	--# host
	if self.isServer then
		local deltaFill2 = 0;
		if self.collectLastFillLevel2 ~= self.fillLevel then
			if self.fillLevel > self.collectLastFillLevel2 then
				deltaFill2 = self.fillLevel - self.collectLastFillLevel2;
			else
				deltaFill2 = 0; --self.lastDeltaFill2;
			end;			
			self.collectLastFillLevel2 = self.fillLevel;
		end	

		if deltaFill2 ~= 0 and self.isFirstRun2 == false then
			table.insert(self.lastFillDeltas, {t=self.time, d=deltaFill2});
		end;
		if self.isFirstRun2 == true then
			self.isFirstRun2 = false;
		end;
		self.lastDeltaFill2 = deltaFill2;
		
		local fill = 0;
		if table.getn(self.lastFillDeltas) > 0 then
			local noMoreOld = false;
			while not noMoreOld do
				if table.getn(self.lastFillDeltas) > 0 then
					for i,lfd in pairs(self.lastFillDeltas) do
						if self.time - lfd.t > 1000 then	
							table.remove(self.lastFillDeltas, i);
							break;
						end
						if i >= table.getn(self.lastFillDeltas) then
							noMoreOld = true;
						end;
					end;
				else
					break;
				end;
			end
			for i,lfd in pairs(self.lastFillDeltas) do
				fill = fill + lfd.d;
			end;
		end
		--print("fill="..tostring(fill).." "..table.getn(self.lastFillDeltas).." "..tostring(self.isFirstRun2));
		
		--print("self.lastDeltaFillMean2 ="..tostring(self.lastDeltaFillMean2).." self.blockTimer="..tostring(self.blockTimer));
		local maxLoad = 150;
		self.actLoad2 = math.max(0, math.min(1, fill / maxLoad));
		
		self:setActLoad( self.actLoad2 ); --math.floor( self.actLoad2*255 + 0.5 ) );
	
		if self.actLoad2 == 1 and self.isServer then --self.lastDeltaFillMean > maxLoad then		
			self.blockTimer = self.blockTimer + dt;
			if self.blockTimer > self.blockMaxTime then
				if not self.isBlocked then
					self.blockTimer = 0;
					self:setIsBlocked(true);
					self.isFirstRun2 = true;
				end
			end;
		elseif self.isServer then
			self.blockTimer = math.max(0, self.blockTimer - dt/10);
		end;
		
	end;	

	--#
	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;

	
	--#
	if self.isTurnedOn then
		for i,rake in pairs(self.rakes) do
			rotate(rake.node, rake.speed*dt, 0, 0);
		end;
	end;
	
	--#
	if self.isClient then
		if deltaFill ~= 0 then 
			Utils.setEmittingState(self.collectPS, true);
			Utils.setEmittingState(self.extraPS, true);
		elseif self.lastFillChangeTime + 300 < self.time then
			Utils.setEmittingState(self.collectPS, false);
			if self.lastFillChangeTime + 1500 < self.time then
				Utils.setEmittingState(self.extraPS, false);
			end;
		end;

		if self.cleaningEndTime > self.time then
			Utils.setEmittingState(self.cleanPS, true);
		else
			Utils.setEmittingState(self.cleanPS, false);
		end;	
	end;
	
	--#	
	if self.pu.bDown == false and self.pu.isUp == false then
		self.pu.isUp = false;
		self.pu.isDown = false;
		local nx = Utils.getMovedLimitedValues(self.pu.curRot, self.pu.minRot, self.pu.maxRot, 1, 2000, dt, self.pu.bDown);
		self.pu.curRot = nx;
		if self.isServer then
			setJointRotationLimit(self.componentJoints[self.pu.jntIdx].jointIndex , 0, true, -nx[1], nx[1]);
		end
		if nx[1] == self.pu.minRot[1] then
			self.pu.isUp = true;
		end;
	end;
	
	if self.pu.bDown == true and self.pu.isDown == false then
		self.pu.isUp = false;
		self.pu.isDown = false;
		local nx = Utils.getMovedLimitedValues(self.pu.curRot, self.pu.minRot, self.pu.maxRot, 1, 2000, dt, self.pu.bDown);			
		self.pu.curRot = nx;
		if self.isServer then
			setJointRotationLimit(self.componentJoints[self.pu.jntIdx].jointIndex , 0, true, -nx[1], nx[1]);
		end
		if nx[1] == self.pu.maxRot[1] then
			self.pu.isDown = true;
		end;
	end;	
	
	--#
	if self.isServer then
		if self.bEmptyBaler and self.lastBaleCount > 0 then
		
		if self.isServer then
			if self.time > self.baleLastPositionTime+100 then
				for i=1, table.getn(self.bales) do
					local bale = self.bales[i];
					bale.lastX, bale.lastY, bale.lastZ = getWorldTranslation(bale.id);
				end;
				self.baleLastPositionTime = self.time;
			end;
		end;		
		
			local done = true;
			--for k, bale in pairs(self.bales) do
			for k=table.getn(self.bales), 1, -1 do
				local bale = self.bales[k];
				if bale.time > 0.4 then
					local sendTime = math.min(1.0, bale.time+(dt/10000));
					--Baler.setBaleTime(self, k, sendTime);
					
					
					if self.baleAnimCurve ~= nil then
						local bale = self.bales[k];
						bale.time = sendTime;
						local v = self.baleAnimCurve:get(bale.time);
						setTranslation(bale.id, v[1], v[2], v[3]);
						setRotation(bale.id, v[4], v[5], v[6]);
						if bale.time >= 1 then
							--Baler.dropBale(self, k);
							
							--local baleIndex = k;						
							--local bale = self.bales[baleIndex];
							local deltaRealTime = (self.time - self.baleLastPositionTime)/1000;
							if self.time == self.baleLastPositionTime then
								deltaRealTime = 1;
							end;
							local x,y,z = getWorldTranslation(bale.id);
							local rx,ry,rz = getWorldRotation(bale.id);
							--link(getRootNode(), bale.id);

							--if self.isServer then
								local baleObject = Bale:new(self.isServer, self.isClient);
								baleObject:load(bale.filename, x,y,z,rx,ry,rz);
								baleObject:register();

								local lx, ly, lz = bale.lastX, bale.lastY, bale.lastZ;
								print("vx/vy/vz="..tostring((x-lx)/deltaRealTime).."/"..tostring((y-ly)/deltaRealTime).."/"..tostring((z-lz)/deltaRealTime));
								setLinearVelocity(baleObject.nodeId, (x-lx)/deltaRealTime, (y-ly)/deltaRealTime, (z-lz)/deltaRealTime);
								--setLinearVelocity(baleObject.nodeId, 0, 0, 0);
							--end;
							delete(bale.id);
							table.remove(self.bales, k);

							-- increase bale count if variable exists (baling mission)
							if g_currentMission.baleCount ~= nil then
								g_currentMission.baleCount = g_currentMission.baleCount + 1;
							end;
												
						end;
						
						if self.isServer then
							if noEventSend == nil or not noEventSend then
								g_server:broadcastEvent(BalerSetBaleTimeEvent:new(self, k, bale.time), nil, nil, self);
							end;
						end;
						
					end;		
					
					done = false;
				end;
			end; 
			self.bEmptyBaler = (not done); -- and self.lastBaleCount > 1;
		end;
		
		if self.bEmptyBaler then
			if self.attacherVehicle ~= nil then
				local kmh = self.attacherVehicle.lastSpeed*self.attacherVehicle.speedDisplayScale*3600;
				if kmh < 10 then
					self.attacherVehicle.motor:setSpeedLevel(1, true);
					self.attVehMoving = true;
				else
					self.attacherVehicle.motor:setSpeedLevel(0, false);
					self.attVehMoving = false;
				end;
			end;
		elseif self.attVehMoving == true then
			if self.attacherVehicle ~= nil then
				self.attacherVehicle.motor:setSpeedLevel(0, false);
			end;
			self.attVehMoving = false;
		end;
	end;
	
end;

function Quadrant:draw()
	
	--self.hud2.overlayBg:render();
	--renderText(self.hud2.xPos+0.04, self.hud2.yPos+0.007, 0.02, string.format("%5.2f %%", 100*self.actLoad_IntSend/255));
	
	--# actual load hud
	self.hud2.overlay_bg:render();
	
	local percentage = self.actLoad_IntSend; --/255; --self.actLoad;
	self.hud2.overlay_bar.width = self.hud2.hudWidth * math.min(1, percentage/2);	
    setOverlayUVs(self.hud2.overlay_bar.overlayId, 0, 0.05, 0, 1, math.min(1, percentage/2), 0.05, math.min(1, percentage/2), 1);
	self.hud2.overlay_bar:render();
		
	self.hud2.overlay_hud:render();	
	
	renderText(self.hud2.xPos+0.04, self.hud2.yPos+0.007, 0.02, string.format("%5.2f %%", 100*self.actLoad_IntSend)); --/255)); --100*self.actLoad)); -- 

	--- bale counter hud
	self.hud.overlayBg:render();
	renderText(self.hud.xPos+0.04, self.hud.yPos+0.007, 0.02, string.format("%5d", self.baleCount));
	
	--#
	g_currentMission:addHelpButtonText( g_i18n:getText("QUADRANT_PICKUP"), InputBinding.LOWER_IMPLEMENT );	
	g_currentMission:addHelpButtonText( g_i18n:getText("QUADRANT_RESETBALECOUNT"), InputBinding.IMPLEMENT_EXTRA3 );	
	if self.sl.isOpen and self.isTurnedOn and self.lastBaleCount > 1 then
		g_currentMission:addHelpButtonText( g_i18n:getText("QUADRANT_EMPTY"), InputBinding.IMPLEMENT_EXTRA2 );	
	end
	--if self.lastBaleCount == 0 then
	--	g_currentMission:addHelpButtonText( g_i18n:getText("QUADRANT_BALESLIDE"), InputBinding.QUADRANT_BALESLIDE );	
	--end;
	if self.isBlocked then
		--g_currentMission:addHelpButtonText( g_i18n:getText("QUADRANT_ISBLOCKED"), InputBinding.QUADRANT_EMPTY  );
		g_currentMission:addWarning(g_i18n:getText("QUADRANT_ISBLOCKED"));
	end
end;

function Quadrant:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)	

	self.bEmptyBaler = getXMLBool(xmlFile, key.."#EmptyBaler");
	
	self.sl.bOpen = getXMLBool(xmlFile, key.."#SlideOpen");
	self.sl.isOpen = getXMLBool(xmlFile, key.."#SlideIsOpen");
	self.sl.isClosed = getXMLBool(xmlFile, key.."#SlideIsClosed");
	local tt = getXMLFloat(xmlFile, key.."#SlideTime");
	enableAnimTrack(self.sl.anim.charset, 0);	
	setAnimTrackTime(self.sl.anim.charset, 0, tt, true);
	disableAnimTrack(self.sl.anim.charset, 0);

	self.pu.bDown = getXMLBool(xmlFile, key.."#PuDown");
	self.pu.isDown = false; --getXMLBool(xmlFile, key.."#PuIsDown");
	self.pu.isUp = false; --getXMLBool(xmlFile, key.."#PuIsUp");
	--self.pu.curRot[1] = getXMLFloat(xmlFile, key.."#PuRot");
	--if self.isServer then	
	--	setJointRotationLimit(self.componentJoints[self.pu.jntIdx].jointIndex , 0, true, -self.pu.curRot[1], self.pu.curRot[1]);
	--end
	
	self.baleCount = getXMLFloat(xmlFile, key.."#BaleCount");
	
    return BaseMission.VEHICLE_LOAD_OK;
end;

function Quadrant:getSaveAttributesAndNodes(nodeIdent)

	local attributes = ' EmptyBaler="'.. tostring(self.bEmptyBaler) .. '"';

	attributes = attributes .. ' SlideOpen="'.. tostring(self.sl.bOpen) .. '"';
	attributes = attributes .. ' SlideIsOpen="'.. tostring(self.sl.isOpen) .. '"';
	attributes = attributes .. ' SlideIsClosed="'.. tostring(self.sl.isClosed) .. '"';
	local tt = getAnimTrackTime(self.sl.anim.charset, 0);
	attributes = attributes .. ' SlideTime="'.. tostring(tt) .. '"';

	attributes = attributes .. ' PuDown="'.. tostring(self.pu.bDown) .. '"';
	attributes = attributes .. ' PuIsDown="'.. tostring(self.pu.isDown) .. '"';
	attributes = attributes .. ' PuIsUp="'.. tostring(self.pu.isUp) .. '"';
	attributes = attributes .. ' PuRot="'.. tostring(self.pu.curRot[1]) .. '"';
	
	attributes = attributes .. ' BaleCount="'.. tostring(self.baleCount) .. '"';
	
	return attributes, nil;
end;

--#
function Quadrant:allowPickingUp()
	local allow = self.allowPick and (self.pu.curRot[1] > self.pu.maxRot[1]/2);	
	return allow;
end;

--#
function Quadrant:setIsBlocked(state, noEventSend)
	SetBlockedEvent.sendEvent(self, state, noEventSend);
	self.isBlocked = state;
	if state == true then
		if self.isClient and self:getIsActiveForSound() then
			playSample(self.blockSound, 1, self.blockSoundVolume, 0);
		end;		
		--if self.
		setVisibility( self.pu.dirt.wheat, true );
	else
		setVisibility( self.pu.dirt.wheat, false );
		self.cleaningEndTime = self.time + 1200;
	end	
end;

--#
function Quadrant:emptyBaler(state, noEventSend)	
	SetEmptyBalerEvent.sendEvent(self, state, noEventSend);
	self.bEmptyBaler = state;
end;

--#
function Quadrant:releasePickUp(state, noEventSend)	
	SetReleasePickUp.sendEvent(self, state, noEventSend);
	self.pu.bDown = state;
end;

--#
function Quadrant:openSlide(state, noEventSend)	
	SetOpenSlideEvent.sendEvent(self, state, noEventSend);
	self.sl.bOpen = state;
end;

--#
function Quadrant: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 Quadrant: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 Quadrant: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 Quadrant:setActLoad(load, noEventSend)
	SetActLoadEvent.sendEvent(self, load, noEventSend);
	self.actLoad_IntSend = load;
end;

function Quadrant:resetBaleCount(noEventSend)
	ResetBaleCountEvent.sendEvent(self, noEventSend);
	self.baleCount = 0;
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;

--
--
--
--
ResetBaleCountEvent = {};
ResetBaleCountEvent_mt = Class(ResetBaleCountEvent, Event);

InitEventClass(ResetBaleCountEvent, "ResetBaleCountEvent");

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

function ResetBaleCountEvent:new(vehicle)
    local self = ResetBaleCountEvent:emptyNew()
    self.vehicle = vehicle;
	return self;
end;

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

function ResetBaleCountEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));	
end;

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

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