-- cylinders
-- Spezi für einfache Hydraulikcylinders oder cylinders aller Arten
-- Ebenfalls sind einfache setDirections möglich (LS11)

-- V2 Rotations die von einem Objekt ausgehend die Bewegung auf ein anderes übertragen sind nun auch möglich.
-- Damit kann man nun die komplette Animation einer Lenkung oder einer Heckhydraulik hierüber laufen lassen! (LS13)

-- by modelleicher
-- final FS19 release version, renamed to cylinderedV3, added i3dmappings, fixed direction rotation issue, script clean up


cylinderedV3 = {};

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


function cylinderedV3.registerEventListeners(vehicleType)
	SpecializationUtil.registerEventListener(vehicleType, "onLoad", cylinderedV3);
	SpecializationUtil.registerEventListener(vehicleType, "onUpdate", cylinderedV3);
end;


function cylinderedV3:onLoad(savegame)
	self.updateCylinderedV3 = cylinderedV3.updateCylinderedV3;
	
	self.spec_cylinderedV3 = {};  -- creating the table where all the variables are stored in
	local spec = self.spec_cylinderedV3; 

	local xmlFile = self.xmlFile;
	
	local path = "vehicle.cylinderedV3.";
	
	spec.cylinders = {};
	local i = 0;
	while true do
		local cyl = {};
		cyl.dir1 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path.."cylinders.cylinder("..i..")#dir1"), self.i3dMappings);
		cyl.dir2 = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path.."cylinders.cylinder("..i..")#dir2"), self.i3dMappings);
		if cyl.dir1 ~= nil and cyl.dir2 ~= nil then
			table.insert(spec.cylinders, cyl);
		else
			break;
		end;
		i = i+1;
	end;
		
    spec.rotations = {};
	i = 0;
	while true do	
		local rot = {};
		local path1 = path.."rotations.rotation("..i..")"
		rot.index = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path1 .. "#node"), self.i3dMappings);
		if rot.index == nil or rot.index == "" then
			break;
		end;		
		rot.ref = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path1 .. "#ref"), self.i3dMappings);
		rot.addDegrees = Utils.getNoNil(getXMLFloat(xmlFile, path1 .. "#addDegrees"), 0.0);
		rot.rotAxis = string.lower(Utils.getNoNil(getXMLString(xmlFile, path1 .. "#rotAxis"), "x"));
		rot.getRotAxis = string.lower(Utils.getNoNil(getXMLString(xmlFile, path1 .. "#getRotAxis"), "y"));
		rot.lengthMultiplicator = Utils.getNoNil(getXMLFloat(xmlFile, path1 .. "#lengthMultiplicator"), 1);	
		table.insert(spec.rotations, rot);
		i = i+1;
	end;

        
		
    spec.directions = {};
	i = 0;
	while true do	
		local dir = {};
		local path1 = path.."directions.direction("..i..")"
		dir.index = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path1 .. "#node"), self.i3dMappings);
		if dir.index == nil or dir.index == "" then
			break;
		end;		
		dir.ref = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path1 .. "#ref"), self.i3dMappings);
		dir.doScaleBool = Utils.getNoNil(getXMLBool(xmlFile, path1 .. "#doScaleBool"));
		if dir.doScaleBool == true then
			dir.scaleRef = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path1 .. "#scaleRef"), self.i3dMappings);            
			local ax, ay, az = getWorldTranslation(dir.index);
			local bx, by, bz = getWorldTranslation(dir.scaleRef);
			dir.scaleDistance = MathUtil.vector3Length(ax-bx, ay-by, az-bz);    
		end;
		table.insert(spec.directions, dir);
		i = i+1;
    end;
	
	spec.scaleOnlys = {};
	i = 0;
	while true do
		local scaleOnly = {};
		local path1 = path.."scaleOnlys.scaleOnly("..i..")"	
		scaleOnly.index = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path1 .. "#node"), self.i3dMappings);
		scaleOnly.ref = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, path1 .. "#ref"), self.i3dMappings);
		if scaleOnly.index == nil or scaleOnly.index == "" then
			break;
		end;	
		local ax, ay, az = getWorldTranslation(scaleOnly.index);
		local bx, by, bz = getWorldTranslation(scaleOnly.ref);
		scaleOnly.scaleDistance = MathUtil.vector3Length(ax-bx, ay-by, az-bz);
		table.insert(spec.scaleOnlys, scaleOnly);
		i = i+1;		
	end;	
	
		
	self:updateCylinderedV3();
	
end;

function cylinderedV3:updateCylinderedV3()

	local spec = self.spec_cylinderedV3;

	if #spec.cylinders > 0 then
		for _, cylinder in pairs(spec.cylinders) do
			if cylinder.dir1 ~= nil and cylinder.dir2 ~= nil then
				
				local ax, ay, az = getWorldTranslation(cylinder.dir1);
				local bx, by, bz = getWorldTranslation(cylinder.dir2);
				x, y, z = worldDirectionToLocal(getParent(cylinder.dir1), bx-ax, by-ay, bz-az);
				
				local ux, uy, uz = localDirectionToWorld(cylinder.dir1, 0,1,0)
				ux, uy, uz = worldDirectionToLocal(getParent(cylinder.dir1), ux, uy, uz)
					

				setDirection(cylinder.dir1, x, y, z, ux, uy, uz);
				
				local ax2, ay2, az2 = getWorldTranslation(cylinder.dir2);
				local bx2, by2, bz2 = getWorldTranslation(cylinder.dir1);
				x2, y2, z2 = worldDirectionToLocal(getParent(cylinder.dir2), bx2-ax2, by2-ay2, bz2-az2);
				
				local ux2, uy2, uz2 = localDirectionToWorld(cylinder.dir2, 0,1,0)
				ux2, uy2, uz2 = worldDirectionToLocal(getParent(cylinder.dir2), ux2, uy2, uz2)		
					
				setDirection(cylinder.dir2, x2, y2, z2, ux2, uy2, uz2); 
				
			end;
		end;
	end;
	
	
	if #spec.rotations > 0 then
		for _, rotation in pairs(spec.rotations) do
			if rotation.index ~= nil and rotation.ref ~= nil then
				local rx, ry, rz = getRotation(rotation.ref);
				local rw = 0;
				if rotation.getRotAxis == "y" then -- ask first for y because it is the most used in this case (for performance reasons)
					rw = ry*rotation.lengthMultiplicator;
				elseif rotation.getRotAxis == "x" then
					rw = rx*rotation.lengthMultiplicator;
				elseif spec.rotations[i].getRotAxis == "z" then
					rw = rz*rotation.lengthMultiplicator;
				end;
				if rotation.rotAxis == "x" then -- ask first for x because it is the most used in this case(for performance reasons)
					setRotation(rotation.index, rw+math.rad(rotation.addDegrees), 0, 0);
				elseif rotation.rotAxis == "y" then
					setRotation(rotation.index, 0, rw+math.rad(rotation.addDegrees), 0);
				elseif rotation.rotAxis == "z" then
					setRotation(rotation.index, 0, 0, rw+math.rad(rotation.addDegrees));
				end;
			end;
		end;
	end;
	if #spec.directions > 0 then
		for _, direction in pairs(spec.directions) do
			if direction.index ~= nil and direction.ref ~= nil then
				local ax, ay, az = getWorldTranslation(direction.index);
				local bx, by, bz = getWorldTranslation(direction.ref);
				x, y, z = worldDirectionToLocal(getParent(direction.index), bx-ax, by-ay, bz-az);

				local ux, uy, uz = localDirectionToWorld(direction.index, 0,1,0)
				ux, uy, uz = worldDirectionToLocal(getParent(direction.index), ux, uy, uz)				
				
				setDirection(direction.index, x, y, z, ux, uy, uz);
				
				if direction.doScaleBool == true and direction.scaleRef ~= nil then
					local distance = MathUtil.vector3Length(ax-bx, ay-by, az-bz);
					local scaleX, scaleY, scaleZ = getScale(direction.index);
					local setScaleWert = scaleZ * (distance / direction.scaleDistance);
					setScale(direction.index, 1, 1, setScaleWert);
				end;
			end;
		end;
	end;
	
	if #spec.scaleOnlys > 0 then
		for _, scaleOnly in pairs(spec.scaleOnlys) do
			if scaleOnly.index ~= nil and scaleOnly.ref ~= nil then
				local ax, ay, az = getWorldTranslation(scaleOnly.index);
				local bx, by, bz = getWorldTranslation(scaleOnly.ref);
				local scaleDistance = MathUtil.vector3Length(ax-bx, ay-by, az-bz);
				setScale(scaleOnly.index, 1, 1/(scaleOnly.scaleDistance/scaleDistance), 1);
			end;
		end;
	end;	

end;

function cylinderedV3:onUpdate(dt) 
	if self:getIsActive() then
		self:updateCylinderedV3();
	end;
end;
