--[[
ExtendedTruckFunctions

Script to animate some indoor parts in a vehicle

@Author:     Ifko[nator]
@Date:       27.11.2017

@Version:    1.0

@History:    v1.0 @ 27.11.2017 - initial release in FS 17
]]

ExtendedTruckFunctions = {};

function ExtendedTruckFunctions.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(AnimatedVehicle, specializations); 
end;

local debugPriority = 0;

local function printError(errorMessage, isWarning)
	local prefix = "Error ";
	
	if isWarning then
		prefix = "Warning ";
	end;
	
	print(prefix .. "from the ExtendedTruckFunctions.lua: " .. tostring(errorMessage));
end;

local function printDebug(debugMessage, priority, addStringDebug)
	if debugPriority >= priority then
		local prefix = "";
		
		if addStringDebug then
			prefix = "Debug: from the ExtendedTruckFunctions.lua: "
		end;
		
		print(prefix .. tostring(debugMessage));
	end;
end;

function ExtendedTruckFunctions:preLoad(savegame)
	self.setLightsTypesMask = Utils.prependedFunction(self.setLightsTypesMask, ExtendedTruckFunctions.setLightsTypesMask);
end;

function ExtendedTruckFunctions:load(savegame)
    self.extendedTruckFunctions = {};
	
	local needleNumber = 0;
	
	printDebug("", 1, false);
	printDebug("-----------------------------------------------Debug from the ExtendedTruckFunctions.lua Start------------------------------------------------", 1, false);
	printDebug("", 1, false);
	printDebug("-----------------------------------------------Debug from the needles Start------------------------------------------------", 1, false);
	
	self.extendedTruckFunctions.needles = {};
	
	while true do
		local needleKey = "vehicle.extendedTruckFunctions.needle(" .. tostring(needleNumber) .. ")";
		
		if not hasXMLProperty(self.xmlFile, needleKey) then
			printDebug("-----------------------------------------------Debug from the needles End------------------------------------------------", 1, false);
			printDebug("", 1, false);
			
			break;
		end;
		
		local name = getXMLString(self.xmlFile, needleKey .. "#name");
		
		if name ~= nil and name ~= "" then
			local needle = {};
			
			needle.name = name;
			needle[needle.name] = {};
			needle[needle.name].node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, needleKey .. "#node"));
			needle[needle.name].maxRot = Utils.getVectorNFromString(Utils.getNoNil(getXMLString(self.xmlFile, needleKey .. "#maxRot"), "0 0 30"), 3);
			needle[needle.name].needleTime = 0;
			needle[needle.name].increaseFactor = Utils.getNoNil(getXMLString(self.xmlFile, needleKey .. "#increaseFactor"), 1) / 100000;
			needle[needle.name].decreaseFactor = Utils.getNoNil(getXMLString(self.xmlFile, needleKey .. "#decreaseFactor"), 2.5) / 100000;
			needle[needle.name].addMax = Utils.getVectorNFromString(Utils.getNoNil(getXMLString(self.xmlFile, needleKey .. "#addMax"), "0 0 50"), 3);
			needle[needle.name].addMin = Utils.getVectorNFromString(Utils.getNoNil(getXMLString(self.xmlFile, needleKey .. "#addMin"), "0 0 5"), 3);
			needle[needle.name].addTime = {0, 0, 0};
			
			printDebug("Load needle '" .. needle.name .. "' number '" .. needleNumber + 1 .. "' successfully.", 1, true);
			
			table.insert(self.extendedTruckFunctions.needles, needle);
		else
			printError("Missing the 'name' attribute for the needle number '" .. needleNumber + 1 .. "'! The script will skip this needle now!", false);
		end;
	
		needleNumber = needleNumber + 1;
	end;
	
	local displayNumber = 0;
	
	printDebug("-----------------------------------------------Debug from the display numbers Start------------------------------------------------", 1, false);
	
	self.extendedTruckFunctions.displayNumbers = {};
	
	while true do
		local displayNumberKey = "vehicle.extendedTruckFunctions.displayNumber(" .. tostring(displayNumber) .. ")";
		
		if not hasXMLProperty(self.xmlFile, displayNumberKey) then
			printDebug("-----------------------------------------------Debug from the display numbers End------------------------------------------------", 1, false);
			printDebug("", 1, false);
			
			break;
		end;
		
		local index = getXMLString(self.xmlFile, displayNumberKey .. "#node");
		local refNode = getXMLString(self.xmlFile, displayNumberKey .. "#refNode");
		
		if index ~= nil and index ~= "" then
			if refNode ~= nil and refNode ~= "" then
				local number = {};
			
				number.node = Utils.indexToObject(self.components, index);
				number.refNode = Utils.indexToObject(self.components, refNode);
				
				number.oldMaterial = getMaterial(number.node, 0);
				number.newMaterial = getMaterial(number.refNode, 0);
				
				printDebug("Load display number " .. displayNumber + 1 .. " successfully.", 1, true);
				
				table.insert(self.extendedTruckFunctions.displayNumbers, number);
			else
				printError("Missing the 'refNode' attribute for the display number '" .. number + 1 .. "'! The script will skip this display number now!", false);
			end;
		else
			printError("Missing the 'node' attribute for the display number '" .. number + 1 .. "'! The script will skip this display number now!", false);
		end;
	
		displayNumber = displayNumber + 1;
	end;
    
    self.extendedTruckFunctions.oldTurnLightStateLeft = Lights.TURNLIGHT_OFF;
    self.extendedTruckFunctions.oldTurnLightStateRight = Lights.TURNLIGHT_OFF;
	
	self.getIsRainTypeActive = ExtendedTruckFunctions.getIsRainTypeActive;
	self.setRainWipper = ExtendedTruckFunctions.setRainWipper;
	
	self.extendedTruckFunctions.rainWipperIsActive = false;
	
	if savegame ~= nil and not savegame.resetVehicles then
		self.extendedTruckFunctions.rainWipperIsActive = Utils.getNoNil(getXMLBool(savegame.xmlFile, savegame.key .. "#rainWipperIsActive"), self.extendedTruckFunctions.rainWipperIsActive);
		
		printDebug("-----------------------------------------------Debug from the rain wipper Start------------------------------------------------", 2, false);
		printDebug("Set rain to active = " .. tostring(self.extendedTruckFunctions.rainWipperIsActive), 2, true);
		printDebug("-----------------------------------------------Debug from the rain wipper End------------------------------------------------", 2, false);
		printDebug("", 2, false);
	end;

	printDebug("-----------------------------------------------Debug from the ExtendedTruckFunctions.lua End------------------------------------------------", 1, false);
	printDebug("", 1, false);
	
	self:setRainWipper(self.extendedTruckFunctions.rainWipperIsActive, false);
	
	self.extendedTruckFunctions.wonderTreeNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.extendedTruckFunctions.wondertree#node"));
	self.extendedTruckFunctions.wonderTreeRefNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.extendedTruckFunctions.wondertree#refNode"));

	if self.extendedTruckFunctions.wonderTreeNode ~= nil and self.extendedTruckFunctions.wonderTreeRefNode ~= nil then
		self.extendedTruckFunctions.wonderTreeNodeDefaultMaterial = getMaterial(self.extendedTruckFunctions.wonderTreeNode, 0);
		self.extendedTruckFunctions.wonderTreeNodeNewMaterial = getMaterial(self.extendedTruckFunctions.wonderTreeRefNode, 0);
	end;
	
	self.extendedTruckFunctions.controllerName = "";
end;

function ExtendedTruckFunctions:update(dt)
	if self.extendedTruckFunctions.oldTurnLightStateLeft ~= self.turnLightState 
        and self.turnLightState ~= Lights.TURNLIGHT_RIGHT 
        and self.animations["setTurnLightSwitchLeft"] ~= nil
    then
        if self.turnLightState == Lights.TURNLIGHT_LEFT then
            self:playAnimation("setTurnLightSwitchLeft", 1, nil, true);
        else
            self:playAnimation("setTurnLightSwitchLeft", -1, nil, true);
        end;
        
        self.extendedTruckFunctions.oldTurnLightStateLeft = self.turnLightState;
    end;

    if self.extendedTruckFunctions.oldTurnLightStateRight ~= self.turnLightState 
        and self.turnLightState ~= Lights.TURNLIGHT_LEFT 
        and self.animations["setTurnLightSwitchRight"] ~= nil
    then
        if self.turnLightState == Lights.TURNLIGHT_RIGHT then
            self:playAnimation("setTurnLightSwitchRight", 1, nil, true);
        else    
            self:playAnimation("setTurnLightSwitchRight", -1, nil, true);
        end;

        self.extendedTruckFunctions.oldTurnLightStateRight = self.turnLightState;
    end;
	
	if self.timeHud ~= nil then
		for _, timeHud in ipairs(self.timeHud) do
			if timeHud.numbers ~= nil then
				--setVisibility(timeHud.numbers, self.isMotorStarted);
			end;
		end;
	end;
	
	if not self.isMotorStarted then
		for _, needle in pairs(self.extendedTruckFunctions.needles) do
			if needle.name == "battery" or needle.name == "amps" then
				needle[needle.name].addTime[1] = math.random(needle[needle.name].addMin[1], needle[needle.name].addMax[1]);
				needle[needle.name].addTime[2] = math.random(needle[needle.name].addMin[2], needle[needle.name].addMax[2]);
				needle[needle.name].addTime[3] = math.random(needle[needle.name].addMin[3], needle[needle.name].addMax[3]);
			end;
		end;
	end;
    
    if self.isMotorStarted then
		setTextAlignment(RenderText.ALIGN_CENTER);
		
		for _, needle in pairs(self.extendedTruckFunctions.needles) do
			if needle[needle.name].node ~= nil then
				needle[needle.name].needleTime = math.min(needle[needle.name].needleTime + needle[needle.name].increaseFactor * dt, 1);
				
				local animTime = needle[needle.name].needleTime;
				local allowAnimate = true;
			
				if needle.name == "airPressure" and self.brakeCompressor ~= nil then
					--## set rotation of the 'air' needle to the current air fill level. When you brake, the needle will be set down and then again slowly up.
					
					animTime = self.brakeCompressor.fillLevel / self.brakeCompressor.capacity;
				elseif needle.name == "turboBoost" then
					animTime = self.axisForward;
					allowAnimate = self.axisForward < 0;
				end;
				
				local rotX = (needle[needle.name].maxRot[1] * animTime) + needle[needle.name].addTime[1];
				local rotY = (needle[needle.name].maxRot[2] * animTime) + needle[needle.name].addTime[2];
				local rotZ = (needle[needle.name].maxRot[3] * animTime) + needle[needle.name].addTime[3];
				
				if needle.name == "turboBoost" then
					local doInvertRotation = true;
			
					if self.driveControl ~= nil then
						if self.dCcheckModule ~= nil 
							and self:dCcheckModule("shuttle") ~= nil
							and self.driveControl.shuttle.isActive 
						then
							doInvertRotation = self.driveControl.shuttle.direction > 0;
						else
							for _, brakeLight in pairs(self.brakeLights) do
								if brakeLight.decoration ~= nil then
									allowAnimate = not getVisibility(brakeLight.decoration);
								end;
							end;
 						end;
					else
						for _, brakeLight in pairs(self.brakeLights) do
							if brakeLight.decoration ~= nil then
								allowAnimate = not getVisibility(brakeLight.decoration);
							end;
						end;
					end;
					
					if doInvertRotation then
						if self.axisForward <= 0 then
							rotX = -rotX;
							rotY = -rotY;
							rotZ = -rotZ;
						end;
					else
						allowAnimate = self.axisForward > 0;
					end;
				end;
		
				if allowAnimate then	
					setRotation(needle[needle.name].node, math.rad(rotX), math.rad(rotY), math.rad(rotZ));
				end;
			end;
		end;
    
        if self:getIsRainTypeActive() and self.animations["playRainWipper"] ~= nil then
			if self:getIsActiveForInput() then	
				if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
					self:setRainWipper(not self.extendedTruckFunctions.rainWipperIsActive, false);
					
					if self.animations["setRainWipperSwitch"] ~= nil then
						if self.extendedTruckFunctions.rainWipperIsActive then
							self:playAnimation("setRainWipperSwitch", 1, nil, true);
						else
							self:playAnimation("setRainWipperSwitch", -1, nil, true);
						end;
					end;
				end;
			end;
			
			
			if self.extendedTruckFunctions.rainWipperIsActive then
				self:playAnimation("playRainWipper", 1, nil, true);
			end;
        end;
    else
		for needleNumber, needle in pairs(self.extendedTruckFunctions.needles) do
			if needle[needle.name].node ~= nil then
				needle[needle.name].needleTime = math.max(needle[needle.name].needleTime - needle[needle.name].decreaseFactor * dt, 0);
				
				setRotation(needle[needle.name].node, 0, 0, 0);
			end;
		end;
    end;
	
	if self.extendedTruckFunctions.wonderTreeNode ~= nil and self.extendedTruckFunctions.wonderTreeRefNode ~= nil and self.extendedTruckFunctions.controllerName ~= self.controllerName then
		if self.controllerName == "SnookieSnu" then
			self:replaceMaterialRec(self.extendedTruckFunctions.wonderTreeNode, self.extendedTruckFunctions.wonderTreeNodeDefaultMaterial, self.extendedTruckFunctions.wonderTreeNodeNewMaterial);
		else
			self:replaceMaterialRec(self.extendedTruckFunctions.wonderTreeNode, self.extendedTruckFunctions.wonderTreeNodeNewMaterial, self.extendedTruckFunctions.wonderTreeNodeDefaultMaterial);
		end;
		
		self.extendedTruckFunctions.controllerName = self.controllerName;
	end;
end;

function ExtendedTruckFunctions:setLightsTypesMask(lightsTypesMask, force, noEventSend)
	for _, displayNumber in pairs(self.extendedTruckFunctions.displayNumbers) do	
		if displayNumber.node ~= nil and displayNumber.refNode ~= nil and lightsTypesMask ~= nil then
			if bitAND(lightsTypesMask, 1) ~= 0 then
				self:replaceMaterialRec(displayNumber.node, displayNumber.oldMaterial, displayNumber.newMaterial);
			else
				self:replaceMaterialRec(displayNumber.node, displayNumber.newMaterial, displayNumber.oldMaterial);
			end;
		end;
	end;
	
	if self.timeHud ~= nil then
		local minutes = g_currentMission.environment.currentMinute;
		local hours = g_currentMission.environment.currentHour;
		local minutesString = string.format("%02d", minutes);
		
		VehicleHudUtils.setHudValue(self, self.timeHud, tonumber(hours .. "." .. minutesString), 9999);
	end;
end;

function ExtendedTruckFunctions:getIsRainTypeActive()
	if g_currentMission.environment.currentRain ~= nil
        and g_currentMission.environment.currentRain.rainTypeId ~= Environment.RAINTYPE_FOG
        and g_currentMission.environment.currentRain.rainTypeId ~= Environment.RAINTYPE_CLOUDY
    then
        local rainType = g_currentMission.environment:getRainType();
        local isRainTypeActive = false;
        
        if rainType ~= nil then
            if rainType.rootNode ~= nil then
                isRainTypeActive = getVisibility(rainType.rootNode);
            end;
        end;
	
		return isRainTypeActive;
	end;
	
	return false;
end;

function ExtendedTruckFunctions:writeStream(streamId, connection)
    streamWriteBool(streamId, self.extendedTruckFunctions.rainWipperIsActive);
end;

function ExtendedTruckFunctions:readStream(streamId, connection)
    local rainWipperIsActive = streamReadBool(streamId);

    self:setRainWipper(rainWipperIsActive, true, true);
end;

function ExtendedTruckFunctions:draw()
	if self:getIsActiveForInput() and self:getIsRainTypeActive() and self.animations["playRainWipper"] ~= nil and self.isMotorStarted then
		if self.rainWipperIsActive then
			g_currentMission:addHelpButtonText(g_i18n:getText("STOP_RAIN_WIPPER"), InputBinding.IMPLEMENT_EXTRA2);
		else	
			g_currentMission:addHelpButtonText(g_i18n:getText("START_RAIN_WIPPER"), InputBinding.IMPLEMENT_EXTRA2);
		end;
	end;
end;

function ExtendedTruckFunctions:setRainWipper(rainWipperIsActive, noEventSend)
	if rainWipperIsActive ~= self.extendedTruckFunctions.rainWipperIsActive then
		self.extendedTruckFunctions.rainWipperIsActive = rainWipperIsActive;
		
		ExtendedTruckFunctionsRainWipperIsActiveEvent.sendEvent(self, rainWipperIsActive, noEventSend);
	end;
end;

function ExtendedTruckFunctions:getSaveAttributesAndNodes(nodeIdent)
	local attributes = 'rainWipperIsActive="' .. tostring(self.extendedTruckFunctions.rainWipperIsActive) .. '"';
	
	return attributes;
end;

--## unused but needed functions

function ExtendedTruckFunctions:delete()end;
function ExtendedTruckFunctions:mouseEvent(posX, posY, isDown, isUp, button)end;
function ExtendedTruckFunctions:keyEvent(unicode, sym, modifier, isDown)end;

--## MP Stuff

--## toggle rain wipper on/off

ExtendedTruckFunctionsRainWipperIsActiveEvent = {};
ExtendedTruckFunctionsRainWipperIsActiveEvent_mt = Class(ExtendedTruckFunctionsRainWipperIsActiveEvent, Event);

InitEventClass(ExtendedTruckFunctionsRainWipperIsActiveEvent, "ExtendedTruckFunctionsRainWipperIsActiveEvent");

function ExtendedTruckFunctionsRainWipperIsActiveEvent:emptyNew()
	local self = Event:new(ExtendedTruckFunctionsRainWipperIsActiveEvent_mt);
    
	return self;
end;

function ExtendedTruckFunctionsRainWipperIsActiveEvent:new(truck, rainWipperIsActive)
	local self = ExtendedTruckFunctionsRainWipperIsActiveEvent:emptyNew();
	
	self.truck = truck;
	self.rainWipperIsActive = rainWipperIsActive;
	
	return self;
end;

function ExtendedTruckFunctionsRainWipperIsActiveEvent:readStream(streamId, connection)
	local id = streamReadInt32(streamId);
	
	self.truck = networkGetObject(id);
	self.rainWipperIsActive = streamReadBool(streamId);
    self:run(connection);
end;

function ExtendedTruckFunctionsRainWipperIsActiveEvent:writeStream(streamId, connection)
	streamWriteInt32(streamId, networkGetObjectId(self.truck));
	streamWriteBool(streamId, self.rainWipperIsActive);
end;

function ExtendedTruckFunctionsRainWipperIsActiveEvent:run(connection)
	self.truck:setRainWipper(self.rainWipperIsActive, true);
	
	if not connection:getIsServer() then
		g_server:broadcastEvent(ExtendedTruckFunctionsRainWipperIsActiveEvent:new(self.truck, self.rainWipperIsActive), nil, connection, self.truck);
	end;
end;

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