Greasy Fork is available in English.

Webgame vylepšení

Sbírka vylepšení pro hru Webgame

// ==UserScript==
// @name         Webgame vylepšení
// @version      2024-06-27
// @description  Sbírka vylepšení pro hru Webgame
// @author       yS
// @match        *://*.webgame.cz/wg/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_info
// @icon         https://www.google.com/s2/favicons?sz=64&domain=webgame.cz
// @namespace https://greasyfork.org/users/1005892
// ==/UserScript==

// TODO:
// nastaveni zobrazeni pomeru ztrat prestize utocnika / obrance
// nastaveni prevodu techu na $
// otestovat obyc listu
// archiv - ztraty nase / nepritele
// archiv - ztraty v $

(function () {
    "use strict";

    // options
    const VALKY_OBRACENE = "valky_flipped";
    const ZOBRAZENE_ZOLDY = "zobrazene_zoldy";
    const MODIFY_ARCHIVE = "modify_archive";
    const MODIFY_ADVANCEMENTS = "modify_advancements";

    // menu options
    const ROZVOJ_MENU_LINK = "rozvoj";
    const GENERALS_MENU_LINK = "generalove";
    const PRODEJ_MENU_LINK = "prodej";
    const POKROKY_MENU_LINK = "pokroky";

    // budovy settings
    const COUNTRY_ALI_DATA = "country_ali_data";
    const COUNTRY_ALI_DATA_TIME_OFFSET_MILLIS = 24 * 60 * 60 * 1000;

    // rozvedka
    const REPEAT_ROZVEDKA = "rozvedka_repeat";
    const MAX_OBTIZNOST_OPERACE = "rozvedka_difficulty";

    // prices
    const ENERGY_PRICE = "energy";
    const FOOD_PRICE = "food";
    const TECHNOLOGY_PRICES = "tech_prices";

    // country stats
    const COUNTRY_STATS = "country_stats";

    // pokroky
    const ADV_SHOW_BASE_PRICE = "advancements_show_base_price";
    const ADV_SHOW_BASE_PRICE_IN_TOOLTIP = "advancements_show_base_price_tooltip";
    const ADV_SHOW_MIN_PRICE = "advancements_show_min_price";
    const ADV_SHOW_MIN_PRICE_IN_TOOLTIP = "advancements_show_min_price_tooltip";
    const ADV_SHOW_CURRENT_PRICE = "advancements_show_current_price";

    // aliance
    const ALI_ALLOW_CHANGES = "aliance_modify";
    const ALI_SHOW_CARDS = "aliance_show_cards";
    const ALI_SHOW_REGIME = "aliance_show_regime";
    const ALI_SHOW_ROCKETS = "aliance_show_rockets";
    const ALI_SHOW_WARS = "aliance_show_wars";
    const ALI_WARS_WITH_ANCHORS = "aliance_show_wars_with_anchors";
    const ALI_SHOW_FILTER = "aliance_show_filter";
    const ALI_SAVED_INFO = "aliance_saved_info";

    // strike
    const STRIKE_SHOW_EXP = "strike_show_exp";
    const STRIKE_WARNING_DISABLES_BUTTON = "strike_warning_disables_button";

    const USED_SETTINGS = [
        ROZVOJ_MENU_LINK,
        GENERALS_MENU_LINK,
        PRODEJ_MENU_LINK,
        POKROKY_MENU_LINK,
        REPEAT_ROZVEDKA,
        MAX_OBTIZNOST_OPERACE,
        VALKY_OBRACENE,
        ZOBRAZENE_ZOLDY,
        ENERGY_PRICE,
        FOOD_PRICE,
        COUNTRY_ALI_DATA,
        COUNTRY_STATS,
        TECHNOLOGY_PRICES,
        MODIFY_ARCHIVE,
        MODIFY_ADVANCEMENTS,
        ADV_SHOW_BASE_PRICE,
        ADV_SHOW_BASE_PRICE_IN_TOOLTIP,
        ADV_SHOW_MIN_PRICE,
        ADV_SHOW_MIN_PRICE_IN_TOOLTIP,
        ADV_SHOW_CURRENT_PRICE,
        ALI_ALLOW_CHANGES,
        ALI_SHOW_CARDS,
        ALI_SHOW_REGIME,
        ALI_SHOW_ROCKETS,
        ALI_SHOW_WARS,
        ALI_WARS_WITH_ANCHORS,
        ALI_SHOW_FILTER,
        ALI_SAVED_INFO,
        STRIKE_SHOW_EXP,
        STRIKE_WARNING_DISABLES_BUTTON,
    ];

    function execute() {
        deleteUnusedSettings();

        createMenuItemSettings();
        addMenuLinks();
        modifyLista();

        if (location.href.indexOf("s=detailyaliance") != -1) {
            modifyDetailyAli();
        } else if (location.href.indexOf("s=ppravidla") != -1) {
            modifyPravidlaValky();
        } else if (location.href.indexOf("index.php?p=konflikty&s=awarstat&getali=") != -1) {
            modifyValky();
        } else if (location.href.indexOf("p=dotace&s=ehelp") != -1) {
            modifyHospoPage();
        } else if (location.href.endsWith("p=detaily") || location.href.indexOf("detaily&sid=") != -1 || location.href.indexOf("p=detaily&s=detaily&adetaily=true") != -1) {
            processCountryDetail();
        } else if (location.href.indexOf("s=detailyprestiz") != -1) {
            processPrestigeDetails();
        } else if (location.href.endsWith("p=rozvedka&s=tajsluzba") == true || location.href.indexOf("p=rozvedka") !== -1) {
            modifyRozvedka();
        } else if (location.href.endsWith("p=zebricek&s=stats")) {
            modifyStatisticsPage();
        } else if (location.href.indexOf("archiv") != -1) {
            modifyArchive();
        } else if (location.href.indexOf("p=technologie&s=pokroky") != -1) {
            modify_advancements.process();
        } else if (location.href.indexOf("p=valka") != -1) {
            modify_valka.process();
        } else if (location.href.indexOf("p=zebricek&s=aliazebr") != -1) {
            ali_zebricek.process();
        }
    }

    let log;
    setLogger(console.log);

    function setLogger(logger) {
        // eslint-disable-next-line no-undef
        log = logger.bind(console, `[${GM_info.script.name}]`);
    }

    function setSetting(setting_name, value) {
        // eslint-disable-next-line no-undef
        GM_setValue(setting_name, value);
    }

    function getSetting(setting_name, default_value) {
        // eslint-disable-next-line no-undef
        return GM_getValue(setting_name, default_value);
    }

    async function deleteUnusedSettings() {
        // eslint-disable-next-line no-undef
        const keys = await GM_listValues();

        keys.forEach((key) => {
            if (!USED_SETTINGS.includes(key)) {
                // eslint-disable-next-line no-undef
                GM_deleteValue(key);
            }
        });
    }

    function createMenuItemSettings() {
        const menu = document.getElementById("left_menu");
        const list = menu.children[0];

        let li = createElement("li");
        list.appendChild(li);

        let ul = createElement("ul");
        li.appendChild(ul);

        li = createElement("li");
        ul.appendChild(li);

        const settings_button = createElement("a", { textContent: "Modifikace ⚙️" });
        settings_button.addEventListener("click", function () {
            let element = document.getElementById("settings_window");
            element.classList.toggle("hidden");
        });

        li.appendChild(settings_button);

        createSettingsWindow();
    }

    function createElement(tag = "div", props = null, attributes = null) {
        let element = document.createElement(tag);

        for (const key in props) {
            if (!Object.hasOwnProperty.call(props, key)) {
                continue;
            }

            const value = props[key];
            element[key] = value;
        }

        for (const key in attributes) {
            if (!Object.hasOwnProperty.call(attributes, key)) {
                continue;
            }

            const value = attributes[key];
            if (typeof value === "object") {
                Object.assign(element[key], value);
            } else {
                element.setAttribute(key, value);
            }
        }

        return element;
    }

    function createButton(text, func = null, params = null, props = {}, attributes = null) {
        if (props === null) props = {};
        props.textContent = text;
        props.type = "button";
        const class_name = props.className ? props.className + " " : "";
        props.className = class_name + "submit btn";
        let button = createElement("button", props, attributes);
        button.addEventListener("click", (e) => {
            if (func !== null) {
                func(params, e);
            }
        });
        return button;
    }

    function createSettingsWindow() {
        addSettingsCss();

        const content = document.getElementById("content");
        let div = createElement("div", { id: "settings_window", className: "settings_window hidden" });
        content.appendChild(div);

        let button = createButton(
            "x",
            () => {
                div.classList.toggle("hidden");
            },
            null,
            { className: "btn-close" }
        );
        div.appendChild(button);

        let settings = {
            menu_link_header: { label: "Zobrazit v menu odkazy na:", header: true },
            rozvoj_menu_link: { setting_name: ROZVOJ_MENU_LINK, label: "Rozvoj", default_value: true },
            generalove_menu_link: { setting_name: GENERALS_MENU_LINK, label: "Generály", default_value: true },
            prodej_menu_link: { setting_name: PRODEJ_MENU_LINK, label: "Prodej", default_value: true },
            pokroky_menu_link: { setting_name: POKROKY_MENU_LINK, label: "Pokroky", default_value: true },

            archive_header: { label: "Archiv:", header: true, space_before: true },
            archive_changes: { setting_name: MODIFY_ARCHIVE, label: "Povolit změny", default_value: true },

            pokroky_header: { label: "Pokroky:", header: true, space_before: true },
            pokroky_changes: { setting_name: MODIFY_ADVANCEMENTS, label: "Povolit změny", default_value: true },
            min_price: { setting_name: ADV_SHOW_MIN_PRICE, label: "Povolit zobrazení minimální ceny", default_value: true },
            min_price_tooltip: { setting_name: ADV_SHOW_MIN_PRICE_IN_TOOLTIP, label: "Zobrazit minimální cenu v tooltipu", default_value: false },
            base_price: { setting_name: ADV_SHOW_BASE_PRICE, label: "Povolit zobrazení základní ceny", default_value: true },
            base_price_tooltip: { setting_name: ADV_SHOW_BASE_PRICE_IN_TOOLTIP, label: "Zobrazit základní cenu v tooltipu", default_value: true },
            current_price: { setting_name: ADV_SHOW_CURRENT_PRICE, label: "Zobrazit aktuální cenu", default_value: true },

            strike_header: { label: "Útok:", header: true, space_before: true },
            strike_exp: { setting_name: STRIKE_SHOW_EXP, label: "Zobrazit zkušenosti za kolo", default_value: true },
            strike_button: { setting_name: STRIKE_WARNING_DISABLES_BUTTON, label: "Potvrzení útoku při porušení pravidel", default_value: true },

            ali_header: { label: "Žebříček aliancí:", header: true, space_before: true },
            ali_changes: { setting_name: ALI_ALLOW_CHANGES, label: "Povolit změny", default_value: true },
            ali_show_cards: { setting_name: ALI_SHOW_CARDS, label: "Zobrazit sloupec kartiček", default_value: true },
            ali_show_regime: { setting_name: ALI_SHOW_REGIME, label: "Zobrazit sloupec zřízení", default_value: true },
            ali_show_rockets: { setting_name: ALI_SHOW_ROCKETS, label: "Zobrazit sloupec rakety", default_value: true },
            ali_show_wars: { setting_name: ALI_SHOW_WARS, label: "Zobrazit sloupec válek", default_value: true },
            ali_show_wars_with_anchors: { setting_name: ALI_WARS_WITH_ANCHORS, label: "Sloupec válek obsahuje i kotvy", default_value: true },
            ali_show_filter: { setting_name: ALI_SHOW_FILTER, label: "Zobrazit filtr", default_value: true },

            others_header: { label: "Jiné:", header: true, space_before: true },
            zobrazene_zoldy: { setting_name: ZOBRAZENE_ZOLDY, label: "Zobrazit výdaje armády v detailu země", default_value: true },
            valky_obracene: { setting_name: VALKY_OBRACENE, label: "Války - poměr prestiží na začátku: vyhlašující ali / cílová", default_value: true },
        };

        for (const key in settings) {
            const setting = settings[key];

            if (setting.header) {
                const h3 = createElement("h3", { textContent: setting.label, className: setting.space_before ? "m-top" : "" });
                div.appendChild(h3);
            } else {
                const config = createConfig(setting.setting_name, setting.label, false, setting.default_value);
                div.appendChild(config);
            }
        }

        button = createButton(
            "Refresh stránky",
            () => {
                refreshPage();
            },
            null,
            { className: "btn-refresh" }
        );
        div.appendChild(button);
    }

    function addArchiveCss() {
        const styles = `
            .row {
                display: grid;
                gap: 1rem;
                grid-template-columns: 5fr 4fr 4fr;
            }

            .row span:first-child {
                font-weight: bold;
            }

            .row span:not(:first-child) {
                text-align: right;
            }
        `;
        addCss(styles);
    }

    function addSettingsCss() {
        const styles = `
    .icon {
        display: inline-block;
        width: 16px;
        height: 16px;
        border: 1px solid black;
        border-radius: 10px;
        background: #aff;
        color: black;
        font-weight: bold;
        text-align: center;
    }

    .btn {
        width: fit-content;
        border-radius: 2px;
        padding: 0.2rem 0.4rem;
        background-color: #444;
        color: white;
        cursor: pointer;
        font-weight: bold;
        text-shadow: 0 0 5px black;
        -webkit-transition: background-color 400ms linear;
        -ms-transition: background-color 400ms linear;
        transition: background-color 400ms linear;
    }

    .btn:hover {
        background-color: #777;
    }
        
    .btn.btn-close {
        position: absolute;
        top: 5px;
        right: 5px;
    }

    .btn.btn-refresh {
        margin-left: auto;
        margin-right: auto;
    }

    .settings_window {
        display: flex;
        flex-direction: column;
        gap: 2px;
        z-index: 1000;
        position: fixed;
        padding: 10px;
        top: 130px;
        left: 150px;
        background: #666;
        border: 1px solid #aaa;
        border-radius: 5px
    }

    .settings_window h3 {
        margin: 0 0 6px 0;
        font-weight: bold;
        text-shadow: 0 0 5px black;
    }

    .settings_window .m-top {
        margin-top: 10px;
    }

    .settings_window input[type=checkbox] {
        margin-right: 10px;
        cursor: pointer;
        box-shadow: 3px 3px #555;
    }

    .hidden {
        display: none;
    }`;

        addCss(styles);
    }

    function addTooltipCss(tooltip_width = 100, to_left = false) {
        const margin_left = to_left ? tooltip_width * 0.75 : tooltip_width / 2;
        const left = to_left ? 75 : 50;

        const styles = `
            .tooltip { position: relative; }
            .tooltip .tooltiptext { 
                visibility: hidden; 
                width: ${tooltip_width}px; 
                top: 100%; 
                left: ${left}%; 
                margin-left: -${margin_left}px; 
                background-color: #363636; 
                color: #fff;
                text-align: left;
                padding: 5px 5px;
                margin-top: 10px;
                border-radius: 6px;
                border: 1px solid white;
                position: absolute;
                z-index: 1;
            }
            .tooltip .tooltiptext.center { text-align: center; }
            .tooltip:hover .tooltiptext { visibility: visible; }
            .tooltip .tooltiptext::after { 
                content: ' ';
                position: absolute;
                bottom: 100%;
                left: ${left}%;
                margin-left: -5px;
                border-width: 5px;
                border-style: solid;
                border-color: transparent transparent white transparent;
            }
            .tooltiptext .header { font-weight: bold; border-bottom: 1px solid white; }
            .tooltiptext .divider { height: 1px; width: 100%; border-bottom: 1px solid white; } `;
        addCss(styles);
    }

    function wrapInTooltip(element) {
        element.classList.add("tooltiptext");
        element.parentElement.classList.add("tooltip");
    }

    function createConfig(setting_name, label_text, force_reload = false, default_value = true) {
        let label = createElement("label");

        let input = createElement("input", { type: "checkbox", checked: getSetting(setting_name, default_value) });
        label.appendChild(input);
        label.appendChild(document.createTextNode(label_text));

        input.addEventListener("change", function () {
            setSetting(setting_name, input.checked);
            if (force_reload) {
                refreshPage();
            }
        });
        return label;
    }

    function addMenuLinks() {
        let vlada_menu_index = 4;
        let play_menu_index = 5;

        addButtonIfToggled(ROZVOJ_MENU_LINK, "Rozvoj", "index.php?p=vlada&s=rozvoj", vlada_menu_index, 1);
        addButtonIfToggled(GENERALS_MENU_LINK, "Generálové", "index.php?p=valka&s=general", play_menu_index, 6);
        addButtonIfToggled(PRODEJ_MENU_LINK, "Prodej", "index.php?p=svetovy_trh&s=trhposlat", play_menu_index, 4);
        addButtonIfToggled(POKROKY_MENU_LINK, "Pokroky", "index.php?p=technologie&s=pokroky", play_menu_index, 3);
    }

    function addButtonIfToggled(value_key, button_text, link, menu_index, submenu_index) {
        if (!getSetting(value_key, true)) {
            return;
        }

        let menu = document.getElementById("left_menu");
        menu = menu.children[0];
        addButton(button_text, link, menu, menu_index, submenu_index);
    }

    function addButton(button_text, link, menu, menu_index, submenu_index) {
        let relevant_menu = menu.children[menu_index].children[0];

        let element = createElement("a", { href: link, textContent: button_text });

        let li = createElement("li");
        li.append(element);

        relevant_menu.insertBefore(li, relevant_menu.children[submenu_index]);
    }
    // DETAILY ALI

    function modifyDetailyAli() {
        let table = document.getElementsByClassName("vis_tbl");
        if (table == null) {
            return;
        }

        table = table[0];
        let exp_object = calculateExp(table);

        let container_element = document.getElementsByClassName("container");
        let content_element = container_element[0].children[0];
        let div = createElement("div", {
            style: "width: 250px; background: #222222; border: 2px solid; border-bottom-color: #000; border-right-color: #000; border-left-color: #555; border-top-color: #555; padding: 3px; margin-left: calc(50% - 125px); text-align: center;",
        });

        let p = createElement("p", { textContent: "Aktualní ali hodnost: " + exp_object.current_ali_level });
        div.append(p);

        p = createElement("p", { textContent: "Celkový počet zkušeností ali: " + exp_object.total_exp + "k" });
        div.append(p);

        p = createElement("p", { textContent: "Chybějící počet zkušeností ali: " + exp_object.missing_exp + "k" });
        div.append(p);

        content_element.insertBefore(div, content_element.children[1]);
    }

    function calculateExp(table) {
        const EXPERIENCE_PER_LEVEL = [10000, 20000, 40000, 80000, 150000, 250000, 400000, 600000, 850000, 1150000, 1500000, 1950000, 2450000];

        let rows = table.getElementsByTagName("tr");
        let exp_total_row = rows[17];
        let exp_total_columns = exp_total_row.getElementsByTagName("td");

        let total_exp = 0;
        let row_count = exp_total_columns.length;
        let member_count = row_count - 1;

        for (let i = 1; i < row_count; i++) {
            total_exp = total_exp + parseInt(exp_total_columns[i].innerHTML) * 1000;
        }

        for (let i = 0; i < EXPERIENCE_PER_LEVEL.length; i++) {
            if (EXPERIENCE_PER_LEVEL[i] * member_count < total_exp) {
                continue;
            }
            let missing_exp = EXPERIENCE_PER_LEVEL[i] * member_count - total_exp;

            let exp_object = {
                missing_exp: missing_exp / 1000,
                total_exp: total_exp / 1000,
                current_ali_level: i + 1,
            };
            return exp_object;
        }
    }

    // DETAILY ALI KONEC

    // HOSPO, MAX PRESTIZ PRESTIZ
    function modifyHospoPage() {
        const table = document.forms[0].children[0];
        if (table == null) return false;

        const row = table.insertRow(table.rows.length - 1);
        row.insertCell().textContent = "Prestiž";

        let total_prestige = 0;
        let prestige_per_unit = [0, 0, 0, 0.002, 0.02, 0.02, 1, 5, 3.5, 3.5, 2.7];
        for (let i = 3; i < table.rows.length - 2; i++) {
            const cell = table.rows[i].cells[1];
            total_prestige += parseFloat(Number(cell.textContent) * prestige_per_unit[i]);

            const input = table.rows[i].cells[2].children[0];
            input.dataset.prestige = prestige_per_unit[i];
            input.addEventListener("keyup", processHospaPrestige);
        }
        row.insertCell().textContent = parseInt(total_prestige * 10) / 10;
        const cell = row.insertCell();
        cell.textContent = 0;
        cell.id = "sent_prestige";
    }

    function processHospaPrestige() {
        const result_cell = document.getElementById("sent_prestige");

        let total_prestige = 0;
        const table = document.forms[0].children[0];

        for (let i = 3; i < table.rows.length - 2; i++) {
            const max_value = Number(table.rows[i].cells[1].textContent);

            const cell = table.rows[i].cells[2];
            const input = cell.children[0];

            let value = input.value;
            if (value <= 0) {
                continue;
            }
            value = value > max_value ? max_value : value;

            const prestige_per_unit = parseFloat(input.dataset.prestige);

            total_prestige += value * prestige_per_unit;
        }

        result_cell.textContent = total_prestige.toFixed(1);
    }
    // HOSPO KONEC

    // PRAVIDLA VALEK
    function modifyPravidlaValky() {
        const table = document.forms[0].elements[1].closest("table");

        if (table == null) {
            return;
        }

        let button;
        let elements = getElementsInTheWarForm(1);

        button = createButton("Reset", resetWar, elements);
        table.parentElement.insertBefore(button, table.nextSibling);

        button = createButton("Přepnout", toggleUtoky, elements);
        table.parentElement.insertBefore(button, table.nextSibling);

        button = createButton("Farmy (10)", warFarmyUtoky, elements);
        table.parentElement.insertBefore(button, table.nextSibling);

        button = createButton("Farmy", setWarAgainstFarms, elements);
        table.parentElement.insertBefore(button, table.nextSibling);

        button = createButton("Klasika", setWarClassic, elements);
        table.parentElement.insertBefore(button, table.nextSibling);
    }

    function toggleUtoky(elements) {
        for (let i = 0; i < elements.length; i++) {
            elements[i].value = 1 - elements[i].value;
            elements[i].checked = !elements[i].checked;
        }
    }

    function setUtoky(elements, values) {
        for (let i = 0; i < elements.length; i++) {
            elements[i].value = values[i] == true ? 1 : 0;
            elements[i].checked = values[i];
        }
    }

    function setTextInputy(elements, values) {
        for (let i = 0; i < elements.length; i++) {
            elements[i].value = values[i];
        }
    }

    function warFarmyUtoky(elements) {
        setWarAgainstFarms(elements);

        let text_input_elements = getElementsInTheWarForm(1, "text");
        setTextInputy(text_input_elements, [0, 0, 10]);
    }

    function setWarAgainstFarms(elements) {
        let values = [
            false,
            false,
            true, // normaly jen lupy
            true,
            true,
            true,
            false,
            true,
            true, // taktika mimo bombeni
            true,
            false,
            false,
            false,
            true,
            false,
            true,
            true,
            false,
            false,
            false, // sabotaz jen epky, bourani, demoralizace, sabotaz
            true,
            true,
            true,
            true,
            true, // kradez rozvedkou = ok
            true,
            false,
            false,
            false, // jen konve
        ];

        setUtoky(elements, values);
    }

    function setWarClassic(elements) {
        let values = [
            true,
            false,
            true, // normaly jen lupy
            true,
            true,
            true,
            false,
            true,
            true, // taktika mimo bombeni
            true,
            false,
            false,
            false,
            true,
            false,
            true,
            true,
            false,
            false,
            false, // sabotaz jen epky, bourani, demoralizace, sabotaz
            true,
            true,
            true,
            true,
            true, // kradez rozvedkou = ok
            true,
            false,
            false,
            false, // jen konve
        ];

        setUtoky(elements, values);
    }

    function resetWar(elements) {
        for (let i = 0; i < elements.length; i++) {
            elements[i].value = 1;
            elements[i].checked = true;
        }
    }

    function getElementsInTheWarForm(nth, type = "checkbox") {
        return document.querySelectorAll("form>div:nth-of-type(" + nth + ") input[type=" + type + "]");
    }

    // PRAVIDLA VALEK KONEC

    // ROZVEDKA

    function repeatAction(difficulties) {
        const repeat_action = getSetting(REPEAT_ROZVEDKA, 0);
        if (repeat_action == 0) {
            // log("opky se uz nemaji opakovat => konec");
            return;
        }

        // ziskat obtiznost z opky
        let content = document.getElementById("icontent");
        if (content == null) return;

        let message_start = content.innerHTML.indexOf("<br><br><br>");
        if (message_start == -1) return;

        let message = content.innerHTML.slice(message_start + 12);
        message = message.slice(0, message.indexOf("<"));

        if (message.indexOf("infiltrovali") != -1) {
            // log("infiltrace úspěšná => konec.");
            setSetting(REPEAT_ROZVEDKA, 0);
            return;
        }

        let difficulty_text = content.querySelector("b");
        if (difficulty_text == null) {
            // log("tucny text nenalezen => konec");
            setSetting(REPEAT_ROZVEDKA, 0);
            return;
        }
        difficulty_text = difficulty_text.innerText;
        let obtiznost = difficulties.indexOf(difficulty_text);
        if (obtiznost == -1) {
            // log("Obtížnost [" + difficulty_text + "] nebyla nalezena => konec");
            setSetting(REPEAT_ROZVEDKA, 0);
            return;
        }

        const max_operation_difficulty = getSetting(MAX_OBTIZNOST_OPERACE, 0);
        if (obtiznost > max_operation_difficulty) {
            // log("Obtížnost je moc vysoka => konec");
            setSetting(REPEAT_ROZVEDKA, 0);
            return;
        }
        setSetting(REPEAT_ROZVEDKA, repeat_action - 1);

        // log("reload page");
        location.reload();
    }

    let technology_prices = (function () {
        let prices = null;

        function fetchTechnologyPrices() {
            return new Promise((resolve) => {
                let req = new XMLHttpRequest();
                let url = "index.php?p=svetovy_trh&s=techkoupit";
                req.open("GET", url);
                req.onload = function () {
                    if (req.readyState == 4 && req.status == 200) {
                        let parser = new DOMParser();
                        let doc = parser.parseFromString(req.response, "text/html");

                        let warning_elements = doc.getElementsByClassName("warn");

                        if (warning_elements.length > 0 || !doc.forms[0]) {
                            // reject("Není přístup na světový trh");
                            return;
                        }

                        let technologie_prices = [];

                        let price_cells = doc.forms[0].children[0].querySelectorAll(".mactprice");

                        price_cells.forEach((price_cell) => {
                            const value = Number(price_cell.innerHTML.split("<")[0].replace(/\s/g, ""));

                            technologie_prices.push(value);
                        });

                        resolve(technologie_prices);
                    } else {
                        // reject("Soubor nenalezen");
                    }
                };
                req.send();
            });
        }

        return {
            async get() {
                if (prices) return prices;

                let technology_prices_data = getSetting(TECHNOLOGY_PRICES, null);
                let time_in_millis = getCurrentTimeInMillis();
                const hour_in_millis = 60 * 60 * 1000;

                if (!technology_prices_data || technology_prices_data.updated_at == undefined || technology_prices_data.updated_at + hour_in_millis < time_in_millis) {
                    let technology_prices = await fetchTechnologyPrices();
                    technology_prices_data.prices = technology_prices;

                    setSetting(TECHNOLOGY_PRICES, {
                        updated_at: time_in_millis,
                        prices: technology_prices,
                    });
                }
                prices = technology_prices_data.prices;

                return prices;
            },
        };
    })();

    async function modifyRozvedka() {
        if (document.forms.tajsluzba === undefined) return;

        let header_elements = document.getElementsByTagName("h1");
        if (header_elements.length == 0 || header_elements[0].innerText != "Rozvědka") {
            return false;
        }

        const difficulties = ["snadná jak facka", "jednoduchá", "středně náročná", "velice náročná", "extrémně náročná", "téměř neproveditelná"];

        repeatAction(difficulties);

        let submit_elements = document.getElementsByClassName("submit");
        let list_elements = document.getElementsByClassName("tbl_sim");

        if (submit_elements.length == 0 || list_elements.length == 0) {
            return true;
        }

        let max_operation_element = document.querySelector("#icontent > p > strong");

        let max_operation_count = parseInt(max_operation_element.innerText);
        if (isNaN(max_operation_count) == true) {
            max_operation_count = 15;
        }

        let submit_element = submit_elements[0];
        let list_element = list_elements[0];

        let inputs = [];

        let li_index = 2;

        // kolikrat opakovat opku
        let li = createElement("li", { textContent: "Opakovat operaci: " });

        let input = createNumberInput("repeat_operation", 5, 1, max_operation_count);
        li.append(input);
        inputs[0] = input;

        list_element.insertBefore(li, list_element.children[li_index++]);
        //

        // do jake obtiznosti
        li = createElement("li", { textContent: "Maximální obtížnost operace: " });

        input = createElement("select");
        li.append(input);

        for (var i = 0; i < difficulties.length; i++) {
            input.append(createElement("option", { value: i, textContent: difficulties[i] }));
        }
        input.options[2].selected = true;

        inputs[1] = input;

        list_element.insertBefore(li, list_element.children[li_index++]);
        //

        // brute force button
        let button = createButton("Opakuj operaci");
        button.params = inputs;

        button.addEventListener("click", (e) => {
            setSetting(REPEAT_ROZVEDKA, e.currentTarget.params[0].value - 1);
            setSetting(MAX_OBTIZNOST_OPERACE, e.currentTarget.params[1].selectedIndex);

            let submit_elements = document.getElementsByClassName("submit");
            let list_elements = document.getElementsByClassName("tbl_sim");
            if (submit_elements.length == 0 || list_elements.length == 0) {
                return true;
            }

            let submit_element = submit_elements[0];
            submit_element.click();
        });
        submit_element.parentElement.append(button);

        const stat_elements = wrapCountrySpyStatsAroundElement(list_element);
        list_element.style.height = "fit-content";
        list_element.style.marginBottom = "0";
        list_element.style.marginTop = "auto";

        stat_elements[0].style.marginTop = "14px";
    }

    function wrapCountrySpyStatsAroundElement(element, country_id = getCountryId()) {
        const country_stats = getCountryStats(country_id);
        if (country_stats === undefined) return;

        const country_stats_table = createCountryStatsSpySuccessTable(country_stats);
        const operations_stats_table = createCountryStatsSpyOperationsTable(country_stats);

        const combined_spy_stats_difficulty = getAllSpyStats();
        const combined_country_stats_table = createCombinedSpySuccessTable(combined_spy_stats_difficulty);

        let tables = [country_stats_table, combined_country_stats_table, operations_stats_table];
        for (let index = 0; index < tables.length; index++) {
            const table = tables[index];
            table.style.margin = "unset";
            table.style.height = "fit-content";
        }

        let container = createElement("div", { style: "position: relative" });
        element.parentElement.insertBefore(container, element);

        let wrapper = createElement("div", { style: "display: flex; gap: 0.5rem; justify-content: center; margin: 5px auto;" });
        container.appendChild(wrapper);

        wrapper.appendChild(operations_stats_table);

        element.style.margin = "unset";
        wrapper.appendChild(element);

        const country_stats_table_wrapper = createElement();
        wrapper.appendChild(country_stats_table_wrapper);

        country_stats_table_wrapper.appendChild(createElement("h2", { textContent: "Statistiky země 🔄", style: "padding-bottom: 0.25rem" }));
        country_stats_table_wrapper.appendChild(country_stats_table);

        const combined_country_stats_table_wrapper = createElement();
        wrapper.appendChild(combined_country_stats_table_wrapper);

        // h2.style.textAlign = "center";
        combined_country_stats_table_wrapper.appendChild(createElement("h2", { textContent: "Kombinované statistiky všech zemí 🔄", style: "padding-bottom: 0.25rem;" }));
        combined_country_stats_table_wrapper.appendChild(combined_country_stats_table);
        combined_country_stats_table_wrapper.style.display = "none";

        country_stats_table_wrapper.addEventListener("click", () => {
            country_stats_table_wrapper.style.display = "none";
            combined_country_stats_table_wrapper.style.display = "block";
        });

        combined_country_stats_table_wrapper.addEventListener("click", () => {
            combined_country_stats_table_wrapper.style.display = "none";
            country_stats_table_wrapper.style.display = "block";
        });

        return [operations_stats_table, country_stats_table_wrapper, combined_country_stats_table_wrapper];
    }

    function getCountryId() {
        let country_id = document.querySelector("#uLista a").href.split("id=")[1];
        if (country_id == undefined) {
            //obyc lista, mozna nefunguje?
            const lista = document.getElementById("#uLista");
            country_id = lista.children[0].textContent.split("#")[1];
        }
        return country_id;
    }

    function createNumberInput(id, default_value = 5, min = 1, max = 15, class_text = "short") {
        if (default_value > max) {
            default_value = max;
        }
        let input = createElement("input", { className: class_text, type: "number", value: default_value, min, max, id });

        return input;
    }

    // ROZVEDKA KONEC

    // valky

    function modifyValky() {
        const flipped = getSetting(VALKY_OBRACENE, 0);

        const textContent = flipped == 1 ? "Přehodit počítání prestiže na: cílová / vyhlašující ali" : "Přehodit počítání prestiže na: vyhlašující ali / cílová";
        let func = () => {
            setSetting(VALKY_OBRACENE, 1 - getSetting(VALKY_OBRACENE, 0));
            location.reload();
        };
        let button = createButton(textContent, func);

        let div = createElement("div", { style: "margin: 0 auto; width: fit-content;" });
        div.append(button);

        let header = document.getElementsByTagName("h1")[0];
        header.parentElement.insertBefore(div, header.nextElementSibling);

        let tables = document.querySelectorAll(".container .vis_tbl");

        // ziskat nazev ali ve valce
        let offset = isGameOver() ? 1 : 0;

        for (let i = 1 + offset; i < tables.length; i++) {
            const table = tables[i];

            for (let column_index = 4; column_index < 6; column_index++) {
                const vyhlasujici_ali_prestiz = parseInt(table.rows[column_index].children[1].innerText);
                const cilova_ali_prestiz = parseInt(table.rows[column_index].children[2].innerText);

                let procentualni_sila_ali = flipped == 0 ? cilova_ali_prestiz / vyhlasujici_ali_prestiz : vyhlasujici_ali_prestiz / cilova_ali_prestiz;
                procentualni_sila_ali = Math.round(procentualni_sila_ali * 10000) / 100;

                let color = "palegreen";
                if ((procentualni_sila_ali > 120 && flipped == 0) || (procentualni_sila_ali < 80 && flipped == 1)) {
                    color = "chartreuse";
                } else if ((procentualni_sila_ali < 80 && flipped == 0) || (procentualni_sila_ali > 120 && flipped == 1)) {
                    color = "coral";
                }

                table.rows[column_index].children[0].append(createElement("span", { textContent: "(" + procentualni_sila_ali + "%)", style: "margin-left: 5px; color: " + color }));
            }
        }
    }

    // valky konec

    // lista
    function saveResourcePrices() {
        if ((location.href.indexOf("svetovy_trh") != -1 && location.href.indexOf("tech") === -1) || location.href.indexOf("domtrh") != -1) {
            getPricesFromMarket();
        } else if (location.href.indexOf("index.php?p=archiv") === -1) {
            let info_messages = document.getElementsByClassName("infomsg");
            if (info_messages.length === 0) {
                return;
            }

            let count = info_messages.length - 1,
                food_set = false,
                energy_set = false,
                max_tries = 3;

            while (--count >= 0 && max_tries-- > 0) {
                let info_message = info_messages[count];
                if (info_message.textContent.indexOf("nakoupili") === -1) {
                    continue;
                }

                const price = Number(info_message.textContent.split("po ")[1].split("$")[0]);
                let variable;

                if (info_message.textContent.indexOf("jídla") != -1) {
                    if (food_set) continue;

                    variable = FOOD_PRICE;
                    food_set = true;
                } else if (info_message.textContent.indexOf("energie") != -1) {
                    if (energy_set) continue;

                    variable = ENERGY_PRICE;
                    energy_set = true;
                }

                if (variable !== undefined) {
                    setSetting(variable, price);
                }

                if (food_set && energy_set) {
                    return;
                }
            }
        }
    }

    function getPricesFromMarket() {
        const column_names = ["cena/kus", "tržní cena"];

        let tables = document.getElementsByClassName("vis_tbl");
        if (tables.length === 0) {
            return;
        }

        let column_price = 0,
            table;

        main_loop: for (let i = 0; i < tables.length; i++) {
            table = tables[i];

            for (let j = 0; j < table.rows[0].cells.length; j++) {
                const cell = table.rows[0].cells[j];
                if (column_names.includes(cell.textContent.toLowerCase())) {
                    column_price = j;
                    break main_loop;
                }
            }
        }

        if (column_price === 0) {
            return;
        }

        let cell = table.rows[1].cells[column_price];
        let price = cell.innerText.split("\n")[0].replace("$", "");
        if (price === "") {
            price = cell.children[0].value;
        }
        setSetting(FOOD_PRICE, Number(price));

        cell = table.rows[2].cells[column_price];
        price = cell.innerText.split("\n")[0].replace("$", "");
        if (price === "") {
            price = cell.children[0].value;
        }
        setSetting(ENERGY_PRICE, Number(price));
    }

    function modifyLista() {
        if (isGameOver()) {
            return;
        }

        saveResourcePrices();

        let lista = document.getElementById("uLista");
        if (lista == null) {
            // lista neni z nejakeho duvodu vubec na strance => skip
            return;
        }

        if (lista.rows[0].cells[0].children[0].tagName == "IMG") {
            processGoldLista();
            addListaObserver();

            return;
        }

        let tables = document.getElementsByTagName("table");
        const dividers = ["$", "t", "MWh"];

        // chceme vsechny tabulky, protoze na strance se muze 2x vyskytovat #uLista - stara + nova po odehrani kol
        for (let i = 0; i < tables.length; i++) {
            const table = tables[i];
            if (table.id != "uLista") {
                // neni uLista => skip
                continue;
            }

            const row = table.rows[0];
            for (let j = 1; j < 4; j++) {
                const cell = row.cells[j];
                processListaCell(cell, dividers[j - 1]);
            }
        }
    }

    function addListaObserver() {
        let lista_elements = document.querySelectorAll("#uLista");
        const lista_element = lista_elements[lista_elements.length - 1];

        addObserver(lista_element, processGoldLista);
    }

    function addObserver(
        element,
        callback,
        options = {
            childList: true,
            subtree: false,
        }
    ) {
        let observer = new MutationObserver(function (mutations) {
            mutations.forEach(function (mutation) {
                if (mutation.type == "childList" && mutation.addedNodes.length > 0) {
                    callback();
                }
            });
        });

        observer.observe(element, options);
    }

    function processGoldLista() {
        let lista_elements = document.querySelectorAll("table#uLista");

        for (let index = 0; index < lista_elements.length; index++) {
            const lista = lista_elements[index];

            updateGoldLista(lista);
        }
    }

    function updateGoldLista(lista) {
        let tables = lista.querySelectorAll("table");
        let table = tables[0];
        if (!table) {
            return;
        }
        const turns_element = lista.querySelector("strong");
        let playable_turns = 140;
        if (turns_element) {
            playable_turns = Number(turns_element.textContent.split("+")[0]);
        }

        const energy_price = getSetting(ENERGY_PRICE, 15);
        const food_price = getSetting(FOOD_PRICE, 15);
        const prices = [1, food_price, energy_price];

        const resource_names = ["money", "food", "energy"];
        const resources = new Map();
        resource_names.forEach((resource_name, index) => {
            let resource = {
                value: Number(table.rows[index].cells[1].textContent.replace(/\s/g, "")),
                per_turn: Number(table.rows[index].cells[2].textContent.replace(/\s/g, "")),
            };

            let stockpiled = 140;
            if (resource.per_turn < 0) {
                stockpiled = Math.floor(resource.value / -resource.per_turn);
            }
            if (stockpiled > 140 || stockpiled < 0) {
                stockpiled = 140;
            }
            resource.stockpiles_for = stockpiled;

            resource.spotreba = -resource.per_turn * prices[index];

            resources.set(resource_name, resource);
        });

        let cell;
        let resources_for_turns = getMoneyFor(resources.get("money"), resources.get("food"), resources.get("energy"));

        cell = table.rows[0].cells[3];
        if (cell === undefined) {
            cell = table.rows[0].insertCell();
            cell.style.textAlign = "right";
            cell.style.paddingLeft = "20px";
        } else {
            for (let index = cell.children.length - 1; index >= 0; index--) {
                const element = cell.children[index];
                element.remove();
            }
        }

        let props = { textContent: resources_for_turns[0], className: "plus" };

        if (Number(resources_for_turns[0].split(" kol")[0]) < playable_turns) {
            props.className = "minus";
        }

        let money_for_turns = createElement("span", props);
        cell.appendChild(money_for_turns);

        let separator = createElement("span", { textContent: " | " });
        cell.appendChild(separator);

        props = { textContent: resources_for_turns[1], className: "minus", style: "padding-right: 0;" };
        if (Number(resources_for_turns[1].replaceAll(String.fromCharCode(160), "").slice(0, -1)) > 0) {
            props.className = "plus";
        }
        let money_change_per_turn = createElement("span", props);
        cell.appendChild(money_change_per_turn);

        resource_names.forEach((resource_name, index) => {
            if (index === 0) return;

            const resource = resources.get(resource_name);

            if (resource.per_turn < 0) {
                const stockpiles_for = Math.round(resource.stockpiles_for * 10) / 10;
                const stockpiles_text = stockpiles_for == 0 ? "" : stockpiles_for + " kol | ";

                cell = table.rows[index].cells[3];
                if (cell === undefined) {
                    cell = table.rows[index].insertCell();
                    cell.style.textAlign = "right";
                    cell.style.paddingLeft = "20px";
                    cell.classList.add("minus");
                }
                cell.textContent = `${stockpiles_text} ${formatNumber(-resource.spotreba)}$`;
            }
        });
    }

    function getMoneyFor(money, food, energy) {
        let current_money = money.value,
            current_spotreba = money.spotreba,
            total_spotreba = current_spotreba + (energy.spotreba < 0 ? 0 : energy.spotreba) + (food.spotreba < 0 ? 0 : food.spotreba),
            money_for = 0,
            money_for_offset = 0;

        if (total_spotreba < 0) {
            return ["140+ kol", formatNumber(-total_spotreba) + "$"];
        }

        if (food.stockpiles_for !== energy.stockpiles_for && food.stockpiles_for !== 0) {
            let is_more_of_food = food.stockpiles_for > energy.stockpiles_for;
            let more_res = is_more_of_food ? food : energy;
            let less_res = is_more_of_food ? energy : food;

            if (less_res.stockpiles_for !== 0) {
                money_for = current_money / current_spotreba;
                if (money_for >= 0 && money_for < less_res.stockpiles_for) {
                    return [Math.floor(money_for) + " kol", formatNumber(-total_spotreba) + "$"];
                }
                money_for_offset = Math.min(money_for, less_res.stockpiles_for);
                current_money -= current_spotreba * less_res.stockpiles_for;
            }

            current_spotreba += less_res.spotreba;
            money_for = money_for_offset + current_money / current_spotreba;
            if (money_for >= 0 && money_for < more_res.stockpiles_for) {
                return [Math.floor(money_for) + " kol", formatNumber(-total_spotreba) + "$"];
            }
            money_for_offset = Math.min(money_for, more_res.stockpiles_for);

            current_money -= current_spotreba * (more_res.stockpiles_for - less_res.stockpiles_for);
        }
        current_spotreba = money.spotreba + food.spotreba + energy.spotreba;
        current_spotreba = money.spotreba + Math.max(food.spotreba, 0) + Math.max(energy.spotreba, 0);
        money_for = money_for_offset + current_money / current_spotreba;

        if (money_for > 140 || money_for < 0) {
            money_for = "140+";
        } else {
            money_for = Math.floor(money_for);
        }

        return [money_for + " kol", formatNumber(-total_spotreba) + "$"];
    }

    function formatNumber(number) {
        return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, String.fromCharCode(160));
    }

    function processListaCell(cell, divider) {
        if (cell == null) return;

        let text = cell.innerText;
        if (text == "") return;
        text = text.replaceAll(" ", "");

        const values = text.split(divider);
        const production = values[1].split(":")[1];

        if (production >= 0) {
            return;
        }

        const at_storage = values[0].split(":")[1];

        const turns_left = Math.floor((parseInt(at_storage) / parseInt(production)) * -1);
        addListaCellInformation(cell, turns_left);
    }

    function addListaCellInformation(cell, turns_left) {
        let color_class = "plus";
        if (turns_left < 20) {
            color_class = "minus";
        }
        const paragraph = createElement("p", { textContent: "Vydrží: ", className: color_class });

        const bold = createElement("b", { textContent: turns_left, className: color_class });
        bold.innerText = turns_left;
        if (color_class != null) {
            bold.classList.add(color_class);
        }
        paragraph.append(bold);
        paragraph.append(document.createTextNode("kol"));

        cell.append(paragraph);
    }
    //

    // detaily zeme
    class ArmyUnit {
        count;
        name;
        base_wage;
        base_food_consumption;
        base_energy_consumption;
        prestige;
        total_prestige;

        wages_total;
        expenses_total;
        expenses_per_unit;
        expenses_per_prestige_point;

        constructor(name, count, prestige) {
            this.name = name;
            this.count = count;
            this.prestige = prestige;
            this.total_prestige = prestige * count;
        }

        setCount(count) {
            this.count = count;
            this.total_prestige = this.prestige * count;
        }
    }

    function createArmyUnit(name, count, base_wage, prestige, base_food_consumption = 0.005, base_energy_consumption = 0) {
        const unit = new ArmyUnit(name, count, prestige);
        unit.base_wage = base_wage;
        unit.base_food_consumption = base_food_consumption;
        unit.base_energy_consumption = base_energy_consumption;
        return unit;
    }

    function createCountryStatsSpySuccessTable(country_stats) {
        const operation_difficulties = ["snadná jak facka", "jednoduchá", "středně náročná", "velice náročná", "extrémně náročná", "téměř neproveditelná"];
        return createCountryStatsSpyTable(country_stats.spy_stats.success_stats, operation_difficulties);
    }

    function createCombinedSpySuccessTable(complete_stats) {
        const operation_difficulties = ["snadná jak facka", "jednoduchá", "středně náročná", "velice náročná", "extrémně náročná", "téměř neproveditelná"];
        return createCountryStatsSpyTable(complete_stats, operation_difficulties);
    }

    function createCountryStatsSpyOperationsTable(country_stats) {
        const operation_names = archive_spy.OPERATION_SEARCH_TERMS.map((value) => value.operation);
        return createCountryStatsSpyTable(country_stats.spy_stats.operation_stats, operation_names, "Název operace", false);
    }

    function getAllSpyStats() {
        let all_country_stats = getSetting(COUNTRY_STATS, {});
        let total_stats = {};

        for (let country_id in all_country_stats) {
            let country_stats = all_country_stats[country_id];
            if (country_stats.spy_stats == null) continue;

            const success_stats = country_stats.spy_stats.success_stats;

            for (let key in success_stats) {
                if (total_stats[key] == null) {
                    total_stats[key] = {
                        count: 0,
                        success: 0,
                    };
                }
                total_stats[key].count += success_stats[key].count;
                total_stats[key].success += success_stats[key].success;
            }
        }

        return total_stats;
    }

    function createCountryStatsSpyTable(stats, keys, information_name = "Obtížnost", add_total_row = true) {
        let table = createElement("table", { className: "vis_tbl" });

        const header_text = [information_name, "Operací", "Úspěšných", "%"];
        const header = table.insertRow();
        header_text.forEach((text) => {
            const header_cell = createElement("th", { textContent: text });
            header.appendChild(header_cell);
        });

        let total_count = 0;
        let total_succeeded = 0;

        let colors = [
            // red to green
            "#900", // 0
            "#B20", // 1
            "#F50", // 2
            "#FA0", // 3
            "#FD0", // 4
            "#DF0", // 5
            "#AF0", // 6
            "#5F0", // 7
            "#2B0", // 8
            "#090", // 9
            "#090", // 10
        ];

        keys.forEach((key) => {
            const current_stat = stats[key];
            if (current_stat === undefined) return;
            const success_chance = (current_stat.count > 0 ? Math.round((current_stat.success / current_stat.count) * 1000) / 10 : 0) + "%";

            total_count += current_stat.count;
            total_succeeded += current_stat.success;

            const row = table.insertRow();

            let cell = row.insertCell();
            cell.classList.add("l");
            cell.innerText = key.charAt(0).toUpperCase() + key.slice(1);

            cell = row.insertCell();
            cell.innerText = current_stat.count;

            cell = row.insertCell();
            cell.innerText = current_stat.success;

            let color_id = 0;
            if (current_stat.count !== 0) {
                color_id = Math.floor((current_stat.success / current_stat.count) * 10);
            }

            cell = row.insertCell();
            cell.style.color = colors[color_id];
            cell.innerText = success_chance;
        });

        if (add_total_row) {
            const row = table.insertRow();
            let cell = row.insertCell();
            cell.classList.add("l");
            cell.innerText = "Celkem";

            cell = row.insertCell();
            cell.innerText = total_count;

            cell = row.insertCell();
            cell.innerText = total_succeeded;

            let color_id = 0;
            if (total_count !== 0) {
                color_id = Math.floor((total_succeeded / total_count) * 10);
            }

            cell = row.insertCell();
            cell.style.color = colors[color_id];
            cell.innerText = (total_count !== 0 ? Math.round((total_succeeded / total_count) * 10000) / 100 : 0) + "%";
        }

        return table;
    }

    async function createWagesTable(eco_detail_table, war_detail_table, prestige, total_wage_reduction, resources_reduction, vlada) {
        let is_robokrat = vlada == "Robokracie";

        let values = getValuesFromTheCell(eco_detail_table.rows[1].cells[1]);
        let people = parseInt(values[2]);

        const base_consumption = 0.005 * ((1 * (100 - resources_reduction)) / 100);
        let consumption_food = base_consumption,
            consumption_energy = 0;
        if (is_robokrat) {
            consumption_food = 0;
            consumption_energy = base_consumption;
        }

        values = getValuesFromTheCell(war_detail_table.rows[1].cells[5]);
        let units = [];

        units.push(createArmyUnit("Vojáci", parseInt(values[0]), 0.06, 1, consumption_food, consumption_energy));
        units.push(createArmyUnit("Tanky", parseInt(values[1]), 0.42, 5, consumption_food, consumption_energy));
        units.push(createArmyUnit("Stíhačky", parseInt(values[2]), 0.32, 3.5, consumption_food, consumption_energy));
        units.push(createArmyUnit("Bunkry", parseInt(values[3]), 0.35, 3.5, consumption_food, consumption_energy));
        units.push(createArmyUnit("Mechové", parseInt(values[4]), 0.2, 2.7, 0, base_consumption));
        units.push(createArmyUnit("Agenti", parseInt(values[5]), 5.0, 15, consumption_food, consumption_energy));

        addUnitsBeingSold(units);

        let food_price = 10;
        let energy_price = 10;

        let prices = await fetchMarketPrices();

        food_price = prices[0];
        energy_price = prices[1];

        let wages_total = calculateTotalWages(units, prestige, -total_wage_reduction);
        let expenses_total = calculateTotalExpenses(units, food_price, energy_price);

        let expenses_container = createExpensesTable(units, expenses_total, wages_total, food_price, energy_price, total_wage_reduction, resources_reduction);
        war_detail_table.parentElement.insertBefore(expenses_container, war_detail_table.nextElementSibling);

        let result = {
            prestige: prestige,
            units: units,
            people: people,
            is_robokrat: is_robokrat,
            wages_total: wages_total,
            expenses_total: expenses_total,
            food_price: food_price,
            energy_price: energy_price,
        };

        return result;
    }

    function addUnitsBeingSold(units) {
        let package_elements = document.querySelectorAll(".gtrans, .gsale");

        for (let index = 0; index < package_elements.length; index++) {
            let cell = package_elements[index];
            let row = cell.parentElement;

            const name_to_find = row.cells[2].textContent.slice(0, -1);

            const unit = units.find(({ name }) => name === name_to_find);
            if (unit !== undefined) {
                unit.count += Number(row.cells[3].textContent.replace(/\s/g, ""));
            }
        }
    }

    function addSpyRobberyColumn(eco_detail_table, war_detail_table, prestige) {
        let values = getValuesFromTheCell(eco_detail_table.rows[1].cells[3]);
        let money = parseInt(values[0].slice(0, -1));
        let food = parseInt(values[1].slice(0, -1));
        let energy = parseInt(values[2].slice(0, -1));

        values = getValuesFromTheCell(war_detail_table.rows[1].cells[3]);
        let spy_tech_points = parseInt(values[10]);
        let safe_technology_points = spy_tech_points * 2;
        let technology_to_steal = 0;

        values.forEach((value) => {
            let technology_points = parseInt(value);
            if (technology_points > safe_technology_points) {
                technology_to_steal += (technology_points - safe_technology_points) * 0.05 + safe_technology_points * 0.002;
            } else {
                technology_to_steal += technology_points * 0.002;
            }
        });

        let safe_money = Math.min(spy_tech_points * 1000, money);
        let unsafe_money = money - safe_money;

        let money_to_steal = Math.round(Math.min(unsafe_money * 0.25 + safe_money * 0.01, prestige));

        let safe_food = Math.min(spy_tech_points * 50, food);
        let unsafe_food = food - safe_food;

        let food_to_steal = Math.round(Math.min(unsafe_food * 0.25 + safe_food * 0.01, prestige / 5));

        let safe_energy = Math.min(spy_tech_points * 50, energy);
        let unsafe_energy = energy - safe_energy;

        let energy_to_steal = Math.round(Math.min(unsafe_energy * 0.25 + safe_energy * 0.01, prestige / 5));

        let th = createElement("th", { textContent: "Další informace", colSpan: 2 });
        war_detail_table.rows[0].appendChild(th);

        // krádez rozvedkou:

        let td = war_detail_table.rows[1].insertCell();
        td.classList.add("rname", "l");
        let div = createElement("div");
        td.appendChild(div);

        let strong = createElement("strong", { textContent: "Krádež rozvědkou" });
        div.appendChild(strong);

        div = createElement("div", { textContent: "Peníze" });
        td.appendChild(div);

        div = createElement("div", { textContent: "Jídlo" });
        td.appendChild(div);

        div = createElement("div", { textContent: "Energie" });
        td.appendChild(div);

        div = createElement("div", { textContent: "Technologie" });
        td.appendChild(div);

        td.appendChild(createElement("br"));

        div = createElement("div", { textContent: "Mechy %" });
        td.appendChild(div);

        td = war_detail_table.rows[1].insertCell();
        td.classList.add("rdata", "r");

        td.appendChild(createElement("br"));

        div = createElement("div", { textContent: formatNumber(money_to_steal) });
        td.appendChild(div);

        div = createElement("div", { textContent: formatNumber(food_to_steal) });
        td.appendChild(div);

        div = createElement("div", { textContent: formatNumber(energy_to_steal) });
        td.appendChild(div);

        div = createElement("div", { textContent: formatNumber(Math.floor(technology_to_steal)) });
        td.appendChild(div);

        td.appendChild(createElement("br"));

        const mechy_percentage_in_army_text = getMechyPercentageInArmy(war_detail_table);
        div = createElement("div", { textContent: mechy_percentage_in_army_text });
        td.appendChild(div);
    }

    function getMechyPercentageInArmy(war_detail_table) {
        const values = getValuesFromTheCell(war_detail_table.rows[1].cells[5]);
        let units = [];

        units.push(new ArmyUnit("Mechové", parseInt(values[4]), 2.7));
        units.push(new ArmyUnit("Vojáci", parseInt(values[0]), 1));
        units.push(new ArmyUnit("Tanky", parseInt(values[1]), 5));
        units.push(new ArmyUnit("Stíhačky", parseInt(values[2]), 3.5));
        units.push(new ArmyUnit("Bunkry", parseInt(values[3]), 3.5));

        let prestige_sum = units.reduce((acc, obj) => acc + obj.total_prestige, 0);
        if (prestige_sum === 0) {
            return "0%";
        }

        return ((units[0].total_prestige / prestige_sum) * 100).toFixed(1) + "%";
    }

    function addCountryDetailButtons(show_wages) {
        let infotext = document.getElementsByClassName("infotext");

        if (infotext.length === 0) {
            return;
        }

        infotext = infotext[0];

        const padding = "0.5rem";

        const div = createElement("div", { style: "margin-top: 0.6rem;" });
        infotext.append(div);

        let text = show_wages ? "Vypnout zobrazení výdajů" : "Zapnout zobrazení výdajů";
        let func = () => {
            setSetting(ZOBRAZENE_ZOLDY, !show_wages);
            refreshPage();
        };
        const show_wages_button = createButton(text, func, null, { style: `padding: ${padding}` });
        div.appendChild(show_wages_button);

        const current_country = location.href.endsWith("p=detaily");

        const refresh_stats_button = createButton("Aktualizovat statistiky země", null, null, { style: `padding: ${padding}` });
        refresh_stats_button.addEventListener("click", async function () {
            await archive_spy.processSpyArchive(countryDetailGetCountryId(), current_country, false, true, refresh_stats_button);
        });
        div.appendChild(refresh_stats_button);

        const fetch_all_stats_button = createButton("Získat kompletní statistiky země", null, null, { style: `padding: ${padding}` });
        fetch_all_stats_button.classList.add("submit");
        fetch_all_stats_button.style.padding = padding;
        fetch_all_stats_button.innerText = "Získat kompletní statistiky země";
        fetch_all_stats_button.addEventListener("click", async function () {
            await archive_spy.processSpyArchive(countryDetailGetCountryId(), current_country, true, true, fetch_all_stats_button);
        });
        div.appendChild(fetch_all_stats_button);

        const fetch_all_stats_for_ali_button = createButton("Získat statistiky všech zemí v alianci", null, null, { style: `padding: ${padding}` });
        fetch_all_stats_for_ali_button.addEventListener("click", async function () {
            await processSpyArchiveForAlliance(fetch_all_stats_for_ali_button);
        });
        div.appendChild(fetch_all_stats_for_ali_button);

        const reset_button = createButton("Vynulovat statistiky všech zemí", null, null, { style: `padding: ${padding}` });
        reset_button.addEventListener("click", async function () {
            resetStats();
        });
        div.appendChild(reset_button);
    }

    async function processCountryDetail() {
        const show_wages = getSetting(ZOBRAZENE_ZOLDY, true);
        const country_id = countryDetailGetCountryId();
        const country_stats = getCountryStats(country_id);

        addCountryDetailButtons(show_wages);

        let eco_detail_table = document.getElementById("tdetail");
        let war_detail_table = document.getElementById("detaily");

        if (!war_detail_table || !eco_detail_table) {
            return;
        }

        let values = getValuesFromTheCell(eco_detail_table.rows[1].cells[1]);
        let prestige = parseInt(values[0]);

        addSpyRobberyColumn(eco_detail_table, war_detail_table, prestige);
        if (country_stats !== undefined) {
            wrapCountrySpyStatsAroundElement(eco_detail_table, country_id);
        }

        if (!show_wages) {
            return;
        }

        let vlada = null,
            hodnost = null,
            ekonom = 0,
            ali_bonus = 0,
            ali_tag = null;

        // ziskat hodnost
        let hodnost_elements = document.getElementsByClassName("hodnost");
        if (hodnost_elements.length === 0) {
            return;
        }
        hodnost = Number(hodnost_elements[0].textContent.split("(")[1].split(")")[0]);
        let table = hodnost_elements[0].closest("table");

        // ziskat zrizeni
        vlada = table.rows[0].cells[3].textContent;

        // ziskat ali tag
        let country_full_name = table.rows[0].cells[1].textContent;
        if (country_full_name.includes("[")) {
            ali_tag = country_full_name.split("[")[1].split("]")[0];
        }

        let generals_table = document.getElementById("dgen");
        if (generals_table) {
            for (let index = 1; index < generals_table.rows.length; index++) {
                const row = generals_table.rows[index];
                let cell = row.cells[1];

                if (cell.textContent.includes("Ekonom")) {
                    ekonom = Number(row.cells[2].textContent);
                    break;
                }
            }
        }

        if (ali_tag !== null && ali_tag !== "Bezalianční") {
            let saved_ali_data = getSetting(COUNTRY_ALI_DATA, null);
            let time_in_millis = getCurrentTimeInMillis();

            if (saved_ali_data == null || saved_ali_data.ali_tag != ali_tag || saved_ali_data.updated_at + COUNTRY_ALI_DATA_TIME_OFFSET_MILLIS < time_in_millis) {
                // pouze provadet request, jestli informace ze settingu jsou ulozene informace z jine aliance a nebo jsou data moc stara
                ali_bonus = await fetchAliFundaBonus(ali_tag);

                setSetting(COUNTRY_ALI_DATA, {
                    ali_tag: ali_tag,
                    ali_bonus: ali_bonus,
                    updated_at: time_in_millis,
                });
            } else {
                ali_bonus = saved_ali_data.ali_bonus;
            }
        }

        let resources_reduction = 0;
        let total_wage_reduction = ali_bonus;
        let econom_effective_level = ekonom;

        if (ekonom > 5) {
            econom_effective_level = 4 + ((ekonom - 4) * (ekonom - 3)) / 2;
        }
        total_wage_reduction += econom_effective_level * 5;
        if (hodnost >= 11) resources_reduction += 15;
        if (hodnost >= 12) total_wage_reduction += 25;
        if (vlada === "Fundamentalismus") total_wage_reduction += 25;

        // ziskat prestiz - done
        // ziskat pocty jednotek - done
        // ziskat vladu (funda? robo?) - done
        // ziskat budovy - skip
        // ziskat techy (asi jen celkem?) - skip
        // ziskat hodnost (>=11 = snizeni vydaju; >=12 = snizeni zoldu)
        // ziskat snizeni vydaju (ze stranky vlada, popr dopocitat pres vladu - fund, gena ekonoma a h12 + funda ali bonus)
        // vypocitat spotrebu armady
        // vypocitat spotrebu jidla pro obyvatele (1000 lidi = 0.25 jidla mimo robo)
        // vypocitat spotrebu energie
        // pocitani spotreby energie pro budovy:
        // mesta, obchodni zony, laborky, tovarny = x0.2
        // vesnice, farmy, zabavni strediska, vojenske zakladny, kasarny, stavebni firmy = x0.1
        // pocitani spotreby energie pres techy = x0.005
        // pocitani spotreby energie pres mechy = x0.005
        // robokrati mnohonasobne vyssi spotreba energie budovami (???)
        // plazmy??

        createWagesTable(eco_detail_table, war_detail_table, prestige, total_wage_reduction, resources_reduction, vlada);
    }

    async function fetchAliFundaBonus(ali_tag) {
        return new Promise((resolve) => {
            const url = "index.php?p=najit&s=najittag&tag=" + ali_tag;

            let req = new XMLHttpRequest();
            req.open("GET", url);
            req.onload = function () {
                if (req.readyState == 4 && req.status == 200) {
                    let parser = new DOMParser();
                    let doc = parser.parseFromString(req.response, "text/html");

                    let funda_bonus = 0;

                    let table = doc.getElementById("alliance-members");
                    let member_count = table.rows.length - 3;
                    const fund_text = "Fund";

                    for (let index = 1; index < member_count + 1; index++) {
                        const row = table.rows[index];
                        let cell = row.cells[6];

                        if (cell.textContent === fund_text) {
                            funda_bonus = Math.floor(member_count / 2) * 3;
                            break;
                        }
                    }

                    resolve(funda_bonus);
                } else {
                    resolve(0);
                }
            };
            req.send();
        });
    }

    function getValuesFromTheCell(cell) {
        return cell.innerHTML.replaceAll("\n\t", "").replaceAll(" ", "").split("<br>");
    }

    function calculateTotalWages(units, prestige, wage_increase = 0) {
        const wage_modification = Math.max((1 + wage_increase / 100) * (1 + prestige / 5000000), 0.1);

        let wages_total = 0;
        for (const unit of units) {
            unit.expenses_per_unit = unit.base_wage * wage_modification;

            unit.wages_total = Math.round(unit.count * unit.expenses_per_unit * 100) / 100;
            wages_total += unit.wages_total;
        }

        return Math.round(wages_total * 100) / 100;
    }

    function calculateTotalExpenses(units, food_price, energy_price) {
        let expenses_total = 0;

        for (const unit of units) {
            unit.expenses_per_unit += unit.base_food_consumption * food_price + unit.base_energy_consumption * energy_price;
            unit.expenses_per_prestige_point = unit.expenses_per_unit / unit.prestige;

            unit.expenses_total = Math.round(unit.expenses_per_unit * unit.count * 100) / 100;

            expenses_total += unit.expenses_total;
        }

        return Math.round(expenses_total * 100) / 100;
    }

    function fetchMarketPrices() {
        return new Promise((resolve) => {
            let req = new XMLHttpRequest();
            let url = "index.php?p=domtrh";
            req.open("GET", url);
            req.onload = function () {
                if (req.readyState == 4 && req.status == 200) {
                    let parser = new DOMParser();
                    let doc = parser.parseFromString(req.response, "text/html");

                    let prices = [];

                    let price_cells = doc.querySelectorAll("#icontent form .vis_tbl .mactprice");
                    for (let i = 0; i < 2; i++) {
                        const price_cell = price_cells[i];
                        prices.push(Number(price_cell.innerText.replaceAll(" ", "").replace("$", "")));
                    }

                    resolve(prices);
                } else {
                    log("File not found");
                }
            };
            req.send();
        });
    }

    function createExpensesTable(units, expenses_total, wages_total, food_price, energy_price, total_wage_reduction, resources_reduction) {
        const div = createElement();

        div.append(createElement("h2", { textContent: "Výdaje za armádu" }));

        const table = createElement("table", { id: "spotreba_jednotek", className: "vis_tbl", style: "table-layout: fixed; min-width: 600px; max-width: 1200px; width: 70%;" });
        div.append(table);

        const th_values = [
            "Jednotka",
            "Počet",
            "Celkem",
            `Žold (${(total_wage_reduction <= 0 ? "+" : "") + -total_wage_reduction}%)`,
            `Suroviny (${(resources_reduction <= 0 ? "+" : "") + -resources_reduction}%)`,
            "Na 1 jednotku",
            "Na bod prestiže",
            "Procento výdajů",
        ];
        let row = table.insertRow();
        th_values.forEach((th_value) => {
            row.appendChild(createElement("th", { textContent: th_value }));
        });

        let total_food_cost = 0,
            total_energy_cost = 0;

        units.forEach((unit) => {
            const food_consumed = unit.base_food_consumption * unit.count;
            const food_cost = food_consumed * food_price;
            total_food_cost += food_cost;

            const energy_consumed = unit.base_energy_consumption * unit.count;
            const energy_cost = energy_consumed * energy_price;
            total_energy_cost += energy_cost;

            const base_wage_per_unit = unit.wages_total / unit.count;
            const base_resources_consumption = unit.base_food_consumption * food_price + unit.base_energy_consumption * energy_price;
            const wage_percentage = (base_wage_per_unit / (base_wage_per_unit + base_resources_consumption)) * 100;
            const others_percentage = (base_resources_consumption / (base_wage_per_unit + base_resources_consumption)) * 100;

            const row = table.insertRow();

            let cell = row.insertCell();
            cell.textContent = unit.name;
            cell.classList.add("rname", "l");

            addCellWithNumericalValue(row, unit.count, 0, "");

            cell = addCellWithNumericalValue(row, unit.expenses_total);
            cell.title = `Žold: ${Math.round(wage_percentage * 10) / 10}% \nSuroviny: ${Math.round(others_percentage * 10) / 10}%`;

            cell = addCellWithNumericalValue(row, unit.wages_total);
            cell.title = `Žold: ${Math.round(wage_percentage * 10) / 10}% \nSuroviny: ${Math.round(others_percentage * 10) / 10}%`;

            if (food_cost > energy_cost) {
                cell = addCellWithNumericalValue(row, food_cost);
                cell.title = `Celková spotřeba jídla jednotkou: ${Math.round(food_consumed * 10) / 10}\nCena jídla: ${food_price}`;
            } else {
                cell = addCellWithNumericalValue(row, energy_cost);
                if (energy_cost > 0) {
                    cell.title = `Celková spotřeba energie jednotkou: ${Math.round(energy_consumed * 10) / 10}\nCena energie: ${energy_price}`;
                }
            }

            cell = addCellWithNumericalValue(row, unit.expenses_per_unit, 3);
            cell.title = `Žold: ${Math.round(base_wage_per_unit * 1000) / 1000}$ \nSuroviny: ${Math.round(base_resources_consumption * 1000) / 1000}$`;

            cell = addCellWithNumericalValue(row, unit.expenses_per_prestige_point, 3);
            cell.title = `Žold: ${Math.round((base_wage_per_unit / unit.prestige) * 1000) / 1000}$ \nSuroviny: ${Math.round((base_resources_consumption / unit.prestige) * 1000) / 1000}$`;

            let percent = expenses_total > 0 ? unit.expenses_total / expenses_total : 0;
            cell = addCellWithNumericalValue(row, percent * 100, 1, "%");
            cell.title = `Žold: ${Math.round(wage_percentage * 10) / 10}% \nSuroviny: ${Math.round(others_percentage * 10) / 10}%`;
        });

        row = table.insertRow();

        let cell = row.insertCell();
        cell.textContent = "Celkem";
        cell.classList.add("rname", "l");
        cell.colSpan = 2;

        addCellWithNumericalValue(row, expenses_total);
        addCellWithNumericalValue(row, wages_total);
        cell = addCellWithNumericalValue(row, total_food_cost + total_energy_cost);
        cell.title = `Celkem jídlo:      \t${formatNumber(total_food_cost.toFixed(1))}$\nCelkem energie: \t${formatNumber(total_energy_cost.toFixed(1))}$`;
        row.insertCell().colSpan = 3;

        return div;
    }

    function processPrestigeDetails() {
        let tables = document.getElementsByClassName("vis_tbl");
        let table = null;

        for (let i = 0; i < tables.length; i++) {
            if (tables[i].rows.length > 10) {
                table = tables[i];
                break;
            }
        }

        if (table === null) return;

        let percent = 0,
            prestige = 0;

        for (let index = 3; index < table.rows.length; index++) {
            const row = table.rows[index];
            if (row.cells[0].tagName === "TH") {
                insertPrestigeDetailsRow(table, index, prestige, percent);
                prestige = 0;
                percent = 0;
                index++;

                continue;
            }

            const percent_cell = row.cells[1];
            const prestige_cell = row.cells[4];

            percent += Number(percent_cell.textContent.split("%")[0]);
            prestige += Number(prestige_cell.textContent.replace(/\s/g, ""));
        }

        insertPrestigeDetailsRow(table, table.rows.length, prestige, percent);
    }

    function insertPrestigeDetailsRow(table, index, prestige, percent) {
        const row = table.insertRow(index);
        let cell = row.insertCell();
        cell.classList.add("rname", "l");
        cell.textContent = "Celkem";

        cell = row.insertCell();
        cell.classList.add("perf");
        cell.textContent = percent.toFixed(1) + "%";

        cell = row.insertCell();
        cell.classList.add("rdata");
        cell.colSpan = 2;

        cell = row.insertCell();
        cell.classList.add("rdata");
        cell.textContent = formatNumber(prestige);
    }

    function modifyStatisticsPage() {
        let table = document.getElementById("statszrizeni");
        if (!table) {
            return;
        }

        let rows = Array.from(table.rows);

        let row = rows.shift();
        row.insertBefore(createElement("th", { innerHTML: "%<br>zemí" }), row.children[2]);

        let countries_count = rows.reduce((acc, row) => acc + Number(row.cells[1].textContent), 0);

        rows.forEach((row) => {
            const count = Number(row.cells[1].textContent);
            const cell = row.insertCell(2);
            const percent = (100 * count) / countries_count;
            cell.textContent = formatNumber(percent.toFixed(1)) + "%";
        });

        let sum_row = table.insertRow();
        let cell = sum_row.insertCell();
        cell.classList.add("l");
        cell.textContent = "Celkem";

        cell = sum_row.insertCell();
        cell.textContent = countries_count;

        cell = sum_row.insertCell();
        cell.textContent = "100.0%";

        const stats = getStatsData();
        if (!stats) {
            return;
        }

        cell = sum_row.insertCell();
        cell.textContent = stats.average_land;

        cell = sum_row.insertCell();
        cell.textContent = stats.average_prestige;

        cell = sum_row.insertCell();
        cell.textContent = stats.max_prestige;

        cell = sum_row.insertCell();
        cell.textContent = stats.average_turns_played;
    }

    function getStatsData() {
        const table = document.getElementById("statvalues");
        if (!table) {
            return null;
        }

        let stats_data = {};

        let rows = Array.from(table.rows);

        const labels = ["average_prestige", "average_land", "max_prestige", "average_turns_played"];
        const row_index = [1, 2, 1, 6];
        const column_index = [1, 1, 3, 1];

        for (let index = 0; index < row_index.length; index++) {
            const row = rows[row_index[index]];
            const value = Number(row.children[column_index[index]].textContent);
            stats_data[labels[index]] = value;
        }

        return stats_data;
    }

    function addCellWithNumericalValue(row, value, fraction_digits = 1, after_value = "$") {
        const cell = row.insertCell();
        cell.textContent = formatNumber(value.toFixed(fraction_digits)) + after_value;
        cell.classList.add("rdata", "r");
        return cell;
    }

    function modifyArchive() {
        const modify_archive = getSetting(MODIFY_ARCHIVE, true);

        if (!modify_archive) {
            return;
        }

        let tables = document.getElementsByClassName("vis_tbl");
        let table = null;
        for (let i = 0; i < tables.length; i++) {
            if (tables[i].rows.length > 2 && tables[i].rows[0].cells.length == 2) {
                table = tables[i];
                break;
            }
        }

        if (table === null) {
            return;
        }

        const TOOLTIP_ROW_VALUES = ["name", "count", "prestige"];

        addArchiveCss();
        addTooltipCss(240, true);

        let header_row = table.rows[0];
        header_row.style.position = "sticky";
        header_row.style.top = "0";
        header_row.style.zIndex = "1";

        header_row.appendChild(createElement("th", { textContent: "Prestiž na expy" }));

        const colors = ["red", "lime", "mediumorchid"];

        for (let index = 1; index < table.rows.length; index++) {
            const row = table.rows[index];
            let message = row.cells[1].textContent;
            message = message.replace("Nová - Útoky", "").replace("Nová - Spojenci", "").replace("Nová - Rozvědka", "").trim();

            let result = null;

            if (message.indexOf("Nepřátelským mechům") !== -1) {
                result = processExpTazeni(message);
            } else if (message.indexOf("na nás podnikla partyzánský útok") !== -1) {
                result = processExpPartyzan(message);
            } else if (message.indexOf("Naši vojáci odvrátili partyzánský útok") !== -1) {
                result = processExpPartyzanSuccessful(message);
            } else if (message.startsWith("Pěchotě země ")) {
                result = processExpVniknutiDoBunkru(message);
            } else if (message.startsWith("Naši vojáci odvrátili vniknutí do bunkrů")) {
                result = processExpVniknutiDoBunkruSuccessful(message);
            } else if (message.startsWith("Naší pěchotě se")) {
                result = processExpVniknutiDoBunkruUtok(message);
            } else if (message.startsWith("Tankové brigádě ")) {
                result = processExpTyl(message);
            } else if (message.startsWith("Bleskový úder tankové brigády ")) {
                result = processExpTyl(message, true);
            } else if (message.startsWith("Letectvo nepřátelské země")) {
                result = processExpNaletSuccessful(message);
            } else if (message.indexOf("na naše strategická vojenská zařízení") !== -1) {
                result = processExpNalet(message);
            } else if (message.startsWith("Stali jsme se obětí  loupežného útoku") || message.startsWith("Stali jsme se obětí částečného loupežného útoku")) {
                result = processExpLoupezak(message);
            } else if (message.indexOf("prolomila naši obranu a zabrala ") !== -1) {
                result = processExpDobyvak(message);
            } else if (message.startsWith("Naší tankové brigádě se")) {
                result = processExpTylUtok(message);
            } else if (message.startsWith("Bleskový úder naší tankové brigády")) {
                result = processExpTylUtok(message, false);
            } else if (message.indexOf("Naši elitní piloti podnikli  taktický nálet") !== -1 || message.indexOf("Naši elitní piloti podnikli částečný taktický nálet") !== -1) {
                result = processExpNaletUtok(message);
            } else if (message.startsWith("Našim mechům se podařilo během nočního tažení")) {
                result = processExpTazeniUtok(message);
            } else if (message.indexOf("Při loupeživém útoku na ") !== -1) {
                result = processExpNormalUtok(message);
            } else if (message.indexOf("Obsadili jsme") !== -1) {
                result = processExpNormalUtok(message);
            } else if (message.startsWith("Partyzánský útok")) {
                result = processExpPartyzanUtok(message);
            } else if (message.startsWith("Naši vojáci neuspěli díky silnému odporu")) {
                result = processExpPartyzanUtok(message, false);
            } else if (message.startsWith("Barbarská armáda")) {
                result = processExpVyhlaz(message);
            } else if (message.indexOf("Při vyhlazovacím útoku") !== -1) {
                result = processExpDobyvakSuccessful(message);
            } else if (message.startsWith("Sebevědomá země")) {
                result = processExpDobyvakSuccessful(message);
            } else if (message.indexOf("bombardovaly naše budovy") !== -1) {
                result = processExpBombeni(message);
            } else if (message.startsWith("Naše mocné protiletecké síly")) {
                result = processExpBombeniSuccessful(message);
            } else if (message.indexOf("úspěšně bombardovaly  civilní cíle") !== -1) {
                result = processExpBombeniUtok(message);
            } else if (message.startsWith("Našim jednotkám se nepodařilo obejít přesilu nepřátelských mechů")) {
                result = processExpTazeniUtok(message);
            } else if (message.startsWith("Země") && message.indexOf("nebyla poražena. Naše ztráty byly ")) {
                result = processExpNormalUtok(message);
            } else if (message.startsWith("Byli jsme povoláni zemí")) {
                result = processExpSpojka(message);
            } else if (message.startsWith("Rozvědka")) {
                // process opku
                if (message.indexOf("Naši agenti ukradli celkem") !== -1) {
                    processTechnologyToMoney(message, row);
                }
            }

            const cell = row.insertCell();

            if (result === null) continue;

            cell.style.textAlign = "center";
            cell.style.userSelect = "none";

            if (result.exp_ratio !== null) {
                cell.textContent = result.exp_ratio;
                cell.style.color = colors[result.type];

                const tooltip = createElement();
                cell.appendChild(tooltip);
                wrapInTooltip(tooltip);

                const header = addRowToTooltip(tooltip, { name: "Jednotka", count: "Počet", prestige: "Prestiž" });
                header.classList.add("header");

                for (let index = 0; index < result.units_lost.length; index++) {
                    const unit = result.units_lost[index];
                    addRowToTooltip(tooltip, unit);
                }
                tooltip.appendChild(createElement("p", { className: "divider" }));

                const additional_info = [
                    {
                        name: "Celkem:",
                        count: "",
                        prestige: result.total_prestige,
                    },
                    {
                        name: "Zkušeností:",
                        count: "",
                        prestige: result.experience,
                    },
                ];

                for (let index = 0; index < additional_info.length; index++) {
                    const info = additional_info[index];
                    addRowToTooltip(tooltip, info);
                }
            }
        }

        function addRowToTooltip(tooltip, information) {
            const p = createElement("p", { className: "row" });
            tooltip.appendChild(p);

            for (let i = 0; i < TOOLTIP_ROW_VALUES.length; i++) {
                const unit_value = TOOLTIP_ROW_VALUES[i];
                const span = createElement("span", { textContent: information[unit_value] });
                p.appendChild(span);
            }

            return p;
        }
    }

    async function processTechnologyToMoney(message, row) {
        message = message.split("Rozpis: ")[1];
        let numbers = message.match(/(\d)+/g);
        let words = message.replace(/[0-9]/g, "").split(".")[0].split(", ");

        let tech_prices = await technology_prices.get();

        let total_money = 0,
            title = "";

        for (let i = 0; i < words.length; i++) {
            let count = Number(numbers[i]);
            if (count === 0) {
                continue;
            }

            let word = words[i];
            let price = tech_prices[i];

            total_money += price * count;
            title += `${word}: ${formatNumber(count)}x ${formatNumber(price)}$ = ${formatNumber(Math.floor(price * count))}$\n`;
        }

        let p = createElement("p", { textContent: "Hodnota celkem: ", title });
        row.cells[1].appendChild(p);

        p.appendChild(createElement("span", { textContent: formatNumber(Math.floor(total_money)) + "$", style: "font-weight: bold; color: lime;" }));
    }

    function processExpTazeni(message) {
        if (message.indexOf("nepodařilo obejít přesilu našich mechů") !== -1) {
            message = message.split("nepodařilo obejít přesilu našich mechů")[1];
            let numbers = message.match(/(\d)+/g);

            let units = [UNITS.get("mechy", Number(numbers[1]))];
            let experience = Number(numbers.pop());
            return processExp(units, experience);
        }

        message = message.split("se podařilo během nočního tažení naší zemí zlikvidovat")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [
            UNITS.get("vojaci", Number(numbers.shift())),
            UNITS.get("tanky", Number(numbers.shift())),
            UNITS.get("stihacky", Number(numbers.shift())),
            UNITS.get("budovy", Number(numbers.shift())),
            UNITS.get("mechy", Number(numbers[1])),
        ];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpTazeniUtok(message) {
        message = message.split("Zničeno bylo")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("mechy", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience, 1);
    }

    function processExpPartyzan(message) {
        message = message.split("%, zabito bylo ")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("agenti", Number(numbers.shift())), UNITS.get("energie", Number(numbers.shift())), UNITS.get("vojaci", Number(numbers[1]))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpPartyzanSuccessful(message) {
        message = message.split("Zahynulo při tom ")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("vojaci", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpPartyzanUtok(message, success = true) {
        let separator = "Při bojích zahynulo";
        if (!success) separator = "Zahynulo při tom";
        message = message.split(separator)[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("vojaci", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience, 1);
    }

    function processExpVniknutiDoBunkru(message) {
        message = message.split("se podařilo vniknout do ")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("bunkry", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpVniknutiDoBunkruSuccessful(message) {
        message = message.split("Zahynulo při tom ")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("vojaci", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpVniknutiDoBunkruUtok(message) {
        message = message.split("Při tom zahynulo")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("vojaci", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience, 1);
    }

    function processExpTyl(message, success = false) {
        let separator = "My jsme při tom přišli o";
        if (success) separator = "Ztratili jsme při tom";
        message = message.split(separator)[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("tanky", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpTylUtok(message, success = true) {
        let separator = "My jsme při tom přišli o ";
        if (!success) {
            separator = "Ztratili jsme při tom ";
        }
        message = message.split(separator)[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("tanky", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience, 1);
    }

    function processExpNalet(message) {
        message = message.split("Nálety nám zničily ")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("budovy_na_ruiny", Number(numbers.shift())), UNITS.get("stihacky", Number(numbers.shift())), UNITS.get("bunkry", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpNaletSuccessful(message) {
        message = message.split("Při obraně jsme ztratili ")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("stihacky", Number(numbers.shift())), UNITS.get("bunkry", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpBombeni(message) {
        message = message.split("Pod troskami")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [
            UNITS.get("budovy", Number(numbers.shift())),
            UNITS.get("ruiny", Number(numbers.shift())),
            UNITS.get("stihacky", Number(numbers.shift())),
            UNITS.get("bunkry", Number(numbers.shift())),
        ];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpBombeniSuccessful(message) {
        message = message.split(". Bylo zničeno ")[1];

        let numbers = message.match(/(\d)+/g);
        numbers.shift(); // nepratelske stihacky

        let units = [UNITS.get("stihacky", Number(numbers.shift())), UNITS.get("bunkry", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience);
    }

    function processExpBombeniUtok(message) {
        message = message.split("Bylo zničeno")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("stihacky", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience, 1);
    }

    function processExpNaletUtok(message) {
        message = message.split("vojenských základen nepřítele")[1];

        let numbers = message.match(/(\d)+/g);

        let units = [UNITS.get("stihacky", Number(numbers.shift()))];
        let experience = Number(numbers.pop());

        return processExp(units, experience, 1);
    }

    function processExpLoupezak(message) {
        message = message.split("Bylo nám násilně zabráno ")[1];
        return processNormal(message);
    }

    function processExpNormalUtok(message) {
        message = message.split("Naše ztráty byly ")[1];
        return processNormalUtok(message);
    }

    function processExpSpojka(message) {
        message = message.split("V bojích bylo ztraceno ")[1];
        return processNormalUtok(message, true);
    }

    function processExpDobyvak(message) {
        message = message.split("prolomila naši obranu a zabrala ")[1];
        return processNormal(message);
    }

    function processExpDobyvakSuccessful(message) {
        message = message.split("Naše ztráty byly")[1];

        let numbers = message.match(/(\d)+/g);

        const experience = Number(numbers.pop());

        let units = [
            UNITS.get("vojaci", Number(numbers.shift())),
            UNITS.get("tanky", Number(numbers.shift())),
            UNITS.get("bunkry", Number(numbers.shift())),
            UNITS.get("mechy", Number(numbers.shift())),
        ];

        return processExp(units, experience);
    }

    function processExpVyhlaz(message) {
        message = message.split(" zničila ")[1];

        let numbers = message.match(/(\d)+/g);

        const experience = Number(numbers.pop());

        let units = [
            UNITS.get("budovy", Number(numbers[0])),
            UNITS.get("ruiny", Number(numbers[1])),
            UNITS.get("penize", Number(numbers[3])),
            UNITS.get("jidlo", Number(numbers[4])),
            UNITS.get("energie", Number(numbers[5])),
            UNITS.get("technologie", Number(numbers[6])),
            UNITS.get("vojaci", Number(numbers[7])),
            UNITS.get("tanky", Number(numbers[8])),
            UNITS.get("bunkry", Number(numbers[9])),
            UNITS.get("mechy", Number(numbers[10])),
        ];

        return processExp(units, experience);
    }

    function processNormal(message) {
        let numbers = message.match(/(\d)+/g);

        const experience = Number(numbers.pop());

        let units = [
            UNITS.get("uzemi", Number(numbers[0])),
            UNITS.get("budovy", Number(numbers[2]) * 2),
            UNITS.get("penize", Number(numbers[3])),
            UNITS.get("jidlo", Number(numbers[4])),
            UNITS.get("energie", Number(numbers[5])),
            UNITS.get("technologie", Number(numbers[6])),
            UNITS.get("vojaci", Number(numbers[7])),
            UNITS.get("tanky", Number(numbers[8])),
            UNITS.get("bunkry", Number(numbers[9])),
            UNITS.get("mechy", Number(numbers[10])),
        ];

        return processExp(units, experience);
    }

    function processNormalUtok(message, is_spojka = false) {
        let numbers = message.match(/(\d)+/g);

        const experience = Number(numbers.pop());

        let units = [
            UNITS.get("vojaci", Number(numbers.shift())),
            UNITS.get("tanky", Number(numbers.shift())),
            UNITS.get("bunkry", Number(numbers.shift())),
            UNITS.get("mechy", Number(numbers.shift())),
        ];

        let type = is_spojka ? 2 : 1;

        return processExp(units, experience, type);
    }

    /**
     *
     * @param {array} units
     * @param {int} experience
     * @param {int} type 0 = defense, 1 = attack, 2 = ally
     * @returns {object}
     */
    function processExp(units, experience, type = 0) {
        let total_prestige = 0,
            units_lost = [];

        for (let index = 0; index < units.length; index++) {
            const unit = units[index];
            const prestige = unit.count * unit.prestige;
            total_prestige += prestige;
            units_lost.push({ name: unit.name + ":", count: formatNumber(unit.count), prestige: formatNumber(prestige.toFixed(1)) });
        }

        let percentage = total_prestige == 0 ? 999 : (experience / total_prestige) * 100;
        experience = formatNumber(experience.toFixed(1));
        total_prestige = formatNumber(total_prestige.toFixed(1));

        return { exp_ratio: formatNumber(percentage.toFixed(2)) + "%", units_lost, total_prestige, type, experience };
    }

    let archive_spy = (function () {
        const URL = "index.php?p=archiv&typ=3&limit=";
        const ALLY_URL = "index.php?p=archiv&typ=3&tag=1&id=$country_id$&limit=";
        const MESSAGES_PER_PAGE = 30;

        const OPERATION_SEARCH_TERMS = [
            // infiltrace
            {
                term: /infiltrova(li|t) vládu/,
                operation: "Infiltrovat vládu",
            },
            {
                term: /infiltrova(li|t) rozvědku/,
                operation: "Infiltrovat rozvědku",
            },
            {
                term: /infiltrova(li|t) generální štáb/,
                operation: "Infiltrovat generální štáb",
            },
            {
                term: /infiltrova(li|t) vědecká centra/,
                operation: "Infiltrovat vědecká centra",
            },

            // sabotazni
            {
                term: /naboura(li|t) ovládání mechů/,
                operation: "Nabourat ovládání mechů",
            },
            {
                term: /zavirova(li|t) počítače/,
                operation: "Počítačový virus",
            },
            {
                term: /sabotova(li|t) letiště/,
                operation: "Sabotovat letiště",
            },
            {
                term: /rozšíři(li|t) epidemii/,
                operation: "Rozšířit epidemii",
            },
            {
                term: /demoralizova(li|t) obyvatele/,
                operation: "Demoralizovat obyvatele",
            },
            {
                term: /zniči.*raket/,
                operation: "Zničit rakety",
            },

            // loupezive
            {
                term: /vykr(a|á)(dli|st) sklady/,
                operation: "Vykrást sklady",
            },
            {
                term: /vykr(a|á)(dli|st) centrální banku/,
                operation: "Vykrást centrální banku",
            },
            {
                term: /napích(li|nout) ropovod/,
                operation: "Napíchnout ropovod",
            },
            {
                term: /ukr(a|á)(st|dli) (celkem )?(\d)*( )?technologi(í|e)/,
                operation: "Ukrást technologie",
            },
            {
                term: /prod(a|á)(vá|t) drogy/,
                operation: "Prodat drogy",
            },

            // TODO zbytek sabotaznich:
            {
                term: " ",
                operation: "Jiné",
            },
        ];

        let country_id;
        let current_page;
        let last_message_time = null;
        let fetch_until_time = null;
        let messages;
        let spy_success_stats;
        let spy_operation_stats;

        let button = null;

        function initValues(_country_id) {
            initSuccessStats();
            initMessagesStats();
            country_id = _country_id;
            current_page = 0;
            last_message_time = null;
            fetch_until_time = null;
            current_page = 0;
            spy_operation_stats = {};
        }

        function initSuccessStats() {
            let value = {
                count: 0,
                success: 0,
            };
            spy_success_stats = {
                "snadná jak facka": value,
                jednoduchá: value,
                "středně náročná": value,
                "velice náročná": value,
                "extrémně náročná": value,
                "téměř neproveditelná": value,
            };
        }

        function initMessagesStats() {
            messages = {
                success: [],
                failed: [],
            };
        }

        function save() {
            let stats = getSetting(COUNTRY_STATS, {});

            stats[country_id] = {
                spy_stats: {
                    success_stats: spy_success_stats,
                    operation_stats: spy_operation_stats,
                    last_message: last_message_time,
                    // spy_messages: archive_spy.messages,
                },
            };
            setSetting(COUNTRY_STATS, stats);
        }

        async function processSpyArchive(is_ally, force_all = false) {
            const country_stats = getCountryStats(country_id);

            if (country_stats !== undefined && !force_all) {
                let spy_stats = country_stats.spy_stats;
                spy_success_stats = spy_stats.success_stats;
                spy_operation_stats = spy_stats.operation_stats;

                let time = spy_stats.last_message;
                fetch_until_time = time ? archiveGetTimeInMillis(time) : null;
            }

            let message_count = 0;
            let url = URL;
            if (is_ally) {
                url = ALLY_URL.replace("$country_id$", country_id);
            }

            do {
                message_count = await fetchPage(url + current_page++ * MESSAGES_PER_PAGE, processArchivePage);
                if (button !== null) {
                    button.textContent = `Probíhá... ${current_page}`;
                }
            } while (message_count === MESSAGES_PER_PAGE);
        }

        function processArchivePage(doc) {
            let tables = doc.getElementsByClassName("vis_tbl");
            let table = null;
            for (let i = 0; i < tables.length; i++) {
                if (tables[i].rows.length > 2 && tables[i].rows[0].cells.length >= 2) {
                    table = tables[i];
                    break;
                }
            }

            if (table === null) {
                return;
            }

            let rows = table.rows;
            if (last_message_time === null) {
                last_message_time = rows[1].cells[0].textContent;
            }
            for (let i = 1; i < rows.length; i++) {
                let time = rows[i].cells[0].textContent;
                let time_in_millis = archiveGetTimeInMillis(time);
                if (fetch_until_time && fetch_until_time === time_in_millis) {
                    return;
                }
                let original_message = rows[i].cells[1].textContent;

                if (original_message.indexOf("Operace byla ") == -1) {
                    continue;
                }

                let difficulty = original_message.split("Operace byla ")[1].split(".")[0];

                let difficulty_results = spy_success_stats[difficulty];
                let count = 0;
                let success = 0;

                if (difficulty_results !== undefined) {
                    count = difficulty_results.count;
                    success = difficulty_results.success;
                }

                // pushing message to correct messages
                const failed_operation = original_message.indexOf("nepodařilo") !== -1;
                if (failed_operation) {
                    messages["failed"].push(original_message);
                } else {
                    success++;
                    messages["success"].push(original_message);
                }

                for (let index = 0; index < OPERATION_SEARCH_TERMS.length; index++) {
                    const search_term = OPERATION_SEARCH_TERMS[index];

                    if (original_message.match(search_term.term) !== null) {
                        let current = spy_operation_stats[search_term.operation];

                        if (current === undefined) {
                            current = {
                                count: 0,
                                success: 0,
                            };
                            spy_operation_stats[search_term.operation] = current;
                        }

                        current.count++;
                        if (!failed_operation) {
                            current.success++;
                        }
                        break;
                    }
                }

                spy_success_stats[difficulty] = {
                    count: ++count,
                    success: success,
                };
            }

            return table.rows.length - 1;
        }

        return {
            OPERATION_SEARCH_TERMS,
            messages() {
                return messages;
            },
            spy_success_stats() {
                return spy_success_stats;
            },
            last_message_time() {
                return last_message_time;
            },
            async processSpyArchive(country_id, is_ally = false, force_all = false, reload_page = false, _button = null) {
                initValues(country_id);
                button = _button;
                if (button !== null) {
                    button.textContent = "Probíhá...";
                }
                await processSpyArchive(is_ally, force_all);
                save();

                if (reload_page) {
                    refreshPage();
                }
            },
            // spy_operation_stats: spy_operation_stats
        };
    })();

    const modify_advancements = (function () {
        const MAX_REDUCTIONS = {
            T: {
                reduction: 80,
                countries: 80,
            },
            K: {
                reduction: 60,
                countries: 300,
            },
        };

        const ADVANCEMENT_EXCEPTIONS = {
            "Fúzní elektrárny": {
                base_price: 400000,
                minimum_price: 160000,
                not_affected_by_difficulty: true,
            },
            "Nejvyšší budova světa": {
                base_price: 4000000,
                minimum_price: 4000000,
                per_country_price: 50000,
            },
            "Vojenská akademie": {
                base_price: 1000,
                minimum_price: 1000,
                per_country_price: 2,
                cap_price: 4000,
            },
        };

        function process() {
            const table = getAdvancementTable();
            if (table === null) {
                return;
            }

            const show_base_price = getSetting(ADV_SHOW_BASE_PRICE, true);
            const show_min_price = getSetting(ADV_SHOW_MIN_PRICE, true);
            const show_current_price = getSetting(ADV_SHOW_CURRENT_PRICE, true);

            const difficulty = getCurrentTechnologicalDifficulty();

            if (show_base_price) {
                addTooltipCss(300);
            }

            for (let index = 1; index < table.rows.length; index++) {
                const row = table.rows[index];

                const type = getAdvancementType(row);
                const advancement_name = getAdvancementName(row);

                const current_price_element = getCurrentPriceElement(row);
                const current_price = getCurrentPrice(current_price_element);
                const times_researched = getTimesResearched(row);
                formatValueOnElement(current_price_element);

                const price_reduction = getPriceReduction(row, type);
                let elements = [];
                if (show_base_price) {
                    elements.push(addBasePrice(current_price_element, current_price, price_reduction, difficulty, advancement_name));
                }

                if (type == "S") {
                    continue;
                }

                if (show_min_price) {
                    elements.push(addMinPrice(current_price_element, current_price, price_reduction, difficulty, type, advancement_name, times_researched));
                }
                if (show_current_price) {
                    addPriceReduction(current_price_element, price_reduction);
                }
                mergeTooltips(elements);
            }
        }

        function mergeTooltips(elements) {
            if (elements.length < 2) {
                return;
            }

            for (let index = 0; index < elements.length; index++) {
                const element = elements[index];
                if (!element.classList.contains("tooltiptext")) return;
            }

            let content = "";
            for (let index = 1; index < elements.length; index++) {
                const element = elements[index];
                content += "<p>" + element.innerHTML + "</p>";
                element.remove();
            }

            const tooltip = elements[0];
            tooltip.innerHTML += content;
        }

        function getPriceReduction(row, type) {
            if (type == "S" || type == "K-") {
                return 100;
            }

            const country_count = Number(row.cells[3].textContent);
            return getPrice(type, country_count);
        }

        function getPrice(type, countries) {
            if (type == "S" || type == "K-") {
                return 100;
            }

            const reduction = MAX_REDUCTIONS[type].reduction;
            const countries_max = MAX_REDUCTIONS[type].countries;

            const base = 100 - reduction;
            const discounted = reduction * (1 - Math.min(countries / countries_max, 1));
            return base + discounted;
        }

        function getMinPrice(type) {
            if (type == "S" || type == "K-") {
                return 100;
            }

            return getPrice(type, MAX_REDUCTIONS[type].countries);
        }

        function getAdvancementType(row) {
            let value = row.cells[0].textContent.match(/\([TSK-]*\)/g)[0];
            return value.slice(1).slice(0, -1);
        }

        function getAdvancementName(row) {
            const cell = row.cells[0];
            const child_nodes = cell.childNodes;
            return child_nodes[child_nodes.length - 1].textContent.split(" (")[0].trim();
        }

        function addPriceReduction(element, price_reduction) {
            const wrapper = createElement("i", { textContent: "Aktuální cena pokroku je: " + Math.round(price_reduction * 10) / 10 + "%" });

            const parent = element.parentElement.parentElement;
            const sibling = element.parentElement.nextSibling;
            if (sibling) {
                parent.insertBefore(wrapper, sibling);
            } else {
                parent.appendChild(wrapper);
            }

            return wrapper;
        }

        function addMinPrice(current_price_element, current_price, price_reduction, difficulty, type, advancement_name, times_researched) {
            let min_price = 0;

            if (ADVANCEMENT_EXCEPTIONS[advancement_name] !== undefined) {
                let exception = ADVANCEMENT_EXCEPTIONS[advancement_name];

                min_price = exception.minimum_price;
                if (exception.per_country_price) {
                    let extra_price = exception.per_country_price * times_researched;

                    if (exception.cap_price && exception.cap_price <= min_price + extra_price) {
                        min_price = exception.cap_price;
                    } else {
                        min_price += extra_price;
                    }
                }

                if (!exception.not_affected_by_difficulty) {
                    min_price = min_price * difficulty;
                }
            } else {
                min_price = (current_price / price_reduction) * getMinPrice(type);
            }

            const element = addPriceValue(current_price_element, min_price, "Minimální cena");
            if (getSetting(ADV_SHOW_MIN_PRICE_IN_TOOLTIP, false)) {
                element.classList.add("center");
                wrapInTooltip(element);
            }
            return element;
        }

        function addBasePrice(current_price_element, current_price, current_reduction, difficulty, advancement_name) {
            let base_price = 0;

            if (ADVANCEMENT_EXCEPTIONS[advancement_name] !== undefined) {
                base_price = ADVANCEMENT_EXCEPTIONS[advancement_name].base_price;
            } else {
                base_price = getBasePrice(current_price, current_reduction, difficulty);
            }

            const element = addPriceValue(current_price_element, base_price, "Základní cena");
            if (getSetting(ADV_SHOW_BASE_PRICE_IN_TOOLTIP, true)) {
                element.classList.add("center");
                wrapInTooltip(element);
            }
            return element;
        }

        function getCurrentPrice(current_price_element) {
            return Number(current_price_element.textContent);
        }

        function getCurrentPriceElement(row) {
            return row.getElementsByTagName("em")[0];
        }

        function getTimesResearched(row) {
            return Number(row.cells[3].textContent);
        }

        function formatValueOnElement(current_price_element) {
            const current_price = Number(current_price_element.textContent);
            current_price_element.textContent = formatNumber(current_price);
        }

        function addPriceValue(element, value, label = "") {
            const wrapper = createElement("p", { className: "imprp" });

            const i = createElement("i");
            wrapper.appendChild(i);

            const em = createElement("em", { textContent: formatNumber(value.toFixed(0)) });
            i.appendChild(em);

            i.innerHTML = label + ": " + i.innerHTML + " " + element.nextSibling.textContent;

            const parent = element.parentElement.parentElement;
            const sibling = element.parentElement.nextSibling;
            if (sibling) {
                parent.insertBefore(wrapper, sibling);
            } else {
                parent.appendChild(wrapper);
            }

            return wrapper;
        }

        function getBasePrice(current_price, current_reduction, difficulty) {
            return ((current_price / current_reduction) * 100) / difficulty;
        }

        function getAdvancementTable() {
            let tables = document.getElementsByClassName("vis_tbl");
            let table = null;
            for (let i = 0; i < tables.length; i++) {
                if (tables[i].rows[0].cells[0].textContent == "Pokrok") {
                    table = tables[i];
                    break;
                }
            }
            return table;
        }

        function getCurrentTechnologicalDifficulty() {
            let elements = document.querySelectorAll(".infotext strong");

            let difficulty = null;
            for (let i = 0; i < elements.length; i++) {
                if (elements[i].textContent.indexOf("Náročnost pokroků je") !== -1) {
                    const parts = elements[i].textContent.split(" ");
                    difficulty = Number(parts[parts.length - 1]);
                    break;
                }
            }
            return difficulty;
        }

        return {
            process() {
                if (getSetting(MODIFY_ADVANCEMENTS, true)) {
                    process();
                }
            },
        };
    })();

    const modify_valka = (function () {
        const pripravenost_za_kolo = 3;
        const vyssi_dopriprava_nad_pripravenosti = 10;
        const WARNING_MESSAGE = "!!! Útokem se dopouštíte porušení pravidel VEDENÍ ALIANCE !!!";
        const STYLES = "input[type=submit][disabled] { background: red; border-color: black; }";

        let last_production_table = null;
        let warning_message_element = null;

        function process() {
            if (getSetting(STRIKE_SHOW_EXP, true)) {
                showExp();
            }
            if (getSetting(STRIKE_WARNING_DISABLES_BUTTON, true)) {
                if (hasWarningMessage()) {
                    addStrikeCss();
                    disableButton();
                    addForceAttackButton();
                }
            }
        }

        function addStrikeCss() {
            addCss(STYLES);
        }

        function disableButton() {
            const button = getStrikeSubmitButton();
            button.disabled = true;
        }

        function addForceAttackButton() {
            const warning_message = getWarningMessage();
            if (warning_message === null) {
                return;
            }

            const parent = warning_message.parentElement;
            parent.style.textAlign = "center";

            const button = createButton("Rozumím a chci pokračovat", () => {
                getStrikeSubmitButton().disabled = false;
            });
            parent.appendChild(button);
        }

        function getStrikeSubmitButton() {
            return document.forms[0].querySelector("input[type=submit]");
        }

        function getWarningMessage() {
            if (warning_message_element !== null) return warning_message_element;

            let warning_messages = document.getElementsByClassName("warn");
            if (warning_messages.length == 0) return null;

            for (let index = 0; index < warning_messages.length; index++) {
                const element = warning_messages[index];
                if (element.textContent == WARNING_MESSAGE) {
                    warning_message_element = element;
                    break;
                }
            }

            return warning_message_element;
        }

        function hasWarningMessage() {
            return getWarningMessage() !== null;
        }

        function showExp() {
            const strong_elements = document.getElementsByTagName("strong");
            let experience_element = null;

            for (let i = 0; i < strong_elements.length; i++) {
                if (strong_elements[i].nextSibling && strong_elements[i].nextSibling.textContent == " zkušeností.") {
                    experience_element = strong_elements[i];
                    break;
                }
            }

            if (experience_element === null) {
                return;
            }

            const experience = Number(experience_element.textContent);
            const turns_played = getPlayedTurns();
            const pripravenost = getCombatReadiness();

            const turns_to_full = getTurnsToBeCombatReady(pripravenost, true);
            const turns_to_less_than_3 = getTurnsToBeCombatReady(pripravenost, false);

            const experience_to_full = getExperiencePerTurn(experience, turns_played + turns_to_full);

            let break_element = createElement("br");
            experience_element.parentElement.insertBefore(break_element, experience_element.nextSibling.nextSibling);

            let element = addResults("Zkušeností / kolo: ", experience_to_full, break_element);

            if (turns_to_full != turns_to_less_than_3) {
                const experience_to_less_than_3 = getExperiencePerTurn(experience, turns_played + turns_to_less_than_3);
                addResults("Zkušeností za kolo (<3%): ", experience_to_less_than_3, element);
            }
        }

        function getPlayedTurns() {
            let tables = document.getElementsByClassName("vis_tbl");
            let turns = 0;

            for (let index = 0; index < tables.length; index++) {
                const table = tables[index];
                if (table.rows[0].cells[0].textContent == "Peníze") {
                    last_production_table = table;
                    turns++;
                }
            }

            return turns;
        }

        function getCombatReadiness() {
            if (!last_production_table) {
                log("Není uložená poslední tabulka.");
                return 100;
            }

            return 100 - Number(last_production_table.rows[2].cells[5].textContent.slice(0, -1));
        }

        function getTurnsToBeCombatReady(pripravenost, doFull = true) {
            let turns = 0;

            if (pripravenost > 10) {
                const per_turn = pripravenost_za_kolo + 1;
                turns = Math.ceil((pripravenost - vyssi_dopriprava_nad_pripravenosti) / per_turn);
                pripravenost -= turns * per_turn;
            }

            if (doFull) {
                turns += Math.ceil(pripravenost / pripravenost_za_kolo);
            } else {
                turns += Math.floor(pripravenost / pripravenost_za_kolo);
            }

            return turns;
        }

        function getExperiencePerTurn(experience, turns) {
            return (experience / turns).toFixed(0);
        }

        function addResults(label, experience, insertBeforeElement) {
            let p = createElement("p", { textContent: label, style: "margin-left: 1ch;" });

            p.appendChild(createElement("strong", { textContent: experience }));

            let parent = insertBeforeElement.parentElement;
            parent.insertBefore(p, insertBeforeElement.nextSibling);

            return p;
        }

        return {
            process() {
                process();
            },
        };
    })();

    function fetchPage(url, callback) {
        return new Promise((resolve) => {
            let req = new XMLHttpRequest();
            req.open("GET", url);
            req.onload = function () {
                if (req.readyState == 4 && req.status == 200) {
                    let parser = new DOMParser();
                    let doc = parser.parseFromString(req.response, "text/html");

                    resolve(callback(doc));
                }
            };
            req.send();
        });
    }

    async function processSpyArchiveForAlliance(button) {
        button.textContent = "Probíhá...";

        let country_ids = await fetchCountryIds();
        button.textContent = `Probíhá... 0 / ${country_ids.length}`;
        for (let index = 0; index < country_ids.length; index++) {
            const country_id = country_ids[index];
            await archive_spy.processSpyArchive(country_id, true, false, false);

            updateProgressBackground(button, index, country_ids.length);
        }

        refreshPage();
    }

    function updateProgressBackground(button, completed, total) {
        button.textContent = "Probíhá ... " + (completed + 1) + " / " + total;
        const progress = Math.floor(((completed + 1) / total) * 100);

        let background = "chocolate";
        if (progress < 100) {
            background = `linear-gradient(to right, chocolate ${progress}%, #363636 ${progress}%)`;
        }
        button.style.background = background;
    }

    function fetchCountryIds() {
        const URL = "index.php?p=detaily&s=detaily&adetaily=true";
        return fetchPage(URL, getCountryIdsCallback);
    }

    function getCountryIdsCallback(doc) {
        let country_ids = [];

        let table = doc.forms[0].children[0];
        for (let i = 1; i < table.rows.length; i++) {
            const row = table.rows[i];
            const img = row.querySelector("img");
            if (img === null) {
                continue;
            }
            const country_id = Number(img.parentElement.href.split("to_id=")[1]);
            country_ids.push(country_id);
        }

        return country_ids;
    }

    function getCountryStats(country_id) {
        const all_country_stats = getSetting(COUNTRY_STATS, {});
        return all_country_stats[country_id];
    }

    function archiveGetTimeInMillis(time) {
        let date = archiveProcessTime(time);
        return date.getTime();
    }

    function archiveProcessTime(time) {
        let parts = time.split(" ");

        let date_parts = parts[0].split(".");
        let date = new Date(date_parts[2], date_parts[1] - 1, date_parts[0]);

        let hour_parts = parts[1].split(":");

        date.setHours(hour_parts[0], hour_parts[1], hour_parts[2]);
        return date;
    }

    function countryDetailGetCountryId() {
        let mail_to = document.querySelector(".vis_tbl td:nth-child(2) a");
        return Number(mail_to.href.split("id=")[1]);
    }

    const ali_detail = (function () {
        const separator = ",";
        const allowed_cards = ["G", "F", "S", "Q", "P", "Z", "R"];
        const rocket_cards = ["L", "B", "N"];
        const cards_to_replace = {
            "?": "Q",
        };
        const REGIMES = {
            Feud: "e",
            Demo: "d",
            Rep: "r",
            Tech: "t",
            Fund: "f",
            Anar: "a",
            Kom: "k",
            Dikt: "i",
            Utop: "u",
            Robo: "b",
        };

        let cards, regimes, rockets;

        // to clear values
        function initializeMaps() {
            cards = new Map();
            regimes = new Map();
            rockets = new Map();
        }

        function transformMapToString(map) {
            let result = Array.from(map, ([key, value]) => ({ key, value }));
            result = result.sort((a, b) => b.value - a.value);
            return result.reduce((value, current_value) => value + current_value.key + current_value.value + separator, "").slice(0, -1);
        }

        function processTable(table) {
            if (table === null) throw new Error("Není tabulka v detailu aliance");
            for (let i = 1; i < table.rows.length - 2; i++) {
                processRow(table.rows[i]);
            }
        }

        function processRow(row) {
            addRegimeFromRow(row);
            processCards(row);
        }

        function cardName(card) {
            return card in cards_to_replace ? cards_to_replace[card] : card;
        }

        function getWarsData(doc) {
            const table = doc.getElementById("find-treaties-wars");
            if (!table) return "";

            const ali_tag = getAliTag(doc);

            const wars = new Map();
            for (let i = 1; i < table.rows.length; i++) {
                const row = table.rows[i];
                const cells = row.cells;
                if (cells[0].colSpan > 1) continue; // is war description = skip

                const declarant = cells[0].children[0].textContent.trim();
                const declaree = cells[0].children[1].textContent.trim();
                const ali = ali_tag == declarant ? declaree : declarant;
                const age = getDeclarationAge(cells[2].textContent);

                if (!wars.has(ali)) {
                    wars.set(ali, { age });
                } else {
                    const war = wars.get(ali);
                    war.kotva = true;
                    war.age = Math.max(war.age, age);
                }
            }

            const war_data = function () {
                return { count: 0, kotev: 0 };
            };
            let wars_simplified = [war_data(), war_data(), war_data(), war_data()];
            for (let [, war] of wars) {
                const age = Math.min(war.age, 3);
                wars_simplified[age].count++;
                if (war.kotva) {
                    wars_simplified[age].kotev++;
                }
            }

            let result = "";
            for (let i = 0; i < wars_simplified.length; i++) {
                if (wars_simplified[i].count === 0) continue;
                result += wars_simplified[i].count + "x" + i + "x" + wars_simplified[i].kotev + ",";
            }
            result = result.slice(0, -1);

            return result;
        }

        function getDeclarationAge(text) {
            const numbers = text.match(/\d+/g);
            const day = numbers[0];
            const month = numbers[1];
            const hours = numbers[2];
            const minutes = numbers[3];

            const date = new Date();
            date.setDate(day);
            date.setMonth(month - 1);
            date.setHours(hours);
            date.setMinutes(minutes);

            const current_time = new Date();

            if (current_time - date < 0) date.setFullYear(date.getFullYear() - 1);

            return Math.floor((current_time - date) / 1000 / 60 / 60 / 24);
        }

        function getAliTag(doc) {
            const table = doc.getElementById("find-alliance-summary");
            return table.rows[0].cells[1].textContent.match(/(?<=\[).*(?=\])/g)[0];
        }

        function processCards(row) {
            const cell = row.cells[9];

            if (!cell.children) return;

            let allowed_types = allowed_cards.concat(rocket_cards);

            for (let index = 0; index < cell.children.length; index++) {
                const element = cell.children[index];

                let card_type = cardName(element.alt.slice(1, -1));
                if (allowed_types.indexOf(card_type) == -1) {
                    continue;
                }

                let map = cards;
                if (rocket_cards.indexOf(card_type) != -1) map = rockets;

                let count = map.get(card_type);
                if (count === undefined) {
                    count = 0;
                }
                map.set(card_type, count + 1);
            }
        }

        function addRegimeFromRow(row) {
            const cell = row.cells[6];
            const regime = cell.textContent;

            let regime_value = REGIMES[regime];
            if (regime_value === undefined) {
                throw new Error("Neznámá vláda - " + regime);
            }

            let count = regimes.get(regime_value);
            if (count === undefined) {
                count = 0;
            }

            regimes.set(regime_value, count + 1);
        }

        function getMembersTable(doc) {
            return doc.getElementById("alliance-members");
        }

        function getInformation(doc) {
            initializeMaps();
            const table = getMembersTable(doc);
            processTable(table);
            const wars = getWarsData(doc);

            const cards_result = transformMapToString(cards);
            const regimes_result = transformMapToString(regimes);
            const rockets_result = transformMapToString(rockets);

            return { cards: cards_result, regimes: regimes_result, rockets: rockets_result, wars };
        }

        return {
            getInformation(doc = document) {
                return getInformation(doc);
            },
        };
    })();

    const ali_zebricek = (function () {
        const FULL_SEPARATOR = "*||*";
        const SEPARATOR = "*|*";
        const URL = "index.php?p=najit&s=najittag&tag=";
        const ICONS = {
            F: "img/f.gif",
            S: "img/x.gif",
            P: "img/p.gif",
            G: "img/gv.gif",
            R: "img/r.gif",
            Q: "img/q.gif",
            Z: "img/z.gif",
            L: "img/l.gif",
            B: "img/b.gif",
            N: "img/n.gif",
        };
        const REGIMES = {
            e: "Feud",
            d: "Demo",
            r: "Rep",
            t: "Tech",
            f: "Fund",
            a: "Anar",
            k: "Kom",
            i: "Dikt",
            u: "Utop",
            b: "Robo",
        };
        const STYLES = `
            .vis_tbl tr:first-child {
                position: sticky;
                top: 0;
                z-index: 1;
            }

            .grid {
                display: grid;
                gap: 1rem;
            }

            .filter_container h2 {
                border-bottom: 1px solid white;
                margin-bottom: 0.5rem;
                font-size: 1.5rem;
                text-transform: inherit;
                letter-spacing: 1.5px;
            }

            .filter_container input {
                padding: 1px 0 1px 4px;
                margin: 0 0.5rem;
            }

            .row {
                width: fit-content;
                margin: auto;
                padding: 0.1rem;
            }

            .header {
                width: 160px;
                display: flex;
                justify-content: space-around;
                font-weight: bold;
            }

            .row img {
                vertical-align: sub;
            }

            tr.fits_not td {
                background: rgb(160, 20, 20);
            }

            .icon.war_count {
                border-radius: unset;
                margin-right: 0.25rem;
            }
        `;
        const show_wars_with_anchors = getSetting(ALI_WARS_WITH_ANCHORS, true);

        const filter_buttons = [];
        const table_data = [];

        async function process() {
            const table = getTable();
            if (table === null) {
                return;
            }

            const saved_information = getAllSavedInformations();

            addControls(table, saved_information);
            saveDataRows(table, saved_information);
            if (getSetting(ALI_SHOW_CARDS, true)) {
                addCards(table, saved_information);
            }
            if (getSetting(ALI_SHOW_REGIME, true)) {
                addRegimes(table, saved_information);
            }
            if (getSetting(ALI_SHOW_ROCKETS, true)) {
                addRockets(table, saved_information);
            }
            if (getSetting(ALI_SHOW_WARS, true)) {
                addWars(table, saved_information);
            }
            if (getSetting(ALI_SHOW_FILTER, true)) {
                addFilter();
            }
        }

        function addCards(table, saved_information) {
            addColumn(table, saved_information, { header_text: "Kartičky", type: "cards", callback: cardContentCallback });
        }

        function addRockets(table, saved_information) {
            addColumn(table, saved_information, { header_text: "Rakety", type: "rockets", callback: cardContentCallback });
        }

        function addRegimes(table, saved_information) {
            addTooltipCss();
            addColumn(table, saved_information, { header_text: "Vlády", type: "regimes", callback: regimeContentCallback });
        }

        function addWars(table, saved_information) {
            addColumn(table, saved_information, { header_text: "Války", type: "wars", callback: warsContentCallback });
        }

        function saveDataRows(table, saved_information) {
            if (saved_information.length == 0) return;

            for (let index = 1; index < table.rows.length; index++) {
                const row = table.rows[index];
                const ali_tag = getAliTag(row);
                const ali_information = saved_information.filter((ali_information) => ali_information.tag === ali_tag)[0];
                if (ali_information === undefined) {
                    continue;
                }

                const cards_data = (ali_information.cards + "," + ali_information.rockets).split(",");
                const data = [];

                cards_data.forEach((card_data) => {
                    if (card_data.length == 0) return;

                    const card_type = card_data.slice(0, -1);
                    const card_count = card_data.slice(-1);
                    data[card_type] = Number(card_count);
                });

                table_data.push({
                    data,
                    row,
                });
            }
        }

        function addFilter() {
            let form = getSearchForm();

            if (form === null) {
                return;
            }

            addZebricekCss();

            const container = createElement("div", { className: "grid", style: "grid-template-columns: 1fr 1fr 1fr;" });
            form.parentElement.insertBefore(container, form);
            container.appendChild(createElement());
            container.appendChild(form);

            const filter_container = createElement("div", { className: "filter_container" });
            container.appendChild(filter_container);

            filter_container.appendChild(createElement("h2", { textContent: "Filtr kartiček" }));

            const header = createElement("div", { className: "row header" });
            filter_container.appendChild(header);
            header.appendChild(createElement("span", { textContent: "Min" }));
            header.appendChild(createElement("span", { textContent: "Max" }));

            const keys = Object.keys(ICONS);
            keys.forEach((key) => {
                filter_container.appendChild(createFilterRow(key));
            });
        }

        function createFilterRow(key) {
            const wrapper = createElement("div", { className: "row" });

            const input_min = createNumberInput(key + "_min", 0, 0, 10);
            input_min.addEventListener("change", () => filterRows());
            wrapper.appendChild(input_min);

            const icon = createElement("img", { src: ICONS[key], alt: key });
            wrapper.appendChild(icon);

            const input_max = createNumberInput(key + "_max", 10, 0, 10);
            input_max.addEventListener("change", () => filterRows());
            wrapper.appendChild(input_max);

            filter_buttons.push({
                min: input_min,
                max: input_max,
            });

            return wrapper;
        }

        function getSearchForm() {
            if (document.forms.length == 0) return null;

            return document.forms[0];
        }

        function filterRows() {
            const filter = {};

            filter_buttons.forEach((buttons) => {
                const type = buttons.min.id.slice(0, 1);
                const min = Number(buttons.min.value);
                const max = Number(buttons.max.value);

                if (min > 0 || max < buttons.max.max) {
                    filter[type] = { min, max };
                }
            });

            for (let index = 0; index < table_data.length; index++) {
                const data = table_data[index];
                const row = data.row;
                const cards_data = data.data;

                if (fitsCriteria(cards_data, filter)) {
                    row.classList.remove("fits_not");
                } else {
                    row.classList.add("fits_not");
                }
            }
        }

        function fitsCriteria(cards_data, filter) {
            const keys = Object.keys(filter);
            for (let index = 0; index < keys.length; index++) {
                const key = keys[index];
                const min = filter[key].min;
                const max = filter[key].max;
                const count = cards_data[key] === undefined ? 0 : cards_data[key];

                if (count < min || count > max) {
                    return false;
                }
            }

            return true;
        }

        function addColumn(table, saved_information, { header_text, type, callback }) {
            const th = createElement("th", { textContent: header_text });
            table.rows[0].appendChild(th);

            for (let index = 1; index < table.rows.length; index++) {
                const row = table.rows[index];
                const ali_tag = getAliTag(row);
                const ali_information = saved_information.filter((ali_information) => ali_information.tag === ali_tag)[0];
                const cell = row.insertCell();

                if (ali_information === undefined) {
                    cell.textContent = "chybí";
                    row.classList.add("not_fetched");
                    continue;
                }

                if (ali_information[type] === "") {
                    continue;
                }

                let data = ali_information[type].split(",");
                callback(cell, data);
            }
        }

        function cardContentCallback(cell, data) {
            for (let index = 0; index < data.length; index++) {
                const value = data[index];
                const count = Number(value.slice(1));
                cell.appendChild(createElement("span", { textContent: count + "x " }));

                const icon_type = value.slice(0, 1);
                const icon = createElement("img", { src: ICONS[icon_type], style: "margin-right: 5px; " });
                cell.appendChild(icon);
            }
        }

        function regimeContentCallback(cell, data) {
            const icon = createElement("span", { textContent: "?", className: "tooltip icon" });
            cell.appendChild(icon);
            cell.classList.add("c");

            const wrapper = createElement("div", { className: "tooltiptext" });
            icon.appendChild(wrapper);

            for (let index = 0; index < data.length; index++) {
                const value = data[index];
                const count = Number(value.slice(1));
                const regime = REGIMES[value.slice(0, 1)];
                wrapper.appendChild(createElement("p", { textContent: count + "x " + regime }));
            }
        }

        function warsContentCallback(cell, data) {
            const background_colors = ["rgb(0, 255, 0)", "rgb(255, 255, 0)", "rgb(255, 128, 0)", "rgb(0, 0, 0)"];
            let war_count = 0;
            const age_values = ["<24", "24-48", "48-72", ">72"];

            for (let index = 0; index < data.length; index++) {
                const parts = data[index].split("x");
                const count = Number(parts[0]);
                war_count += count;

                const days = Number(parts[1]);
                const kotev = Number(parts[2]);
                const age = age_values[days];
                const background_color = background_colors[days];
                const color = days == 3 ? "color: white;" : "";

                const props = {
                    className: "icon",
                    style: `background:${background_color}; ${color}`,
                };
                let anchor_element = null;
                if (show_wars_with_anchors) {
                    if (count > kotev) {
                        props.textContent = count - kotev;
                        props.title = count - kotev + "x " + age;
                    }
                    if (kotev > 0) {
                        anchor_element = createElement("span", {
                            textContent: kotev,
                            className: "icon",
                            title: "Zakotveno: " + kotev + "x " + age,
                            style: `background: linear-gradient(135deg, ${background_color} 50%, #99F 50%); ${color})`,
                        });
                    }
                } else {
                    props.textContent = count;
                    props.title = count + "x " + age + ", z toho zakotveno: " + kotev;
                }
                if (props.textContent !== undefined) {
                    cell.appendChild(createElement("span", props));
                }
                if (anchor_element) {
                    cell.appendChild(anchor_element);
                }
            }

            cell.prepend(createElement("span", { textContent: war_count, className: "icon war_count", title: `Celkem: ${war_count} ${war_count == 1 ? "válka" : "války"}` }));
        }

        async function resetAllInformations() {
            setSetting(ALI_SAVED_INFO, "");
            refreshPage();
        }

        function getAliTagsFromRows(rows) {
            let ali_tags = [];
            for (let index = 0; index < rows.length; index++) {
                const row = rows[index];
                const ali_tag = getAliTag(row);
                ali_tags.push(ali_tag);
            }

            return ali_tags;
        }

        async function fetchInformationForAliances(ali_tags, button) {
            let data = [];
            const length = ali_tags.length;
            for (let index = 0; index < length; index++) {
                const result = await fetchInformationForAnAliance(ali_tags[index]);

                data.push(result);

                updateProgressBackground(button, index, length);
            }
            return data;
        }

        function removeDuplicates(saved_information, ali_tags) {
            for (let index = 0; index < ali_tags.length; index++) {
                const tag = ali_tags[index];
                const found_index = saved_information.findIndex((card) => card.tag === tag, tag);

                if (found_index !== -1) {
                    saved_information.splice(found_index, 1);
                }
            }
            return saved_information;
        }

        function saveInformation(saved_information, new_ali_information, ali_tags = null, remove_duplicates = false) {
            if (remove_duplicates) {
                saved_information = removeDuplicates(saved_information, ali_tags);
            }

            let result = "";
            if (saved_information.length > 0) {
                result = saved_information.reduce(
                    (acc, information) =>
                        acc + information.tag + SEPARATOR + information.cards + SEPARATOR + information.regimes + SEPARATOR + information.rockets + SEPARATOR + information.wars + FULL_SEPARATOR,
                    ""
                );
            }
            result += new_ali_information.join(FULL_SEPARATOR);
            result.slice(0, -FULL_SEPARATOR.length);

            setSetting(ALI_SAVED_INFO, result);
            refreshPage();
        }

        async function fetchMissingInformations({ table, saved_information }, e) {
            const rows = table.querySelectorAll(".not_fetched");
            const ali_tags = getAliTagsFromRows(rows);
            let ali_Information = await fetchInformationForAliances(ali_tags, e.target);

            saveInformation(saved_information, ali_Information);
        }

        async function refreshAllInformations({ table, saved_information }, e) {
            let rows = Array.from(table.rows);
            rows.shift();
            const ali_tags = getAliTagsFromRows(rows);
            let ali_Information = await fetchInformationForAliances(ali_tags, e.target);

            saveInformation(saved_information, ali_Information, ali_tags, true);
        }

        function getAllSavedInformations() {
            let saved_information = getSetting(ALI_SAVED_INFO, "");
            if (saved_information.length === 0) {
                return [];
            }
            saved_information = saved_information.split(FULL_SEPARATOR);

            const ali_informations = [];
            for (let index = 0; index < saved_information.length; index++) {
                const cards = saved_information[index];
                const parts = cards.split(SEPARATOR);

                const formatted_cards = {
                    tag: parts[0],
                    cards: parts[1],
                    regimes: parts[2],
                    rockets: parts[3],
                    wars: parts[4],
                };

                ali_informations.push(formatted_cards);
            }

            return ali_informations;
        }

        function addControls(table, saved_information) {
            const h1 = document.getElementsByTagName("h1")[0];

            const container = createElement("div", { style: "text-align: end" });
            h1.parentElement.insertBefore(container, h1.nextSibling);

            container.appendChild(createButton("Získat informace pro chybějící aliance", fetchMissingInformations, { table, saved_information }));
            container.appendChild(createButton("Získat informace o aliancích pro tuto stránku", refreshAllInformations, { table, saved_information }));
            container.appendChild(createButton("Smazat všechna data o aliancích", () => resetAllInformations()));
        }

        function getTable() {
            const tables = document.getElementsByClassName("vis_tbl");
            if (tables.length == 0) {
                return null;
            }

            for (let index = 0; index < tables.length; index++) {
                if (tables[index].rows[0].cells[1].textContent === "Aliance") {
                    return tables[index];
                }
            }

            return null;
        }

        function getAliTag(row) {
            return row.cells[1].textContent.match(/(?<=\[).+(?=\])/)[0];
        }

        async function fetchInformationForAnAliance(ali_tag) {
            const results = await fetchPage(URL + ali_tag, ali_detail.getInformation);
            return ali_tag + SEPARATOR + results.cards + SEPARATOR + results.regimes + SEPARATOR + results.rockets + SEPARATOR + results.wars;
        }

        function addZebricekCss() {
            addCss(STYLES);
        }

        return {
            process() {
                if (!getSetting(ALI_ALLOW_CHANGES, true)) return;
                return process();
            },
        };
    })();
    //

    function resetStats() {
        setSetting(COUNTRY_STATS, {});
        refreshPage();
    }

    function refreshPage() {
        window.location.replace(location.href);
    }

    function getCurrentTimeInMillis() {
        return new Date().getTime();
    }

    function isGameOver() {
        let table = document.querySelector(".container .vis_tbl");
        if (!table) {
            return false;
        }
        return table.rows[0].children[0].innerText == "Věk skončil";
    }

    function addCss(css) {
        let style = document.createElement("style");
        if (style.styleSheet) {
            style.styleSheet.cssText = css;
        } else {
            style.appendChild(document.createTextNode(css));
        }

        document.getElementsByTagName("head")[0].appendChild(style);
    }

    const UNITS = {
        vojaci: new ArmyUnit("Vojáci", 0, 1),
        tanky: new ArmyUnit("Tanky", 0, 5),
        stihacky: new ArmyUnit("Stíhačky", 0, 3.5),
        bunkry: new ArmyUnit("Bunkry", 0, 3.5),
        mechy: new ArmyUnit("Mechy", 0, 2.7),
        agenti: new ArmyUnit("Agenti", 0, 15),

        penize: new ArmyUnit("Peníze", 0, 0.002),
        jidlo: new ArmyUnit("Jídlo", 0, 0.02),
        energie: new ArmyUnit("Energie", 0, 0.02),

        uzemi: new ArmyUnit("Uzemi", 0, 15),
        budovy: new ArmyUnit("Budovy", 0, 5),
        budovy_na_ruiny: new ArmyUnit("Budovy Na Ruiny", 0, 3),
        ruiny: new ArmyUnit("Ruiny", 0, 2),
        technologie: new ArmyUnit("Technologie", 0, 1),

        get(unit_name, count = 0) {
            const unit = this[unit_name];
            unit.setCount(count);
            return unit;
        },
    };

    execute();
    //
})();