RLSettings = {}
local modDirectory = g_currentModDirectory
local modName = g_currentModName
local modSettingsDirectory = g_currentModSettingsDirectory

local modDirectoryPath = string.split(modDirectory, "/")
local baseDirectory = ""

for i = 1, #modDirectoryPath - 2 do

	baseDirectory = baseDirectory .. (i == 1 and "" or "/") .. modDirectoryPath[i]

end

g_gui:loadProfiles(modDirectory .. "gui/guiProfiles.xml")


function RLSettings.onClickTagColour()

	EarTagColourPickerDialog.show()

end


function RLSettings.onClickExportCSV()

	local file = io.open(modSettingsDirectory .. "animals.csv", "w")

	file:write("Type,Subtype,Country,Farm Id,Unique Id,Age,Health,Weight,Value,Value / kg,Pregnant,Expected Offspring,Lactating,Food,Water,Straw,Product,Manure,Liquid Manure")

	local husbandrySystem = g_currentMission.husbandrySystem
	local animalSystem = g_currentMission.animalSystem

	for _, placeable in pairs(husbandrySystem.placeables) do

		local animals = placeable:getClusters()

		for _, animal in pairs(animals) do

			local hasMonitor = animal.monitor.active or animal.monitor.removed

			local foodInput = animal:getInput("food") * 24
			local waterInput = animal:getInput("water") * 24
			local strawInput = animal:getInput("straw") * 24
			local manureOutput = animal:getOutput("manure") * 24
			local liquidManureOutput = animal:getOutput("liquidManure") * 24
			local milkOutput = animal:getOutput("milk") * 24
			local palletsOutput = animal:getOutput("pallets") * 24

			local productOutput = milkOutput > palletsOutput and milkOutput or palletsOutput

			local value = animal:getSellPrice()
			local valuePerKg = hasMonitor and (value / animal.weight) or "no monitor"
			
			local expectedOffspring = animal.pregnancy ~= nil and animal.pregnancy.pregnancies ~= nil and #animal.pregnancy.pregnancies or 0

			file:write(string.format("\n%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", animalSystem.types[animal.animalTypeIndex].name, animal.subType, RLConstants.AREA_CODES[animal.birthday.country].code, animal.farmId, animal.uniqueId, animal.age, hasMonitor and animal.health or "no monitor", hasMonitor and animal.weight or "no monitor", value, valuePerKg, animal.isPregnant and "yes" or "no", expectedOffspring, (hasMonitor and (animal.isLactating and "yes" or "no") or "no monitor"), hasMonitor and foodInput or "no monitor", hasMonitor and waterInput or "no monitor", hasMonitor and strawInput or "no monitor", hasMonitor and productOutput or "no monitor", hasMonitor and manureOutput or "no monitor", hasMonitor and liquidManureOutput or "no monitor"))

		end

	end

	file:close()

	InfoDialog.show(modSettingsDirectory .. "animals.csv")

end


local function getFilesRecursively(path, parent)

	local files = Files.new(path).files

	for _, file in pairs(files) do

		if file.isDirectory then
		
			table.insert(parent.folders, { ["folders"] = {}, ["files"] = {}, ["name"] = file.filename, ["path"] = file.path })
			getFilesRecursively(file.path, parent.folders[#parent.folders])
			continue

		end

		local name = file.filename

		if #name >= 4 and string.sub(name, #name - 3) == ".xml" then table.insert(parent.files, { ["name"] = name, ["valid"] = true }) end

	end

end


function RLSettings.onClickChangeAnimalsXML()

	local files = { { ["folders"] = {}, ["files"] = {}, ["name"] = baseDirectory, ["path"] = baseDirectory } }

	getFilesRecursively(baseDirectory, files[1])

	FileExplorerDialog.show(files, baseDirectory, RLSettings.onFileExplorerCallback)

end


function RLSettings.onFileExplorerCallback(path)

	RLSettings.animalsXMLPath = path

end


-- Render order is set by setting.index; both the legacy in-game settings
-- page and the RL Tabbed Menu Settings -> General subtab walk this table
-- in index order. Sections (1..17): Mortality (1-2), Health & Disease (3-4),
-- Husbandry & Economy (5-6), Custom Animals (7-8), Message Log (9-10),
-- Display Preferences (11-14), Tools & Admin (15-17).
RLSettings.SETTINGS = {

	["deathEnabled"] = {
		["index"] = 1,
		["adminOnly"] = true,
		["type"] = "BinaryOption",
		["dynamicTooltip"] = true,
		["default"] = 2,
		["binaryType"] = "offOn",
		["values"] = { false, true },
		["callback"] = Animal.onSettingChanged
	},

	["accidentsChance"] = {
		["index"] = 2,
		["adminOnly"] = true,
		["type"] = "MultiTextOption",
		["default"] = 11,
		["valueType"] = "float",
		["values"] = { 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0 },
		["callback"] = Animal.onSettingChanged,
		["dependancy"] = {
			["name"] = "deathEnabled",
			["state"] = 2
		}
	},

	["diseasesEnabled"] = {
		["index"] = 3,
		["adminOnly"] = true,
		["type"] = "BinaryOption",
		["dynamicTooltip"] = true,
		["default"] = 2,
		["binaryType"] = "offOn",
		["values"] = { false, true },
		["callback"] = DiseaseManager.onSettingChanged
	},

	["diseasesChance"] = {
		["index"] = 4,
		["adminOnly"] = true,
		["type"] = "MultiTextOption",
		["default"] = 4,
		["valueType"] = "float",
		["values"] = { 0.25, 0.5, 0.75, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5 },
		["callback"] = DiseaseManager.onSettingChanged,
		["dependancy"] = {
			["name"] = "diseasesEnabled",
			["state"] = 2
		}
	},

	["foodScale"] = {
		["index"] = 5,
		["adminOnly"] = true,
		["type"] = "MultiTextOption",
		["default"] = 2,
		["valueType"] = "float",
		["values"] = { 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5 },
		["callback"] = RealisticLivestock_PlaceableHusbandryFood.onSettingChanged
	},

	["maxDealerAnimals"] = {
		["index"] = 6,
		["adminOnly"] = true,
		["type"] = "MultiTextOption",
		["default"] = 4,
		["valueType"] = "int",
		["values"] = { 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200 },
		["callback"] = AnimalSystem.onSettingChanged
	},

	["useCustomAnimals"] = {
		["index"] = 7,
		["adminOnly"] = true,
		["type"] = "BinaryOption",
		["dynamicTooltip"] = true,
		["default"] = 1,
		["binaryType"] = "offOn",
		["values"] = { false, true },
		["callback"] = RLSettings.onSettingChanged
	},

	["animalsXML"] = {
		["index"] = 8,
		["adminOnly"] = true,
		["type"] = "Button",
		["ignore"] = true,
		["callback"] = RLSettings.onClickChangeAnimalsXML,
		["dependancy"] = {
			["name"] = "useCustomAnimals",
			["state"] = 2
		}
	},

	["messageSummary"] = {
		["index"] = 9,
		["adminOnly"] = true,
		["type"] = "BinaryOption",
		["dynamicTooltip"] = true,
		["default"] = 1,  -- Individual (current behavior) as default
		["binaryType"] = "offOn",
		["values"] = { false, true },
		["callback"] = RLMessageAggregator.onSettingChanged
	},

	["maxNumMessages"] = {
		["index"] = 10,
		["adminOnly"] = true,
		["type"] = "MultiTextOption",
		["default"] = 5,
		["valueType"] = "int",
		["values"] = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 3500, 4000, 4500, 5000 },
		["callback"] = RealisticLivestock_PlaceableHusbandryAnimals.onSettingChanged
	},

	["geneticsDisplay"] = {
		["index"] = 11,
		["adminOnly"] = true,
		["type"] = "MultiTextOption",
		["default"] = 1,
		["values"] = { 1, 2, 3 }
	},

	["geneticsPosition"] = {
		["index"] = 12,
		["adminOnly"] = true,
		["type"] = "BinaryOption",
		["default"] = 1,
		["values"] = { 1, 2 }
	},

	["sortByGenetics"] = {
		["index"] = 13,
		["adminOnly"] = true,
		["type"] = "BinaryOption",
		["dynamicTooltip"] = true,
		["default"] = 1,
		["binaryType"] = "offOn",
		["values"] = { false, true }
	},

	["tagColour"] = {
		["index"] = 14,
		["adminOnly"] = true,
		["type"] = "Button",
		["ignore"] = true,
		["callback"] = RLSettings.onClickTagColour
	},

	["exportCSV"] = {
		["index"] = 15,
		["adminOnly"] = true,
		["type"] = "Button",
		["ignore"] = true,
		["callback"] = RLSettings.onClickExportCSV
	},

	["resetDealer"] = {
		["index"] = 16,
		["type"] = "Button",
		["ignore"] = true,
		["adminOnly"] = true,
		["callback"] = AnimalSystem.onClickResetDealer
	},

	["resetAIAnimals"] = {
		["index"] = 17,
		["type"] = "Button",
		["ignore"] = true,
		["adminOnly"] = true,
		["callback"] = AnimalSystem.onClickResetAIAnimals
	}

}

RLSettings.BinaryOption = nil
RLSettings.MultiTextOption = nil
RLSettings.Button = nil


function RLSettings.loadFromXMLFile()

	if g_currentMission.missionInfo == nil or g_currentMission.missionInfo.savegameDirectory == nil then return end

	local savegameDir = g_currentMission.missionInfo.savegameDirectory

	-- Try new filename first, fall back to old filename (migration support)
	local path = savegameDir .. "/rm_RlSettings.xml"
	local xmlFile = XMLFile.loadIfExists("rm_RlSettings", path)
	local rootKey = "rm_RlSettings"

	if xmlFile == nil then
		-- Fall back to legacy filename
		path = savegameDir .. "/rlSettings.xml"
		xmlFile = XMLFile.loadIfExists("rlSettings", path)
		rootKey = "settings"
	end

	if xmlFile ~= nil then

		local key = rootKey

		for name, setting in pairs(RLSettings.SETTINGS) do

			if setting.ignore then continue end

			setting.state = xmlFile:getInt(key .. "." .. name .. "#value", setting.default)

			if setting.state > #setting.values then setting.state = #setting.values end

			if name == "useCustomAnimals" and setting.state == 2 then RLSettings.animalsXMLPath = xmlFile:getString(key .. ".useCustomAnimals#path") end

		end

		xmlFile:delete()

	end

end


--- Deferred filter-load entry point. Called from AnimalSystem:loadFromXMLFile
--- after the savegame's animal/aiAnimals data has loaded, which is the
--- earliest point at which the AnimalType registry is populated and
--- RLFilterSerialization.animalTypeNameToIndex can resolve filter scope
--- strings ("CHICKEN" / "COW" / ...) to indices. If we load filters at
--- RLSettings.initialize time (loadMapData), every filter falls through to
--- global scope and the cycle pulls every saved filter regardless of the
--- active animal type. Server-only - matches the saveToXMLFile gate.
function RLSettings.loadFiltersFromXMLFile()

	if g_currentMission.missionInfo == nil or g_currentMission.missionInfo.savegameDirectory == nil then return end
	if g_server == nil then return end

	if g_rlFilterService == nil then
		Log:warning("RLSettings.loadFiltersFromXMLFile: g_rlFilterService is nil; skipping filter load (load-order regression?)")
		return
	end

	local path = g_currentMission.missionInfo.savegameDirectory .. "/rm_RlSettings.xml"
	local xmlFile = XMLFile.loadIfExists("rm_RlSettings", path)
	if xmlFile == nil then
		Log:debug("RLSettings.loadFiltersFromXMLFile: no rm_RlSettings.xml on disk; filter registry stays empty")
		return
	end

	g_rlFilterService:loadFromXMLFile(xmlFile, RLFilterService.XML_BASE_KEY)
	xmlFile:delete()
end


--- Deferred rule-load entry point (M-Service S2). Sibling of
--- loadFiltersFromXMLFile: server-only, GUI-free, called from
--- AnimalSystem:loadFromXMLFile (NOT from the GUI-coupled RLSettings.initialize,
--- which is reached only through the in-game-menu builder). Rules carry no
--- animalType, so the AnimalType-registry timing reason that drives the filter
--- load does not apply here - rules just need a GUI-free, server-side,
--- once-per-load savegame hook, which AnimalSystem:loadFromXMLFile already is.
--- Re-opens rm_RlSettings.xml once more so the rule registry owns its own error
--- boundary (a corrupt filters subtree cannot abort rule load, or vice versa).
function RLSettings.loadRulesFromXMLFile()

	if g_currentMission.missionInfo == nil or g_currentMission.missionInfo.savegameDirectory == nil then return end
	if g_server == nil then return end

	if g_rlHerdsmanRuleService == nil then
		Log:warning("RLSettings.loadRulesFromXMLFile: g_rlHerdsmanRuleService is nil; skipping rule load (load-order regression?)")
		return
	end

	local path = g_currentMission.missionInfo.savegameDirectory .. "/rm_RlSettings.xml"
	local xmlFile = XMLFile.loadIfExists("rm_RlSettings", path)
	if xmlFile == nil then
		-- No file: clear so a previously-populated singleton cannot leak across a
		-- second savegame load in the same session (g_rlHerdsmanRuleService is an
		-- eager source-time singleton that persists between loads). The non-nil
		-- xmlFile path clears via the service's own loadFromXMLFile; this branch
		-- short-circuits before that, so clear here to honour "registry empty".
		g_rlHerdsmanRuleService:clear()
		Log:debug("RLSettings.loadRulesFromXMLFile: no rm_RlSettings.xml on disk; rule registry cleared (empty)")
		return
	end

	g_rlHerdsmanRuleService:loadFromXMLFile(xmlFile, RLHerdsmanRuleService.XML_BASE_KEY)
	xmlFile:delete()
end


function RLSettings.saveToXMLFile(name, state)

	if RLSettings.isSaving or g_currentMission.missionInfo == nil or g_currentMission.missionInfo.savegameDirectory == nil then return end

	if g_server ~= nil then

		RLSettings.isSaving = true

		-- Always save to new filename with versioning
		local path = g_currentMission.missionInfo.savegameDirectory .. "/rm_RlSettings.xml"
		local xmlFile = XMLFile.create("rm_RlSettings", path, "rm_RlSettings")

		if xmlFile ~= nil then

			-- Add version attribute for future migrations
			xmlFile:setInt("rm_RlSettings#version", 1)

			for settingName, setting in pairs(RLSettings.SETTINGS) do

				if setting.ignore then continue end
				xmlFile:setInt("rm_RlSettings." .. settingName .. "#value", setting.state or setting.default)

				if settingName == "useCustomAnimals" and setting.state == 2 and RLSettings.animalsXMLPath ~= nil then xmlFile:setString("rm_RlSettings.useCustomAnimals#path", RLSettings.animalsXMLPath) end

			end

			-- Saveable filters share rm_RlSettings.xml as their on-disk
			-- home. Server-only by virtue of the surrounding g_server
			-- branch above.
			if g_rlFilterService ~= nil then
				g_rlFilterService:saveToXMLFile(xmlFile, RLFilterService.XML_BASE_KEY)
			else
				Log:warning("RLSettings.saveToXMLFile: g_rlFilterService is nil; skipping filter save (load-order regression?)")
			end

			-- Herdsman rules share the same rm_RlSettings.xml file (their own
			-- subtree under RLHerdsmanRuleService.XML_BASE_KEY, M-Service S2).
			-- Symmetric with the filter save above; server-only.
			if g_rlHerdsmanRuleService ~= nil then
				g_rlHerdsmanRuleService:saveToXMLFile(xmlFile, RLHerdsmanRuleService.XML_BASE_KEY)
			else
				Log:warning("RLSettings.saveToXMLFile: g_rlHerdsmanRuleService is nil; skipping rule save (load-order regression?)")
			end

			local saved = xmlFile:save(false, true)

			xmlFile:delete()

		end

	end

	RLSettings.isSaving = false

end


function RLSettings.initialize()

	if g_server ~= nil then RLSettings.loadFromXMLFile() end

	local settingsPage = g_inGameMenu.pageSettings
	local scrollPanel = settingsPage.gameSettingsLayout

	local sectionHeader, binaryOptionElement, multiOptionElement, buttonElement

	for _, element in pairs(scrollPanel.elements) do

		if element.name == "sectionHeader" and sectionHeader == nil then sectionHeader = element:clone(scrollPanel) end

		if element.typeName == "Bitmap" then

			if element.elements[1].typeName == "BinaryOption" and binaryOptionElement == nil then binaryOptionElement = element end

			if element.elements[1].typeName == "MultiTextOption" and multiOptionElement == nil then multiOptionElement = element end

			if element.elements[1].typeName == "Button" and buttonElement == nil then buttonElement = element end

		end

		if multiOptionElement and binaryOptionElement and sectionHeader and buttonElement then break end	

	end

	if multiOptionElement == nil or binaryOptionElement == nil or sectionHeader == nil or buttonElement == nil then return end

	RLSettings.BinaryOption = binaryOptionElement
	RLSettings.MultiTextOption  = multiOptionElement
	RLSettings.Button = buttonElement

	local prefix = "rl_settings_"

	sectionHeader:setText(g_i18n:getText("rl_settings"))

	local maxIndex = 0

	for _, setting in pairs(RLSettings.SETTINGS) do maxIndex = maxIndex < setting.index and setting.index or maxIndex end

	for i = 1, maxIndex do

		for name, setting in pairs(RLSettings.SETTINGS) do

			if setting.index ~= i then continue end
	
			setting.state = setting.state or setting.default
			local template = RLSettings[setting.type]:clone(scrollPanel)
			local settingsPrefix = "rl_settings_" .. name .. "_"
			template.id = nil
		
			for _, element in pairs(template.elements) do

				if element.typeName == "Text" then
					element:setText(g_i18n:getText(settingsPrefix .. "label"))
					element.id = nil
				end

				if element.typeName == setting.type then

					if setting.type == "Button" then
						element:setText(g_i18n:getText(settingsPrefix .. "text"))
						element:applyProfile("rl_settingsButton")
						element.isAlwaysFocusedOnOpen = false
						element.focused = false
					else

						local texts = {}

						if setting.binaryType == "offOn" then
							texts[1] = g_i18n:getText("rl_settings_off")
							texts[2] = g_i18n:getText("rl_settings_on")
						else

							for i, value in pairs(setting.values) do

								if setting.valueType == "int" then
									texts[i] = tostring(value)
								elseif setting.valueType == "float" then
									texts[i] = string.format("%.0f%%", value * 100)
								else
									texts[i] = g_i18n:getText(settingsPrefix .. "texts_" .. i)
								end
							end

						end

						element:setTexts(texts)
						element:setState(setting.state)

						if setting.dynamicTooltip then
							element.elements[1]:setText(g_i18n:getText(settingsPrefix .. "tooltip_" .. setting.state))
						else
							element.elements[1]:setText(g_i18n:getText(settingsPrefix .. "tooltip"))
						end

					end

					element.id = "rls_" .. name
					element.onClickCallback = RLSettings.onSettingChanged

					setting.element = element

					if setting.dependancy then
						local dependancy = RLSettings.SETTINGS[setting.dependancy.name]
						if dependancy ~= nil and dependancy.element ~= nil then element:setDisabled(dependancy.state ~= setting.dependancy.state) end
					end

				end
			
			end

		end

	end

end


--- Apply a state change to a stateful setting.
--- Single write path shared by the legacy in-game settings page (via
--- onSettingChanged) and the RL Tabbed Menu Settings -> General subtab
--- (via RLMenuSettingsFrame's row click handlers). The order is:
---   (1) run the per-setting callback with the NEW value (callback sees
---       newState before setting.state has been written - this preserves
---       legacy onSettingChanged behaviour, where callbacks read the new
---       value via the second arg, not via setting.state)
---   (2) write setting.state = newState
---   (3) cascade-disable children of this setting on legacy element refs
---   (4) refresh the legacy element's dynamic tooltip if applicable
---
--- The new RL menu page maintains its OWN element registry and runs ITS
--- OWN cascade after this call returns; this function does not know
--- about the new frame's elements (and must not - setting.element is
--- legacy-only by design until the systemic single-element coupling is
--- refactored in a follow-up).
---
--- Action rows (setting.ignore == true) skip this path; their callers
--- invoke setting.callback directly.
---
--- Pre-existing dormant MP broadcast + per-setting save lines (kept
--- commented in onSettingChanged) are NOT activated here. Mid-session
--- single-setting MP sync remains a follow-up.
--- @param name string The key of the setting in RLSettings.SETTINGS
--- @param newState number 1-based state index into setting.values
--- @return boolean true on successful state write; false if the setting is
---                 unknown (caller passed a bad name) OR if it is an action
---                 row (setting.ignore == true) - both failure modes are
---                 conflated under the same false return because callers
---                 (legacy onSettingChanged + new onClickGeneralSetting)
---                 never need to distinguish them.
function RLSettings.applyChange(name, newState)

	local setting = RLSettings.SETTINGS[name]

	if setting == nil then
		Log:warning("RLSettings.applyChange: unknown setting '%s'", tostring(name))
		return false
	end

	if setting.ignore then
		Log:trace("RLSettings.applyChange: '%s' is an ignored/action row, skipping state write", name)
		return false
	end

	Log:debug("RLSettings.applyChange: name='%s' newState=%s (was=%s)",
		name, tostring(newState), tostring(setting.state))

	if setting.callback then setting.callback(name, setting.values[newState]) end

	setting.state = newState

	-- Cascade to children whose dependancy parent is this setting.
	-- Operates on legacy element refs (setting.element); the new RL menu
	-- page runs an equivalent cascade on its own controls registry.
	for childName, s in pairs(RLSettings.SETTINGS) do
		if s.dependancy and s.dependancy.name == name and s.element ~= nil then
			local shouldDisable = (s.dependancy.state ~= newState)
			Log:trace("RLSettings.applyChange: cascading -> '%s' setDisabled(%s)",
				childName, tostring(shouldDisable))
			s.element:setDisabled(shouldDisable)
		end
	end

	if setting.dynamicTooltip and setting.element ~= nil then
		Log:trace("RLSettings.applyChange: refreshing dynamic tooltip for '%s' state=%d", name, newState)
		setting.element.elements[1]:setText(g_i18n:getText("rl_settings_" .. name .. "_tooltip_" .. newState))
	end

	-- Push new state to legacy widget so the in-game settings page reflects
	-- changes made from the RL Tabbed Menu's General subtab. forceEvent=false
	-- avoids re-entering RLSettings.onSettingChanged. Harmless redundancy
	-- when applyChange was called from the legacy click path (the widget
	-- already advanced itself to newState before its onClickCallback fired);
	-- necessary when called from RLMenuSettingsFrame:onClickGeneralSetting
	-- where the legacy widget would otherwise stay at its previous state
	-- and show stale data on next open of the in-game pause menu Settings tab.
	if setting.element ~= nil then
		Log:trace("RLSettings.applyChange: pushing newState=%d to legacy element for '%s'", newState, name)
		setting.element:setState(newState, false)
	end

	return true

end


function RLSettings.onSettingChanged(_, state, button)

	if button == nil then button = state end

	if button == nil or button.id == nil then return end

	if not string.contains(button.id, "rls_") then return end

	local name = string.sub(button.id, 5)
	local setting = RLSettings.SETTINGS[name]

	if setting == nil then return end

	if setting.ignore then
		if setting.callback then setting.callback() end
		return
	end

	RLSettings.applyChange(name, state)

	if g_server ~= nil then

		--RLSettings.saveToXMLFile(name, state)

	else

		--RL_BroadcastSettingsEvent.sendEvent(name)

	end

end


function RLSettings.applyDefaultSettings()

	if g_server == nil then

		--RL_BroadcastSettingsEvent.sendEvent()

	else

		for name, setting in pairs(RLSettings.SETTINGS) do
		
			if setting.ignore then continue end

			if setting.callback ~= nil then setting.callback(name, setting.values[setting.state]) end

			if setting.dynamicTooltip and setting.element ~= nil then setting.element.elements[1]:setText(g_i18n:getText("rl_settings_" .. name .. "_tooltip_" .. setting.state)) end

			for _, s in pairs(RLSettings.SETTINGS) do
				if s.dependancy and s.dependancy.name == name and s.element ~= nil then
					s.element:setDisabled(s.dependancy.state ~= state)
				end
			end
		end

	end
end


function RLSettings.getAnimalsXMLPath()
	
	if RLSettings.customAnimals == nil then return nil end

	return RLSettings.customAnimals.basePath .. RLSettings.customAnimals.animals

end


function RLSettings.getFillTypesXMLPath()
	
	if RLSettings.customAnimals == nil then return nil end

	return RLSettings.customAnimals.basePath .. RLSettings.customAnimals.fillTypes

end


function RLSettings.getTranslationsFolderPath()
	
	if RLSettings.customAnimals == nil then return nil end

	return RLSettings.customAnimals.basePath .. RLSettings.customAnimals.translations

end


function RLSettings.getAnimalsBasePath()
	
	if RLSettings.customAnimals == nil then return nil end

	return RLSettings.customAnimals.basePath

end


function RLSettings.getOverrideVanillaAnimals()

	if RLSettings.customAnimals == nil then return false end

	return RLSettings.customAnimals.override

end


function RLSettings.validateCustomAnimalsConfiguration()

	if RLSettings.SETTINGS.useCustomAnimals.state == 1 or RLSettings.animalsXMLPath == nil or g_currentMission.missionDynamicInfo.isMultiplayer then return end

	local xmlFile = XMLFile.loadIfExists("customAnimalsConfig", RLSettings.animalsXMLPath)

	if xmlFile == nil then return end

	local basePath
	local splitPath = string.split(RLSettings.animalsXMLPath, "/")

	for i = #splitPath, 1, -1 do

		local path = table.concat(splitPath, "/", 1, i)

		if path == baseDirectory then
			basePath = table.concat(splitPath, "/", 1, i + 1) .. "/"
			break
		end

	end

	if basePath == nil then return end

	RLSettings.customAnimals = {
		["basePath"] = basePath,
		["animals"] = xmlFile:getString("RealisticLivestock#animals", "animals.xml"),
		["fillTypes"] = xmlFile:getString("RealisticLivestock#fillTypes", "fillTypes.xml"),
		["translations"] = xmlFile:getString("RealisticLivestock#translations", "l10n/"),
		["override"] = xmlFile:getBool("RealisticLivestock#override", false)
	}

	xmlFile:delete()

	local l10nNames = {
		g_languageShort,
		"en",
		"de"
	}

	local l10nXML
	
	for _, l10nName in pairs(l10nNames) do
		l10nXML = XMLFile.loadIfExists("l10n", basePath .. RLSettings.customAnimals.translations .. "_" .. l10nName .. ".xml")
		if l10nXML ~= nil then break end
	end

	if l10nXML ~= nil then

		l10nXML:iterate("l10n.texts.text", function(_, key)
		
			local name = l10nXML:getString(key .. "#name")
			local text = l10nXML:getString(key .. "#text")

			if name ~= nil and text ~= nil then
				
				if g_i18n:hasModText(name) then
					printWarning("Warning: Duplicate l10n entry \'" .. name .. "\'. Ignoring this definition.")
				else
					g_i18n:setText(name, text:gsub("\r\n", "\n"))
				end
			
			end
		
		end)

		l10nXML:delete()

	end

	local fillTypesXML = loadXMLFile("fillTypes", basePath .. RLSettings.customAnimals.fillTypes)
	g_fillTypeManager:loadFillTypes(fillTypesXML, basePath, false, modName)

end