The West - Experience Record

Tracks and displays gained experience in The West with activity details, including reset functionality and draggable window

As of 2025-04-18. See the latest version.

// ==UserScript==
// @name         The West - Experience Record
// @namespace    http://tampermonkey.net/
// @version      5.6
// @description  Tracks and displays gained experience in The West with activity details, including reset functionality and draggable window
// @author       DK, Shikokuchuo
// @include      https://*.the-west.*/game.php*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Inicjalizacja zmiennych
    let totalExperience = JSON.parse(localStorage.getItem('totalExperience')) || 0;
    let experienceLog = JSON.parse(localStorage.getItem('experienceLog')) || [];
    let savedPosition = JSON.parse(localStorage.getItem('experienceTrackerPosition')) || { top: '50px', left: 'auto', right: '310px' };
    let isCollectingHistory = false;
    let isPaused = false;
    let shouldCancel = false;
    let collectionStartTime = null;
    let processedPagesTime = [];
    let tempExpLog = [];
    let messageTimeout = null;
    let isTrackerVisible = JSON.parse(localStorage.getItem('experienceTrackerVisible')) !== false; // domyślnie widoczny

    // Funkcja do wyświetlania komunikatów
    function showError(message, duration = 5000) {
        if (messageTimeout) {
            clearTimeout(messageTimeout);
            messageTimeout = null;
        }

        const statusElement = document.querySelector('#collection-status');
        if (statusElement) {
            statusElement.innerHTML = `
                <div style="
                    background: rgba(0,0,0,0.7);
                    padding: 10px;
                    border-radius: 5px;
                    margin-top: 10px;
                    color: white;
                    text-align: center;
                    animation: fadeIn 0.3s ease-in-out;
                ">
                    ${message}
                </div>
            `;

            if (duration > 0) {
                messageTimeout = setTimeout(() => {
                    if (statusElement) {
                        statusElement.innerHTML = '';
                    }
                    messageTimeout = null;
                }, duration);
            }
        } else {
            console.log('Status message:', message);
        }
    }

    // Funkcja do pokazywania komunikatu o postępie
    function showProgress(message) {
        const statusElement = document.querySelector('#collection-status');
        if (statusElement) {
            statusElement.innerHTML = `
                <div style="
                    background: rgba(0,0,0,0.7);
                    padding: 10px;
                    border-radius: 5px;
                    margin-top: 10px;
                    color: white;
                    text-align: center;
                ">
                    ${message}
                </div>
            `;
        } else {
            console.log('Progress:', message);
        }
    }

    // Dodaj style CSS dla animacji
    const style = document.createElement('style');
    style.textContent = `
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(-10px); }
            to { opacity: 1; transform: translateY(0); }
        }
    `;
    document.head.appendChild(style);

    // Funkcja do aktualizacji statusu kolekcji
    function updateCollectionStatus(processed, total, foundEntries) {
        const statusElement = document.querySelector('#collection-status');
        if (statusElement) {
            const percent = Math.round((processed / total) * 100);

            // Używamy tempExpLog zamiast experienceLog podczas zbierania danych
            const workEntries = tempExpLog.filter(e => e.type === 'work').length;
            const duelEntries = tempExpLog.filter(e => e.type === 'duel').length;
            const battleEntries = tempExpLog.filter(e => e.type === 'battle').length;

            // Dodajmy też szacowany pozostały czas
            const timeEstimate = calculateTimeEstimate(processed, total);
            const timeInfo = timeEstimate ? `<br>Szacowany pozostały czas: ${timeEstimate}` : '';

            statusElement.innerHTML = `
                <div style="background: rgba(0,0,0,0.7); padding: 10px; border-radius: 5px;">
                    <strong>Status pobierania:</strong><br>
                    Postęp: ${processed}/${total} stron (${percent}%)<br>
                    Znalezione wpisy z XP: ${foundEntries}<br>
                    - Prace: ${workEntries} (${tempExpLog.filter(e => e.type === 'work').reduce((sum, e) => sum + e.amount, 0)} XP)<br>
                    - Pojedynki: ${duelEntries} (${tempExpLog.filter(e => e.type === 'duel').reduce((sum, e) => sum + e.amount, 0)} XP)<br>
                    - Bitwy fortowe: ${battleEntries} (${tempExpLog.filter(e => e.type === 'battle').reduce((sum, e) => sum + e.amount, 0)} XP)${timeInfo}
                    <div class="progress-bar" style="
                        background: #444;
                        height: 20px;
                        border-radius: 10px;
                        margin-top: 10px;
                        overflow: hidden;
                    ">
                        <div style="
                            width: ${percent}%;
                            background: #4CAF50;
                            height: 100%;
                            transition: width 0.3s ease;
                        "></div>
                    </div>
                </div>
            `;
        }
    }

    // Funkcja do aktualizacji statystyk XP
    function updateXPStats() {
        const workXP = experienceLog.reduce((sum, entry) => {
            if (entry.type === 'work') {
                return sum + entry.amount;
            }
            return sum;
        }, 0);

        const duelXP = experienceLog.reduce((sum, entry) => {
            if (entry.type === 'duel') {
                return sum + entry.amount;
            }
            return sum;
        }, 0);

        const battleXP = experienceLog.reduce((sum, entry) => {
            if (entry.type === 'battle') {
                return sum + entry.amount;
            }
            return sum;
        }, 0);

        const workXPElement = document.querySelector('#work-xp');
        const duelXPElement = document.querySelector('#duel-xp');
        const battleXPElement = document.querySelector('#battle-xp');

        if (workXPElement) workXPElement.textContent = workXP + ' XP';
        if (duelXPElement) duelXPElement.textContent = duelXP + ' XP';
        if (battleXPElement) battleXPElement.textContent = battleXP + ' XP';
    }

    // Funkcja do debugowania systemu raportów
    function debugReportSystem() {
        console.log('Debugowanie systemu raportów...');

        // Sprawdź różne parametry zapytań
        const testQueries = [
            { page: 1, folder: 'all' },
            { page: 1, folder: 'all', offset: 0 },
            { page: 1, folder: 'all', offset: 0, limit: 50 }
        ];

        function makeTestQuery(params, index) {
            setTimeout(() => {
                Ajax.remoteCall('reports', 'get_reports', params, function(response) {
                    console.log('Test query ' + (index + 1) + ':', params);
                    console.log('Response:', response);

                    if (response) {
                        console.log('Total reports:', response.count);
                        console.log('Reports per page:', response.reports ? response.reports.length : 0);
                        console.log('Response structure:', Object.keys(response));

                        if (response.reports && response.reports.length > 0) {
                            console.log('Sample report structure:', Object.keys(response.reports[0]));
                        }
                    }
                });
            }, index * 1000);
        }

        testQueries.forEach(makeTestQuery);
    }

    function formatTimeRemaining(milliseconds) {
        if (!milliseconds || isNaN(milliseconds)) return "obliczanie...";
        const seconds = milliseconds / 1000;
        if (seconds < 60) return Math.round(seconds) + ' sekund';
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = Math.round(seconds % 60);
        return minutes + ' min ' + remainingSeconds + ' sek';
    }

    function calculateTimeEstimate(processedPages, totalPages) {
        if (processedPages < 2) return null;

        const currentTime = Date.now();
        const timeElapsed = currentTime - collectionStartTime;

        // Oblicz średni czas na stronę w milisekundach
        const avgTimePerPage = timeElapsed / processedPages;

        if (isNaN(avgTimePerPage)) return null;

        // Oblicz pozostały czas w milisekundach
        const remainingPages = totalPages - processedPages;
        const estimatedTimeRemaining = remainingPages * avgTimePerPage;

        return formatTimeRemaining(estimatedTimeRemaining);
    }

    // Funkcja do tłumaczenia nazw kategorii
    function getCategoryName(folder) {
        switch(folder) {
            case 'all': return 'wszystkich raportów';
            case 'job': return 'raportów z prac';
            case 'duel': return 'raportów z pojedynków';
            case 'fortbattle': return 'raportów z bitew fortowych';
            default: return folder;
        }
    }

    // Funkcja do zbierania historii doświadczenia
    function collectExperienceHistory(maxPages = null, folder = 'all', callback = null) {
        if (isCollectingHistory) {
            console.log('Pobieranie danych jest już w trakcie. Poczekaj na zakończenie.');
            if (callback) callback();
            return;
        }

        isCollectingHistory = true;
        isPaused = false;
        shouldCancel = false;
        collectionStartTime = Date.now();
        processedPagesTime = [];
        tempExpLog = [];

        console.log(`Rozpoczynam zbieranie danych dla ${getCategoryName(folder)}`);

        // Zachowujemy istniejące wpisy z innych kategorii
        let existingEntries = [];
        if (folder !== 'all') {
            existingEntries = experienceLog.filter(entry => {
                const entryCategory =
                    entry.type === 'work' ? 'job' :
                    entry.type === 'duel' ? 'duel' :
                    entry.type === 'battle' ? 'fortbattle' : 'other';
                return entryCategory !== folder;
            });
        }

        let processedPages = 0;
        let failedAttempts = 0;
        const MAX_RETRIES = 3;

        // Pokaż przyciski kontrolne i ukryj standardowe
        const controlButtons = document.querySelector('#collection-controls');
        const standardButtons = document.querySelector('#standard-buttons');
        if (controlButtons) {
            controlButtons.style.display = 'grid';
        }
        if (standardButtons) {
            standardButtons.style.display = 'none';
        }

        // Dostosowane opóźnienia
        const MIN_DELAY = 2000; // minimalne opóźnienie między zapytaniami (2 sekundy)
        const MAX_DELAY = 2200; // maksymalne opóźnienie między zapytaniami (2.2 sekundy)
        const RETRY_DELAY = 3000; // opóźnienie przy ponownej próbie po błędzie (3 sekundy)

        function getRandomDelay() {
            return Math.floor(Math.random() * (MAX_DELAY - MIN_DELAY + 1)) + MIN_DELAY;
        }

        function finishCollection(wasSuccessful = true) {
            console.log(`Kończę zbieranie danych dla ${getCategoryName(folder)}`);
            isCollectingHistory = false;
            isPaused = false;

            // Przywróć standardowe przyciski i ukryj kontrolne
            const controlButtons = document.querySelector('#collection-controls');
            const standardButtons = document.querySelector('#standard-buttons');
            if (controlButtons) {
                controlButtons.style.display = 'none';
            }
            if (standardButtons) {
                standardButtons.style.display = 'grid';
            }

            // Ukryj status pobierania
            const statusElement = document.querySelector('#collection-status');
            if (statusElement) {
                statusElement.innerHTML = '';
            }

            if (!wasSuccessful) {
                showError('Anulowano zbieranie danych.');
                if (callback) callback();
                return;
            }

            if (tempExpLog.length === 0 && existingEntries.length === 0) {
                showError('Nie znaleziono żadnych wpisów z doświadczeniem.');
                if (callback) callback();
                return;
            }

            // Łączymy nowe wpisy z zachowanymi wpisami z innych kategorii
            if (folder === 'all') {
                experienceLog = tempExpLog;
            } else {
                // Usuń stare wpisy z aktualnej kategorii
                const otherEntries = experienceLog.filter(entry => {
                    const entryCategory =
                        entry.type === 'work' ? 'job' :
                        entry.type === 'duel' ? 'duel' :
                        entry.type === 'battle' ? 'fortbattle' : 'other';
                    return entryCategory !== folder;
                });
                experienceLog = [...tempExpLog, ...otherEntries];
            }

            totalExperience = experienceLog.reduce((sum, entry) => sum + entry.amount, 0);

            localStorage.setItem('experienceLog', JSON.stringify(experienceLog));
            localStorage.setItem('totalExperience', JSON.stringify(totalExperience));

            updateDisplay();
            updateXPStats();

            const newEntriesCount = tempExpLog.length;
            showError(`Zakończono pobieranie ${getCategoryName(folder)}! ${folder === 'all' ? 'Znaleziono' : 'Dodano'} ${newEntriesCount} wpisów.`);

            if (callback) {
                console.log(`Wywołuję callback dla kategorii: ${getCategoryName(folder)}`);
                callback();
            }
        }

        // Pobierz wszystkie raporty
        Ajax.remoteCall('reports', 'get_reports', {
            page: 1,
            folder: folder
        }, function(initialData) {
            console.log('Pobieranie wszystkich raportów...');
            processedPages = 0;

            if (!initialData || initialData.error) {
                console.error('Błąd pobierania danych:', initialData);
                isCollectingHistory = false;
                alert('Nie udało się pobrać informacji o raportach. Spróbuj ponownie.');
                return;
            }

            const totalPages = initialData.count;
            const pagesToProcess = maxPages ? Math.min(maxPages, totalPages) : totalPages;
            console.log(`Znaleziono ${totalPages} stron raportów`);

            function processPage(page) {
                if (shouldCancel) {
                    finishCollection(false);
                    return;
                }

                if (!isCollectingHistory || page > pagesToProcess) {
                    console.log('Zakończono przetwarzanie raportów');
                    finishCollection(true);
                    return;
                }

                if (isPaused) {
                    setTimeout(() => processPage(page), 500);
                    return;
                }

                console.log(`Pobieram stronę ${page}...`);
                Ajax.remoteCall('reports', 'get_reports', {
                    page: page,
                    folder: folder
                }, function(data) {
                    if (!isCollectingHistory || shouldCancel) return;

                    if (data && !data.error && data.reports && data.reports.length > 0) {
                        failedAttempts = 0;
                        console.log(`Znaleziono ${data.reports.length} raportów na stronie ${page}`);

                        data.reports.forEach(report => {
                            const expMatch = report.popupData.match(/experience.png[^>]*>(?:[^<]*<\/[^>]+>)*[^<]*<td>(\d+)<\/td>/) ||
                                            report.popupData.match(/Doświadczenie<\/span>\s*<span[^>]*>(\d+)\s*punktów/);
                            const exp = expMatch ? parseInt(expMatch[1]) : 0;

                            if (exp > 0) {
                                const reportType = report.title.includes('Raport dot. pracy') ? 'work' :
                                                    report.title.includes('Pojedynek') ? 'duel' :
                                                    (report.title.includes('Bitwa') || report.title.includes('Fort')) ? 'battle' : 'other';

                                console.log(`Znaleziono XP w raporcie:`, {
                                    title: report.title,
                                    exp: exp,
                                    type: reportType
                                });

                                tempExpLog.push({
                                    amount: exp,
                                    source: report.title,
                                    timestamp: report.date_received,
                                    page: page,
                                    type: reportType
                                });
                            }
                        });

                        processedPages++;
                        updateCollectionStatus(processedPages, pagesToProcess, tempExpLog.length);
                        setTimeout(() => processPage(page + 1), getRandomDelay());
                    } else {
                        console.error(`Błąd pobierania strony ${page}:`, data);
                        failedAttempts++;
                        if (failedAttempts < MAX_RETRIES) {
                            setTimeout(() => processPage(page), RETRY_DELAY);
                        } else {
                            console.error(`Nie udało się przetworzyć strony ${page} po ${MAX_RETRIES} próbach`);
                            finishCollection(true);
                        }
                    }
                });
            }

            processPage(1);
        });
    }

    // Funkcja do aktualizacji wyświetlania
    function updateDisplay() {
        const totalExperienceElement = document.querySelector('#total-experience');
        if (totalExperienceElement) {
            totalExperienceElement.textContent = totalExperience;
        }
    }

    // Funkcja do dodawania okna z doświadczeniem
    function addExperienceWindow() {
        const existingWindow = document.querySelector('#experience-tracker');
        if (existingWindow) {
            existingWindow.style.display = isTrackerVisible ? 'block' : 'none';
            return;
        }

        const trackerDiv = document.createElement('div');
        trackerDiv.id = 'experience-tracker';
        trackerDiv.style.position = 'fixed';
        trackerDiv.style.display = isTrackerVisible ? 'block' : 'none';

        // Wczytaj zapisaną pozycję
        const savedPosition = JSON.parse(localStorage.getItem('experienceTrackerPosition')) || {
            top: '50px',
            left: 'auto',
            right: '310px'
        };

        // Zastosuj zapisaną pozycję
        trackerDiv.style.top = savedPosition.top;
        trackerDiv.style.left = savedPosition.left;
        trackerDiv.style.right = savedPosition.right;

        trackerDiv.style.width = '280px';
        trackerDiv.style.padding = '15px';
        trackerDiv.style.background = 'rgba(20, 20, 20, 0.95)';
        trackerDiv.style.color = '#fff';
        trackerDiv.style.border = '1px solid #444';
        trackerDiv.style.borderRadius = '8px';
        trackerDiv.style.zIndex = '1000';
        trackerDiv.style.cursor = 'move';
        trackerDiv.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.3)';
        trackerDiv.style.fontFamily = 'Arial, sans-serif';

        trackerDiv.innerHTML = `
            <div style="margin-bottom: 15px; border-bottom: 1px solid #444; padding-bottom: 10px;">
                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
                    <h3 style="margin: 0; font-size: 16px; color: #FFD700;">Doświadczenie</h3>
                    <span id="total-experience" style="font-size: 20px; font-weight: bold; color: #2196F3;">${totalExperience} XP</span>
                </div>
                <div style="display: flex; flex-direction: column; gap: 5px; margin-top: 8px; font-size: 13px;">
                    <div style="display: flex; justify-content: space-between; padding: 3px 0;">
                        <span style="color: #BBB;">Z prac:</span>
                        <span id="work-xp" style="color: #27AE60;">0 XP</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; padding: 3px 0;">
                        <span style="color: #BBB;">Z pojedynków:</span>
                        <span id="duel-xp" style="color: #e74c3c;">0 XP</span>
                    </div>
                    <div style="display: flex; justify-content: space-between; padding: 3px 0;">
                        <span style="color: #BBB;">Z bitew fortowych:</span>
                        <span id="battle-xp" style="color: #9b59b6;">0 XP</span>
                    </div>
                </div>
            </div>

            <div id="collection-status" style="margin: 10px 0; font-size: 13px; color: #BBB;"></div>

            <div id="collection-controls" style="display: none; grid-template-columns: 1fr 1fr; gap: 8px; margin: 10px 0;">
                <button id="pause-collection" style="
                    padding: 8px;
                    background: #F39C12;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 13px;
                    transition: background 0.2s;
                ">Wstrzymaj</button>

                <button id="cancel-collection" style="
                    padding: 8px;
                    background: #E74C3C;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 13px;
                    transition: background 0.2s;
                ">Anuluj</button>
            </div>

            <div id="standard-buttons" style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 15px;">
                <button id="update-history" style="
                    padding: 8px;
                    background: #2196F3;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 13px;
                    transition: background 0.2s;
                ">Aktualizuj dane</button>

                <button id="collect-history" style="
                    padding: 8px;
                    background: #27AE60;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 13px;
                    transition: background 0.2s;
                ">Zbierz dane</button>

                <button id="show-experience-log" style="
                    padding: 8px;
                    background: #2C3E50;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 13px;
                    transition: background 0.2s;
                ">Szczegóły</button>

                <button id="reset-experience" style="
                    padding: 8px;
                    background: #C0392B;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 13px;
                    transition: background 0.2s;
                ">Kasuj</button>
            </div>
        `;

        // Dodanie hover efektów dla przycisków
        const buttons = trackerDiv.getElementsByTagName('button');
        for (let button of buttons) {
            button.addEventListener('mouseover', function() {
                const currentBg = this.style.background;
                this.style.background = currentBg.replace(')', ' brightness(120%)');
            });
            button.addEventListener('mouseout', function() {
                const currentBg = this.style.background;
                this.style.background = currentBg.replace(' brightness(120%)', '');
            });
        }

        document.body.appendChild(trackerDiv);

        // Dodanie obsługi przycisków
        document.querySelector('#collect-history').addEventListener('click', () => {
            if (!isCollectingHistory) {
                showCategorySelectionPanel('collect');
            }
        });

        document.querySelector('#pause-collection').addEventListener('click', function() {
            isPaused = !isPaused;
            this.textContent = isPaused ? 'Wznów' : 'Wstrzymaj';
            this.style.background = isPaused ? '#27AE60' : '#F39C12';
        });

        document.querySelector('#cancel-collection').addEventListener('click', function() {
            if (confirm('Czy na pewno chcesz anulować zbieranie danych?')) {
                shouldCancel = true;
            }
        });

        document.querySelector('#show-experience-log').addEventListener('click', showExperienceDetails);
        document.querySelector('#reset-experience').addEventListener('click', resetExperience);

        // Dodaj obsługę przycisku aktualizacji
        document.querySelector('#update-history').addEventListener('click', () => {
            if (!isCollectingHistory) {
                showCategorySelectionPanel('update');
            }
        });

        makeDraggable(trackerDiv);

        // Aktualizuj statystyki przy tworzeniu okna
        updateXPStats();

        // Modyfikacja funkcji addExperience aby aktualizowała statystyki
        const originalAddExperience = addExperience;
        addExperience = function(amount, source) {
            originalAddExperience.call(this, amount, source);
            updateXPStats();
        };

        // Dodanie aktualizacji statystyk po zebraniu danych
        const originalCollectExperienceHistory = collectExperienceHistory;
        collectExperienceHistory = function(maxPages, folder, callback = null) {
            originalCollectExperienceHistory.call(this, maxPages, folder, callback);
            setTimeout(updateXPStats, 1000); // Aktualizuj po zakończeniu zbierania
        };
    }

    // Funkcja przeciągania elementu
    function makeDraggable(element) {
        let isDragging = false;
        let startX, startY, initialX, initialY;

        // Znajdujemy element nagłówka (cały górny obszar z doświadczeniem)
        const headerArea = element.querySelector('div:first-child');

        if (headerArea) {
            headerArea.style.cursor = 'move'; // Zmiana kursora na "move" dla całego obszaru nagłówka
        }

        element.onmousedown = (event) => {
            // Sprawdzamy, czy kliknięcie było w obszarze nagłówka lub jego potomków
            let targetElement = event.target;
            let isInHeader = false;

            // Sprawdzamy, czy kliknięty element jest w obszarze nagłówka
            while (targetElement && targetElement !== element) {
                if (targetElement === headerArea) {
                    isInHeader = true;
                    break;
                }
                targetElement = targetElement.parentElement;
            }

            // Jeśli kliknięcie było w obszarze nagłówka i nie w przycisk
            if (isInHeader && event.target.tagName !== 'BUTTON') {
                isDragging = true;
                startX = event.clientX;
                startY = event.clientY;
                initialX = parseInt(window.getComputedStyle(element).left, 10) || 0;
                initialY = parseInt(window.getComputedStyle(element).top, 10) || 0;
                document.onmousemove = onMouseMove;
                document.onmouseup = onMouseUp;
            }
        };

        function onMouseMove(event) {
            if (!isDragging) return;
            const deltaX = event.clientX - startX;
            const deltaY = event.clientY - startY;
            element.style.left = `${initialX + deltaX}px`;
            element.style.top = `${initialY + deltaY}px`;
            element.style.right = 'auto'; // Deaktywuje "right" gdy "left" jest ustawione
        }

        function onMouseUp() {
            if (isDragging) {
                // Zapisz pozycję w localStorage
                const position = {
                    top: element.style.top,
                    left: element.style.left,
                    right: 'auto'
                };
                localStorage.setItem('experienceTrackerPosition', JSON.stringify(position));
            }
            isDragging = false;
            document.onmousemove = null;
            document.onmouseup = null;
        }
    }

    // Funkcja zapisywania pozycji okna
    function savePosition(element) {
        const top = element.style.top;
        const left = element.style.left;
        const right = element.style.right;

        localStorage.setItem('experienceTrackerPosition', JSON.stringify({ top, left, right }));
        alert('Pozycja okna została zapisana!');
    }

    // Funkcja do zapisywania zdobytego doświadczenia
    function addExperience(amount, source) {
        const now = new Date();
        const day = String(now.getDate()).padStart(2, '0');
        const month = String(now.getMonth() + 1).padStart(2, '0');
        const year = now.getFullYear();
        const formattedDate = `${day}.${month}.${year}`;

        totalExperience += amount;
        experienceLog.push({ amount, source, timestamp: formattedDate });

        // Aktualizuj lokalne przechowywanie
        localStorage.setItem('totalExperience', JSON.stringify(totalExperience));
        localStorage.setItem('experienceLog', JSON.stringify(experienceLog));

        // Zaktualizuj wyświetlaną wartość
        const totalExperienceElement = document.querySelector('#total-experience');
        if (totalExperienceElement) {
            totalExperienceElement.textContent = totalExperience;
        }
    }

    // Funkcja do pokazywania szczegółów doświadczenia
    function showExperienceDetails() {
        const logWindow = window.open('', '_blank');

        // Ensure we have a valid window reference
        if (logWindow === null) {
            alert('Proszę zezwolić na otwieranie nowych kart w przeglądarce.');
            return;
        }

        logWindow.document.write(`
            <!DOCTYPE html>
            <html>
            <head>
                <title>Historia doświadczenia</title>
                <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
                <style>
                    * {
                        margin: 0;
                        padding: 0;
                        box-sizing: border-box;
                        font-family: 'Roboto', sans-serif;
                    }

                    html {
                        min-width: 1300px;
                    }

                    body {
                        background: #f5f6fa;
                        color: #2d3436;
                        line-height: 1.6;
                        padding: 20px;
                        min-width: 1300px;
                        width: 100%;
                    }

                    .container {
                        min-width: 1300px;
                        width: 100%;
                        margin: 0 auto;
                        padding: 20px;
                    }

                    .controls {
                        background: white;
                        padding: 15px;
                        border-radius: 12px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.05);
                        display: grid;
                        grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
                        gap: 10px;
                        margin-bottom: 15px;
                    }

                    .controls select,
                    .controls input,
                    .controls button {
                        padding: 8px 12px;
                        border: 1px solid #dfe6e9;
                        border-radius: 6px;
                        font-size: 13px;
                        width: 100%;
                    }

                    .controls button {
                        background: #2196F3;
                        color: white;
                        border: none;
                        cursor: pointer;
                        transition: background 0.2s;
                    }

                    .controls button:hover {
                        background: #1976D2;
                    }

                    .chart-container {
                        background: white;
                        padding: 20px;
                        border-radius: 12px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.05);
                        height: 400px;
                        margin: 20px 0;
                    }

                    .content-wrapper {
                        display: grid;
                        grid-template-columns: 20% 35% 45%;
                        gap: 20px;
                        margin-top: 20px;
                        width: 100%;
                        height: calc(100vh - 220px); /* Dodajemy stałą wysokość dla wrappera */
                    }

                    .left-column, .middle-column, .right-column {
                        width: 100%;
                        min-width: 0;
                        height: 100%; /* Ustawiamy pełną wysokość dla kolumn */
                    }

                    .right-column {
                        display: flex;
                        flex-direction: column;
                        gap: 15px;
                        height: 560px; /* Całkowita wysokość kolumny */
                    }

                    .right-content {
                        background: white;
                        padding: 20px;
                        border-radius: 12px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.05);
                        height: 500px !important;
                        overflow: hidden;
                        min-height: 500px !important;
                        max-height: 500px !important;
                        display: flex;
                        flex-direction: column;
                    }

                    .right-content h2 {
                        margin: 0 0 15px 0;
                        color: #2d3436;
                        font-size: 18px;
                        flex: 0 0 auto;
                    }

                    .entries-list {
                        flex: 1;
                        overflow-y: auto;
                    }

                    .entries-header {
                        display: grid;
                        grid-template-columns: 100px 1fr 100px;
                        gap: 15px;
                        padding: 10px 15px;
                        background: white;
                        font-weight: bold;
                        border-bottom: 2px solid #ddd;
                        position: sticky;
                        top: 0;
                        z-index: 1;
                        flex: 0 0 auto; /* Dodajemy flex aby header nie rozpychał kontenera */
                    }

                    .entry {
                        display: grid;
                        grid-template-columns: 100px 1fr 100px;
                        gap: 15px;
                        padding: 15px;
                        border-bottom: 1px solid #f1f2f6;
                        transition: all 0.2s ease;
                        align-items: center;
                    }

                    .entry:hover {
                        background: #f8f9fa;
                    }

                    .entry:last-child {
                        border-bottom: none;
                    }

                    .entry .timestamp {
                        color: #636e72;
                        font-size: 14px;
                        white-space: nowrap;
                    }

                    .entry .source {
                        color: #2d3436;
                        font-weight: 500;
                    }

                    .entry .amount {
                        color: #00b894;
                        font-weight: 700;
                        font-size: 16px;
                    }

                    .badge {
                        display: inline-block;
                        padding: 4px 8px;
                        border-radius: 6px;
                        font-size: 12px;
                        font-weight: 500;
                        margin-right: 8px;
                    }

                    .badge-church { background: #81ecec; color: #00cec9; }
                    .badge-work { background: #55efc4; color: #00b894; }
                    .badge-duel-player { background: #ff7675; color: #d63031; }
                    .badge-duel-bandit { background: #fab1a0; color: #e17055; }
                    .badge-battle { background: #9b59b6; color: #8e44ad; }
                    .badge-other { background: #81ecec; color: #00cec9; }

                    .pagination {
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        gap: 10px;
                        padding: 10px;
                        background: white;
                        border-radius: 8px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.05);
                        height: 45px;
                        flex: 0 0 auto; /* Dodajemy flex aby paginacja nie rozpychała kontenera */
                    }

                    .pagination button {
                        padding: 8px 12px;
                        border: 1px solid #dfe6e9;
                        background: white;
                        color: #2d3436;
                        border-radius: 8px;
                        cursor: pointer;
                        transition: all 0.3s ease;
                    }

                    .pagination button:hover {
                        border-color: #0984e3;
                        color: #0984e3;
                    }

                    .pagination button.active {
                        background: #0984e3;
                        color: white;
                        border-color: #0984e3;
                    }

                    .pagination .info {
                        color: #636e72;
                        font-size: 14px;
                    }

                    .daily-xp-container {
                        background: white;
                        padding: 20px;
                        border-radius: 12px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.05);
                        height: 500px !important;
                        overflow: hidden;
                        min-height: 500px !important;
                        max-height: 500px !important;
                        display: flex;
                        flex-direction: column;
                    }

                    .daily-xp-container h2 {
                        margin: 0 0 15px 0;
                        color: #2d3436;
                        font-size: 18px;
                        flex: 0 0 auto;
                    }

                    .daily-xp-header {
                        display: grid;
                        grid-template-columns: 100px minmax(150px, 1fr) 100px;
                        gap: 15px;
                        padding: 10px 15px;
                        background: white;
                        font-weight: bold;
                        border-bottom: 2px solid #ddd;
                        flex: 0 0 auto;
                    }

                    .daily-xp-header > div:nth-child(3) {
                        text-align: right;
                        padding-right: 30px;
                    }

                    .daily-xp-entries {
                        flex: 1;
                        overflow-y: auto;
                        padding-right: 5px;
                    }

                    .daily-xp-table {
                        width: 100%;
                        border-collapse: collapse;
                        table-layout: fixed;
                    }

                    .daily-xp-table td {
                        padding: 10px 15px;
                        border-bottom: 1px solid #f1f2f6;
                    }

                    .daily-xp-table td:first-child {
                        width: 100px;
                    }

                    .daily-xp-table td:nth-child(2) {
                        width: auto;
                        min-width: 150px;
                    }

                    .daily-xp-table td:last-child {
                        width: 100px;
                    }

                    .daily-xp-entry {
                        transition: all 0.2s ease;
                    }

                    .daily-xp-entry:hover {
                        background: #f8f9fa;
                    }

                    .daily-xp-entry:last-child td {
                        border-bottom: none;
                    }

                    .daily-xp-entry .date {
                        color: #636e72;
                        font-size: 14px;
                    }

                    .daily-xp-entry .xp {
                        color: #2d3436;
                        font-weight: 500;
                        text-align: center;
                    }

                    .daily-xp-entry .difference {
                        text-align: right;
                        font-weight: 700;
                        font-size: 14px;
                        padding-right: 30px;
                    }

                    .daily-xp-entry .difference.positive {
                        color: #27ae60;
                    }

                    .daily-xp-entry .difference.negative {
                        color: #e74c3c;
                    }

                    .daily-xp-entry .difference.neutral {
                        color: #666;
                    }

                    .stat-card {
                        background: white;
                        padding: 20px;
                        border-radius: 12px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.05);
                        transition: all 0.3s ease;
                        margin-bottom: 15px;
                    }

                    .stat-card:hover {
                        transform: translateY(-2px);
                        box-shadow: 0 4px 15px rgba(0,0,0,0.1);
                    }

                    .stat-card h3 {
                        color: #636e72;
                        font-size: 14px;
                        font-weight: 500;
                        text-transform: uppercase;
                        letter-spacing: 0.5px;
                        margin-bottom: 10px;
                    }

                    .stat-card .value {
                        font-size: 24px;
                        font-weight: 700;
                        color: #2d3436;
                        margin-bottom: 5px;
                    }

                    .stat-card .sub-value {
                        font-size: 14px;
                        color: #636e72;
                    }
                </style>
            </head>
            <body>
                <div class="container">
                    <div class="controls">
                        <select id="categoryFilter">
                            <option value="all">Wszystkie źródła</option>
                            <option value="Prace">Zwykłe prace</option>
                            <option value="Kosciol">Budowa kościoła</option>
                            <option value="PojedynkiGracze">Pojedynki z graczami</option>
                            <option value="PojedynkiBandyci">Pojedynki z bandytami</option>
                            <option value="Bitwy">Bitwy fortowe</option>
                            <option value="Inne">Inne</option>
                        </select>
                        <select id="sortBy">
                            <option value="date-desc">Od najnowszych</option>
                            <option value="exp-desc">Najwięcej XP</option>
                            <option value="exp-asc">Najmniej XP</option>
                        </select>
                        <input type="text" id="searchInput" placeholder="Szukaj...">
                        <button onclick="applyFilters()">Filtruj</button>
                    </div>

                    <div class="chart-container">
                        <canvas id="dailyExpChart"></canvas>
                    </div>

                    <div class="content-wrapper">
                        <div class="left-column">
                            <div id="summary" class="stats-grid"></div>
                        </div>
                        <div class="middle-column">
                            <div class="daily-xp-container">
                                <h2>Dzienne XP</h2>
                                <div class="daily-xp-header">
                                    <div>Data</div>
                                    <div style="text-align: center;">XP</div>
                                    <div style="text-align: right;">Różnica</div>
                                </div>
                                <div class="daily-xp-entries">
                                    <table class="daily-xp-table">
                                        <tbody id="daily-xp-tbody"></tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                        <div class="right-column">
                            <div class="right-content">
                                <h2>Raporty</h2>
                                <div class="entries-header">
                                    <div>Data</div>
                                    <div>Raport</div>
                                       <div>XP</div>
                                </div>
                                <div class="entries-list" id="experience-list"></div>
                            </div>
                            <div id="pagination" class="pagination"></div>
                        </div>
                    </div>
                </div>

                <script>
                    let currentPage = 1;
                    const itemsPerPage = 25;
                    let filteredData = [];
                    const originalData = ${JSON.stringify(experienceLog)};

                    function updateSummary() {
                        const summary = {
                            total: 0,
                            categories: {
                                'Zwykłe prace': { exp: 0, count: 0, max: 0 },
                                'Budowa kościoła': { exp: 0, count: 0, max: 0 },
                                'Pojedynki z graczami': { exp: 0, count: 0, max: 0 },
                                'Pojedynki z bandytami': { exp: 0, count: 0, max: 0 },
                                'Bitwy fortowe': { exp: 0, count: 0, max: 0 },
                                'Inne': { exp: 0, count: 0, max: 0 }
                            }
                        };

                        // Funkcja pomocnicza do formatowania daty
                        function formatDate(timestamp) {
                            if (!timestamp) return '';

                            // Jeśli timestamp zaczyna się od "Godzina:"
                            if (timestamp.startsWith('Godzina:')) {
                                const today = new Date();
                                const day = String(today.getDate()).padStart(2, '0');
                                const month = String(today.getMonth() + 1).padStart(2, '0');
                                const year = today.getFullYear();
                                return day + '.' + month + '.' + year;
                            }

                            // Jeśli data jest w formacie DD.MM.YYYY, HH:mm:ss
                            if (timestamp.includes('.') && timestamp.includes(':')) {
                                const [datePart] = timestamp.split(',');
                                return datePart.trim();
                            }

                            // Jeśli data jest w formacie "DD MMM YYYY"
                            const monthMap = {
                                'sty': '01', 'lut': '02', 'mar': '03', 'kwi': '04',
                                'maj': '05', 'cze': '06', 'lip': '07', 'sie': '08',
                                'wrz': '09', 'paź': '10', 'lis': '11', 'gru': '12'
                            };

                            const parts = timestamp.split(' ');
                            if (parts.length === 3) {
                                const day = String(parseInt(parts[0])).padStart(2, '0');
                                const monthAbbr = parts[1].toLowerCase();
                                const year = parts[2];

                                if (monthMap[monthAbbr]) {
                                    return day + '.' + monthMap[monthAbbr] + '.' + year;
                                }
                            }

                            // Dla pozostałych przypadków zwróć oryginalny timestamp
                            return timestamp;
                        }

                        // Analiza dziennego XP
                        const dailyExp = {};
                        const dailyMaxNpcExp = {};

                        originalData.forEach(entry => {
                            const formattedDate = formatDate(entry.timestamp);

                            if (!dailyExp[formattedDate]) {
                                dailyExp[formattedDate] = 0;
                                dailyMaxNpcExp[formattedDate] = 0;
                            }
                            dailyExp[formattedDate] += entry.amount;

                            if (entry.source.includes('Pojedynek z bandytą')) {
                                dailyMaxNpcExp[formattedDate] = Math.max(dailyMaxNpcExp[formattedDate], entry.amount);
                            }

                            // Aktualizacja statystyk ogólnych
                            let category = 'Inne';
                            if (entry.type === 'work') {
                                if (entry.source.includes('rozbudowa Kościół')) {
                                    category = 'Budowa kościoła';
                                } else {
                                    category = 'Zwykłe prace';
                                }
                            } else if (entry.type === 'duel') {
                                if (entry.source.includes('bandytą') || entry.source.includes('bandit')) {
                                    category = 'Pojedynki z bandytami';
                                } else {
                                    category = 'Pojedynki z graczami';
                                }
                            } else if (entry.type === 'battle') {
                                category = 'Bitwy fortowe';
                            }

                            summary.categories[category].exp += entry.amount;
                            summary.categories[category].count++;
                            summary.categories[category].max = Math.max(summary.categories[category].max, entry.amount);
                            summary.total += entry.amount;
                        });

                        // Sortowanie dat
                        const sortedDates = Object.keys(dailyExp).sort((a, b) => {
                            const [dayA, monthA, yearA] = a.split(' ');
                            const [dayB, monthB, yearB] = b.split(' ');
                            const monthMap = {
                                'sty': 1, 'lut': 2, 'mar': 3, 'kwi': 4, 'maj': 5, 'cze': 6,
                                'lip': 7, 'sie': 8, 'wrz': 9, 'paź': 10, 'lis': 11, 'gru': 12
                            };

                            if (yearA !== yearB) return yearB - yearA;
                            if (monthMap[monthA] !== monthMap[monthB]) return monthMap[monthB] - monthMap[monthA];
                            return dayB - dayA;
                        });

                        // Aktualizacja HTML
                        const summaryDiv = document.getElementById('summary');
                        const summaryHtml = [
                            '<div class="stats-grid">',
                            '<div class="stat-card">',
                            '<h3>Całkowite XP</h3>',
                            '<div class="value">' + summary.total.toLocaleString() + ' XP</div>',
                            '<div class="sub-value">' + originalData.length + ' wpisów</div>',
                            '</div>',
                            Object.entries(summary.categories).map(([category, data]) =>
                                data.count > 0 ? [
                                    '<div class="stat-card">',
                                    '<h3>' + category + '</h3>',
                                    '<div class="value">' + data.exp.toLocaleString() + ' XP</div>',
                                    '<div class="sub-value">',
                                    'Ilość: ' + data.count + '<br>',
                                    'Średnio: ' + Math.round(data.exp / data.count).toLocaleString() + ' XP<br>',
                                    'Max: ' + data.max.toLocaleString() + ' XP',
                                    '</div>',
                                    '</div>'
                                ].join('') : ''
                            ).join(''),
                            '</div>'
                        ].join('');

                        summaryDiv.innerHTML = summaryHtml;

                        // Aktualizacja tabeli dziennego XP
                        const dailyXpTbody = document.getElementById('daily-xp-tbody');
                        if (dailyXpTbody) {
                            let htmlContent = '';

                            htmlContent += sortedDates.map((date, index) => {
                                const prevDayXP = index < sortedDates.length - 1 ? dailyExp[sortedDates[index + 1]] : 0;
                                const difference = dailyExp[date] - prevDayXP;
                                const differenceText = index < sortedDates.length - 1 ?
                                    (difference > 0 ? '+' + difference.toLocaleString() : difference.toLocaleString()) : '-';
                                const differenceClass = difference > 0 ? 'positive' : difference < 0 ? 'negative' : 'neutral';

                                return '<tr class="daily-xp-entry">' +
                                    '<td class="date">' + date + '</td>' +
                                    '<td class="xp">' + dailyExp[date].toLocaleString() + '</td>' +
                                    '<td class="difference ' + differenceClass + '">' + differenceText + ' XP</td>' +
                                    '</tr>';
                            }).join('');

                            dailyXpTbody.innerHTML = htmlContent;
                        }

                        // Tworzenie wykresu
                        const ctx = document.getElementById('dailyExpChart');
                        new Chart(ctx, {
                            type: 'line',
                            data: {
                                labels: sortedDates.slice(0, 30), // Ostatnie 30 dni
                                datasets: [{
                                    label: 'XP dziennie',
                                    data: sortedDates.slice(0, 30).map(date => dailyExp[date]),
                                    borderColor: '#4CAF50',
                                    backgroundColor: 'rgba(76, 175, 80, 0.1)',
                                    tension: 0.1,
                                    fill: true
                                }]
                            },
                            options: {
                                responsive: true,
                                maintainAspectRatio: false,
                                plugins: {
                                    legend: {
                                        display: true,
                                        position: 'top'
                                    },
                                    tooltip: {
                                        callbacks: {
                                            label: function(context) {
                                                return context.raw.toLocaleString() + ' XP';
                                            }
                                        }
                                    }
                                },
                                scales: {
                                    y: {
                                        beginAtZero: true,
                                        ticks: {
                                            callback: function(value) {
                                                return value.toLocaleString();
                                            }
                                        }
                                    }
                                }
                            }
                        });
                    }

                    function applyFilters() {
                        const category = document.getElementById('categoryFilter').value;
                        const sortBy = document.getElementById('sortBy').value;
                        const searchText = document.getElementById('searchInput').value.toLowerCase();

                        filteredData = originalData.filter(entry => {
                            const matchesCategory =
                                category === 'all' ? true :
                                category === 'Kosciol' ? (entry.type === 'work' && entry.source.includes('rozbudowa Kościół')) :
                                category === 'Prace' ? (entry.type === 'work' && !entry.source.includes('rozbudowa Kościół')) :
                                category === 'PojedynkiGracze' ? (entry.type === 'duel' && !entry.source.includes('bandytą') && !entry.source.includes('bandit')) :
                                category === 'PojedynkiBandyci' ? (entry.type === 'duel' && (entry.source.includes('bandytą') || entry.source.includes('bandit'))) :
                                category === 'Bitwy' ? entry.type === 'battle' :
                                category === 'Inne' ? (entry.type !== 'work' && entry.type !== 'duel' && entry.type !== 'battle') : false;

                            const matchesSearch = searchText === '' ||
                                entry.source.toLowerCase().includes(searchText);

                            return matchesCategory && matchesSearch;
                        });

                        filteredData.sort((a, b) => {
                            switch(sortBy) {
                                case 'date-desc':
                                    return new Date(b.timestamp) - new Date(a.timestamp);
                                case 'exp-desc':
                                    return b.amount - a.amount;
                                case 'exp-asc':
                                    return a.amount - b.amount;
                                default:
                                    return 0;
                            }
                        });

                        currentPage = 1;
                        renderPage();
                    }

                    function renderPage() {
                        const start = (currentPage - 1) * itemsPerPage;
                        const end = start + itemsPerPage;
                        const pageItems = filteredData.slice(start, end);

                        const listDiv = document.getElementById('experience-list');
                        listDiv.innerHTML = '';

                        if (pageItems.length === 0) {
                            listDiv.innerHTML = '<div style="text-align: center; padding: 20px; color: #666;">Brak wpisów spełniających kryteria</div>';
                            return;
                        }

                        // Funkcja pomocnicza do formatowania daty
                        function formatDate(timestamp) {
                            if (!timestamp) return '';

                            // Jeśli timestamp zaczyna się od "Godzina:"
                            if (timestamp.startsWith('Godzina:')) {
                                const today = new Date();
                                const day = String(today.getDate()).padStart(2, '0');
                                const month = String(today.getMonth() + 1).padStart(2, '0');
                                const year = today.getFullYear();
                                return day + '.' + month + '.' + year;
                            }

                            // Jeśli data jest w formacie DD.MM.YYYY, HH:mm:ss
                            if (timestamp.includes('.') && timestamp.includes(':')) {
                                const [datePart] = timestamp.split(',');
                                return datePart.trim();
                            }

                            // Jeśli data jest w formacie "DD MMM YYYY"
                            const monthMap = {
                                'sty': '01', 'lut': '02', 'mar': '03', 'kwi': '04',
                                'maj': '05', 'cze': '06', 'lip': '07', 'sie': '08',
                                'wrz': '09', 'paź': '10', 'lis': '11', 'gru': '12'
                            };

                            const parts = timestamp.split(' ');
                            if (parts.length === 3) {
                                const day = String(parseInt(parts[0])).padStart(2, '0');
                                const monthAbbr = parts[1].toLowerCase();
                                const year = parts[2];

                                if (monthMap[monthAbbr]) {
                                    return day + '.' + monthMap[monthAbbr] + '.' + year;
                                }
                            }

                            // Dla pozostałych przypadków zwróć oryginalny timestamp
                            return timestamp;
                        }

                        pageItems.forEach(entry => {
                            const entryDiv = document.createElement('div');
                            entryDiv.className = 'entry';

                            let badgeClass = '';
                            let badgeText = '';

                            if (entry.type === 'work') {
                                if (entry.source.includes('rozbudowa Kościół')) {
                                    badgeClass = 'badge-church';
                                    badgeText = 'Kościół';
                                } else {
                                    badgeClass = 'badge-work';
                                    badgeText = 'Praca';
                                }
                            } else if (entry.type === 'duel') {
                                if (entry.source.includes('bandytą')) {
                                    badgeClass = 'badge-duel-bandit';
                                    badgeText = 'Bandyta';
                                } else {
                                    badgeClass = 'badge-duel-player';
                                    badgeText = 'PvP';
                                }
                            } else if (entry.type === 'battle') {
                                badgeClass = 'badge-battle';
                                badgeText = 'Bitwa';
                            } else {
                                badgeClass = 'badge-other';
                                badgeText = 'Inne';
                            }

                            entryDiv.innerHTML = [
                                '<span class="timestamp">' + formatDate(entry.timestamp) + '</span>',
                                '<span class="source">',
                                '<span class="badge ' + badgeClass + '">' + badgeText + '</span>',
                                entry.source,
                                '</span>',
                                '<span class="amount">+' + entry.amount.toLocaleString() + ' XP</span>'
                            ].join('');

                            listDiv.appendChild(entryDiv);
                        });

                        updatePagination();
                    }

                    function updatePagination() {
                        const totalPages = Math.ceil(filteredData.length / itemsPerPage);
                        const paginationDiv = document.getElementById('pagination');

                        let html = [
                            '<div class="pagination">',
                            '<span class="info">Strona ' + currentPage + ' z ' + totalPages + ' (' + filteredData.length + ' wpisów)</span>'
                        ];

                        if (totalPages > 1) {
                            if (currentPage > 1) {
                                html.push('<button onclick="changePage(1)">«</button>');
                                html.push('<button onclick="changePage(' + (currentPage - 1) + ')">‹</button>');
                            }

                            for (let i = Math.max(1, currentPage - 2); i <= Math.min(totalPages, currentPage + 2); i++) {
                                html.push('<button onclick="changePage(' + i + ')"' +
                                        (i === currentPage ? ' class="active"' : '') + '>' + i + '</button>');
                            }

                            if (currentPage < totalPages) {
                                html.push('<button onclick="changePage(' + (currentPage + 1) + ')">›</button>');
                                html.push('<button onclick="changePage(' + totalPages + ')">»</button>');
                            }
                        }

                        html.push('</div>');
                        paginationDiv.innerHTML = html.join('');
                    }

                    function changePage(newPage) {
                        currentPage = newPage;
                        renderPage();
                        window.scrollTo(0, 0);
                    }

                    // Inicjalizacja
                    filteredData = [...originalData];
                    updateSummary();
                    applyFilters();
                </script>
                <script>
                    // Add resize handling
                    window.addEventListener('resize', function() {
                        if (window.outerWidth < 1300) {
                            window.resizeTo(1300, window.outerHeight);
                        }
                    });
                </script>
            </body>
            </html>
        `);
        logWindow.document.close();
    }

    // Funkcja resetująca doświadczenie do 0
    function resetExperience() {
        if (confirm('Jesteś pewien, że chcesz zresetować całkowite doświadczenie?')) {
            totalExperience = 0;
            experienceLog = [];

            // Zapisz zresetowane wartości w localStorage
            localStorage.setItem('totalExperience', JSON.stringify(totalExperience));
            localStorage.setItem('experienceLog', JSON.stringify(experienceLog));

            // Zaktualizuj wyświetlaną wartość doświadczenia
            const totalExperienceElement = document.querySelector('#total-experience');
            if (totalExperienceElement) {
                totalExperienceElement.textContent = totalExperience + ' XP';
            }

            // Zaktualizuj statystyki z prac i pojedynków
            const workXPElement = document.querySelector('#work-xp');
            const duelXPElement = document.querySelector('#duel-xp');

            if (workXPElement) workXPElement.textContent = '0 XP';
            if (duelXPElement) duelXPElement.textContent = '0 XP';
        }
    }

    // Nasłuchiwanie na zmiany w doświadczeniu (np. praca, bitwa)
    const originalSetExperience = window.Character.setExperience;
    window.Character.setExperience = function(newExperience) {
        const currentExperience = this.experience;
        const gainedExperience = newExperience - currentExperience;
        if (gainedExperience > 0) {
            addExperience(gainedExperience, 'Aktywność w grze'); // Domyślna aktywność
        }
        originalSetExperience.call(this, newExperience);
    };

    // Dodajemy nową funkcję do tworzenia panelu wyboru
    function showCategorySelectionPanel(action) {
        const existingPanel = document.querySelector('#category-selection-panel');
        if (existingPanel) existingPanel.remove();

        const panel = document.createElement('div');
        panel.id = 'category-selection-panel';
        panel.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(20, 20, 20, 0.95);
            padding: 20px;
            border-radius: 8px;
            border: 1px solid #444;
            z-index: 1001;
            color: white;
            min-width: 300px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
        `;

        const title = action === 'collect' ? 'Zbieranie danych' : 'Aktualizacja danych';

        panel.innerHTML = `
            <div style="margin-bottom: 15px; text-align: center; font-size: 16px; color: #FFD700;">
                ${title}
            </div>
            <div style="display: grid; gap: 10px;">
                <label style="
                    display: flex;
                    align-items: center;
                    padding: 10px;
                    background: #2C3E50;
                    border-radius: 4px;
                    cursor: pointer;
                    transition: background 0.2s;
                ">
                    <input type="checkbox" data-folder="all" class="category-checkbox" style="margin-right: 10px;">
                    Wszystkie raporty
                </label>

                <label style="
                    display: flex;
                    align-items: center;
                    padding: 10px;
                    background: #27AE60;
                    border-radius: 4px;
                    cursor: pointer;
                    transition: background 0.2s;
                ">
                    <input type="checkbox" data-folder="job" class="category-checkbox" style="margin-right: 10px;">
                    PRACE
                </label>

                <label style="
                    display: flex;
                    align-items: center;
                    padding: 10px;
                    background: #E74C3C;
                    border-radius: 4px;
                    cursor: pointer;
                    transition: background 0.2s;
                ">
                    <input type="checkbox" data-folder="duel" class="category-checkbox" style="margin-right: 10px;">
                    POJEDYNKI
                </label>

                <label style="
                    display: flex;
                    align-items: center;
                    padding: 10px;
                    background: #9B59B6;
                    border-radius: 4px;
                    cursor: pointer;
                    transition: background 0.2s;
                ">
                    <input type="checkbox" data-folder="fortbattle" class="category-checkbox" style="margin-right: 10px;">
                    BITWY FORTOWE
                </label>

                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px;">
                    <button id="start-collection" style="
                        padding: 10px;
                        background: #2196F3;
                        color: white;
                        border: none;
                        border-radius: 4px;
                        cursor: pointer;
                        transition: background 0.2s;
                    ">Start</button>

                    <button id="cancel-selection" style="
                        padding: 10px;
                        background: #95A5A6;
                        color: white;
                        border: none;
                        border-radius: 4px;
                        cursor: pointer;
                        transition: background 0.2s;
                    ">Anuluj</button>
                </div>
            </div>
        `;

        // Dodajemy efekt hover dla przycisków i labelów
        const buttons = panel.getElementsByTagName('button');
        for (let button of buttons) {
            button.addEventListener('mouseover', function() {
                this.style.filter = 'brightness(120%)';
            });
            button.addEventListener('mouseout', function() {
                this.style.filter = 'brightness(100%)';
            });
        }

        const labels = panel.getElementsByTagName('label');
        for (let label of labels) {
            label.addEventListener('mouseover', function() {
                this.style.filter = 'brightness(120%)';
            });
            label.addEventListener('mouseout', function() {
                this.style.filter = 'brightness(100%)';
            });
        }

        // Obsługa checkboxa "Wszystkie raporty"
        const allCheckbox = panel.querySelector('input[data-folder="all"]');
        allCheckbox.addEventListener('change', function() {
            const checkboxes = panel.querySelectorAll('.category-checkbox:not([data-folder="all"])');
            checkboxes.forEach(checkbox => {
                checkbox.checked = false;
                checkbox.disabled = this.checked;
            });
        });

        // Obsługa pozostałych checkboxów
        const otherCheckboxes = panel.querySelectorAll('.category-checkbox:not([data-folder="all"])');
        otherCheckboxes.forEach(checkbox => {
            checkbox.addEventListener('change', function() {
                if (this.checked) {
                    allCheckbox.checked = false;
                }
            });
        });

        // Obsługa przycisku Start
        panel.querySelector('#start-collection').addEventListener('click', async function() {
            const selectedFolders = Array.from(panel.querySelectorAll('.category-checkbox:checked'))
                .map(cb => cb.dataset.folder);

            if (selectedFolders.length === 0) {
                alert('Wybierz przynajmniej jedną kategorię!');
                return;
            }

            panel.remove();

            if (selectedFolders.includes('all')) {
                // Jeśli wybrano "wszystkie", ignoruj pozostałe
                if (action === 'collect') {
                    collectExperienceHistory(null, 'all');
                } else {
                    updateExperienceHistory('all');
                }
            } else {
                // Sekwencyjne przetwarzanie wybranych kategorii
                const processCategories = async () => {
                    for (let i = 0; i < selectedFolders.length; i++) {
                        const folder = selectedFolders[i];
                        console.log(`Rozpoczynam zbieranie danych dla ${getCategoryName(folder)}`);

                        await new Promise((resolve) => {
                            const processCategory = () => {
                                if (action === 'collect') {
                                    collectExperienceHistory(null, folder, () => {
                                        console.log(`Zakończono przetwarzanie ${getCategoryName(folder)}`);
                                        setTimeout(resolve, 2000);
                                    });
                                } else {
                                    updateExperienceHistory(folder, () => {
                                        console.log(`Zakończono przetwarzanie ${getCategoryName(folder)}`);
                                        setTimeout(resolve, 2000);
                                    });
                                }
                            };

                            // Jeśli to nie pierwsza kategoria, poczekaj chwilę przed rozpoczęciem
                            if (i > 0) {
                                setTimeout(processCategory, 2000);
                            } else {
                                processCategory();
                            }
                        });

                        if (shouldCancel) {
                            console.log('Anulowano zbieranie pozostałych danych');
                            showError('Anulowano zbieranie pozostałych danych.');
                            break;
                        }
                    }

                    if (!shouldCancel) {
                        showError('Zakończono przetwarzanie wszystkich wybranych kategorii!');
                    }
                };

                // Rozpocznij przetwarzanie kategorii
                processCategories().catch(error => {
                    console.error('Błąd podczas przetwarzania kategorii:', error);
                    showError('Wystąpił błąd podczas przetwarzania kategorii');
                });
            }
        });

        // Poprawiona obsługa przycisku Anuluj
        panel.querySelector('#cancel-selection').addEventListener('click', () => {
            panel.remove();
            shouldCancel = true; // Upewnij się, że wszystkie trwające operacje zostaną anulowane
        });

        document.body.appendChild(panel);
    }

    // Modyfikacja funkcji updateExperienceHistory
    function updateExperienceHistory(folder = 'all', callback = null) {
        if (isCollectingHistory) {
            console.log('Aktualizacja danych jest już w trakcie. Poczekaj na zakończenie.');
            if (callback) callback();
            return;
        }

        console.log(`Rozpoczynam aktualizację danych dla ${getCategoryName(folder)}`);

        isCollectingHistory = true;
        isPaused = false;
        shouldCancel = false;
        tempExpLog = [];
        let processedPages = 0;
        let failedAttempts = 0;
        const MAX_RETRIES = 3;
        const MIN_DELAY = 2000; // minimalne opóźnienie między zapytaniami (2 sekundy)
        const MAX_DELAY = 2200; // maksymalne opóźnienie między zapytaniami (2.2 sekundy)
        const RETRY_DELAY = 3000; // opóźnienie przy ponownej próbie po błędzie (3 sekundy)

        // Pokaż przyciski kontrolne i ukryj standardowe
        const controlButtons = document.querySelector('#collection-controls');
        const standardButtons = document.querySelector('#standard-buttons');
        if (controlButtons) {
            controlButtons.style.display = 'grid';
        }
        if (standardButtons) {
            standardButtons.style.display = 'none';
        }

        // Tworzymy zbiór istniejących raportów (unikalny po source+timestamp)
        const existingKeys = new Set(experienceLog.map(e => `${e.source}|${e.timestamp}`));
        let foundDuplicate = false;

        function getRandomDelay() {
            return Math.floor(Math.random() * (MAX_DELAY - MIN_DELAY + 1)) + MIN_DELAY;
        }

        Ajax.remoteCall('reports', 'get_reports', {
            page: 1,
            folder: folder
        }, function(initialData) {
            if (!initialData || initialData.error) {
                console.error('Błąd pobierania danych:', initialData);
                isCollectingHistory = false;
                alert('Nie udało się pobrać informacji o raportach. Spróbuj ponownie.');
                return;
            }

            const totalPages = initialData.count;

            function processPage(page) {
                if (!isCollectingHistory || shouldCancel || page > totalPages || foundDuplicate) {
                    finishUpdate(shouldCancel);
                    return;
                }

                if (isPaused) {
                    setTimeout(() => processPage(page), 500);
                    return;
                }

                Ajax.remoteCall('reports', 'get_reports', {
                    page: page,
                    folder: folder
                }, function(data) {
                    if (!isCollectingHistory || shouldCancel || foundDuplicate) {
                        finishUpdate(shouldCancel);
                        return;
                    }

                    if (data && !data.error && data.reports && data.reports.length > 0) {
                        failedAttempts = 0;
                        for (const report of data.reports) {
                            const expMatch = report.popupData.match(/experience.png[^>]*>(?:[^<]*<\/[^>]+>)*[^<]*<td>(\d+)<\/td>/) ||
                                            report.popupData.match(/Doświadczenie<\/span>\s*<span[^>]*>(\d+)\s*punktów/);
                            const exp = expMatch ? parseInt(expMatch[1]) : 0;

                            if (exp > 0) {
                                const key = `${report.title}|${report.date_received}`;
                                if (existingKeys.has(key)) {
                                    foundDuplicate = true;
                                    break;
                                }

                                const reportType = report.title.includes('Raport dot. pracy') ? 'work' :
                                    report.title.includes('Pojedynek') ? 'duel' :
                                    (report.title.includes('Bitwa') || report.title.includes('Fort')) ? 'battle' : 'other';

                                tempExpLog.push({
                                    amount: exp,
                                    source: report.title,
                                    timestamp: report.date_received,
                                    page: page,
                                    type: reportType
                                });
                            }
                        }

                        processedPages++;
                        updateCollectionStatus(processedPages, totalPages, tempExpLog.length);

                        if (!foundDuplicate && !shouldCancel) {
                            setTimeout(() => processPage(page + 1), getRandomDelay());
                        } else {
                            finishUpdate(shouldCancel);
                        }
                    } else {
                        failedAttempts++;
                        if (failedAttempts < MAX_RETRIES && !shouldCancel) {
                            setTimeout(() => processPage(page), RETRY_DELAY);
                        } else {
                            finishUpdate(shouldCancel);
                        }
                    }
                });
            }

            processPage(1);
        });

        function finishUpdate(wasCancelled = false) {
            console.log(`Kończę aktualizację danych dla ${getCategoryName(folder)}`);
            isCollectingHistory = false;
            isPaused = false;

            // Przywróć standardowe przyciski i ukryj kontrolne
            const controlButtons = document.querySelector('#collection-controls');
            const standardButtons = document.querySelector('#standard-buttons');
            if (controlButtons) {
                controlButtons.style.display = 'none';
            }
            if (standardButtons) {
                standardButtons.style.display = 'grid';
            }

            if (wasCancelled) {
                showError('Anulowano aktualizację danych.');
                if (callback) callback();
                return;
            }

            if (tempExpLog.length === 0) {
                showError(`Brak nowych wpisów w kategorii ${getCategoryName(folder)}.`);
                if (callback) callback();
                return;
            }

            // Dodaj nowe wpisy na początek loga (bo są najnowsze)
            experienceLog = tempExpLog.concat(experienceLog);
            totalExperience = experienceLog.reduce((sum, entry) => sum + entry.amount, 0);

            localStorage.setItem('experienceLog', JSON.stringify(experienceLog));
            localStorage.setItem('totalExperience', JSON.stringify(totalExperience));

            updateDisplay();
            updateXPStats();

            showError(`Zaktualizowano ${getCategoryName(folder)}! Dodano ${tempExpLog.length} nowych wpisów.`);

            if (callback) {
                console.log(`Wywołuję callback dla kategorii: ${getCategoryName(folder)}`);
                callback();
            }
        }
    }

    // Funkcja do sprawdzania ilości stron dla różnych typów raportów
    function checkReportPages() {
        const types = [
            { name: 'Wszystkie raporty', params: { folder: 'all' } },
            { name: 'Raporty z pracy', params: { folder: 'job' } },
            { name: 'Raporty z pojedynków', params: { folder: 'duel' } },
            { name: 'Raporty z bitew fortowych', params: { folder: 'fortbattle' } }
        ];

        let results = {};
        let completed = 0;

        console.log('%c=== Rozpoczynam sprawdzanie raportów ===', 'font-weight: bold; font-size: 14px; color: #3498db;');

        types.forEach(type => {
            console.log(`%cSprawdzam: ${type.name}...`, 'color: #7f8c8d;');

            Ajax.remoteCall('reports', 'get_reports', {
                page: 1,
                ...type.params
            }, function(response) {
                completed++;
                console.log(`%cOtrzymano odpowiedź dla: ${type.name}`, 'color: #7f8c8d;');
                console.log('Surowa odpowiedź:', response);

                if (response && !response.error) {
                    results[type.name] = {
                        totalPages: response.pages || response.count || 0,
                        reportsPerPage: response.reports ? response.reports.length : 0,
                        totalReports: response.total || (response.reports ? response.reports.length * (response.pages || response.count || 0) : 0)
                    };
                } else {
                    results[type.name] = {
                        error: response ? response.error : 'Brak odpowiedzi'
                    };
                }

                if (completed === types.length) {
                    console.log('%c=== Statystyki raportów ===', 'font-weight: bold; font-size: 14px; color: #3498db;');
                    Object.entries(results).forEach(([name, data]) => {
                        console.log(`%c${name}:`, 'font-weight: bold; color: #2ecc71;');
                        if (data.error) {
                            console.log(`  Błąd: ${data.error}`);
                        } else {
                            console.log(`  Liczba stron: ${data.totalPages}`);
                            console.log(`  Raportów na stronę: ${data.reportsPerPage}`);
                            console.log(`  Łączna liczba raportów: ${data.totalReports}`);
                            if (data.reportsPerPage > 0) {
                                console.log(`  Szacowana łączna liczba raportów: ${data.totalPages * data.reportsPerPage}`);
                            }
                        }
                        console.log('------------------------');
                    });
                }
            });
        });

        return "Sprawdzanie ilości stron... Zobacz wyniki w konsoli.";
    }

    // Dodaj do window aby było dostępne w konsoli
    window.checkReportPages = checkReportPages;

    // Dodanie okna śledzenia doświadczenia
    setInterval(() => {
        addExperienceWindow();
        createVisibilityToggle();
    }, 1000);

    // Funkcja do przełączania widoczności
    function toggleTrackerVisibility() {
        const tracker = document.querySelector('#experience-tracker');
        const toggleCheckbox = document.querySelector('#toggle-tracker-visibility');
        if (tracker && toggleCheckbox) {
            isTrackerVisible = toggleCheckbox.checked;
            tracker.style.display = isTrackerVisible ? 'block' : 'none';
            localStorage.setItem('experienceTrackerVisible', JSON.stringify(isTrackerVisible));
        }
    }

    // Funkcja do tworzenia checkboxa widoczności
    function createVisibilityToggle() {
        const existingToggle = document.querySelector('#tracker-visibility-toggle');
        if (existingToggle) return;

        const toggleContainer = document.createElement('div');
        toggleContainer.id = 'tracker-visibility-toggle';

        // Pozycjonowanie względem ui_topbar
        const uiTopbar = document.querySelector('#ui_topbar');
        const topbarRect = uiTopbar ? uiTopbar.getBoundingClientRect() : null;

        toggleContainer.style.cssText = `
            position: fixed;
            top: ${topbarRect ? topbarRect.top + 'px' : '10px'};
            right: ${topbarRect ? (window.innerWidth - topbarRect.left) + 'px' : '310px'};
            background: rgba(20, 20, 20, 0.95);
            padding: 5px 10px;
            border-radius: 4px;
            border: 1px solid #444;
            z-index: 1000;
            color: white;
            font-family: Arial, sans-serif;
            font-size: 12px;
            display: flex;
            align-items: center;
            gap: 5px;
            cursor: pointer;
        `;

        toggleContainer.innerHTML = `
            <input type="checkbox" id="toggle-tracker-visibility" ${isTrackerVisible ? 'checked' : ''}>
        `;

        document.body.appendChild(toggleContainer);

        // Aktualizacja pozycji przy zmianie rozmiaru okna
        window.addEventListener('resize', () => {
            const uiTopbar = document.querySelector('#ui_topbar');
            const topbarRect = uiTopbar ? uiTopbar.getBoundingClientRect() : null;
            if (topbarRect && toggleContainer) {
                toggleContainer.style.top = topbarRect.top + 'px';
                toggleContainer.style.right = (window.innerWidth - topbarRect.left) + 'px';
            }
        });

        const checkbox = toggleContainer.querySelector('#toggle-tracker-visibility');
        checkbox.addEventListener('change', toggleTrackerVisibility);
    }
})();