--[[
BasegameAdjustments

Specialization to adjust some things in the base game

Author: 	Ifko[nator]
Date: 		30.10.2025
Version:	1.1

History:	v1.0 @05.01.2025 - initial implemation in FS 25
			-----------------------------------------------
			v1.1 @30.10.2025 - fix for Patch 1.14.0.0
]]

BasegameAdjustments = {};

BasegameAdjustments.currentModDirectory = "";

for _, mod in pairs(g_modManager.mods) do
	if mod.title:upper() == "ENHANCED MIXER WAGONS" or mod.title:upper() == "VERBESSERTE FUTTERMISCHWAGEN" then		
		if g_modIsLoaded[mod.modName] then	
			BasegameAdjustments.currentModDirectory = mod.modDir;

			break;
		end;
	end;
end;

BasegameAdjustments.COLOR = {
	STATUS_BAR_BACKGROUND = {0.00439, 0.00478, 0.00368, 0.9},
	STATUS_BAR_GOOD = {0.2384, 0.4621, 0.0015, 1},
	STATUS_BAR_ALMOST_GOOD = {0.6, 0.5, 0, 1},
	STATUS_BAR_BETTER = {1, 0.2, 0, 1},
	STATUS_BAR_BAD = {0.8069, 0.0097, 0.0097, 1}
};

local MixerWagonHUDExtension_mt = Class(MixerWagonHUDExtension);

function BasegameAdjustments.mixerWagoNew(vehicle, superFunc, customMt)
	local self = setmetatable({}, customMt or MixerWagonHUDExtension_mt);

    self.priority = GS_PRIO_HIGH;

    local red, green, blue, alpha = unpack(HUD.COLOR.BACKGROUND);

    self.backgroundTop = g_overlayManager:createOverlay("gui.hudExtension_top", 0, 0, 0, 0);
    self.backgroundTop:setColor(red, green, blue, alpha);

    self.backgroundScale = g_overlayManager:createOverlay("gui.hudExtension_middle", 0, 0, 0, 0);
    self.backgroundScale:setColor(red, green, blue, alpha);
    
	self.backgroundBottom = g_overlayManager:createOverlay("gui.hudExtension_bottom", 0, 0, 0, 0);
    self.backgroundBottom:setColor(red, green, blue, alpha);

    self.bar = ThreePartOverlay.new();

    self.bar:setLeftPart("gui.progressbar_left", 0, 0);
    self.bar:setMiddlePart("gui.progressbar_middle", 0, 0);
    self.bar:setRightPart("gui.progressbar_right", 0, 0);

    self.marker = g_overlayManager:createOverlay("gui.tmr_marker", 0, 0, 0, 0);

    self.vehicle = vehicle;
    self.mixerWagon = vehicle.spec_mixerWagon;
    self.numFillTypes = #self.mixerWagon.mixerWagonFillTypes;

    self.fillTypeStatus = {};

    for _, mixerWagonFillType in ipairs(self.mixerWagon.mixerWagonFillTypes) do
        local firstFilltype = next(mixerWagonFillType.fillTypes);
        local fillType = g_fillTypeManager:getFillTypeByIndex(firstFilltype);

        if fillType ~= nil then
            if fillType.name == "FORAGE_STRAW_FAILED" and g_fillTypeManager:getFillTypeIndexByName("GRASS_SILAGE") ~= nil then
				--## Fix for grass silage. The grass silage no longer become forage straw failed in the mixer wagon
				
				fillType = g_fillTypeManager:getFillTypeByName("GRASS_SILAGE");
			end;
			
			local icon = Overlay.new(fillType.hudOverlayFilename, 0, 0, 0, 0);

            local status = {
                icon = icon,
                fillLevel = 0,
                minPercentage = mixerWagonFillType.minPercentage,
                maxPercentage = mixerWagonFillType.maxPercentage
            };

            table.insert(self.fillTypeStatus, status);
        end;
    end;

    self.badMixColor = {0.8069, 0.0097, 0.0097, 1};

    self.title = utf8ToUpper(string.format("%s - %s",  g_i18n:getText("info_mixingRatio"), vehicle:getFullName()));

    self:storeScaledValues();

    g_messageCenter:subscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.UI_SCALE], self.storeScaledValues, self);

    return self;
end;

MixerWagonHUDExtension.new = Utils.overwrittenFunction(MixerWagonHUDExtension.new, BasegameAdjustments.mixerWagoNew);

function BasegameAdjustments:storeScaledValues()
	local modDesc = loadXMLFile("modDesc", Utils.getFilename("modDesc.xml", BasegameAdjustments.currentModDirectory));

	local requiredMixingTime = Utils.getNoNil(getXMLInt(modDesc, "modDesc.enhancedMixerWagon#mixingTime"), 120); --## in seconds, 0 = NO MIXING TIME!

	delete(modDesc);

    requiredMixingTime = self.vehicle.xmlFile:getValue("vehicle.enhancedMixerWagon#mixingTime", requiredMixingTime); --## in seconds, 0 = NO MIXING TIME!

	if Utils.getNoNil(requiredMixingTime, 0) > 0 then
		local uiScale = g_gameSettings:getValue(GameSettings.SETTING.UI_SCALE);
	
		local totalHeight = self.totalHeight * 1.25;

		local width, height = getNormalizedScreenValues(330 * uiScale, 6 * uiScale);
	
		self.backgroundScale:setDimension(width, totalHeight - 2 * height);
	end;
end;

MixerWagonHUDExtension.storeScaledValues = Utils.appendedFunction(MixerWagonHUDExtension.storeScaledValues, BasegameAdjustments.storeScaledValues);

function BasegameAdjustments:mixerWagonDraw(superFunc, inputHelpDisplay, posX, posY)
    local backGroundColor = BasegameAdjustments.COLOR.STATUS_BAR_BACKGROUND;
	
	self.backgroundTop:setPosition(posX, posY - self.backgroundTop.height);
	self.backgroundScale:setPosition(posX, self.backgroundTop.y - self.backgroundScale.height);
	self.backgroundBottom:setPosition(posX, self.backgroundScale.y - self.backgroundBottom.height);

	self.backgroundTop:render();
	self.backgroundScale:render();
	self.backgroundBottom:render();

	setTextAlignment(RenderText.ALIGN_LEFT);
	setTextColor(1, 1, 1, 1);
	setTextBold(true);
	
	local textWidth = self.backgroundTop.width - 2 * self.titleOffsetX;
	local title = Utils.limitTextToWidth(self.title, self.titleSize, textWidth, false, "...");
	
	renderText(posX + self.titleOffsetX, posY + self.titleOffsetY, self.titleSize, title);
	setTextAlignment(RenderText.ALIGN_RIGHT);
	
	local specMixerWagon = self.mixerWagon;
	local totalFillLevel = 0;
	
	if self.vehicle:getFillUnitFillLevel(self.mixerWagon.fillUnitIndex) > 0 then
		for fillTypeNumber, mixerWagonFillType in ipairs(specMixerWagon.mixerWagonFillTypes) do
			totalFillLevel = totalFillLevel + mixerWagonFillType.fillLevel;

			self.fillTypeStatus[fillTypeNumber].fillLevel = mixerWagonFillType.fillLevel;
		end;
	end;

	local posY = posY - self.offsetTop;
	local leftPosX = posX + self.backgroundTop.width + self.textOffsetX;
	
	local failedOutputFillType = specMixerWagon.failedOutputFillType;
	
	if specMixerWagon.hasStrawLoaded then
		failedOutputFillType = specMixerWagon.failedOutputFillTypeStraw;
	end;
	
	for _, fillTypeDisplay in ipairs(self.fillTypeStatus) do
		posY = posY - self.heightPerFillType;
		
		fillTypeDisplay.icon:setPosition(posX + self.iconOffsetX, posY + self.iconOffsetY);
		fillTypeDisplay.icon:render();
		
		self.bar:setMiddlePart(nil, self.middlePartMaxWidth, nil);
		self.bar:setColor(unpack(backGroundColor));
		self.bar:setPosition(posX + self.barOffsetX, posY + self.barOffsetY);
		self.bar:render();

		local percentage = self.vehicle:getFillUnitFillLevel(self.mixerWagon.fillUnitIndex) <= 0 and 0 or fillTypeDisplay.fillLevel / totalFillLevel;
		local barPartsWidth = self.barMaxWidth * (fillTypeDisplay.maxPercentage - fillTypeDisplay.minPercentage) - self.barPartsWidth;
		local barOffsetX = self.barMaxWidth * fillTypeDisplay.minPercentage;
		local barColor = BasegameAdjustments.COLOR.STATUS_BAR_BAD;

		if fillTypeDisplay.fillLevel > 0 and (self.vehicle:getFillUnitFillType(self.mixerWagon.fillUnitIndex) ~= failedOutputFillType or fillTypeDisplay.minPercentage <= percentage and percentage <= fillTypeDisplay.maxPercentage) then
			barColor = BasegameAdjustments.COLOR.STATUS_BAR_GOOD;
		end;

		self.bar:setColor(unpack(barColor));
		self.bar:setMiddlePart(nil, barPartsWidth, nil);
		self.bar:setPosition(posX + self.barOffsetX + barOffsetX, nil);
		self.bar:render();
		self.marker:setPosition(posX + self.barOffsetX + self.barMaxWidth * percentage - self.marker.width * 0.5, self.bar.y - (self.marker.height - self.barPartHeight) * 0.5);
		self.marker:render();
		
		renderText(leftPosX, posY + self.textOffsetY, self.textSize, string.format("%d%%", percentage * 100));
	end;

	if Utils.getNoNil(specMixerWagon.requiredMixingTime, 0) > 0 then
		local uiScale = g_gameSettings:getValue(GameSettings.SETTING.UI_SCALE);
		local _, heightOffset = getNormalizedScreenValues(0, 6 * uiScale);
		local width, height = getNormalizedScreenValues(330 * uiScale, ((self:getHeight() - 2) * 1.2) * heightOffset);
		
		--self.backgroundScale:setDimension(width, height);

		local function getFormattedTime()
            local function getExtraZero(time, skip)    
                if time < 10 and not skip then
                    time = "0" .. time;
                end;
				
                return tostring(time);
            end;
			
			local neededTime = math.clamp(math.ceil(specMixerWagon.requiredMixingTime - (specMixerWagon.currentMixingTime / 1000)), 0, specMixerWagon.requiredMixingTime);

			local time = {};

            time.hours = getExtraZero(math.floor(neededTime / 3600), true);
            time.minutes = getExtraZero(math.floor((neededTime % 3600) / 60), time.hours == "0");
            time.seconds = getExtraZero(math.floor(neededTime % 60), time.minutes == "0");

			local text = {};

			for _, timeInterval in pairs({"hours", "minutes", "seconds"}) do
				local needToShortText = time[timeInterval] == "01" or time[timeInterval] == "1";

				text[timeInterval] = g_i18n:getText("text_" .. timeInterval, EnhancedMixerWagons.currentModName);

				if timeInterval == "hours" then
					needToShortText = time[timeInterval] == "1";
				end;
				
				if needToShortText then
					text[timeInterval] = text[timeInterval]:sub(1, text[timeInterval]:len() - 1);
				end;
			end;
        
            local formatedTime = time.hours .. " " .. text.hours .. " : " .. time.minutes .. " " .. text.minutes .. " : " .. time.seconds .. " " .. text.seconds;

			if time.hours == "0" then
				formatedTime = time.minutes .. " " .. text.minutes .. " : " .. time.seconds .. " " .. text.seconds;

				if minutes == "0" then
					formatedTime = time.seconds .. " " .. text.seconds;
				end;
			end;

			return formatedTime;
        end;

		barY = posY - self.textOffsetY * 2;

		setTextAlignment(RenderText.ALIGN_LEFT);
		setTextBold(true);

        renderText(posX + self.titleOffsetX, barY, self.titleSize, g_i18n:getText("NEEDED_MIXING_TIME", EnhancedMixerWagons.currentModName) .. " " .. getFormattedTime());
        
		setTextBold(false);
		setTextAlignment(RenderText.ALIGN_RIGHT);
		
        if specMixerWagon.timeOverlay ~= nil then
            width, height = getNormalizedScreenValues(25 * uiScale, 25 * uiScale);

			specMixerWagon.timeOverlay:setDimension(width, height);
			specMixerWagon.timeOverlay:setPosition(posX + self.iconOffsetX, barY - self.titleSize * 2.5);
            specMixerWagon.timeOverlay:render();
        end;

		local mixingTimePercentage = specMixerWagon.mixingTimePercentage;

		renderText(leftPosX, barY - self.titleSize * 1.8, self.textSize, ("%d%%"):format(mixingTimePercentage * 100));

		width, height = getNormalizedScreenValues(3, 6);
		
		specMixerWagon.statusBar:setLeftPart(nil, width, height);
		specMixerWagon.statusBar:setMiddlePart(nil, self.middlePartMaxWidth, height);
		specMixerWagon.statusBar:setRightPart(nil, width, height);

		specMixerWagon.statusBar:setColor(unpack(backGroundColor));
		specMixerWagon.statusBar:setPosition(posX + self.barOffsetX, barY - self.titleSize * 1.7);
		specMixerWagon.statusBar:render();

		local barPartsWidth = self.barMaxWidth * mixingTimePercentage;
		
		if mixingTimePercentage == 1 then
            specMixerWagon.statusBar:setColor(unpack(BasegameAdjustments.COLOR.STATUS_BAR_GOOD));
        elseif mixingTimePercentage > 0.7 and mixingTimePercentage < 1 then
            specMixerWagon.statusBar:setColor(unpack(BasegameAdjustments.COLOR.STATUS_BAR_ALMOST_GOOD));
        elseif mixingTimePercentage > 0.35 and mixingTimePercentage < 0.7 then
            specMixerWagon.statusBar:setColor(unpack(BasegameAdjustments.COLOR.STATUS_BAR_BETTER));
        else
            specMixerWagon.statusBar:setColor(unpack(BasegameAdjustments.COLOR.STATUS_BAR_BAD));
        end;

		specMixerWagon.statusBar:setLeftPart(nil, width * mixingTimePercentage, height);
		specMixerWagon.statusBar:setMiddlePart(nil, barPartsWidth, nil);
		specMixerWagon.statusBar:setRightPart(nil, width * mixingTimePercentage, height);
		
		specMixerWagon.statusBar:render();
    end;

	setTextBold(false);

	return self.backgroundBottom.y;
end;

MixerWagonHUDExtension.draw = Utils.overwrittenFunction(MixerWagonHUDExtension.draw, BasegameAdjustments.mixerWagonDraw);

function BasegameAdjustments:getFillTypesFromXML(superFunc, fillTypes, usedFillTypes, xmlFile, key)
	local fillTypeNameStr = xmlFile:getValue(key);

	if fillTypeNameStr == nil then
		Logging.xmlError(xmlFile, "Missing fillTypes for ingredient '%s'", key);

		return false;
	end;

	if fillTypeNameStr:upper():find("GRASS") and not fillTypeNameStr:upper():find("DRY") then
		--## add forage failed and forage straw failed to food group grass
		
		fillTypeNameStr = fillTypeNameStr .. " FORAGE_FAILED FORAGE_STRAW_FAILED";
	end;
	
	local fillTypeNames = fillTypeNameStr:split(" ");

	for _, fillTypeName in pairs(fillTypeNames) do
		local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName);

		if fillTypeIndex ~= nil then
			if usedFillTypes[fillTypeIndex] == nil then
				table.addElement(fillTypes, fillTypeIndex);
			else
				Logging.xmlWarning(xmlFile, "FillType '%s' already used in other ingredient", fillTypeName);
			end;
		else
			Logging.xmlWarning(xmlFile, "FillType '%s' not defined. Ignoring it", fillTypeName);
		end;
	end;

	if #fillTypes == 0 then
		Logging.xmlError(xmlFile, "No fillTypes defined - '%s'", key);

		return false;
	end;

	return true;
end;

AnimalFoodSystem.getFillTypesFromXML = Utils.overwrittenFunction(AnimalFoodSystem.getFillTypesFromXML, BasegameAdjustments.getFillTypesFromXML);

function BasegameAdjustments:loadMapData(xmlFile, missionInfo, baseDirectory) 
	if #self.modsToLoad > 0 then
    	--## Load additional fill types from mods

    	for _, data in ipairs(self.modsToLoad) do
    	    local xmlFilename, baseDirectoryMod, customEnvironment = unpack(data);
    	    local fillTypesXMLFile = XMLFile.load("fillTypes", xmlFilename, FillTypeManager.xmlSchema);

    	    if fillTypesXMLFile ~= nil then
    	        local numFillTypes = #self.fillTypes;

    	        g_fillTypeManager:loadFillTypes(fillTypesXMLFile, baseDirectoryMod, false, customEnvironment, false);
    	        fillTypesXMLFile:delete();
    	        
				local numLoadedFillTypes = #self.fillTypes - numFillTypes;
    	        
				if numLoadedFillTypes > 0 then
    	            Logging.info("Loaded %d fill types from mod: %s", numLoadedFillTypes, customEnvironment);
    	        end;
    	    end;
    	end;
	end;
	
	for _, fillType in ipairs(self.fillTypes) do
        fillType:finalize();
    end;
end;

FillTypeManager.loadMapData = Utils.appendedFunction(FillTypeManager.loadMapData, BasegameAdjustments.loadMapData);