--
-- FAP_Pump
--
-- @autor nilBrain
-- @date 09/01/2021
-- Copyright (C) nilBrain, Confidential, All Rights Reserved.

FAP_Pump = {};
FAP_Pump.MOVE_STEPS = 400;

local modName = g_currentModName;

function FAP_Pump.prerequisitesPresent(specializations)
	return SpecializationUtil.hasSpecialization(Attachable, specializations);
end;

function FAP_Pump.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "updateSupportFrame", FAP_Pump.updateSupportFrame);
	SpecializationUtil.registerFunction(vehicleType, "coppleManureHoseToSlurryTank", FAP_Pump.coppleManureHoseToSlurryTank);
	SpecializationUtil.registerFunction(vehicleType, "decoppleManureHoseToSlurryTank", FAP_Pump.decoppleManureHoseToSlurryTank);
	SpecializationUtil.registerFunction(vehicleType, "getSlurryBarrel", FAP_Pump.getSlurryBarrel);
end;

function FAP_Pump.registerEventListeners(vehicleType)
	SpecializationUtil.registerEventListener(vehicleType, "onLoad", FAP_Pump);
	SpecializationUtil.registerEventListener(vehicleType, "onPostAttach", FAP_Pump);
	SpecializationUtil.registerEventListener(vehicleType, "onPreDetach", FAP_Pump);
	SpecializationUtil.registerEventListener(vehicleType, "onPostDetach", FAP_Pump);
end;

function FAP_Pump.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "addFillUnitFillLevel", FAP_Pump.addFillUnitFillLevel);
end;

function FAP_Pump.initSpecialization(vehicleType)
    local schema = Vehicle.xmlSchema

    schema:setXMLSpecializationType("FAP_Pump");
    schema:register(XMLValueType.NODE_INDEX, "vehicle.fapPump.manureHose#hoseNode", "Manure Hose node");
	schema:register(XMLValueType.NODE_INDEX, "vehicle.fapPump.manureHose#jointNode", "Manure Hose JointNode");
	ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.fapPump.manureHose");

	schema:register(XMLValueType.NODE_INDEX, "vehicle.fapPump.hydraulicHose#hoseNode", "Hydraulic Hose node");
	schema:register(XMLValueType.NODE_INDEX, "vehicle.fapPump.hydraulicHose#jointNode", "Hydraulic Hose JointNode");
	ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.fapPump.hydraulicHose");

	schema:register(XMLValueType.NODE_INDEX, "vehicle.fapPump.supportArm#node", "Supportarm rootNode");
    schema:register(XMLValueType.NODE_INDEX, "vehicle.fapPump.supportArm#rotNode", "Supportarm rotNode");
	schema:register(XMLValueType.FLOAT, "vehicle.fapPump.supportArm#minTranslationZ", "");
	schema:register(XMLValueType.FLOAT, "vehicle.fapPump.supportArm#maxTranslationZ", "");
	schema:register(XMLValueType.FLOAT, "vehicle.fapPump.supportArm#zOffset", "");

	ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.fapPump.manureHose")

    schema:setXMLSpecializationType();

    local schemaSavegame = Vehicle.xmlSchemaSavegame
    schemaSavegame:register(XMLValueType.FLOAT, ("vehicles.vehicle(?).%s.fapPump#z"):format(modName), "The guidance line distance")
end

function FAP_Pump:onLoad(savegame)
	self.spec_fapPump = self[string.format("spec_%s.fapPump", modName)];
	local spec = self.spec_fapPump;
	spec.parrentVehicle = nil;
	spec.rootVehicleTrailerAttacher = nil;
	spec.rootVehicleBackImplementAttacher = nil;

	spec.manureHose = {};
	spec.manureHose.node = self.xmlFile:getValue("vehicle.fapPump.manureHose#hoseNode", nil, self.components, self.i3dMappings);
	spec.manureHose.jointNode = self.xmlFile:getValue("vehicle.fapPump.manureHose#jointNode", nil, self.components, self.i3dMappings);

	spec.manureHose.objectChanges = {};
	ObjectChangeUtil.loadObjectChangeFromXML(self.xmlFile, "vehicle.fapPump.manureHose", spec.manureHose.objectChanges, self.components, self)
	ObjectChangeUtil.setObjectChanges(spec.manureHose.objectChanges, false, self, self.setMovingToolDirty, true)

	spec.hydraulicHose = {};
	spec.hydraulicHose.node = self.xmlFile:getValue("vehicle.fapPump.hydraulicHose#hoseNode", nil, self.components, self.i3dMappings);
	spec.hydraulicHose.jointNode = self.xmlFile:getValue("vehicle.fapPump.hydraulicHose#jointNode", nil, self.components, self.i3dMappings);

	spec.hydraulicHose.objectChanges = {};
	ObjectChangeUtil.loadObjectChangeFromXML(self.xmlFile, "vehicle.fapPump.hydraulicHose", spec.manureHose.objectChanges, self.components, self)
	ObjectChangeUtil.setObjectChanges(spec.manureHose.objectChanges, false, self, self.setMovingToolDirty, true)

	spec.debugrefNode = nil;
	spec.supportArm = {};
	spec.supportArm.node = self.xmlFile:getValue("vehicle.fapPump.supportArm#node", nil, self.components, self.i3dMappings);
	spec.supportArm.rotNode = self.xmlFile:getValue("vehicle.fapPump.supportArm#rotNode", nil, self.components, self.i3dMappings);

	local _, _, z = getTranslation(spec.supportArm.node);
	spec.supportArm.minZ = self.xmlFile:getValue("vehicle.fapPump.supportArm#minTranslationZ") + z or 0;
	spec.supportArm.maxZ = self.xmlFile:getValue("vehicle.fapPump.supportArm#maxTranslationZ") + z or 0;
	spec.supportArm.zOffset = self.xmlFile:getValue("vehicle.fapPump.supportArm#zOffset") + z or 0;

	if savegame ~= nil and not savegame.resetVehicles then
		if spec.supportArm.node ~= nil then
			local x, y = getTranslation(spec.supportArm.node);
			local z = savegame.xmlFile:getValue(string.format("%s.%s.fapPump",savegame.key, modName) .. "#z");
			if z ~= nil then
				setTranslation(spec.supportArm.node, x, y, z);
			end;
		end;
	end;
end;

function FAP_Pump:onPostAttach()
	local rootVehicle = self:getRootVehicle();
	if rootVehicle ~= nil and rootVehicle.spec_attacherJoints ~= nil then
		local spec = self.spec_fapPump;

		-- Create many transform groups, so the vehicle has enough time to pair up.
		for i=0, 1000 do
			delete(createTransformGroup("getTimeTG"));
		end;

		for attacherJointIndex, attacherJoint in pairs(rootVehicle.spec_attacherJoints.attacherJoints) do
			if attacherJoint.jointType == AttacherJoints.JOINTTYPE_IMPLEMENT and getName(attacherJoint.jointTransform):lower():find("back") then
				spec.rootVehicleBackImplementAttacher = attacherJoint;
				if attacherJoint.topArm ~= nil and attacherJoint.topArm.rotationNode ~= nil then
					setVisibility(attacherJoint.topArm.rotationNode, false)
				else
					Logging.warning("[FAP_Pump] topArm or rotationNode missing in attacherJoint %s", getName(attacherJoint.jointTransform))
				end
			end;
			if attacherJoint.jointType == AttacherJoints.JOINTTYPE_TRAILER and getName(attacherJoint.jointTransform):lower():find("back") then
				spec.rootVehicleBackTrailerAttacherattacherJoint = attacherJoint;
				if attacherJoint.transNode ~= nil then
					setVisibility(attacherJoint.transNode, false)
				else
					Logging.warning("[FAP_Pump] transNode is nil for attacherJoint %s", getName(attacherJoint.jointTransform))
				end
			end;

			self:getSlurryBarrel(attacherJointIndex, attacherJoint);
		end;

		if spec.parrentVehicle ~= nil then
			local pV = spec.parrentVehicle;
			--Should we install a checker function to indicate when the slurry tank is not prepared?!
			if pV.canCoppleFapHose ~= nil and pV:canCoppleFapHose() then
				self:coppleManureHoseToSlurryTank();
			end;
		end;

		self:updateSupportFrame();
	end;
end;

function FAP_Pump:onPreDetach()
	local spec = self.spec_fapPump;

	if spec.rootVehicleBackImplementAttacher ~= nil then
		if spec.rootVehicleBackImplementAttacher.topArm ~= nil and spec.rootVehicleBackImplementAttacher.topArm.rotationNode ~= nil then
			setVisibility(spec.rootVehicleBackImplementAttacher.topArm.rotationNode, true);
		end;
		if spec.rootVehicleBackTrailerAttacherattacherJoint ~= nil and spec.rootVehicleBackTrailerAttacherattacherJoint.transNode ~= nil then
			setVisibility(spec.rootVehicleBackTrailerAttacherattacherJoint.transNode, true);
		end;
		spec.rootVehicleBackImplementAttacher = nil;
		spec.rootVehicleBackTrailerAttacherattacherJoint = nil;
	end;
end;


function FAP_Pump:onPostDetach()
	local spec = self.spec_fapPump;

	spec.rootVehicleTrailerAttacher = nil;

	self:decoppleManureHoseToSlurryTank();
	setTranslation(spec.supportArm.node, 0, 0, spec.supportArm.maxZ);
end;

function FAP_Pump:coppleManureHoseToSlurryTank()
	local spec = self.spec_fapPump;

	if spec ~= nil and spec.parrentVehicle ~= nil then
		local specManureBarrel = spec.parrentVehicle.spec_fapConnectedVehicle;
		if specManureBarrel ~= nil then
			if specManureBarrel.fapManureReference ~= nil then
				if specManureBarrel.fapManureReference.nodeAtDecoupled ~= nil then
					setVisibility(specManureBarrel.fapManureReference.nodeAtCoupled, true);
				end;
				if specManureBarrel.fapManureReference.nodeAtDecoupled ~= nil then
					setVisibility(specManureBarrel.fapManureReference.nodeAtDecoupled, false);
				end;
			end;

			if specManureBarrel.fapHydraulicReference ~= nil then
				if specManureBarrel.fapHydraulicReference.nodeAtCoupled ~= nil then
					setVisibility(specManureBarrel.fapHydraulicReference.nodeAtCoupled, true);
				end;
				if specManureBarrel.fapHydraulicReference.nodeAtDecoupled ~= nil then
					setVisibility(specManureBarrel.fapHydraulicReference.nodeAtDecoupled, false);
				end;
			end;
		end;

		local manureRefPoint = spec.parrentVehicle:getFapManureReferenceNode();
		local hydRefPoint = spec.parrentVehicle:getFapHydraulicReferenceNode();

		if manureRefPoint ~= nil then
			ObjectChangeUtil.setObjectChanges(spec.manureHose.objectChanges, true, self, self.setMovingToolDirty, true)
			link(manureRefPoint, spec.manureHose.jointNode);
			local x, y, z = 0, 0, 0;

			local rY = 0;
			if specManureBarrel.fantManureAdapterNode ~= nil then
				rY = MathUtil.degToRad(180);
			else
				if specManureBarrel.fapManureReference.nodeOffset ~= nil then
					x, y, z = unpack(specManureBarrel.fapManureReference.nodeOffset);
				end;
			end;

			--setTranslation(spec.manureHose.jointNode, x, y, z);
			setTranslation(spec.manureHose.jointNode, 0, 0, 0);
			setRotation(spec.manureHose.jointNode, 0, rY, 0);

			self:removeAllSubWashableNodes(spec.manureHose.node);
			spec.parrentVehicle:addAllSubWashableNodes(spec.manureHose.node);
		end;

		if hydRefPoint ~= nil then
			ObjectChangeUtil.setObjectChanges(spec.hydraulicHose.objectChanges, true, self, self.setMovingToolDirty, true)
			link(hydRefPoint, spec.hydraulicHose.jointNode);
			--if specManureBarrel.fapHydraulicReference.nodeOffset ~= nil then
			--	setTranslation(spec.hydraulicHose.jointNode, unpack(specManureBarrel.fapHydraulicReference.nodeOffset));
			--else
				setTranslation(spec.hydraulicHose.jointNode, 0, 0, 0);
			--end;
			setRotation(spec.hydraulicHose.jointNode, 0, 0, 0);
			self:removeAllSubWashableNodes(spec.hydraulicHose.node);
			spec.parrentVehicle:addAllSubWashableNodes(spec.hydraulicHose.node);
		end;
	end;
end;

function FAP_Pump:saveToXMLFile(xmlFile, key, usedModNames)
	local spec = self.spec_fapPump;
	if spec.supportArm.node ~= nil then
		local _, _, z = getTranslation(spec.supportArm.node);
		xmlFile:setValue(key .. "#z", z);
	end;
end

function FAP_Pump:decoppleManureHoseToSlurryTank()
	local spec = self.spec_fapPump;
	if spec ~= nil and spec.parrentVehicle ~= nil then
		--ObjectChangeUtil.setObjectChanges(spec.manureHose.objectChanges, false);
		ObjectChangeUtil.setObjectChanges(spec.manureHose.objectChanges, false, self, self.setMovingToolDirty, true)
		unlink(spec.manureHose.jointNode);
		link(self.rootNode, spec.manureHose.jointNode);
		spec.parrentVehicle:removeAllSubWashableNodes(spec.manureHose.node);
		self:addAllSubWashableNodes(spec.manureHose.node);
		--ObjectChangeUtil.setObjectChanges(spec.hydraulicHose.objectChanges, false);
		ObjectChangeUtil.setObjectChanges(spec.hydraulicHose.objectChanges, false, self, self.setMovingToolDirty, true)
		unlink(spec.hydraulicHose.jointNode);
		link(self.rootNode, spec.hydraulicHose.jointNode);
		spec.parrentVehicle:removeAllSubWashableNodes(spec.hydraulicHose.node);
		self:addAllSubWashableNodes(spec.hydraulicHose.node);
		local specManureBarrel = spec.parrentVehicle.spec_fapConnectedVehicle;
		if specManureBarrel ~= nil then
			if specManureBarrel.fapManureReference ~= nil then
				if specManureBarrel.fapManureReference.nodeAtCoupled ~= nil then
					setVisibility(specManureBarrel.fapManureReference.nodeAtCoupled, false);
				end;
				if specManureBarrel.fapManureReference.nodeAtDecoupled ~= nil then
					setVisibility(specManureBarrel.fapManureReference.nodeAtDecoupled, true);
				end;
			end;

			if specManureBarrel.fapHydraulicReference ~= nil then
				if specManureBarrel.fapHydraulicReference.nodeAtCoupled ~= nil then
					setVisibility(specManureBarrel.fapHydraulicReference.nodeAtCoupled, false);
				end;
				if specManureBarrel.fapHydraulicReference.nodeAtDecoupled ~= nil then
					setVisibility(specManureBarrel.fapHydraulicReference.nodeAtDecoupled, true);
				end;
			end;
		end;

		spec.parrentVehicle:setFapPump(nil);
		spec.parrentVehicle = nil;
	end;
end;

function FAP_Pump:getSlurryBarrel(attacherJointIndex, attacherJoint)
	if attacherJoint.jointType == AttacherJoints.JOINTTYPE_TRAILERLOW then
		local implement = self:getRootVehicle():getImplementByJointDescIndex(attacherJointIndex);
		if implement ~= nil then
			local spec = self.spec_fapPump;
			spec.parrentVehicle = implement.object;
			if spec.parrentVehicle.setFapPump ~= nil then
				spec.parrentVehicle:setFapPump(self);
			end;
			return spec.parrentVehicle;
		end;
	end;
	return;
end;

function FAP_Pump:updateSupportFrame()
	local spec = self.spec_fapPump;
	
	assert(spec.supportArm.node ~= nil, "spec.supportArm.node ~= nil");
	assert(spec.supportArm.rotNode ~= nil, "spec.supportArm.rotNode ~= nil");
	assert(spec.rootVehicleBackTrailerAttacherattacherJoint ~= nil, "spec.rootVehicleBackTrailerAttacherattacherJoint ~= nil");

	local getDistance = function()
		local x, _, z = getWorldTranslation(spec.supportArm.rotNode);
		local x1, _, z1 = getWorldTranslation(spec.rootVehicleBackTrailerAttacherattacherJoint.transNode);
		local dx, dz = x1-x, z1-z;
		return dx*dx + dz*dz;
	end;
	print(tostring(spec.rootVehicleBackTrailerAttacherattacherJoint.transNode) .." : " .. tostring(spec.supportArm.node))
	local distance = calcDistanceFrom(spec.rootVehicleBackTrailerAttacherattacherJoint.transNode, spec.supportArm.node);
	local lastDistance = distance;
	local c = 0;
	while c <= FAP_Pump.MOVE_STEPS do
		translate(spec.supportArm.node, 0, 0, spec.supportArm.minZ/FAP_Pump.MOVE_STEPS);
		print(tostring(spec.rootVehicleBackTrailerAttacherattacherJoint.transNode) .." :1 " .. tostring(spec.supportArm.node))
		distance = calcDistanceFrom(spec.rootVehicleBackTrailerAttacherattacherJoint.transNode, spec.supportArm.node);
		if lastDistance < distance then
			break;
		end;
		lastDistance = distance;
		c = c + 1;
	end;

	if getDistance() > 1.13 then
		if self:getRootVehicle() ~= nil and self:getRootVehicle().spec_enterable:getIsEntered()
			and self:getRootVehicle().spec_enterable:getControllerName() == g_currentMission.missionInfo.playerStyle.playerName then
			g_currentMission:showBlinkingWarning(g_i18n:getText("fap_inappropriate"), 5000);
		end;
		setVisibility(getParent(spec.supportArm.node), false);
		translate(spec.supportArm.node, 0, 0, spec.supportArm.minZ*-1)
	end;
end;

function FAP_Pump:addFillUnitFillLevel(superFunc, farmId, fillUnitIndex, fillLevelDelta, fillType, toolType, fillInfo)
	local spec = self.spec_fapPump;
	if spec.parrentVehicle ~= nil then
		return spec.parrentVehicle:addFillUnitFillLevel(farmId, fillUnitIndex, fillLevelDelta, fillType, toolType, fillInfo);
	end;
	return superFunc(self, farmId, fillUnitIndex, fillLevelDelta, fillType, toolType, fillInfo);
end;