--[[************************************************************************************************************************************************************
	
	universalToggleParts v1.4.1
	
	04/2014 © Saty MODS (Saty / http://forum.lsczech.cz)
	
****************************************************************************************************************************************************************
	
	(EN) INFO
	
	Free for use on mods. Modifications only with my permission !
	
	This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (Attribution / NonCommercial / NoDerivatives)
	http://creativecommons.org/licenses/by-nc-nd/4.0/deed.en
	
****************************************************************************************************************************************************************
	
	(CZ) INFO
	
	Pro volne pouziti v modech. ZAKAZ modifikaci bez meho souhlasu !
	
	Toto dilo podleha licenci : Creative Commons 4.0 International - Uvedte autora / Neuzivejte dilo komercne / Nezasahujte do dila 
	http://creativecommons.org/licenses/by-nc-nd/4.0/deed.cs
	
	--------------------------------------------------------------------------------------------------------------------------------------------------------	
	 
	Zapis do XML souboru stroje :
	*****************************
	
	<universalToggleParts>
		<part node="1" name="P1" showHelp="true" rotMin="0 0 0" rotMax="0 0 90" moveTime="1" />
		<part node="2" name="P2" showHelp="false" rotMin="0 0 0" rotMax="0 0 90" moveTime="1.5" autoReturn="true" />
		<part node="3" name="P3" showHelp="false" transMin="0 0 0" transMax="0 0.5 0" moveTime="0.5" save="false" />
		<part node="4" name="P4" showHelp="true" transMin="0 0 0" transMax="0 0.5 0" moveTime="0.5" onfoot="only" onfootNode="4|1" onfootDistance="1" />
		<part node="5" name="P5" showHelp="true" scaleMin="0.5 1 1" scaleMax="1 1 1" moveTime="2" alwaysActive="true" />
		<part node="6" name="P6" showHelp="false" scaleMin="0.5 1 1" scaleMax="1 1 1" moveTime="2" soundFile="Sounds/pipe.wav" pitchOffset="0.7" volume="1" />
		<part node="7" name="P7" showHelp="true" visToggle="true" defaultVis="false" />
		<part node="8" name="P8" showHelp="true" visToggle="true" defaultVis="true" onfoot="only" onlyInRepairTrigger="true" />
		<part node="9" name="P9" showHelp="true" useAnimation="true" animSpeed="1"/>
	</universalToggleParts>
	
	Vysvetlivky k funkcim:
	
	part				- jednotlive casti (neomezeny pocet)
	node				- index ovladane casti v GE
	name				- jmeno, ktere musi souhlasit s nazvem klavesy, pripadne napovedy v souboru modDesc.xml
	showHelp			- (true/false) zobrazeni napovedy pres F1 (pri nezadani defaultne hodnota "false")
	rotMin/rotMax			- min/max rotace ve 3 osach
	transMin/transMax		- min/max posun ve 3 osach
	scaleMin/scaleMax		- min/max meritko ve 3 osach  
	moveTime			- cas v sekundach, ktery je potreba na provedeni zmeny min/max
	autoReturn			- automaticke vraceni do min polohy (reaguje na drzeni klavesy)
	useAnimation			- pouzije pro pohyb animaci zadanou v animations pod stejnym nazvem jako je NAME (nutna specializace animatedVehicle)
	animSpeed			- rychlost prehravani animace (pri nezadani defaultne hodnota "1" = 1:1; vyssi cislo = vyssi rychlost)
	visToggle			- (true/false) funkce prepinani viditelnosti casti (pri nezadani defaultne hodnota "false")
	defaultVis			- (true/false) hodnota viditelnosti casti pri koupeni stroje (pri nezadani defaultne hodnota "true")
	save				- (true/false) ukladani pozice/viditelnosti casti pri ulozeni hry (pri nezadani defaultne hodnota "true")
	alwaysActive			- (true/false) pri hodnote "true" funguje i pri prepnuti na zarizeni (pri nezadani defaultne hodnota "false")
	soundFile			- zvuk prehravany pri zmene polohy nebo meritka casti
	pitchOffset			- rychlost prehravani zvuku (pri nezadani defaultne hodnota "1" = 1:1)
	volume				- hlasitost zvuku (pri nezadani defaultne hodnota "1" = 1:1)
	onfoot				- (false/also/only) moznost provadeni akce mimo vozidlo [nelze/z vozu i zvenci/pouze zvenci] (pri nezadani defaultne hodnota "false")
	onfootNode			- index u ktereho je mozno provadet akci zvenci (pri nezadani defaultne hodnota "node")
	onfootDistance			- polomer kruznice od bodu onfootNode, ktera urcuje moznost akce zvenci (pri nezadani defaultne hodnota "2.5")
	onlyInRepairTrigger	- funkce je aktivni pouze, pokud se vozidlo/zarízeni nachazi v oblasti RepairTriggeru (soucast DamageMod)
	
	( Hodnoty rot/trans/scale lze navzajem kombinovat u jedne akce. Funkce "save" neni nikdy aktivni s funkci "autoReturn". )
	( Funkce useAnimation vyradi rot/trans/scale + moveTime. )
	
	--------------------------------------------------------------------------------------------------------------------------------------------------------	
	
	Zapis do souboru modDesc.xml :
	******************************
	
	vsadit do <specializations> :
		<specialization name="universalToggleParts" className="universalToggleParts" filename="scripts/universalToggleParts.lua"/> 
	
	vsadit do <vehicleTypes> :
		<specialization name="universalToggleParts"/>
	
	vsadit do <l10n> :
		<text name="P1"> <en>Action 1</en> <cz>Akce 1</cz> />
		( Hodnoty "name" se musi shodovat s udaji v XML stroje! Pokud je v XML stroje u dane casti showHelp="false", napovedu nezadavame. )
	
	vsadit do <inputBindings> :
		<input name="P1" key1="KEY_5" button="" />
		<input name="P2" key1="KEY_6" button="" />
		<input name="P3" key1="KEY_7" button="" />
		( Vsechny InputBindings musi byt definovany a "name" se musi shodovat s udaji v XML stroje! )
	
************************************************************************************************************************************************************]]--

source(g_currentModDirectory .. "scripts/universalTogglePartsEvent.lua");  -- pokud nejsou skripty umistene ve slozce "scripts" tak zde upravit cestu !!
universalToggleParts = {};

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

function universalToggleParts:load(xmlFile)
	self.togglePart = SpecializationUtil.callSpecializationsFunction("togglePart");
	self.toggleVisibility = SpecializationUtil.callSpecializationsFunction("toggleVisibility");
	
	self.isSelectable = true;
	
	self.parts = {};
	local i = 0;
	while true do
		local baseString = string.format("vehicle.universalToggleParts.part(%d)", i);
		if not hasXMLProperty(xmlFile, baseString) then
			break;
		end;
		
		local part = {};
		
		part.node = Utils.indexToObject(self.components, getXMLString(xmlFile, baseString.."#node"));
		part.name = getXMLString(xmlFile, baseString.."#name");
		
		local onfoot = getXMLString(xmlFile, baseString.."#onfoot");
		if onfoot == "only" or onfoot == "also" then
			part.onfoot = onfoot;
			part.onfootNode = Utils.getNoNil(Utils.indexToObject(self.components, getXMLString(xmlFile, baseString.."#onfootNode")), part.node);
			part.onfootDistance = Utils.getNoNil(getXMLFloat(xmlFile, baseString.."#onfootDistance"), 2.5);
			part.onfootInRange = false;
		else
			part.onfoot = false;
		end;
		
		part.onlyRepair = Utils.getNoNil(getXMLString(xmlFile, baseString.."#onlyInRepairTrigger"), false);
		
		local rotMin = getXMLString(xmlFile, baseString.."#rotMin");
		local rotMax = getXMLString(xmlFile, baseString.."#rotMax");
		if rotMin ~= nil and rotMax ~= nil then
			local x,y,z = Utils.getVectorFromString(rotMin);
			part.rotMin = {math.rad(Utils.getNoNil(x,0)),math.rad(Utils.getNoNil(y,0)),math.rad(Utils.getNoNil(z,0))};
			local x,y,z = Utils.getVectorFromString(rotMax);
			part.rotMax = {math.rad(Utils.getNoNil(x,0)),math.rad(Utils.getNoNil(y,0)),math.rad(Utils.getNoNil(z,0))};
		end;
		
		local transMin = getXMLString(xmlFile, baseString.."#transMin");
		local transMax = getXMLString(xmlFile, baseString.."#transMax");
		if transMin ~= nil and transMax ~= nil then
			local x,y,z = Utils.getVectorFromString(transMin);
			part.transMin = {Utils.getNoNil(x,0),Utils.getNoNil(y,0),Utils.getNoNil(z,0)};
			local x,y,z = Utils.getVectorFromString(transMax);
			part.transMax = {Utils.getNoNil(x,0),Utils.getNoNil(y,0),Utils.getNoNil(z,0)};
		end;
		
		local scaleMin = getXMLString(xmlFile, baseString.."#scaleMin");
		local scaleMax = getXMLString(xmlFile, baseString.."#scaleMax");
		if scaleMin ~= nil and scaleMax ~= nil then
			local x,y,z = Utils.getVectorFromString(scaleMin);
			part.scaleMin = {Utils.getNoNil(x,1),Utils.getNoNil(y,1),Utils.getNoNil(z,1)};
			local x,y,z = Utils.getVectorFromString(scaleMax);
			part.scaleMax = {Utils.getNoNil(x,1),Utils.getNoNil(y,1),Utils.getNoNil(z,1)};
		end;
		
		local sound = getXMLString(xmlFile, baseString.."#soundFile");
		if sound ~= nil and sound ~= "" then
			sound = Utils.getFilename(sound, self.baseDirectory);
			part.sound = createSample("#partSound");
			loadSample(part.sound, sound, false);
			local soundPitchOffset = Utils.getNoNil(getXMLFloat(xmlFile, baseString.."#pitchOffset"), 1);
			part.soundVolume = Utils.getNoNil(getXMLFloat(xmlFile, baseString.."#volume"), 1);
			setSamplePitch(part.sound, soundPitchOffset);
			part.soundPlay = false;
		end;
		
		part.help = Utils.getNoNil(getXMLBool(xmlFile, baseString.."#showHelp"), false);
		part.alwaysActive = Utils.getNoNil(getXMLBool(xmlFile, baseString.."#alwaysActive"), false);
		part.visToggle = Utils.getNoNil(getXMLBool(xmlFile, baseString.."#visToggle"), false);
		part.vis = Utils.getNoNil(getXMLBool(xmlFile, baseString.."#defaultVis"), true);
		part.save = Utils.getNoNil(getXMLBool(xmlFile, baseString.."#save"), true);
		part.moveTime = Utils.getNoNil(getXMLFloat(xmlFile, baseString.."#moveTime"), 1) * 1000;
		part.autoReturn = Utils.getNoNil(getXMLBool(xmlFile, baseString.."#autoReturn"), false);
		
		local animation = Utils.getNoNil(getXMLBool(xmlFile, baseString.."#useAnimation"), false);
		local animSpeed = Utils.getNoNil(getXMLFloat(xmlFile, baseString.."#animSpeed"), 1);
		if animation and self.playAnimation ~= nil and self.animations ~= nil then
			if self.animations[part.name] ~= nil then
				part.animation = part.name;
				part.animSpeed = animSpeed;
				part.playAnim = false;
				part.rotMin = nil;
				part.rotMax = nil;
				part.transMin = nil;
				part.transMax = nil;
				part.scaleMin = nil;
				part.scaleMax = nil;
			end;
		end;
		
		if part.autoReturn then
			part.save = false;
		end;
		
		part.toMax = false;
		part.move = false;
		
		table.insert(self.parts, part);
		i = i + 1;
	end;
end;

function universalToggleParts:getSaveAttributesAndNodes(nodeIdent)
	local nodes = "";
	local partNum = 0;
	for i=1, table.getn(self.parts) do
		local part = self.parts[i];
		if partNum > 0 then
			nodes = nodes.."\n";
		end;
		nodes = nodes..nodeIdent..'<universalToggleParts  partVisible="' .. tostring(part.vis) .. '" partMax="'..tostring(part.toMax)..'"' .. ' />';
		partNum = partNum + 1;
	end;
	
	return nil,nodes;
end;

function universalToggleParts:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	if not resetVehicles then
		for nr, part in ipairs(self.parts) do
			local partKey = key..string.format(".universalToggleParts(%d)", nr - 1);
			local vis = Utils.getNoNil(getXMLBool(xmlFile, partKey.."#partVisible"), part.vis);
			local toMax = Utils.getNoNil(getXMLBool(xmlFile, partKey.."#partMax"), part.toMax);
			if part.save then
				self:toggleVisibility(nr, vis);
				if not part.autoReturn then
					self:togglePart(nr, toMax); 
				end;
			end;
		end;
	end;
	
	return BaseMission.VEHICLE_LOAD_OK;
end;

function universalToggleParts:update(dt)
	for nr, part in ipairs(self.parts) do
		if not part.onlyRepair or (part.onlyRepair and self.isInRepairTrigger) then
			if self:getIsActiveForInput() or (part.alwaysActive and self.isClient and self.isEntered and not self:hasInputConflictWithSelection()) then
				if part.onfoot ~= "only" then
					if part.visToggle then
						if InputBinding.hasEvent(InputBinding[part.name]) then
							self:toggleVisibility(nr, not part.vis);
						end;
					else
						if not part.autoReturn then
							if InputBinding.hasEvent(InputBinding[part.name]) then
								self:togglePart(nr, not part.toMax); 
							end;
						else
							if InputBinding.isPressed(InputBinding[part.name]) then
								if not part.toMax then
									self:togglePart(nr, true); 
								end;
							else
								if part.toMax then
									self:togglePart(nr, false); 
								end;
							end;
						end;
					end;
				end;
			end;
			
			if g_currentMission.player ~= nil then
				if part.onfootInRange and not self.visualSettingsSetupMode then
					if part.help then
						g_currentMission:addHelpButtonText(g_i18n:getText(part.name), InputBinding[part.name]);
					end;
					if part.visToggle then
						if InputBinding.hasEvent(InputBinding[part.name]) then
							self:toggleVisibility(nr, not part.vis); 
						end;
					else
						if not part.autoReturn then
							if InputBinding.hasEvent(InputBinding[part.name]) then
								self:togglePart(nr,not part.toMax); 
							end;
						else
							if InputBinding.isPressed(InputBinding[part.name]) then
								if not part.toMax then
									self:togglePart(nr, true); 
								end;
							else
								if part.toMax then
									self:togglePart(nr, false); 
								end;
							end;
						end;
					end;
				end;
			end;
			
			setVisibility(part.node, part.vis);
			
			if part.move then
				part.move = false;
				
				--[[if self.movingParts ~= nil then
					for _, part in pairs(self.movingParts) do
						Cylindered.updateMovingPart(self, part);
					end;
				end;]]
				
				if part.animation ~= nil then
					if part.toMax then
						self:playAnimation(part.animation, part.animSpeed, nil, true);
					end;
					if not part.toMax then
						self:playAnimation(part.animation, - part.animSpeed, nil, true);
					end;
				end;
				
				if part.rotMin ~= nil then
					local curRot = {getRotation(part.node)};
					local newRot = Utils.getMovedLimitedValues(curRot, part.rotMax, part.rotMin, 3, part.moveTime, dt, not part.toMax);
					setRotation(part.node, unpack(newRot));
					for i=1,3 do
						if math.abs(newRot[i]-curRot[i]) > 0.001 then
							part.move = true;
						end;
					end;
				end;
				
				if part.transMin ~= nil then
					local curTrans = {getTranslation(part.node)};
					local newTrans = Utils.getMovedLimitedValues(curTrans, part.transMax, part.transMin, 3, part.moveTime, dt, not part.toMax);
					setTranslation(part.node, unpack(newTrans));
					for i=1,3 do
						if math.abs(newTrans[i]-curTrans[i]) > 0.001 then
							part.move = true;
						end;
					end;
				end;
				
				if part.scaleMin ~= nil then
					local curScale = {getScale(part.node)};
					local newScale = Utils.getMovedLimitedValues(curScale, part.scaleMax, part.scaleMin, 3, part.moveTime, dt, not part.toMax);
					setScale(part.node, unpack(newScale));
					for i=1,3 do
						if math.abs(newScale[i]-curScale[i]) > 0.001 then
							part.move = true;
						end;
					end;
				end;
				
				if part.sound ~= nil then
					if self.isClient then
						if part.move then
							if self:getIsActiveForSound() and not part.soundPlay then
								playSample(part.sound, 0, part.soundVolume, 0);
								part.soundPlay = true;
							end;
						else
							stopSample(part.sound);
							part.soundPlay = false;
						end;
					end;
				end;
			end;
		end;
	end;
end;

function universalToggleParts:updateTick(dt)
	if g_currentMission.player ~= nil then
		for _, part in ipairs(self.parts) do
			if part.onfoot ~= false then
				if g_currentMission.player.rootNode ~= nil then
					local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
					local sx, sy, sz = getWorldTranslation(part.onfootNode); 
					local distance = Utils.vector3Length(sx-vx, sy-vy, sz-vz);
					if distance < part.onfootDistance then
						part.onfootInRange = true; 
					else
						part.onfootInRange = false; 
					end;
				end;
			end;
		end;
	end;
end;

function universalToggleParts:draw()
	for _, part in ipairs(self.parts) do
		if not part.onlyRepair or (part.onlyRepair and self.isInRepairTrigger) then
			if self:getIsActiveForInput() or (part.alwaysActive and self.isClient and self.isEntered) then
				if part.help and part.onfoot ~= "only" then
					g_currentMission:addHelpButtonText(g_i18n:getText(part.name), InputBinding[part.name]);
				end;
			end;
		end;
	end;
end;

function universalToggleParts:togglePart(name, state, noEventSend)
	togglePartEvent.sendEvent(self, name, state, noEventSend);
	self.parts[name].toMax = state;
	self.parts[name].move = true;
end;

function universalToggleParts:toggleVisibility(name, state, noEventSend)
	toggleVisEvent.sendEvent(self, name, state, noEventSend);
	self.parts[name].vis = state;
end;

function universalToggleParts:readStream(streamId, connection)
	for nr, part in ipairs(self.parts) do
		local state1 = streamReadBool(streamId);
		local state2 = streamReadBool(streamId);
		self:toggleVisibility(nr, state1, true);
		self:togglePart(nr, state2, true);
	end;
end;
	
function universalToggleParts:writeStream(streamId, connection)
	for nr, part in ipairs(self.parts) do
		streamWriteBool(streamId, part.vis);
		streamWriteBool(streamId, part.toMax);
	end;
end;

function universalToggleParts:delete()
	for nr, part in ipairs(self.parts) do
		if part.sound ~= nil then
			delete(part.sound);
		end;
	end;
end;

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

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