// is used to format the K/D value
function formatKD(num) {
    switch (true) {
        case (isNaN(num)):
            return "";
        case (!isFinite(num)):
            return translate("\u221E");
        case (num == 0 || num >= 10):
            return num.toFixed(0);
        case (num < 1):
            return num.toFixed(2);
        default:
            // avoid trailing zeros
            return Number(num.toFixed(1));
    }
}

// boonGUI: limit attack/speed numbers in the HUD
function limitNumber(num) {
    if (num < 10) {
        return Number(num.toFixed(1));
    }
    return Math.round(num);
}

// boonGUI: colored right-click
function showTemplateViewerOnRightClickTooltip() {
    // Translation: Appears in a tooltip to indicate that right-clicking the corresponding GUI element will open the Template Details GUI page.
    return translate(`${setStringTags("\\[Right-Click]", g_HotkeyTags)} to view more information.`);
}

// boonGUI: Colored Click
function showTemplateViewerOnClickTooltip() {
    // Translation: Appears in a tooltip to indicate that clicking the corresponding GUI element will open the Template Details GUI page.
    return translate(`${setStringTags("\\[Click]", g_HotkeyTags)} to view more information.`);
}
function getSecondsString(seconds) {
    return sprintf(translatePlural("%(time)s %(second)s", "%(time)s %(second)s", seconds), {
        "time": seconds,
        "second": unitFont(translatePlural("Second", "Seconds", seconds))
    });
}

function attackRateDetails(interval, currentProjectiles, baseProjectiles = currentProjectiles, garrisonProjectiles = 0, colorSteepness) {
    if (!interval)
        return "";

    if (currentProjectiles === 0)
        return translate("Garrison to fire arrows");
    const attackRateString = getSecondsString(interval / 1000);
    const techBonus = currentProjectiles - baseProjectiles - garrisonProjectiles;
    const totalBonus = currentProjectiles - baseProjectiles;

    let projectileString;
    if (currentProjectiles > 1) {
        const projectileName = unitFont(translatePlural("Arrow", "Arrows", currentProjectiles));

        if (totalBonus > 0) {
            // Format with both tech and garrison bonuses
            const baseDisplay = baseProjectiles;
            const techBonusDisplay = techBonus > 0 ? 
                colorizeText(`+${techBonus}`, getTextGreenColorFromBonus(
                    Math.round((techBonus / baseProjectiles * 100)),
                    colorSteepness
                )) : "";
            const garrisonBonusDisplay = garrisonProjectiles > 0 ?
                colorizeText(`+${garrisonProjectiles}`, getTextBlueColorFromBonus(garrisonProjectiles)) : ""; // Fixed blue color
            
            const bonuses = [techBonusDisplay, garrisonBonusDisplay]
                .filter(Boolean).join(") (");

            projectileString = sprintf(translate("%(count)s (%(bonuses)s) %(name)s"), {
                "count": baseDisplay,
                "bonuses": bonuses,
                "name": projectileName
            });
        } else {
            projectileString = sprintf(translate("%(count)s %(name)s"), {
                "count": currentProjectiles,
                "name": projectileName
            });
        }
    }

    // Construct the full rate string
    let rateDescription;
    if (currentProjectiles > 1) {
        rateDescription = sprintf(translate("%(projectiles)s / %(rate)s %(interval)s"), {
            "projectiles": projectileString,
            "rate": attackRateString,
            "interval": unitFont(translate("Interval"))
        });
    } else {
        rateDescription = sprintf(translate("%(rate)s %(interval)s"), {
            "rate": attackRateString,
            "interval": unitFont(translate("Interval"))
        });
    }

    return rateDescription;
}

function setupStatHUDAttackTooltip(template, templateData, projectiles, projectilesDefaultEmpty, projectilesDefaultTemplate, colorSteepness) {
    const tooltips = [];
    for (const attackType in template.attack) {
        if (["Slaughter", "Capture"].some(s => attackType.includes(s)))
            continue;

        const attackTypeTemplate = template.attack[attackType];
        const baseAttackType = templateData?.attack?.[attackType];
        
        // Calculate garrison projectiles
        const garrisonProjectiles = Math.max(0, projectiles - projectilesDefaultEmpty);
        
        // Calculate base and current intervals
        const baseInterval = baseAttackType?.repeatTime || attackTypeTemplate.repeatTime;
        const currentInterval = attackTypeTemplate.repeatTime;
        const intervalBonusPercent = baseInterval > 0 ?
            Math.round(((baseInterval - currentInterval) / baseInterval * 100)) : 0;

        let totalBonusPercent = 0;
        if (baseAttackType) {
            // Calculate base and current power
            const basePower = ["Hack", "Pierce", "Crush"].reduce((sum, type) =>
                sum + (baseAttackType.Damage?.[type] || 0), 0);
            const currentPower = ["Hack", "Pierce", "Crush"].reduce((sum, type) =>
                sum + (attackTypeTemplate.Damage?.[type] || 0), 0);
            
            if (basePower > 0) {
                // Calculate damage bonus percentage
                const damageBonusPercent = (currentPower - basePower) / basePower;
                
                // Calculate attack rate multiplier (lower interval = faster attacks)
                const attackRateMultiplier = baseInterval > 0 ? baseInterval / currentInterval : 1;
                
                // Calculate total DPS bonus percentage
                const totalDPSMultiplier = (1 + damageBonusPercent) * attackRateMultiplier - 1;
                totalBonusPercent = Math.round(totalDPSMultiplier * 100);
                
            }
        }


        // Get color for bonus percentage (handles negative values)
        const bonusColor = getTextGreenColorFromBonus(totalBonusPercent, colorSteepness);   
        const intervalColor = getTextGreenColorFromBonus(intervalBonusPercent, colorSteepness);  
        const intervalString = colorizeText(
            attackRateDetails(attackTypeTemplate.repeatTime, 1, 1).trim(),
            intervalColor
        );

        const attackLabel = sprintf(headerFont(translate("%(bonus)s%(attackType)s")), {
            "bonus": totalBonusPercent !== 0 ?
                `${g_Indent}${colorizeText(
                    `${totalBonusPercent > 0 ? '+' : '-'} ${Math.abs(totalBonusPercent)}% ${translate("Bonus")}`,
                    bonusColor
                )}\n` : "",
            "attackType": translateWithContext(
                attackTypeTemplate.attackName.context || "Name of an attack, usually the weapon.",
                attackTypeTemplate.attackName.name
            ) + (projectiles <= 1 ? ` (${intervalString}) :` : " :")
        });

        // Format damage with colored bonuses (handles negative values)
        let damageParts = [];
        for (const type of ["Hack", "Pierce", "Crush"]) {
            const current = attackTypeTemplate.Damage?.[type] || 0;
            const base = baseAttackType?.Damage?.[type] || 0;
            const bonus = current - base;

            if (current > 0) {
                const formatNumber = num => num.toFixed(num % 1 ? 1 : 0);
                const typeDisplay = unitFont(translate(type));
                
                let damageValue = formatNumber(base);
                if (Math.abs(bonus) > 0.05) {  // Threshold to avoid floating-point artifacts
                    const bonusPercent = base > 0 ? Math.round((bonus / base * 100)) : 0;
                    const bonusColor = getTextGreenColorFromBonus(bonusPercent, colorSteepness);
                    const bonusSign = bonus > 0 ? '+' : '-';
                    const coloredBonus = colorizeText(
                        `${bonusSign}${formatNumber(Math.abs(bonus))}`,
                        bonusColor
                    );
                    damageValue += ` (${coloredBonus})`;
                }
                
                damageParts.push(`\n${g_Indent}${damageValue} ${typeDisplay}`);
            }
        }
        const damageDisplay = damageParts.join("");

        // Splash damage
        let splashDisplay = "";
        if (attackTypeTemplate.splash) {
            splashDisplay = `\n${g_Indent}${splashDetails(attackTypeTemplate.splash)}`;
        }

        // Attack rate (only show if multiple projectiles)
        const rateDisplay = projectiles > 1 ?
            `\n${g_Indent}${attackRateDetails(
                attackTypeTemplate.repeatTime, 
                projectiles, 
                projectilesDefaultTemplate,
                garrisonProjectiles,
                colorSteepness
            )}` : "";

        // Status effects
        let statusEffects = [];
        if (attackTypeTemplate.ApplyStatus) {
            for (const status in attackTypeTemplate.ApplyStatus) {
                statusEffects.push(`\n${g_Indent}${getStatusEffectsTooltip(status, attackTypeTemplate.ApplyStatus[status], true)}`);
            }
        }

        tooltips.push(sprintf(translate("%(attackLabel)s%(damage)s%(rate)s%(statusEffects)s%(splash)s"), {
            "attackLabel": attackLabel,
            "damage": damageDisplay,
            "rate": rateDisplay,
            "splash": splashDisplay,
            "statusEffects": statusEffects.join("")
        }));
    }

    return sprintf(translate("%(label)s %(details)s"), {
        "label": headerFont(translate(`Damage per Second\n`)),
        "details": tooltips.join(`\n${g_Indent}`)
    });
}
// Helper function to colorize text
function colorizeText(text, color) {
    return `[color="${color}"]${text}[/color]`;
}
function setupStatHUDHackResistanceTooltip(template, templateData, bonusColor) {
    const baseResistance = templateData?.resistance?.Damage?.Hack || 0;
    const currentResistance = template.resistance.Damage.Hack;
    const resistanceBonus = currentResistance - baseResistance;

    return sprintf(translate("%(label)s%(resistanceLine)s\n%(effectiveness)s\n%(info)s"), {
        "label": headerFont(translate(`Hack Resistance Level\n`)),
        "resistanceLine": sprintf(translate("%(level)s %(type)s"), {
            "level": `${g_Indent}${baseResistance}${resistanceBonus !== 0 ? ` (${colorizeText(`${resistanceBonus > 0 ? '+' : ''}${resistanceBonus}`, bonusColor)})` : ''}`,
            "type": unitFont(translatePlural("Hack Resistance Point", "Hack Resistance Points", currentResistance))
        }),
        "effectiveness": `${g_Indent}${resistanceLevelToPercentageString(currentResistance)} ${unitFont(translate("Hack Damage Reduction"))}`,
        "info": translate("Hack damage usually inflicted in close combat by infantry or cavalry units.")
    });
}

function setupStatHUDPierceResistanceTooltip(template, templateData, bonusColor) {
    const baseResistance = templateData?.resistance?.Damage?.Pierce || 0;
    const currentResistance = template.resistance.Damage.Pierce;
    const resistanceBonus = currentResistance - baseResistance;

    return sprintf(translate("%(label)s%(resistanceLine)s\n%(effectiveness)s\n%(info)s"), {
        "label": headerFont(translate(`Pierce Resistance Level\n`)),
        "resistanceLine": sprintf(translate("%(level)s %(type)s"), {
            "level": `${g_Indent}${baseResistance}${resistanceBonus !== 0 ? ` (${colorizeText(`${resistanceBonus > 0 ? '+' : ''}${resistanceBonus}`, bonusColor)})` : ''}`,
            "type": unitFont(translatePlural("Pierce Resistance Point", "Pierce Resistance Points", currentResistance))
        }),
        "effectiveness": `${g_Indent}${resistanceLevelToPercentageString(currentResistance)} ${unitFont(translate("Pierce Damage Reduction"))}`,
        "info": translate("Pierce damage is dealt by ranged units e.g. archers, skirmishers or even special siege units such as bolt shooters.")
    });
}

function setupStatHUDCrushResistanceTooltip(template, templateData, bonusColor) {
    const baseResistance = templateData?.resistance?.Damage?.Crush || 0;
    const currentResistance = template.resistance.Damage.Crush;
    const resistanceBonus = currentResistance - baseResistance;

    return sprintf(translate("%(label)s%(resistanceLine)s\n%(effectiveness)s\n%(info)s"), {
        "label": headerFont(translate(`Crush Resistance Level\n`)),
        "resistanceLine": sprintf(translate("%(level)s %(type)s"), {
            "level": `${g_Indent}${baseResistance}${resistanceBonus !== 0 ? ` (${colorizeText(`${resistanceBonus > 0 ? '+' : ''}${resistanceBonus}`, bonusColor)})` : ''}`,
            "type": unitFont(translatePlural("Crush Resistance Point", "Crush Resistance Points", currentResistance))
        }),
        "effectiveness": `${g_Indent}${resistanceLevelToPercentageString(currentResistance)} ${unitFont(translate("Crush Damage Reduction"))}`,
        "info": translate("Crush damage is mostly caused by siege units such as catapults or rams.")
    });
}
function setupStatHUDRangeTooltip(rangedAttack, templateData, colorSteepness) {
    const minRange = rangedAttack.minRange;
    const maxRange = rangedAttack.maxRange;
    const baseMaxRange = templateData?.attack?.Ranged?.maxRange || maxRange;
    const rangeBonus = maxRange - baseMaxRange;
    const elevationBonus = Math.round(rangedAttack.elevationAdaptedRange - maxRange);

    // Calculate range bonus percentage
    const rangeBonusPercent = baseMaxRange > 0 ? 
        Math.round((rangeBonus / baseMaxRange * 100)) : 0;
    const rangeColor = getTextGreenColorFromBonus(rangeBonusPercent, colorSteepness);

    // Calculate elevation bonus percentage
    const elevationBonusPercent = maxRange > 0 ?
        Math.round((elevationBonus / maxRange * 100)) : 0;
    const elevationColor = getTextGreenColorFromBonus(elevationBonusPercent, colorSteepness);

    // Format base range and bonus
    let rangeDisplay = baseMaxRange.toString();
    if (rangeBonus !== 0) {
        const bonusSign = rangeBonus > 0 ? '+' : '';
        rangeDisplay += ` (${colorizeText(
            `${bonusSign}${Math.abs(rangeBonus)}`,
            rangeColor
        )})`;
    }

    let rangeInfo = sprintf(translate("%(label)s %(rangeInfo)s"), {
        "label": headerFont(translate("Attack Range\n" + g_Indent)),
        "rangeInfo":
            sprintf(translate("%(rangeDisplay)s %(unit)s"), {
                "rangeDisplay": rangeDisplay,
                "unit": unitFont(translate("meters"))
            })
    });

    if (elevationBonus !== 0) {
        rangeInfo += commaFont(translate(", ")) +
            sprintf(translate("%(label)s: %(bonus)s %(unit)s"), {
                "label": translate("Elevation Bonus"),
                "bonus": colorizeText(
                    (elevationBonus > 0 ? "+" : "") + elevationBonus,
                    elevationColor
                ),
                "unit": unitFont(translate("meter" + (Math.abs(elevationBonus) !== 1 ? "s" : "")))
            });
    }

    return rangeInfo;
}

function setupStatHUDSpeedTooltip(template, templateData, colorSteepness) {
    const baseSpeed = templateData?.speed || template.speed;
    const currentWalk = template.speed.walk;
    const currentRun = template.speed.run;

    if (currentWalk === 0 && currentRun === 0)
        return "";

    const currentAccel = template.speed.acceleration;

    // Calculate bonuses (can be negative)
    const walkBonus = currentWalk - baseSpeed.walk;
    const runBonus = currentRun - baseSpeed.run;
    const accelBonus = currentAccel - baseSpeed.acceleration;

    // Calculate bonus percentages (can be negative)
    const walkBonusPercent = baseSpeed.walk > 0 ? 
        Math.round((walkBonus / baseSpeed.walk * 100)) : 0;
    const runBonusPercent = baseSpeed.run > 0 ? 
        Math.round((runBonus / baseSpeed.run * 100)) : 0;
    const accelBonusPercent = baseSpeed.acceleration > 0 ? 
        Math.round((accelBonus / baseSpeed.acceleration * 100)) : 0;

    // Get colors for bonuses (handles negative values)
    const walkColor = getTextGreenColorFromBonus(walkBonusPercent, colorSteepness);
    const runColor = getTextGreenColorFromBonus(runBonusPercent, colorSteepness);
    const accelColor = getTextGreenColorFromBonus(accelBonusPercent, colorSteepness);

    // Total bonus percentage (can be negative)
    const totalBase = baseSpeed.walk + baseSpeed.run;
    const totalCurrent = currentWalk + currentRun;
    const totalBonus = totalCurrent - totalBase;
    const totalBonusPercent = totalBase > 0 ? 
        Math.round((totalBonus / totalBase * 100)) : 0;
    const totalBonusColor = getTextGreenColorFromBonus(totalBonusPercent, colorSteepness);

    // Format numbers with exactly 1 decimal
    const formatNumber = num => num.toFixed(1);

    // Format speed values with colored bonuses
    const formatSpeed = (base, bonus, color) => {
        if (Math.abs(bonus) > 0.01) {
            const bonusSign = bonus > 0 ? '+' : '-';
            return `${formatNumber(base)} (${colorizeText(
                `${bonusSign}${formatNumber(Math.abs(bonus))}`,
                color
            )})`;
        }
        return formatNumber(base);
    };

    return sprintf(translate("%(label)s %(speeds)s"), {
        "label": headerFont(translate(`Movement Speed${
            totalBonusPercent !== 0 ?
                `: \n${g_Indent}${colorizeText(
                    `${totalBonusPercent > 0 ? '' : '-'}${Math.abs(totalBonusPercent)}% ${translate("Bonus")}`,
                    totalBonusColor
                )}` :
                ''
        }\n${g_Indent}`)),
        "speeds":
            sprintf(translate("%(speed)s %(movementType)s"), {
                "speed": formatSpeed(baseSpeed.walk, walkBonus, walkColor),
                "movementType": unitFont(translate("Walk"))
            }) +
            `\n${g_Indent}` +
            sprintf(translate("%(speed)s %(movementType)s"), {
                "speed": formatSpeed(baseSpeed.run, runBonus, runColor),
                "movementType": unitFont(translate("Run"))
            }) +
            `\n${g_Indent}` +
            sprintf(translate("%(speed)s %(movementType)s"), {
                "speed": formatSpeed(baseSpeed.acceleration, accelBonus, accelColor),
                "movementType": unitFont(translate("Acceleration"))
            })
    });
}

function setupStatHUDTreasureInfo(template) {
    const resources = {};
    for (const resource of g_ResourceData.GetResources()) {
        const type = resource.code;
        if (template.treasure.resources[type])
            resources[type] = template.treasure.resources[type];
    }

    const resourceName = Object.keys(resources);
    if (!resourceName.length)
        return "";

    const resourceAmount = resourceName.map(type => resources[type]);
    return { resourceName, resourceAmount };
}

/**
 * @param   {object}  resources  {wood:330}
 * @param   {string}  color      color code, e.g. "200 0 0"
 * @return  {string}             Colored amount and resource icon
 */
function getLocalizedResourceAmounts(resources, color = "255 255 255") {
    if (/^[^0-9]/.test(color)) {
        warn(`The received color input "${color}" cannot be processed. Falling back to the default color.`);
        color = "255 255 255";
    }
    color = color;
    const amounts = g_ResourceData.GetCodes()
        .filter(type => !!resources[type])
        .map(type => sprintf(translate("%(amount)s %(resourceType)s"), {
            "amount": coloredText(resources[type], color),
            "resourceType": resourceIcon(type)
        }));

    if (amounts.length < 2)
        return amounts.join();

    const lastAmount = amounts.pop();
    return sprintf(translate("%(previousAmounts)s and %(lastAmount)s"), {
        // Translation: This comma is used for separating first to penultimate elements in an enumeration.
        "previousAmounts": coloredText(amounts.join(translate(", ")), color),
        "lastAmount": coloredText(lastAmount, color)
    });
}

function splashDetails(splashTemplate) {
    const splashLabel = sprintf(headerFont(translate("%(splashShape)s Splash")), {
        "splashShape": translate(splashTemplate.shape)
    });
    let splashDamageTooltip = sprintf(translate("%(label)s: %(effects)s"), {
        "label": splashLabel,
        "effects": attackEffectsDetails(splashTemplate)
    });

    if (splashTemplate.radius !== undefined) {
        splashDamageTooltip += commaFont(translate(", ")) + sprintf(translate("Radius: %(radius)s %(unit)s"), {
            "radius": splashTemplate.radius,
            "unit": unitFont(translate("meters"))
        });
    }

    if (g_AlwaysDisplayFriendlyFire || splashTemplate.friendlyFire)
        splashDamageTooltip += commaFont(translate(", ")) + sprintf(translate("Friendly Fire: %(enabled)s"), {
            "enabled": splashTemplate.friendlyFire ? translate("Yes") : translate("No")
        });

    return splashDamageTooltip;
}
