--
-- ExtendedTipTrigger
-- Specialization for ExtendedTipTrigger
--
-- SFM-Modding
-- @author  Manuel Leithner
-- @date:		31/07/11
-- @version:	v1.5
-- @history:	v1.0 - initial implementation
-- 				v1.5 - FS13 Conversion and other stuff - Xentro (Marcus@Xentro.se)
--
--

-- local copy of directory
local modName = g_currentModName;
local modDir = g_currentModDirectory;


ExtendedTipTrigger = {};
ExtendedTipTrigger_mt = Class(ExtendedTipTrigger, Object);
InitObjectClass(ExtendedTipTrigger, modName .. ".ExtendedTipTrigger");


function ExtendedTipTrigger:new(isServer, isClient, customMt)
    local mt = customMt;
    if mt == nil then
        mt = ExtendedTipTrigger_mt;
    end;
	
    local self = Object:new(isServer, isClient, mt);
	
    self.triggerId = 0;
    self.className = modName .. ".ExtendedTipTrigger";
	self.extendedTipTriggerDirtyFlag = self:getNextDirtyFlag();
	
	g_currentMission:addTipTrigger(self);
	
    return self;
end;

function ExtendedTipTrigger:load(id, trigger, fillType)
	self.triggerId = id;
	
    addTrigger(id, "triggerCallback", self);

    self.appearsOnPDA = false;
    self.isFarmTrigger = false;
    self.stationName = "AlternativeTip" .. Utils.getNoNil(getUserAttribute(id, "stationName"), "Station");
	
	self.isRemovalAllowed = true;
	self.isExtendedTrigger = true;
	self.currentShovels = {};
	self.currentCombines = {};
	
	self.fixpoint = trigger.fixpoint;
	self.transform = trigger.transform;
	self.planes = trigger.planes;
	
	self.triggerStartId = getChildAt(id, 1);
	self.triggerEndId = getChildAt(id, 2);
	self.triggerWidth = Utils.getNoNil(getUserAttribute(id, "triggerWidth"), 3);
	
	self.capacity = Utils.getNoNil(getUserAttribute(id, "capacity"), 100000);
	self.currentFillType = fillType;
	self.fillLevel = 0;
	
--**************************************************** DURAL *****************************************************************
--** getting heapSlopeFactor. see ExtendedTipTrigger:updateMoving
	self.heapSlopeFactor = Utils.getNoNil(getUserAttribute(id, "heapSlopeFactor"), 1);	
	self.displayYOffset = Utils.getNoNil(getUserAttribute(id, "displayYOffset"), 1.5);	
--****************************************************************************************************************************
	
	
	
    self.acceptedFillTypes = {};
	self.acceptedFillTypes[self.currentFillType] = true;
    self.priceMultipliers = {};
    self.priceMultipliers[self.currentFillType] = 0
	
	local parent = getParent(id);
    local movingIndex = getUserAttribute(id, "movingIndex");
    if movingIndex ~= nil then
        self.movingId = Utils.indexToObject(parent, movingIndex);
        if self.movingId ~= nil then
            self.moveMinY = Utils.getNoNil(getUserAttribute(id, "moveMinY"), 0);
            self.moveMaxY = Utils.getNoNil(getUserAttribute(id, "moveMaxY"), 0);
            self.moveScale = Utils.getNoNil(getUserAttribute(id, "moveScale"), 0.001) * 0.01;
            self.moveBackScale = (self.moveMaxY - self.moveMinY) / Utils.getNoNil(getUserAttribute(id, "moveBackTime"), 10000);
        end;
    end;
	
	if self.planes ~= nil then		
		local found = false;
		local defaultPlane = nil;
		for k,plane in pairs(self.planes) do
			local currentObj = Utils.indexToObject(id, plane.path);
			if k == 1 then
				defaultPlane = currentObj;
				self.movingId = getParent(defaultPlane);
			end;
			if plane.type == Fillable.fillTypeIntToName[self.currentFillType] then				
				setVisibility(currentObj, true);
				found = true;
			else
				setVisibility(currentObj, false);
			end;				
		end;	
		if not found then
			setVisibility(defaultPlane, true);
			print("Warning: No plane defined for fruittpye " .. Fillable.fillTypeIntToName[self.currentFillType]);
		end;
	else
		print("Error: Could not set trigger plane. No planes defined");
	end;
	
    local shovelNode = getUserAttribute(id, "shovelNode");
    if shovelNode ~= nil then
		local node = Utils.indexToObject(id, shovelNode);
		
		if node ~= nil then
			self.shovelNode = node;
			g_currentMission:addNodeObject(self.shovelNode, self);
		end;
	end;
	
	local x, _, z = getWorldTranslation(id);
	local iconSize = g_currentMission.missionPDA.pdaMapWidth / 15;
	self.mapHotspot = g_currentMission.missionPDA:createMapHotspot("TipPlace", Utils.getFilename("hud/pda_heap.dds", g_alternativeTipTriggerDir), x, z, iconSize, iconSize * (4 / 3), false, false, false, 0, true);
	
	self.rootNode = self.triggerId;
    self.isEnabled = true;
	self.playerInRange = false;
	self.vehiclesInRange = {};
	
    return self;
end;

function ExtendedTipTrigger:delete()
    ExtendedTipTrigger:superClass().delete(self);
	if self.mapHotspot ~= nil then
		g_currentMission.missionPDA:deleteMapHotspot(self.mapHotspot);
	end;
	
	if self.shovelNode ~= nil then		
		g_currentMission:removeNodeObject(self.shovelNode);
	end;
	
	for shovel in pairs(self.currentShovels) do
		self:removeShovel(shovel);
	end;
	
	for combine in pairs(self.currentCombines) do
		self:removeCombine(combine);
	end;
	
    g_currentMission:removeTipTrigger(self);
	
    if self.triggerId ~= 0 then
		removeTrigger(self.triggerId);
		delete(self.triggerId);
	end;
	
	for _, v in pairs(g_currentMission.trailerTipTriggers) do
		for key, tipTrigger in pairs(v) do
			if tipTrigger == self then
				v[key] = nil;
			end;
		end;
	end;
end;

function ExtendedTipTrigger:readStream(streamId, connection)
	ExtendedTipTrigger:superClass().readStream(self, streamId, connection);
end;

function ExtendedTipTrigger:writeStream(streamId, connection)
	ExtendedTipTrigger:superClass().writeStream(self, streamId, connection);
end;

function ExtendedTipTrigger:readUpdateStream(streamId, timestamp, connection)
	ExtendedTipTrigger:superClass().readUpdateStream(self, streamId, timestamp, connection);
	if connection:getIsServer() then
		self.fillLevel = streamReadFloat32(streamId);
--**************************************************** DURAL *****************************************************************
		--local x, y, z = getTranslation(self.movingId);
		--y = self.moveMinY + (self.fillLevel / self.capacity) * (self.moveMaxY - self.moveMinY);
        --setTranslation(self.movingId, x, y, z);	
		ExtendedTipTrigger.setMovingIdHeight(self);	
--************************************************************** END DURAL *******************************************************************************
		if self.fillLevel <= 0 and self.isServer then
			g_currentMission.vehiclesToDelete[self] = self;
		end;
    end;
end;

function ExtendedTipTrigger:writeUpdateStream(streamId, connection, dirtyMask)
	ExtendedTipTrigger:superClass().writeUpdateStream(self, streamId, connection, dirtyMask);
    if not connection:getIsServer() then
		streamWriteFloat32(streamId, self.fillLevel);
    end;
end;

function ExtendedTipTrigger:update(dt)
	if self.isRemovalAllowed then
		for shovel in pairs(self.currentShovels) do
			if shovel.objectFound ~= nil and shovel.objectFound == self then				
				self:fillShovel(shovel, dt);
			else
				self:removeShovel(shovel);
			end;
		end;
	end;
end;

function ExtendedTipTrigger:updateTick(dt)
	for combine in pairs(self.currentCombines) do
		if combine.heapFound == 0 or combine.heapFound ~= 0 and combine.heapFound ~= self or not combine.pipeIsUnloading then
			self:removeCombine(combine);
		end;
	end;
end;

function ExtendedTipTrigger:draw()
	if self:getCanInteract() and g_gui.currentGui == nil then
		local width = g_currentMission.fruitSymbolSize;
		local overlay = g_currentMission.fillTypeOverlays[self.currentFillType];
		local fillType = Fillable.fillTypeIndexToDesc[self.currentFillType];
		local x, y, z = getWorldTranslation(self.movingId);
	
		if overlay ~= nil then
			width = overlay.width;
		end;
		
--**************************************************** DURAL *****************************************************************
		x, y, z = project(x, y + self.displayYOffset, z); -- now using displayYOffset instead of fixed value
		--x, y, z = project(x, y + 1.5, z);
--****************************************************************************************************************************		
		x = x - (width / 4);
		
		if x < 1 and y < 1 and z < 1 and x > -0.1 and y > -0.1 and z > -0.1 then
			if overlay ~= nil then
				renderOverlay(overlay.overlayId, x, y, overlay.width / 2, overlay.height / 2);
			end;

--**************************************************** DURAL *****************************************************************
			--setting the display color  to white
			--example : when pressing F1 key to disable the help hud, or when playing with mouse-axis vehicle
			setTextColor(1, 1, 1, 1);
--****************************************************************************************************************************
			setTextAlignment(RenderText.ALIGN_CENTER);
			if fillType ~= nil then
				renderText(x + (width / 4), y + 0.035 + 0.02, 0.017, fillType.nameI18N);
			end;
			renderText(x + (width / 4), y - 0.02, 0.017, string.format("%d", self.fillLevel));
			setTextAlignment(RenderText.ALIGN_LEFT);
		end;
	end;
end;

function ExtendedTipTrigger:updateMoving(delta)

	self.fillLevel = math.min(self.fillLevel+delta, self.capacity);

	--"g_currentMission.trailerInTipRange" only works for client side !!!!!! whereas "updateMoving" is called by "updateTrailerTipping" that is only server side !!!! not mp compatible
	--all this code should be called directly in "updateTrailerTipping", not in "updateMoving"

	--[[
	local oldFillLevel = self.fillLevel + delta;
	self.fillLevel = math.min(oldFillLevel, self.capacity);
	
	local refill = oldFillLevel - self.fillLevel;
	if refill > 0 then
		local trailer = g_currentMission.trailerInTipRange;
		if trailer ~= nil then
			trailer:setFillLevel(trailer.fillLevel + refill, trailer.currentFillType);			
			trailer:onEndTip();
		end;
	end;
	]]
	
	if self.movingId ~= nil then
--*************************************************************** DURAL ************************************************************************************
		--local x,y,z = getTranslation(self.movingId);		
		--y = self.moveMinY + (self.fillLevel / self.capacity)*(self.moveMaxY-self.moveMinY);
		ExtendedTipTrigger.setMovingIdHeight(self);		
		--setTranslation(self.movingId, x, y, z);
--************************************************************** END DURAL *******************************************************************************
		if self.isServer then
			self:raiseDirtyFlags(self.extendedTipTriggerDirtyFlag);
		end;
	end;
	
	if self.fillLevel <= 0 and self.isServer then
		g_currentMission.vehiclesToDelete[self] = self;
	end;
end;


--*************************************************************** DURAL ************************************************************************************
function ExtendedTipTrigger.setMovingIdHeight(self)
	--** adding heapSlopeFactor
	--** heap are not always cubique !!!! they can be cones !
	--** if the heap is a perfect cone, then the heapSlopeFactor = 1/3
	--print("ExtendedTipTrigger.setMovingIdHeight self.fillLevel / self.capacity = " .. tostring(self.fillLevel) .. " / " .. tostring(self.capacity));
	local x,y,z = getTranslation(self.movingId);
	y = self.moveMinY + (self.moveMaxY-self.moveMinY) * (self.fillLevel / self.capacity)^self.heapSlopeFactor;
	setTranslation(self.movingId, x, y, z);	
end;
--************************************************************** END DURAL *******************************************************************************



function ExtendedTipTrigger:getCanInteract()
	if g_currentMission.controlPlayer and self.playerInRange then
		return true;
	end;
	
	if not g_currentMission.controlPlayer then
		for vehicle in pairs(self.vehiclesInRange) do
			if vehicle:getIsActiveForInput(false) then
				return true;
			end;
		end;
		
		for shovel in pairs(self.currentShovels) do
			if shovel:getIsActiveForInput(false) then
				return true;
			end;
		end;
		
		for combine in pairs(self.currentCombines) do
			if combine:getIsActiveForInput(false) then
				return true;
			end;
		end;
	end;
	
	return false;
end;

function ExtendedTipTrigger:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
	if self.isEnabled then
		if onEnter or onLeave then
			if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then
				if onEnter then
					self.playerInRange = true;
				else
					self.playerInRange = false;
				end;
			else
				local vehicle = g_currentMission.nodeToVehicle[otherId];
				if vehicle ~= nil then
					if onEnter then
						if self.vehiclesInRange[vehicle] == nil then
							self.vehiclesInRange[vehicle] = true;
						end;
					else
						if self.vehiclesInRange[vehicle] then
							self.vehiclesInRange[vehicle] = nil;
						end;
					end;
				end;
			end;
        end;
		
		local vehicle = g_currentMission.objectToTrailer[otherId];

		--********************************  DURAL ****************************************
		--** no need to continue filling process possibility if the tip is "full"
		--** allow us to play with a trator with double trailers or more ("road train") and making many "heaps" side by side
		local heapIsFull = self.fillLevel>=self.capacity;
		
		if onEnter and not heapIsFull then
			if vehicle ~= nil then
				if vehicle.currentFillType == self.currentFillType then
				--*******************************  DURAL ***************************
				-- vehicle~=nil useless here
					--if vehicle ~= nil and vehicle.allowTipDischarge then
					if vehicle.allowTipDischarge then
						if g_currentMission.trailerTipTriggers[vehicle] == nil then
							g_currentMission.trailerTipTriggers[vehicle] = {};
						end;
						table.insert(g_currentMission.trailerTipTriggers[vehicle], self);
					end;
				end;
			end;			
		elseif onLeave or heapIsFull then
			if vehicle ~= nil and vehicle.allowTipDischarge then
				local triggers = g_currentMission.trailerTipTriggers[vehicle];
				if triggers ~= nil then
					for i=1, table.getn(triggers) do
						if triggers[i] == self then
							table.remove(triggers, i);
							if table.getn(triggers) == 0 then
								g_currentMission.trailerTipTriggers[vehicle] = nil;
							end;
							break;
						end;
					end;
					if table.getn(triggers) == 1 then
						if triggers[1] == self then
							g_currentMission.trailerTipTriggers[vehicle] = nil;
						end;
					end;
				end;
			end;
		end;
	end;
end;


-- for Trailers
function ExtendedTipTrigger:updateTrailerTipping(trailer, fillDelta, fillType)	

	--print(trailer.time .. " ExtendedTipTrigger:updateTrailerTipping - fillDelta = " .. tostring(fillDelta) .. " / fillType=" .. tostring(fillType));

	if fillDelta < 0 then	

		--code previously called in "updateMoving"
		local newFillLevel = self.fillLevel - fillDelta;
		if newFillLevel>self.capacity then
			local refill = newFillLevel - self.capacity;						
			if trailer ~= nil then
				--***************************************************  DURAL  *******************************************
				--** take into account "fillLevelToTippedFillLevel" when refilling the trailer
				trailer:setFillLevel(trailer.fillLevel + refill, trailer.currentFillType, false, nil, trailer.fillLevelToTippedFillLevel);
				--print(trailer.time .. " ExtendedTipTrigger:updateTrailerTipping - stop tipping");
				trailer:onEndTip();
			end;
		end;
	
		self:updateMoving(-fillDelta);
	end;
	
end;

function ExtendedTipTrigger:getTipInfoForTrailer(trailer, tipReferencePointIndex)
	local minDistance, bestPoint = self:getTipDistanceFromTrailer(trailer, tipReferencePointIndex);

	local isAllowed = false;
	if self.currentFillType == trailer.currentFillType then
		isAllowed = true;
	end;
 
	return isAllowed, minDistance, bestPoint;
end;

function ExtendedTipTrigger:getTipDistanceFromTrailer(trailer, tipReferencePointIndex)
	local minDistance = math.huge;
	local bestPoint = nil;
	local bestWidth = -1;
  
	local sx, _, sz = getWorldTranslation(self.triggerStartId);
	local ex, _, ez = getWorldTranslation(self.triggerEndId);
	local dx, dz = ex-sx, ez-sz;
	local length = math.sqrt(dx*dx + dz*dz);
	
	if length > 0.00001 then
		dx = dx / length;
		dz = dz / length;
	end;
	
	if tipReferencePointIndex ~= nil then
		local trailerX, _, trailerZ = getWorldTranslation(trailer.tipReferencePoints[tipReferencePointIndex].node);
		return Utils.getDistanceToRectangle2D(trailerX, trailerZ, sx, sz, dx, dz, length, self.triggerWidth), tipReferencePointIndex;
	else
		for i, point in pairs(trailer.tipReferencePoints) do
			local trailerX, _, trailerZ = getWorldTranslation(point.node);
			local distance = Utils.getDistanceToRectangle2D(trailerX, trailerZ, sx, sz, dx, dz, length, self.triggerWidth);
			
			if point.width <= self.triggerWidth and point.width > bestWidth then
				if distance <= minDistance + 0.001 then
					bestPoint = i;
					bestWidth = point.width;
					minDistance = distance;
				end;
			else
				if distance < minDistance then
					bestPoint = i;
					minDistance = distance;
				end;
			end;
		end;
	end;
	
	return minDistance, bestPoint;
end;

function ExtendedTipTrigger:getNoAllowedText(trailer)
	if trailer.currentFillType ~= Fillable.FILLTYPE_UNKNOWN and self.currentFillType ~= trailer.currentFillType then
		return Fillable.fillTypeIndexToDesc[trailer.currentFillType].nameI18N .. g_i18n:getText("notAcceptedHere");
	end;
	
	return nil;
end;


-- for Shovels
function ExtendedTipTrigger:addShovel(shovel)
	if self.currentShovels[shovel] == nil then
		self.currentShovels[shovel] = true;
	end;
end;

function ExtendedTipTrigger:removeShovel(shovel)
	if self.currentShovels[shovel] ~= nil then
		self.currentShovels[shovel] = nil;
	end;
end;

function ExtendedTipTrigger:fillShovel(shovel, dt)
	--if self.fillLevel > 0 and ((shovel.attacherVehicle ~= nil and shovel.attacherVehicle.movingDirection ~= 0) or (shovel.attacherVehicle == nil and shovel.movingDirection >= 1) or shovel.isSugarbeetCleanerLoader ~= nil or shovel.mixerWagonFillTypes ~= nil) then
	if self.fillLevel > 0 and (math.abs(shovel.lastSpeedReal)>0.0001 or shovel.isSugarbeetCleanerLoader ~= nil or shovel.mixerWagonFillTypes ~= nil) then
		local delta = shovel:fillShovelFromTrigger(self, self.fillLevel, self.currentFillType, dt);
		if delta > 0 then
			self:updateMoving(-delta);
		end;
	end;
end;

function ExtendedTipTrigger:getAllowShovelFillType(fillType)
	if fillType == self.currentFillType then
		return true;
	end;
	
	return false;
end;
 
function ExtendedTipTrigger:addShovelFillLevel(shovel, fillLevelDelta, fillType)
	if self:getAllowShovelFillType(fillType) then
		local oldFillLevel = self.fillLevel;
		self:updateMoving(fillLevelDelta);
		
		return self.fillLevel - oldFillLevel;
	end;
	
	return 0;
end; 


-- for Combines
function ExtendedTipTrigger:addCombine(combine)
	if self.currentCombines[combine] == nil then
		self.currentCombines[combine] = true;
	end;
end;

function ExtendedTipTrigger:removeCombine(combine)
	if self.currentCombines[combine] ~= nil then
		self.currentCombines[combine] = nil;
	end;
end;

function ExtendedTipTrigger:setFillLevel(fillDelta, fillType)
	local delta = self.fillLevel - fillDelta;
	self:updateMoving(-delta);
end;

function ExtendedTipTrigger:resetFillLevelIfNeeded(fillType)
end;