-- author: 	fruktor

-- Änderungen am Skript nur mit meiner Zustimmung!
-- Modification only with my permission!

CylinderedFixes = {}

function CylinderedFixes:prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Cylindered, specializations);
end;

function CylinderedFixes:load(xmlFile)
	self.setMovingTool = CylinderedFixes.setMovingTool; 
end;

function CylinderedFixes:loadFromAttributesAndNodes(func, xmlFile, key, resetVehicles)
	local saveInfo = func(self, xmlFile, key, resetVehicles);

	for a=1, table.getn(self.movingParts) do
		Cylindered.updateMovingPart(self, self.movingParts[a], true);
	end;
		
	return saveInfo;
end;

function CylinderedFixes:updateTick(_func, dt)
	if self:getIsActive() then
		if self.isClient and self:getIsActiveForInput(false) and not self:hasInputConflictWithSelection() then
			local foldAnimTime = self.foldAnimTime;
			for i=1, table.getn(self.movingTools) do
				local tool = self.movingTools[i];
				local foldActive = (foldAnimTime == nil or (foldAnimTime <= tool.foldMaxLimit and foldAnimTime >= tool.foldMinLimit));
				if tool.isActive and foldActive and (tool.rotSpeed ~= nil or tool.transSpeed ~= nil) and tool.axisActionIndex ~= nil then
					local move, axisType = InputBinding.getInputAxis(tool.axisActionIndex);
  
					if axisType == InputBinding.INPUTTYPE_MOUSE_AXIS then
						if tool.invertMouseAxis then
							move = -move;
						end
						move = move * tool.speedFactor;
					else
						if tool.invertAxis then
							move = -move;
						end
					end
					
					local rotSpeed = 0;
					local transSpeed = 0;
					if not InputBinding.isAxisZero(move) then
						if tool.rotSpeed ~= nil and tool.rotMinActive == nil then													
							rotSpeed = move*tool.rotSpeed;
							if tool.rotAcceleration ~= nil and math.abs(rotSpeed - tool.lastRotSpeed) >= tool.rotAcceleration*dt then
								if rotSpeed > tool.lastRotSpeed then
									rotSpeed = tool.lastRotSpeed + tool.rotAcceleration*dt;
								else
									rotSpeed = tool.lastRotSpeed - tool.rotAcceleration*dt;
								end;
							end;
						end;
						if tool.transSpeed ~= nil and tool.rotMinActive == nil then															
							transSpeed = move*tool.transSpeed;
							if tool.transAcceleration ~= nil and math.abs(transSpeed - tool.lastTransSpeed) >= tool.transAcceleration*dt then
								if transSpeed > tool.lastTransSpeed then
									transSpeed = tool.lastTransSpeed + tool.transAcceleration*dt;
								else
									transSpeed = tool.lastTransSpeed - tool.transAcceleration*dt;
								end;
							end;
						end;
					else
						-- decelerate
						if tool.externalRotSpeed ~= 0 then
							rotSpeed = tool.externalRotSpeed;
						elseif tool.rotAcceleration ~= nil then
							if tool.lastRotSpeed < 0 then
								rotSpeed = math.min(tool.lastRotSpeed + tool.rotAcceleration*dt, 0);
							else
								rotSpeed = math.max(tool.lastRotSpeed - tool.rotAcceleration*dt, 0);
							end;
						end;
						if tool.externalTransSpeed ~= 0 then
							transSpeed = tool.externalTransSpeed;
						elseif tool.transAcceleration ~= nil then
							if tool.lastTransSpeed < 0 then
								transSpeed = math.min(tool.lastTransSpeed + tool.transAcceleration*dt, 0);
							else
								transSpeed = math.max(tool.lastTransSpeed - tool.transAcceleration*dt, 0);
							end;
						end;
					end;

					local changed = false;
					local value = 0;
					
					if rotSpeed ~= 0 then
						local newRot = tool.curRot[tool.rotationAxis]+rotSpeed*dt;
						if tool.rotMax ~= nil then
							newRot = math.min(newRot, tool.rotMax);
						end
						if tool.rotMin ~= nil then
							newRot = math.max(newRot, tool.rotMin);
						end
						local diff = newRot - tool.curRot[tool.rotationAxis];
						tool.lastRotSpeed = diff/dt;
						if math.abs(diff) > 0.0001 then
							-- wrap if not limited
							if tool.rotMin == nil and tool.rotMax == nil then
								if newRot > 2*math.pi then
									newRot = newRot - 2*math.pi;
								end;
								if newRot < 0 then
									newRot = newRot + 2*math.pi;
								end;
							end

							changed = true;
							value = newRot;
						end;
					else
						tool.lastRotSpeed = 0;
					end;				
					if transSpeed ~= 0 then
						local newTrans = tool.curTrans[tool.translationAxis]+transSpeed*dt;
						if tool.transMax ~= nil then
							newTrans = math.min(newTrans, tool.transMax);
						end;
						if tool.transMin ~= nil then
							newTrans = math.max(newTrans, tool.transMin);
						end;
						local diff = newTrans - tool.curTrans[tool.translationAxis];
						tool.lastTransSpeed = diff/dt;
						if math.abs(diff) > 0.0001 then
							changed = true;
							value = newTrans;
						end;
					else
						tool.lastTransSpeed = 0;
					end;

					for _, dependentTool in pairs(tool.dependentMovingTools) do
						if dependentTool.rotSpeedScale ~= nil and tool.lastRotSpeed ~= 0 then
							dependentTool.movingTool.externalRotSpeed = dependentTool.rotSpeedScale * tool.lastRotSpeed;
						end
						if dependentTool.transSpeedScale ~= nil and tool.lastTransSpeed ~= 0 then
							dependentTool.movingTool.externalTransSpeed = dependentTool.transSpeedScale * tool.lastTransSpeed;
						end
					end

					if changed then
						CylinderedFixes.setMovingTool( self, i, value );
					end;
				end;
				tool.externalRotSpeed = 0;
				tool.externalTransSpeed = 0;
			end;
		end;

		for _, part in pairs(self.activeDirtyMovingParts) do
			Cylindered.setDirty(self, part);
		end
	end

	for _, tool in pairs(self.movingTools) do
		if tool.isDirty then
			if self.isServer then
				-- update component joint
				Cylindered.updateComponentJoints(self, tool, false);
			end;
			tool.isDirty = false;
		end;
	end;
	
	for i, part in ipairs(self.movingParts) do
		if part.isDirty then
			Cylindered.updateMovingPart(self, part, false);
			if part.playSound and not self.cylinderedHydraulicSoundEnabled and self:getIsActiveForSound() then
				self.cylinderedHydraulicSoundPartNumber = i;
				playSample(self.cylinderedHydraulicSound, 0, self.cylinderedHydraulicSoundVolume, 0);
				self.cylinderedHydraulicSoundEnabled = true;
			end;
		else
			if self.cylinderedHydraulicSoundEnabled and self.cylinderedHydraulicSoundPartNumber == i then
				stopSample(self.cylinderedHydraulicSound);
				self.cylinderedHydraulicSoundEnabled = false;
			end;
		end;
	end;
end;

function CylinderedFixes:readUpdateStream(_func, streamId, timestamp, connection)

end;

function CylinderedFixes:writeUpdateStream(_func, streamId, connection, dirtyMask)

end;

Cylindered.updateTick = Utils.overwrittenFunction(Cylindered.updateTick, CylinderedFixes.updateTick);
Cylindered.readUpdateStream = Utils.overwrittenFunction(Cylindered.readUpdateStream, CylinderedFixes.readUpdateStream);
Cylindered.writeUpdateStream = Utils.overwrittenFunction(Cylindered.writeUpdateStream, CylinderedFixes.writeUpdateStream);
Cylindered.loadFromAttributesAndNodes = Utils.overwrittenFunction(Cylindered.loadFromAttributesAndNodes, CylinderedFixes.loadFromAttributesAndNodes);

function CylinderedFixes:setMovingTool(idx, value, noEventSend)
	CylinderedFixesEvent.sendEvent(self, idx, value, noEventSend)
	
	local tool = self.movingTools[idx];
	if tool ~= nil then
		if tool.rotSpeed ~= nil then
			tool.curRot[tool.rotationAxis] = value;
			setRotation(tool.node, tool.curRot[1], tool.curRot[2], tool.curRot[3]);
		end;
		if tool.transSpeed ~= nil then
			tool.curTrans[tool.translationAxis] = value;
			setTranslation(tool.node, tool.curTrans[1], tool.curTrans[2], tool.curTrans[3]);
		end;
		Cylindered.setDirty(self, tool);
	end;
end;

CylinderedFixesEvent = {};
CylinderedFixesEvent_mt = Class(CylinderedFixesEvent, Event);
InitEventClass(CylinderedFixesEvent, "CylinderedFixesEvent");
function CylinderedFixesEvent:emptyNew()
    local self = Event:new(CylinderedFixesEvent_mt);
    self.className="CylinderedFixesEvent";
    return self;
end;

function CylinderedFixesEvent:new(object, idx, value)
    local self = CylinderedFixesEvent:emptyNew(); 
    self.object = object; 
	self.idx = idx;
	self.value = value;
    return self;
end;

function CylinderedFixesEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.object));
	streamWriteUIntN(streamId, self.idx, 8);
	streamWriteFloat32(streamId, self.value);
end;

function CylinderedFixesEvent:readStream(streamId, connection)
    self.object = networkGetObject(streamReadInt32(streamId));
	self.idx = streamReadUIntN(streamId, 8);
	self.value = streamReadFloat32(streamId);
    self:run(connection);
end;

function CylinderedFixesEvent:run(connection)
	CylinderedFixes.setMovingTool(self.object, self.idx, self.value, true);
	if not connection:getIsServer() then
		g_server:broadcastEvent(CylinderedFixesEvent:new(self.object, self.idx, self.value), nil, connection, self.object);
	end; 
end;

function CylinderedFixesEvent.sendEvent(object, idx, value, noEventSend)
	if noEventSend == nil or noEventSend == false then  
		if g_server ~= nil then   
			g_server:broadcastEvent(CylinderedFixesEvent:new(object, idx, value), nil, nil, object); 
		else  
			g_client:getServerConnection():sendEvent(CylinderedFixesEvent:new(object, idx, value)); 
		end;
	end;
end;