--
-- ESLimiter
-- Specialization for ESLimiter & OperatingHours
--
-- SFM-Modding
-- @author  Manuel Leithner
-- @date:		13/11/12
-- @version:	v4.3

-- @history:	v1.0 - initial implementation
--				v2.0 - convert to LS2011 and some bugfixes
--				v2.1 - Network-Fixes
--				v2.2 - Bugfixes and new huds
--				v3.0 - LS13 & integration of OperatingHours
--				v3.1 - self-installing modification for standard-vehicles and mod-vehicles & manualIgnition compatible (by gotchTOM)
--				v4.0 - "only in version ESLimiter PLUS ..."
--				v4.1 - "only in version ESLimiter PLUS ..."
--				v4.2 - adjustment for compatible with AP (by Defender)
--				v4.3 - eliminate collisions with ESL & OP contained in mods (by Saty)
--
-- free for noncommerical-usage
--

ESLimiter = {};

local esl_directory = g_currentModDirectory;

function ESLimiter.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Motorized, specializations);
end;

function ESLimiter:load(xmlFile)
	self.setNewLimit = SpecializationUtil.callSpecializationsFunction("setNewLimit");
	self.addOperatingTime = SpecializationUtil.callSpecializationsFunction("addOperatingTime");
	
	self.ESLimiter = {};
	self.ESLimiter.maxRPM = {};
	self.ESLimiter.orgMaxRPM = {};
	self.ESLimiter.orgPercent = {};
	self.ESLimiter.percentage = {};
		
	for k,_ in pairs(self.motor.maxRpm) do
		local percentage = string.format("%d",(self.motor.maxRpm[k] * 100 / self.motor.maxRpm[3]));
		self.ESLimiter.percentage[k+1] = tonumber(percentage);
		self.ESLimiter.orgPercent[k+1] = tonumber(percentage);
		self.ESLimiter.maxRPM[k+1] = self.motor.maxRpm[3];
		self.ESLimiter.orgMaxRPM[k+1] = self.motor.maxRpm[k];
	end;
	
	-- set limits for reverse
	self.ESLimiter.maxRPM[1] = self.ESLimiter.maxRPM[2];
	self.ESLimiter.percentage[1] = self.ESLimiter.percentage[2];
	self.ESLimiter.default = self.motor.maxRpm[3];
	
	self.ESLimiter.isButtonPressed = false;
	
	self.ESLimiter.xPos = Utils.getNoNil(getXMLFloat(xmlFile,  "vehicle.ESLimiter#posX"), 0.5029);
	self.ESLimiter.yPos = Utils.getNoNil(getXMLFloat(xmlFile,  "vehicle.ESLimiter#posY"), 0.01238);
	self.ESLimiter.xOffset = 0.130;
	self.ESLimiter.yOffset = 0.009;
	
	self.ESLimiter.hudBarWidth = 0.205;
	self.ESLimiter.overlay = Overlay:new("hudESLOverlay", Utils.getFilename("textures/ESLimiter_hud.png", esl_directory), self.ESLimiter.xPos, self.ESLimiter.yPos, 0.2371, 0.039525);
	self.ESLimiter.overlayBg = Overlay:new("hudESLBackground", Utils.getFilename("textures/ESLimiter_bg.png", esl_directory), self.ESLimiter.xPos, self.ESLimiter.yPos, 0.2371, 0.039525);
	self.ESLimiter.overlayBar = Overlay:new("hudESLbar", Utils.getFilename("textures/ESLimiter_bar.png", esl_directory), self.ESLimiter.xPos + 0.123, self.ESLimiter.yPos + 0.01, 0.21, 0.02190);
	
	self.ESLimiter.operatingTime = 0;
	self.lastLevelUpdate = 0;
	self.lastLevel = 0;
	
	self.isSelectable = true;
end;

function ESLimiter:delete()
end;

function ESLimiter:readStream(streamId, connection)
	for level,_ in pairs(self.ESLimiter.percentage) do
		local percentage = streamReadInt16(streamId) / 10;
		self:setNewLimit(level, percentage, true);
	end;
	
	self:addOperatingTime(streamReadFloat32(streamId), true);
end;

function ESLimiter:writeStream(streamId, connection)
	for _,percentage in pairs(self.ESLimiter.percentage) do
		streamWriteInt16(streamId, percentage*10);
	end;
	
	streamWriteFloat32(streamId, self.ESLimiter.operatingTime);
end;

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

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

function ESLimiter:update(dt)
	if self.isMotorStarted then  
		self:addOperatingTime(dt);
	end;
	
	if self:getIsActive() and self.isMotorStarted then
		local level = 0;
		if self.movingDirection < 0 then
			level = 1;
		else
			if self.motor.speedLevel > 0 and self.isMotorStarted then
				level = self.motor.speedLevel + 1;
			end;
		end;
		
		if self.isHired and ((self.autoPilotEnabled == nil) or (not self.autoPilotEnabled)) then
			if self.attachedCutters ~= nil then
				level = 2;
				for cutter,implement in pairs(self.attachedCutters) do
					if Cutter.getUseLowSpeedLimit(cutter) then
						level = 1;
						break;
					end;
				end;
			else
				level = 1;
			end;
			level = level + 1;
		end;
		
		if self.lastLevel == level then
			self.lastLevelUpdate = self.lastLevelUpdate + dt;
		else
			self.lastLevelUpdate = 0;
		end;
		self.lastLevel = level;
		
		if self.isClient and self:getIsActiveForInput(false) and not self:hasInputConflictWithSelection() then
			if InputBinding.hasEvent(InputBinding.ESLimiter_RESET) then
				for k,_ in pairs(self.motor.maxRpm) do
					self:setNewLimit(k, self.ESLimiter.orgPercent[k], true);
				end;
			end;
			if level ~= 0 then
				local percentage = self.ESLimiter.percentage;
				if InputBinding.isPressed(InputBinding.ESLimiter_PLUS) and percentage[level] < 100 then
					percentage[level] = percentage[level] + 0.3;
					self:setNewLimit(level, percentage[level], true);
					self.ESLimiter.isButtonPressed = true;
				elseif InputBinding.isPressed(InputBinding.ESLimiter_MINUS) and percentage[level] > 10 then
					percentage[level] = percentage[level] - 0.3;
					self:setNewLimit(level, percentage[level], true);
					self.ESLimiter.isButtonPressed = true;
				else
					if self.ESLimiter.isButtonPressed then
						-- only send event to other players if key was released -> performance issue
						self:setNewLimit(level, percentage[level]);
						self.ESLimiter.isButtonPressed = false;
					end;
				end;
			end;
		end;
		
		if level ~= 0 then
			local newRpm = tonumber(string.format("%d",(self.ESLimiter.percentage[level] * self.ESLimiter.maxRPM[level] / 100)));
			if self.movingDirection >= 0 then
				self.motor.maxRpm[level - 1] = newRpm;
			else
				-- backward
				self.motor.maxRpm[3] = newRpm;
			end;
		else
			-- reset to default value if no level is set
			self.motor.maxRpm[3] = self.ESLimiter.default;
		end;
	end;
end;

function ESLimiter:draw()
	local text = Utils.getNoNil(g_i18n:getText("ESLimiter_deactivated"), "Deactivated");
	local currentLevel = 0;
	local currentLevelText = 0;
	local percentage = 0;
	
	if self.isMotorStarted then
		currentLevel = self.motor.speedLevel;
	end;
	
	if self.movingDirection < 0 then
		currentLevel = 1;
		currentLevelText = "R";
	else
		if self.isHired and ((self.autoPilotEnabled == nil) or (not self.autoPilotEnabled)) then
			if self.attachedCutters ~= nil then
				currentLevel = 2;
				for cutter,implement in pairs(self.attachedCutters) do
					if Cutter.getUseLowSpeedLimit(cutter) then
						currentLevel = 1;
						break;
					end;
				end;
			else
				currentLevel = 1;
			end;
		end;
	
		if currentLevel ~= 0 then
			currentLevel = currentLevel + 1;
			currentLevelText = currentLevel - 1;
		end;
	end;
	
	if currentLevel ~= 0 and (self.lastLevelUpdate > 500 or self.movingDirection > 0) then
		percentage = tonumber(string.format("%d", self.ESLimiter.percentage[currentLevel]));
		if percentage < 100 then
			percentage = " " .. percentage;
		end;
		text = g_i18n:getText("ESLimiter_level") .. currentLevelText .. ":" .. percentage .. "% ";
		g_currentMission:addExtraPrintText(string.format(g_i18n:getText("ESLimiter_controls"), self.typeDesc) .. " " .. InputBinding.getKeyNamesOfDigitalAction(InputBinding.ESLimiter_PLUS) .. "/" .. InputBinding.getKeyNamesOfDigitalAction(InputBinding.ESLimiter_MINUS));
	end;
	
	self.ESLimiter.overlayBg:render();
	
	self.ESLimiter.overlayBar.width = self.ESLimiter.hudBarWidth * math.min(1, percentage/190);	
    setOverlayUVs(self.ESLimiter.overlayBar.overlayId, 0, 0.05, 0, 1, math.min(1, percentage/190), 0.05, math.min(1, percentage/190), 1);
	self.ESLimiter.overlayBar:render();
	
	self.ESLimiter.overlay:render();
	setTextBold(true);
	setTextAlignment(RenderText.ALIGN_LEFT);
	setTextColor(0, 0, 0, 1);
    renderText(self.ESLimiter.xPos + self.ESLimiter.xOffset, self.ESLimiter.yPos + self.ESLimiter.yOffset, 0.02, text);	
	setTextColor(1, 1, 1, 1);
	renderText(self.ESLimiter.xPos + self.ESLimiter.xOffset, self.ESLimiter.yPos + self.ESLimiter.yOffset + 0.002, 0.02, text);	
	
	local operatingHours = math.floor((self.ESLimiter.operatingTime / 1000 / 60 / 60)*10) / 10;
	local hours = math.floor(operatingHours);
	local minutes = tostring(math.floor((operatingHours - hours)*10));
	setTextAlignment(RenderText.ALIGN_RIGHT);
	setTextBold(true);
	setTextColor(0, 0, 0, 1);
    renderText(self.ESLimiter.xPos+0.062, self.ESLimiter.yPos + self.ESLimiter.yOffset, 0.02, tostring(hours) .. ",");
	setTextColor(1,1,1,1);
	renderText(self.ESLimiter.xPos+0.062, self.ESLimiter.yPos + self.ESLimiter.yOffset + 0.002, 0.02, tostring(hours) .. ",");
	setTextColor(0, 0, 0, 1);
    renderText(self.ESLimiter.xPos+0.071, self.ESLimiter.yPos + self.ESLimiter.yOffset, 0.02, minutes);
	setTextColor(0.95,0,0,1);
	renderText(self.ESLimiter.xPos+0.071, self.ESLimiter.yPos + self.ESLimiter.yOffset + 0.002, 0.02, minutes);
	setTextColor(0, 0, 0, 1);
    renderText(self.ESLimiter.xPos+0.08, self.ESLimiter.yPos + self.ESLimiter.yOffset, 0.02, "h");
	setTextColor(1,1,1,1);
	renderText(self.ESLimiter.xPos+0.08, self.ESLimiter.yPos + self.ESLimiter.yOffset + 0.002, 0.02, "h");
	setTextAlignment(RenderText.ALIGN_LEFT);
end;

function ESLimiter:setNewLimit(level, percentage, noEventSend)	
	ESLimiterEvent.sendEvent(self, level, percentage, noEventSend);	
	self.ESLimiter.percentage[level] = percentage;
end;

function ESLimiter:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	local valueStr = getXMLString(xmlFile, key.."#limiterValues");
	if valueStr ~= nil then
		local found = string.find(valueStr, ";");
		local index = 1;
		while found ~= nil do
			local value = string.sub(valueStr, 0, found-1);
			valueStr = string.sub(valueStr, found+1);
			found = string.find(valueStr, ";");
			self:setNewLimit(index, tonumber(value), true);
			if found == nil then
				value = tonumber(valueStr);
				self:setNewLimit(index+1, tonumber(value), true);
			end;
			index = index + 1;
		end;
	end;
	
	local operatingTime = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#operatingTime"), self.ESLimiter.operatingTime) * 1000;
	self:addOperatingTime(operatingTime);

    return BaseMission.VEHICLE_LOAD_OK;
end;

function ESLimiter:getSaveAttributesAndNodes(nodeIdent)
	local values = "";
	for k,v in pairs(self.ESLimiter.percentage) do
		if k ~= 1 then
			values = values .. ";";
		end;
		values = values .. string.format("%d", v);
	end;
	
    local attributes = 'limiterValues="'.. values .. '" operatingTime="' .. tostring((self.ESLimiter.operatingTime / 1000)) ..'"';
    return attributes, nil;
end;

function ESLimiter:addOperatingTime(addTime)
	
	local time = math.max(Utils.getNoNil(addTime, 0), 0);
	self.ESLimiter.operatingTime = self.ESLimiter.operatingTime + time;
	
	if self.ESLimiter.operatingTime > (99999.9 * 1000 * 60 * 60) then
		self.ESLimiter.operatingTime = 0;
	end;
end;


--
-- ESLimiterEvent
-- Networkevent for ESLimiter
--
-- SFM-Modding
-- @author  Manuel Leithner
-- @date:		14/12/11
-- @version:	v1.0
-- @history:	v1.0 - initial implementation
--
ESLimiterEvent = {};
ESLimiterEvent_mt = Class(ESLimiterEvent, Event);

InitEventClass(ESLimiterEvent, "ESLimiterEvent");

function ESLimiterEvent:emptyNew()
    local self = Event:new(ESLimiterEvent_mt);
    return self;
end;

function ESLimiterEvent:new(vehicle, level, percentage)
    local self = ESLimiterEvent:emptyNew()
    self.vehicle = vehicle;
	self.level = level;
	self.percentage = percentage;
    return self;
end;

function ESLimiterEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
	self.level = streamReadInt8(streamId);
	self.percentage = streamReadInt16(streamId)/10;
    self.vehicle = networkGetObject(id);
    self:run(connection);
end;

function ESLimiterEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));	
	streamWriteInt8(streamId, self.level);
	streamWriteInt16(streamId, self.percentage*10);
end;

function ESLimiterEvent:run(connection)
	self.vehicle:setNewLimit(self.level, self.percentage, true);
end;

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