ComponentConfigurations = {
	MOD_NAME = g_currentModName,
	SPEC_NAME = g_currentModName .. ".componentConfigurations",
	SPEC_TABLE_NAME = "spec_" .. g_currentModName .. ".componentConfigurations",
	prerequisitesPresent = function (specializations)
		return true
	end,
	initSpecialization = function ()
		if g_iconGenerator == nil and g_configurationManager.configurations.usedComponents == nil then
			g_configurationManager:addConfigurationType("usedComponents", g_i18n:getText("shop_configuration"), "base", nil, _, _, ConfigurationUtil.SELECTOR_MULTIOPTION)
		end

		local schema = Vehicle.xmlSchema

		schema:setXMLSpecializationType("ComponentConfigurations")
		ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.base.usedComponentsConfigurations.usedComponentsConfiguration(?)")
		schema:register(XMLValueType.VECTOR_N, "vehicle.base.usedComponentsConfigurations.usedComponentsConfiguration(?)#usedIndices", "Indices of components that are loaded")
		schema:register(XMLValueType.VECTOR_N, "vehicle.base.usedComponentsConfigurations.usedComponentsConfiguration(?)#usedJointIndices", "Indices of component joints that are loaded")
		schema:setXMLSpecializationType()
	end
}

function ComponentConfigurations.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "getIsComponentAvailable", ComponentConfigurations.getIsComponentAvailable)
	SpecializationUtil.registerFunction(vehicleType, "getIsComponentJointAvailable", ComponentConfigurations.getIsComponentJointAvailable)
end

function ComponentConfigurations.registerOverwrittenFunctions(vehicleType)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "addToPhysics", ComponentConfigurations.addToPhysics)
	SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadWheelFromXML", ComponentConfigurations.loadWheelFromXML)
end

function ComponentConfigurations.registerEventListeners(vehicleType)
	SpecializationUtil.registerEventListener(vehicleType, "onPreLoad", ComponentConfigurations)
end

function ComponentConfigurations:onPreLoad(savegame)
	local spec = self[ComponentConfigurations.SPEC_TABLE_NAME]
	local configurationId = Utils.getNoNil(self.configurations.usedComponents, 1)
	local configKey = string.format("vehicle.base.usedComponentsConfigurations.usedComponentsConfiguration(%d)", configurationId - 1)

	ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.base.usedComponentsConfigurations.usedComponentsConfiguration", configurationId, self.components, self)

	spec.usedIndices = self.xmlFile:getValue(configKey .. "#usedIndices", nil, true)
	spec.usedJointIndices = self.xmlFile:getValue(configKey .. "#usedJointIndices", nil, true)
	spec.loadCustomComponents = self.xmlFile:getString(configKey .. "#usedIndices") ~= nil
	spec.loadCustomComponentJoints = self.xmlFile:getString(configKey .. "#usedJointIndices") ~= nil
end

function ComponentConfigurations:getIsComponentAvailable(componentIndex)
	local spec = self[ComponentConfigurations.SPEC_TABLE_NAME]

	if spec.loadCustomComponents then
		for j = 1, #spec.usedIndices do
			if componentIndex == spec.usedIndices[j] then
				return true
			end
		end

		return false
	end

	return true
end

function ComponentConfigurations:getIsComponentJointAvailable(componentJointIndex)
	local spec = self[ComponentConfigurations.SPEC_TABLE_NAME]

	if spec.loadCustomComponentJoints then
		for j = 1, #spec.usedJointIndices do
			if componentJointIndex == spec.usedJointIndices[j] then
				return true
			end
		end

		return false
	end

	return true
end

function ComponentConfigurations:addToPhysics(superFunc)
	if not self.isAddedToPhysics then
		local lastMotorizedNode = nil

		for componentIndex, component in pairs(self.components) do
			if self:getIsComponentAvailable(componentIndex) then
				addToPhysics(component.node)
			else
				removeFromPhysics(component.node)

				component.initialTranslationOffset = {
					localToLocal(component.node, self.rootNode, 0, 0, 0)
				}
				component.initialRotationOffset = {
					localRotationToLocal(component.node, self.rootNode, 0, 0, 0)
				}
			end

			if component.motorized then
				if lastMotorizedNode ~= nil and self.isServer then
					addVehicleLink(lastMotorizedNode, component.node)
				end

				lastMotorizedNode = component.node
			end
		end

		self.isAddedToPhysics = true

		if self.isServer then
			for jointDescIndex, jointDesc in pairs(self.componentJoints) do
				if self:getIsComponentJointAvailable(jointDescIndex) then
					self:createComponentJoint(self.components[jointDesc.componentIndices[1]], self.components[jointDesc.componentIndices[2]], jointDesc)
				end
			end

			addWakeUpReport(self.rootNode, "onVehicleWakeUpCallback", self)
		end

		for _, collisionPair in pairs(self.collisionPairs) do
			setPairCollision(collisionPair.component1.node, collisionPair.component2.node, collisionPair.enabled)
		end

		self:setMassDirty()
	end

	superFunc(self)

	return true
end

function ComponentConfigurations:loadWheelFromXML(superFunc, xmlFile, configKey, wheelKey, wheel)
	local repr = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#repr", nil, self.components, self.i3dMappings)

	if repr ~= nil then
		local componentNode = self:getParentComponent(repr)

		for componentIndex, component in pairs(self.components) do
			if component.node == componentNode and not self:getIsComponentAvailable(componentIndex) then
				return false
			end
		end
	end

	return superFunc(self, xmlFile, configKey, wheelKey, wheel)
end

Vehicle.saveToXMLFile = Utils.overwrittenFunction(Vehicle.saveToXMLFile, function (self, superFunc, xmlFile, key, usedModNames)
	if self[ComponentConfigurations.SPEC_TABLE_NAME] ~= nil then
		for componentIndex, component in pairs(self.components) do
			if not self:getIsComponentAvailable(componentIndex) then
				setWorldTranslation(component.node, localToWorld(self.rootNode, unpack(component.initialTranslationOffset)))
				setWorldRotation(component.node, localRotationToWorld(self.rootNode, unpack(component.initialRotationOffset)))
			end
		end
	end

	superFunc(self, xmlFile, key, usedModNames)
end)