--
-- ManualIgnition
-- Specialization for manual motor ignition
--
-- @v1: Templaer - 01 May   2009
-- @v2: Henly20  - 24 April 2012
-- 
-- @author:    	Xentro (Marcus@Xentro.se)
-- @website:	www.Xentro.se
-- @history:	v4.0 - 2015-01-18 - FS15 Convert
-- 				v3.063 - 2014-02-20 - FS 13
-- 
--
--[[
<dashLights>
	<dashLight index="" />
</dashLights>

Replace x y z with you rotation value.
<key index="" off="x y z" on="x y z" start="x y z" />

-- other options --
preHeatStart - This will stop user from starting while preHeatHud is displayed.
preHeat - How long the pre heat mode should be.

<ManualIgnition preHeatStart="false" preHeat="1400" />
]]--

ManualIgnition = {};

ManualIgnition.TYPE_NORMAL = 0;

ManualIgnition.STAGE_OFF = 0;
ManualIgnition.STAGE_PRE_HEAT = 1;
ManualIgnition.STAGE_START = 2;
ManualIgnition.STAGE_TURN_OFF = 3;

ManualIgnition.KEY_OFF = 0;
ManualIgnition.KEY_ON = 1;
ManualIgnition.KEY_STARTING = 2;

ManualIgnition.ERROR_NONE = 0;
ManualIgnition.ERROR_WAIT = 1;

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

function ManualIgnition:load(xmlFile)
	self.setManualIgnitionMode = SpecializationUtil.callSpecializationsFunction("setManualIgnitionMode");
	
	if self.setIsTurnedOn ~= nil then
		self.setIsTurnedOn = Utils.overwrittenFunction(self.setIsTurnedOn, ManualIgnition.setIsTurnedOn);
	end;
	if self.getIsFoldAllowed ~= nil then
		self.getIsFoldAllowed = Utils.overwrittenFunction(self.getIsFoldAllowed, ManualIgnition.overrideReturnFunctions);
	end;
	if self.getIsPipeUnloadingAllowed ~= nil then
		self.getIsPipeUnloadingAllowed = Utils.overwrittenFunction(self.getIsPipeUnloadingAllowed, ManualIgnition.overrideReturnFunctions);
	end;
	if self.getIsPipeStateChangeAllowed ~= nil then
		self.getIsPipeStateChangeAllowed = Utils.overwrittenFunction(self.getIsPipeStateChangeAllowed, ManualIgnition.overrideReturnFunctions);
	end;
	
	-- self.overrideIgnitionLightFeature = false;
	-- self.overrideIgnitionInput = false;
	-- self.overrideHud = false;
	
	self.dirtyExhaustEffectFix = false; -- dirty fix but will do for now.
	
	self.ignitionMode = ManualIgnition.STAGE_OFF;
	self.ignitionType = ManualIgnition.TYPE_NORMAL;
	self.ignitionActive = false;
	self.ignitionAllowed = false;
	self.ignitionForceIsActive = false;
	
	self.isTurnedOnOnLeave = false;
	
	self.deactivateLightsOnLeave = false;
	self.lastLightsTypesMask = 0;
	
	self.ignitionMotorStopSoundPlayed = true;
	self.ignitionMotorStopSoundVolume = 0;
	if self.sampleMotorStop ~= nil then
		self.ignitionMotorStopSound = self.sampleMotorStop;
		self.ignitionMotorStopSoundVolume = self.sampleMotorStop.volume;
		self.sampleMotorStop.volume = 0;
	end;
	
	self.dashLights = {};
	self.dashLights.activated = false;
	self.dashLights.table = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.dashLights.dashLight(%d)", i);
		if not hasXMLProperty(xmlFile, key) then break; end;
		
		local index = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#index"));
		if index ~= nil then
			table.insert(self.dashLights.table, {node = index});
			i = i + 1;
		end;		
	end;
	
	self.key = {};
	self.key.mode = ManualIgnition.KEY_OFF;
	self.key.lastMode = -1; 
	
	local keyPath = "vehicle.key";
	if hasXMLProperty(xmlFile, keyPath) then
		local index = getXMLString(xmlFile, keyPath .. "#index")
		if index ~= nil then
			self.key.node = Utils.indexToObject(self.components, index);
			
			local off = Utils.getRadiansFromString(getXMLString(xmlFile, keyPath .. "#off"), 3);
			local on = Utils.getRadiansFromString(getXMLString(xmlFile, keyPath .. "#on"), 3);
			local start = Utils.getRadiansFromString(getXMLString(xmlFile, keyPath .. "#start"), 3);
			
			if off == nil then
				local x, y, z = getRotation(self.key.node);
				off = {x, y, z};
			end;
			
			if on ~= nil and start ~= nil then
				self.key.rotation = {};
				self.key.rotation['off'] = off;
				self.key.rotation['on'] = on;
				self.key.rotation['start'] = start;
			else
				print('ManualIgnition - Error: <key> is missing a value for on="x y z" or starting="x y z" in ' .. self.configFileName);
			end;
		end;
	end;
	
	self.ignitionSettings = {};
	if g_currentMission.ignitionSettings ~= nil then
		for n, v in pairs(g_currentMission.ignitionSettings) do
			self.ignitionSettings[n] = v;
		end;
	else
		local defaultValues = {{"error_time_1", 1000}, {"error_time_2", 800}, {"axis_limit", 0.5}, {"engine_cooldown", 4}, {"engine_cooldown_rain", 2}, {"max_heat_time", 2}, {"pre_heat_start", false}, {"pre_heat_time", 1200}, {"show_hud_after_start_time", 3000}};
		for _, v in pairs(defaultValues) do
			self.ignitionSettings[v[1]] = v[2];
		end;
	end;
	
	self.heater = {};
	self.heater["fastStart"] = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.ManualIgnition#preHeatStart"), self.ignitionSettings["pre_heat_start"]);
	self.heater["maxTime"] = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.ManualIgnition#preHeat"), self.ignitionSettings["pre_heat_time"]);
	self.heater["maxHeatTime"] = self.ignitionSettings["max_heat_time"];
	self.heater["currentTime"] = 0;
	self.heater["CurrentHeatMin"] = 0; 
		
	ManualIgnition.MIMovingTool(self, false);
	
	self:addConflictCheckedInput(InputBinding.MANUAL_IGNITION_1);
end;

function ManualIgnition:delete()
end;

function ManualIgnition:readStream(streamId, connection)
	self:setManualIgnitionMode(streamReadInt8(streamId), true);
end;

function ManualIgnition:writeStream(streamId, connection)
	streamWriteInt8(streamId, self.ignitionMode);
end;

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

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

function ManualIgnition:update(dt)
	if self:getIsActive() and self.isClient then
		if self:getIsActiveForInput(false) and not self.overrideIgnitionInput then
			if self.fuelFillLevel > 0 then
				if InputBinding.hasEvent(InputBinding.MANUAL_IGNITION_1) then
					if self.ignitionMode >= ManualIgnition.STAGE_TURN_OFF then
						self.ignitionMode = ManualIgnition.STAGE_OFF;
					end;
					local allow = false;
					local errorId = ManualIgnition.ERROR_NONE;
					local steps = 1;
					
					if self.ignitionType == ManualIgnition.TYPE_NORMAL then
						if (self.ignitionMode ~= ManualIgnition.STAGE_PRE_HEAT or self.ignitionMode == ManualIgnition.STAGE_PRE_HEAT and (self.heater["currentTime"] == 0 and not self.heater["fastStart"] or self.heater["fastStart"] and self.heater["currentTime"] >= 0)) then
							allow = true;
						else
							errorId = ManualIgnition.ERROR_WAIT;
						end;
					end;
					
					if allow then
						self:setManualIgnitionMode(self.ignitionMode + steps);
					else						
						g_currentMission:showBlinkingWarning(g_i18n:getText("MANUAL_IGNITION_5"), self.ignitionSettings["error_time_1"]);
					end;
				end;
				
				if InputBinding.isPressed(InputBinding.MANUAL_IGNITION_1) and self.ignitionMode == ManualIgnition.STAGE_START then
					self.key.mode = ManualIgnition.KEY_STARTING;
				else		
					if self.isMotorStarted then
						self.key.mode = ManualIgnition.KEY_ON;
					end;
				end;
				
				if (self.axisForward < -self.ignitionSettings["axis_limit"] or self.axisForward > self.ignitionSettings["axis_limit"]) and self.ignitionMode == ManualIgnition.STAGE_OFF then
					g_currentMission:showBlinkingWarning(g_i18n:getText("MANUAL_IGNITION_4"), self.ignitionSettings["error_time_2"]);
				end;
			end;
		end;
	end;
 end;

function ManualIgnition:updateTick(dt)
	if g_currentMission.driveControl ~= nil and g_currentMission.driveControl.useModules ~= nil then -- cant be sure that MI is loaded after DriveControl.
		g_currentMission.driveControl.useModules.manMotorStart = false;
		g_currentMission.driveControl.useModules.manMotorKeepTurnedOn = false;
		g_currentMission.driveControl.useModules.toggleActive = false;
	end;
	
	local speed = dt / 60000 * g_currentMission.environment.timeScale;
	
	if self.isMotorStarted then
		if self.heater["CurrentHeatMin"] < self.heater["maxHeatTime"] then
			self.heater["CurrentHeatMin"] = self.heater["CurrentHeatMin"] + speed;
		end;
	else
		if self.heater["CurrentHeatMin"] > 0 then
			local rainAffect = self.ignitionSettings["engine_cooldown"];
			if g_currentMission.environment.currentRain ~= nil and g_currentMission.environment.currentRain.rainTypeId == "rain" then
				if g_currentMission.environment.lastRainScale > 0.01 then
					rainAffect = self.ignitionSettings["engine_cooldown_rain"];
				end;
			end;
			
			self.heater["CurrentHeatMin"] = self.heater["CurrentHeatMin"] - (speed / rainAffect);
		end;
	end;
	
	if self.ignitionMode == ManualIgnition.STAGE_PRE_HEAT then
		if self.heater["currentTime"] > 0 then
			self.heater["currentTime"] = math.max(self.heater["currentTime"] - dt, 0);
		end;
	end;
	
	if self:getIsActive() then		
		if self.key.mode ~= self.key.lastMode then
			if self.key.rotation ~= nil then
				if self.key.mode == ManualIgnition.KEY_ON then
					setRotation(self.key.node, unpack(self.key.rotation['on']));
				elseif self.key.mode == ManualIgnition.KEY_STARTING then
					setRotation(self.key.node, unpack(self.key.rotation['start']));
				else
					setRotation(self.key.node, unpack(self.key.rotation['off']));
				end;
			end;
			
			self.key.lastMode = self.key.mode;
		end;
		
		if self.ignitionMode ~= ManualIgnition.STAGE_PRE_HEAT then
			local updateTime = self.heater["maxTime"];
			local updateHeat = updateTime * (self.heater["CurrentHeatMin"] / self.heater["maxHeatTime"]);
			updateTime = updateTime - updateHeat
			
			if self.heater["currentTime"] ~= updateTime then
				self.heater["currentTime"] = updateTime;
			end;
		end;
		
		if self.lastLightsTypesMask ~= self.lightsTypesMask and not self.overrideIgnitionLightFeature then
			self.lastLightsTypesMask = self.lightsTypesMask;
		end;
		
		if self.isTurnedOnOnLeave then
			self.isTurnedOnOnLeave = ManualIgnition.checkIsTurnedOn(self);
			
			if not self.isTurnedOnOnLeave then
				self.motor.lastMotorRpm = self.motor.minRpm;
			end;
		end;
		
		if self.ignitionForceIsActive and not self.isTurnedOnOnLeave then
			self.forceIsActive = false;
			self.ignitionForceIsActive = false;
		end;
	else
		if self.isMotorStarted then
			if not self.ignitionForceIsActive and self.isTurnedOnOnLeave then
				self.forceIsActive = true;
				self.ignitionForceIsActive = true;
				
			elseif not self.forceIsActive and self.ignitionForceIsActive then
				self.forceIsActive = true; -- fail safe if other scripts mess with it..
			end;
			
			if self.lastLightsTypesMask ~= self.lightsTypesMask and not self.overrideIgnitionLightFeature then
				self:setLightsTypesMask(self.lastLightsTypesMask, true);
			end;
		else
			if self.ignitionForceIsActive then
				self.forceIsActive = false;
				self.ignitionForceIsActive = false;
			end;
		end;
	end;
	
	local stopAI = false;
	if not self:getIsHired() then
		if self.ignitionActive and self.ignitionAllowed then
			if self.exhaustEffects ~= nil then
				for _, v in ipairs(self.exhaustEffects) do
					setVisibility(v.node, false);
					setVisibility(v.effectNode, false);
				end;
			end;
			if self.exhaustParticleSystems ~= nil then
				Utils.setEmittingState(self.exhaustParticleSystems, true);
			end;
			
			self.dirtyExhaustEffectFix = true;
			self:startMotor(true);
			
			self.deactivateOnLeave = false;
			self.stopMotorOnLeave = false;
			self.ignitionAllowed = false;
		elseif not self.ignitionActive and self.ignitionAllowed then
			self:stopMotor(true);
			self.deactivateOnLeave = true;
			self.stopMotorOnLeave = true;
			self.ignitionAllowed = false;
		elseif self.ignitionActive and not self.ignitionAllowed and self.deactivateOnLeave then
			self.deactivateOnLeave = false;
			self.stopMotorOnLeave = false;
		elseif self.ignitionActive and not self.ignitionAllowed and self.dirtyExhaustEffectFix then
			self.dirtyExhaustEffectFix = false;
			
			if self.exhaustEffects ~= nil then
				for _, v in ipairs(self.exhaustEffects) do
					setVisibility(v.node, true);
					setVisibility(v.effectNode, true);
				end;
			end;
		end;

		if not self.ignitionMotorStopSoundPlayed and self.ignitionMotorStopSound ~= nil then
			playSample(self.ignitionMotorStopSound.sample, 1, self.ignitionMotorStopSoundVolume, 0);
			self.ignitionMotorStopSoundPlayed = true;
		end;
	elseif not self.ignitionActive and not self.deactivateOnLeave then
		stopAI = true;
	end;
	
	if (self.isBroken or not self.ignitionActive and self.ignitionMode == ManualIgnition.STAGE_START) then
		stopAI = true;
	elseif self.fuelFillLevel == 0 then
		stopAI = true;
	end;
	
	if stopAI then
		if self:getIsHired() then
			
			if SpecializationUtil.hasSpecialization(AICombine, self.specializations) then
				AICombine.stopAIThreshing(self, true);
			end;
			if SpecializationUtil.hasSpecialization(AITractor, self.specializations) then
				AITractor.stopAITractor(self, true);
			end;
		end;
		
		if self.ignitionMode ~= ManualIgnition.STAGE_OFF then
			self:setManualIgnitionMode(ManualIgnition.STAGE_OFF, true);
		end;
	end;
	
	for _, v in ipairs(self.dashLights.table) do
		if getVisibility(v.node) ~= self.dashLights.activated then
			setVisibility(v.node, self.dashLights.activated);
		end;
	end;
	
	if self.isServer and g_dedicatedServerInfo ~= nil then
		if table.getn(g_currentMission.users) == 1 then
			if self.ignitionMode ~= ManualIgnition.STAGE_OFF then
				self:setManualIgnitionMode(ManualIgnition.STAGE_OFF);
			end;
		end;
	end;
end;

function ManualIgnition:onLeave()
	if not self.deactivateOnLeave then
		if self.exhaustParticleSystems ~= nil then
			Utils.setEmittingState(self.exhaustParticleSystems, true);
		end;
		
		self.ignitionActive = true;
		self.ignitionAllowed = false;
		self.isMotorStarted = true;
		
		local turnedOn = ManualIgnition.checkIsTurnedOn(self);
		
		if self.lastSpeedAcceleration ~= 0 then
			self.lastSpeedAcceleration = 0;
		end;
		
		if not turnedOn then
			self.motor.lastMotorRpm = self.motor.minRpm;
		end;
		
		self.isTurnedOnOnLeave = turnedOn;
		
		if self.isServer and (self.isRealistic == nil or self.isRealistic ~= nil and not self.isRealistic) then
			for k, wheel in pairs(self.wheels) do
				setWheelShapeProps(wheel.node, wheel.wheelShape, 0, self.motor.brakeForce, 0);
			end;
		end;
	else
		self.ignitionActive = false;
		self.ignitionAllowed = false;
		self.key.mode = ManualIgnition.KEY_OFF;
	end;
end;

function ManualIgnition:onEnter()
	self.isTurnedOnOnLeave = false;
	
	if not self.ignitionActive then
		self.isMotorStarted = false;
		Motorized.stopSounds(self);
		
		if self.exhaustParticleSystems ~= nil then
			Utils.setEmittingState(self.exhaustParticleSystems, false);
		end;
		
		self:stopMotor(true);
	else
		if self.ignitionMode == ManualIgnition.STAGE_START then
			self.ignitionActive = true;
			self.ignitionAllowed = true;
			self.key.mode = ManualIgnition.KEY_ON;
			
			if self.ignitionForceIsActive then
				self.forceIsActive = false;
				self.ignitionForceIsActive = false;
			end;
		end;	
	end;
end;

function ManualIgnition:draw()
	if g_currentMission.showHelpText then
		if self.fuelFillLevel <= 0 then
			g_currentMission:addExtraPrintText(g_i18n:getText("MANUAL_IGNITION_7"));
		else
			if self.ignitionType == ManualIgnition.TYPE_NORMAL then
				if self.ignitionMode == ManualIgnition.STAGE_OFF then
					if not self.overrideIgnitionInput then
						g_currentMission:addHelpButtonText(g_i18n:getText("MANUAL_IGNITION_6"), InputBinding.MANUAL_IGNITION_1);
					end;
				elseif self.ignitionMode == ManualIgnition.STAGE_PRE_HEAT then
					if self.heater["currentTime"] > 0 then
						g_currentMission:addExtraPrintText(g_i18n:getText("MANUAL_IGNITION_3"));
					end;
				end;
			end;
		end;
	end;
	
	if g_currentMission.ignitionOverlay ~= nil and not self.overrideHud and self.fuelFillLevel > 0 then
		if (self.ignitionMode <= ManualIgnition.STAGE_PRE_HEAT or (self.motorStartTime + self.ignitionSettings["show_hud_after_start_time"]) > g_currentMission.time) then
			g_currentMission.ignitionOverlay[1]:render();
			
			if self.key.mode >= ManualIgnition.KEY_ON and self.ignitionMode <= ManualIgnition.STAGE_PRE_HEAT then
				g_currentMission.ignitionOverlay[2]:render();
			end;
			if self.heater["currentTime"] > 0 and self.ignitionMode == ManualIgnition.STAGE_PRE_HEAT then
				g_currentMission.ignitionOverlay[3]:render();
			end;
			if self.ignitionMode == ManualIgnition.STAGE_START then
				g_currentMission.ignitionOverlay[4]:render();
			end;
		end;
	end;
end;

function ManualIgnition:setManualIgnitionMode(ignition, noEventSend)
	manualIgnitionEvent.sendEvent(self, ignition, noEventSend);

	self.ignitionMode = ignition;
    self.ignitionActive = false;
	self.ignitionAllowed = false;
	self.dashLights.activated = false;
	self.key.mode = ManualIgnition.KEY_OFF;
	
    self.lastSpeedAcceleration = 0;
	self.deactivateOnLeave = true;
	self.stopMotorOnLeave = true;
	self:stopMotor(true);
	
	if self.ignitionMode == ManualIgnition.STAGE_PRE_HEAT then
		self.key.mode = ManualIgnition.KEY_ON;
		self.dashLights.activated = true;
	elseif self.ignitionMode == ManualIgnition.STAGE_START then
		self.ignitionActive = true;
		self.ignitionAllowed = true;
		self.key.mode = ManualIgnition.KEY_ON;
		ManualIgnition.MIMovingTool(self, true);
	elseif self.ignitionMode >= ManualIgnition.STAGE_TURN_OFF then
		self.ignitionMode = ManualIgnition.STAGE_OFF;
		self.ignitionMotorStopSoundPlayed = false;
		ManualIgnition.MIMovingTool(self, false);
		
		self:onDeactivate(true);
		self:onDeactivateSounds(true);
	end;
	
	if self.isServer and (self.isRealistic == nil or self.isRealistic ~= nil and not self.isRealistic) and self.ignitionMode ~= ManualIgnition.STAGE_START then
		for k, wheel in ipairs(self.wheels) do
			setWheelShapeProps(wheel.node, wheel.wheelShape, 0, self.motor.brakeForce, 0);
		end;
    end;
end;

function ManualIgnition:setIsTurnedOn(oldFunc, isTurnedOn, noEventSend)
	if (self.isMotorStarted or not isTurnedOn) then
		oldFunc(self, isTurnedOn, noEventSend);
	end;
end;

function ManualIgnition:overrideReturnFunctions(oldFunc, val1, val2)
	if self.isMotorStarted then
		return oldFunc(self, val1, val2);
	end;
end;

function ManualIgnition.MIMovingTool(self, active)
	if self.movingTools ~= nil then
		for i, tool in ipairs(self.movingTools) do
			if tool.lastMIActive ~= nil then
				tool.lastMIActive = nil;
				active = tool.lastMIActive;
			end;
			
			if not tool.isActive and not active then
				tool.lastMIActive = tool.isActive;
			end;
			
			tool.isActive = active;
		end;
	end;
end;

function ManualIgnition.checkIsTurnedOn(self)
	local turnedOn = false;
	
	if self.getIsTurnedOn ~= nil then
		turnedOn = self:getIsTurnedOn();
	end;
	
	if not turnedOn then
		for _, v in pairs(self.attachedImplements) do
			if v.object.getIsTurnedOn ~= nil then
				turnedOn = ManualIgnition.checkIsTurnedOn(v.object);
				
				if turnedOn then
					break;
				end;
			end;
		end;
	end;
	
	return turnedOn;
end;


manualIgnitionEvent = {};
manualIgnitionEvent_mt = Class(manualIgnitionEvent, Event);

InitEventClass(manualIgnitionEvent, "manualIgnitionEvent");

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

function manualIgnitionEvent:new(vehicle, ignition)
    local self = manualIgnitionEvent:emptyNew()
    self.vehicle = vehicle;
	self.ignition = ignition;
	
    return self;
end;

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

function manualIgnitionEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
	streamWriteInt8(streamId, self.ignition);
end;

function manualIgnitionEvent:run(connection)
	self.vehicle:setManualIgnitionMode(self.ignition, true);
	
	if not connection:getIsServer() then
		g_server:broadcastEvent(manualIgnitionEvent:new(self.vehicle, self.ignition), nil, connection, self.object);
	end;
end;

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


if not Utils.ignitionFixApplied20141122 then
	Utils.ignitionFixApplied20141122 = true;
		
	local oldVehicleFunc = Vehicle.getIsActiveForInput;
	Vehicle.getIsActiveForInput = function(self, onlyTrueIfSelected)
		local oldAllowed = oldVehicleFunc(self, onlyTrueIfSelected);
		
		if self.isMotorStarted ~= nil then
			return oldAllowed;
		else
			if oldAllowed then
				local rootAttacherVehicle = self:getRootAttacherVehicle();
				oldAllowed = Utils.getNoNil(rootAttacherVehicle.isMotorStarted, true);
			end;
			
			return oldAllowed;
		end;
	end;
end;