-- by modelleicher
-- Farming Agency (former: ls-modcompany.com)
-- 05.04.2020 (LS19)

-- converted by PeterAH
-- 14.12.2022 (LS22)
-- 05.03.2025 (LS25)
-- RepiGaming and Ifko
-- MP Fix 09.06.2025  (LS25)


clickLatchLoader = {}

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

function clickLatchLoader.registerEventListeners(vehicleType)	
	SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", clickLatchLoader);
	SpecializationUtil.registerEventListener(vehicleType, "onUpdate", clickLatchLoader);
	SpecializationUtil.registerEventListener(vehicleType, "onDelete", clickLatchLoader);
	SpecializationUtil.registerEventListener(vehicleType, "onReadStream", clickLatchLoader);
	SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", clickLatchLoader);
	SpecializationUtil.registerEventListener(vehicleType, "onPostAttachImplement", clickLatchLoader);
	SpecializationUtil.registerEventListener(vehicleType, "onPreDetachImplement", clickLatchLoader);
	SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", clickLatchLoader);
end;

function clickLatchLoader:onRegisterActionEvents(isActiveForInput)
	local spec = self.spec_clickLatchLoader;
	spec.actionEvents = {};
	self:clearActionEventsTable(spec.actionEvents);

	if isActiveForInput then
		self:addActionEvent(spec.actionEvents, InputAction.CLICK_LATCH_LOADER, self, clickLatchLoader.CLICK_LATCH_LOADER_INPUT, true, false, false, true, nil);
	end;
end;

function clickLatchLoader.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "openLatch", clickLatchLoader.openLatch);
	SpecializationUtil.registerFunction(vehicleType, "latch", clickLatchLoader.latch);
	SpecializationUtil.registerFunction(vehicleType, "synchState", clickLatchLoader.synchState);
end;

function clickLatchLoader:CLICK_LATCH_LOADER_INPUT()
	self:openLatch()
end;

function clickLatchLoader:onPostLoad(savegame)
	self.spec_clickLatchLoader = {};

	local spec = self.spec_clickLatchLoader;

	local key = "vehicle.clickLatchLoader";

	spec.jointDescIndex = Utils.getNoNil(getXMLInt(self.xmlFile.handle, key .. ".jointSettings#jointDescIndex"), 1);
	spec.latchRefNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile.handle, key .. "#latchRefNode"), self.i3dMappings)
	spec.latchDistance = Utils.getNoNil(getXMLFloat(self.xmlFile.handle, key .. "#latchDistance"), 0.1);

	spec.jointSettings = {};
	local rot = Utils.getNoNil(getXMLString(self.xmlFile.handle, key .. ".jointSettings#latchedRotLimit"), "0 0 0")
    local latchedRotLimit = {}
    local i = 1
    local str
    for str in string.gmatch(rot, "%S+") do
        latchedRotLimit[i] = str
        i = i + 1
    end

	rot = Utils.getNoNil(getXMLString(self.xmlFile.handle, key .. ".jointSettings#openRotLimit"), "80 0 0")
    local openRotLimit = {}
    i = 1
    for str in string.gmatch(rot, "%S+") do
        openRotLimit[i] = str
        i = i + 1
    end

	--local latchedRotLimit = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, key .. ".jointSettings#latchedRotLimit"), "0 0 0"));
	--local openRotLimit = StringUtil.splitString(" ", Utils.getNoNil(getXMLString(self.xmlFile, key .. ".jointSettings#openRotLimit"), "80 0 0"));

	spec.jointSettings.latchedRotLimit = latchedRotLimit
	spec.jointSettings.openRotLimit = openRotLimit

	spec.latchAnimation = getXMLString(self.xmlFile.handle, key .. ".latchAnimation#name");
	if spec.latchAnimation ~= nil then
		-- animation calculation stuffs 
		spec.animationSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile.handle, key ..".latchAnimation#speed"), 1);
		spec.animationDistance = Utils.getNoNil(getXMLFloat(self.xmlFile.handle, key ..".latchAnimation#animationDistance"), spec.latchDistance);
	end;

	if self.isClient then
		spec.samples = {}
		spec.samples.openLatch = g_soundManager:loadSampleFromXML(self.xmlFile, key ..".sounds", "openLatch", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
		spec.samples.closeLatch = g_soundManager:loadSampleFromXML(self.xmlFile, key ..".sounds", "closeLatch", self.baseDirectory, self.components, 1, AudioGroup.VEHICLE, self.i3dMappings, self)
	end;


	spec.latchOpen = false;

	spec.latchTimerTotal = 600;
	spec.latchTimer = 0;

	-- reset latched limit 
	if self.isServer then
		local jointDesc = self.spec_attacherJoints.attacherJoints[spec.jointDescIndex];
		for i = 1, 3 do
			setJointRotationLimit(jointDesc.jointIndex, i-1, true, 0, math.rad(tonumber(spec.jointSettings.latchedRotLimit[i])))
		end;
	end;
end;

function clickLatchLoader:onReadStream(streamId, connection)
    local state = streamReadBool(streamId)
	if state then
		self:openLatch();
	else
		self:latch();
	end;
end;

function clickLatchLoader:onWriteStream(streamId, connection)
    streamWriteBool(streamId, self.spec_clickLatchLoader.latchOpen)
end

function clickLatchLoader:synchState(state, noEventSend)
    clickLatchEvent.sendEvent(self, state, noEventSend);
	if noEventSend then
		if state then
			self:openLatch();
		else
			self:latch();
		end;
	end;
end;

function clickLatchLoader:onUpdate(dt)
	local spec = self.spec_clickLatchLoader;
	if spec.latchOpen and spec.targetRefNode ~= nil then
		spec.latchTimer = spec.latchTimer - dt;

		local x,y,z = getWorldTranslation(spec.latchRefNode)
		local tx,ty,tz = getWorldTranslation(spec.targetRefNode)

		local distance = MathUtil.vector3Length(x - tx, y - ty, z - tz);

		if spec.latchAnimation ~= nil then
			local animationTime = 0.5 - (math.min(distance, spec.animationDistance) / spec.animationDistance * 0.5); -- play animation half way 

			self:setAnimationTime(spec.latchAnimation, animationTime)
		end;

		if self.isServer then
			if spec.latchTimer < 0 then
				if distance < spec.latchDistance then
					self:latch()
				end;
			end;
		end;
	end;
end;

function clickLatchLoader:onPostAttachImplement(implement)
	local spec = self.spec_clickLatchLoader;
	if implement.spec_clickLatchLoaderImplement ~= nil then
		spec.targetRefNode = implement.spec_clickLatchLoaderImplement.latchRefNode;
	end;
end;
function clickLatchLoader:onPreDetachImplement(implementIndex)
	local spec = self.spec_clickLatchLoader;

	spec.targetRefNode = nil;
end;

function clickLatchLoader:openLatch()
	local spec = self.spec_clickLatchLoader;
	if not spec.latchOpen and spec.targetRefNode ~= nil then
		if self.isServer then
			local jointDesc = self.spec_attacherJoints.attacherJoints[spec.jointDescIndex];

			for i = 1, 3 do
				setJointRotationLimit(jointDesc.jointIndex, i-1, true, -math.rad(tonumber(spec.jointSettings.openRotLimit[i])), 0)
			end;
		end;

		if spec.latchAnimation ~= nil then
			self:playAnimation(spec.latchAnimation, spec.animationSpeed, 0, true);
		end;

		if spec.samples.openLatch ~= nil then
			g_soundManager:playSample(spec.samples.openLatch, 1)
		end;

		spec.latchOpen = true;
		self:synchState(spec.latchOpen);
		spec.latchTimer = spec.latchTimerTotal;
	end;
end;

function clickLatchLoader:latch()
	local spec = self.spec_clickLatchLoader;
	if spec.latchOpen then
		if self.isServer then
			local jointDesc = self.spec_attacherJoints.attacherJoints[spec.jointDescIndex];
			for i = 1, 3 do
				setJointRotationLimit(jointDesc.jointIndex, i-1, true, -math.rad(tonumber(spec.jointSettings.latchedRotLimit[i])), 0)
			end;
		end;

		if spec.latchAnimation ~= nil then
			self:playAnimation(spec.latchAnimation, spec.animationSpeed, self:getAnimationTime(spec.latchAnimation), true);
		end;

		if spec.samples.closeLatch ~= nil then
			g_soundManager:playSample(spec.samples.closeLatch, 1)
		end;
		spec.latchOpen = false;
		self:synchState(spec.latchOpen);
	end;
end;

function clickLatchLoader:onDelete()
	local spec = self.spec_clickLatchLoader
	if self.isClient then
	   g_soundManager:deleteSamples(spec.samples)
	end;
end;



clickLatchEvent = {};
clickLatchEvent_mt = Class(clickLatchEvent, Event);
InitEventClass(clickLatchEvent, "clickLatchEvent");

function clickLatchEvent.emptyNew()
    local self = Event.new(clickLatchEvent_mt );
    self.className="clickLatchEvent";
    return self;
end;
function clickLatchEvent.new(vehicle, state)
	local self = clickLatchEvent.emptyNew();

    self.vehicle = vehicle;
    self.state = state;
    return self;
end;
function clickLatchEvent:readStream(streamId, connection)
    self.vehicle = NetworkUtil.readNodeObject(streamId);
    self.state = streamReadBool(streamId);
    self:run(connection);
end;
function clickLatchEvent:writeStream(streamId, connection)
	NetworkUtil.writeNodeObject(streamId, self.vehicle);
    streamWriteBool(streamId, self.state );
end;
function clickLatchEvent:run(connection)
    self.vehicle:synchState(self.state, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(clickLatchEvent.new(self.vehicle, self.state), nil, connection, self.object);
    end;
end;
function clickLatchEvent.sendEvent(vehicle, state, noEventSend)
    if noEventSend == nil or noEventSend == false then
        if g_server ~= nil then
            g_server:broadcastEvent(clickLatchEvent.new(vehicle, state), nil, nil, vehicle);
        else
            g_client:getServerConnection():sendEvent(clickLatchEvent.new(vehicle, state));
        end;
    end;
end;