Torn City Company Trainer Tracker (Working Version)

Track and manage company training in Torn City (guaranteed to work)

// ==UserScript==
// @name         Torn City Company Trainer Tracker (Working Version)
// @namespace    https://www.torn.com/profiles.php?XID=2302951
// @version      0.2.0
// @description  Track and manage company training in Torn City (guaranteed to work)
// @author       Obliviondrift1 [2302951]
// @match        https://www.torn.com/companies.php*
// @grant        none
// @license      MIT
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    console.log('🎯 Trainer Tracker: Starting...');

    // Configuration
    const CONFIG = {
        STORAGE_KEY: 'trainer_employees',
        RETRY_ATTEMPTS: 10,
        RETRY_DELAY: 500,
        OVERLAY_ID: 'trainer-tracker-overlay'
    };

    // Data management
    function getEmployees() {
        try {
            const data = localStorage.getItem(CONFIG.STORAGE_KEY);
            return data ? JSON.parse(data) : [];
        } catch (e) {
            console.error('Error loading employees:', e);
            return [];
        }
    }

    function saveEmployees(employees) {
        try {
            localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(employees));
            console.log('Employees saved:', employees.length);
        } catch (e) {
            console.error('Error saving employees:', e);
        }
    }

    // Robust initialization with multiple retry attempts
    function initializeTracker() {
        let attempts = 0;

        function tryInit() {
            attempts++;
            console.log(`🎯 Trainer Tracker: Attempt ${attempts}/${CONFIG.RETRY_ATTEMPTS}`);

            // Check if we're on the right page
            if (!window.location.href.includes('/companies.php')) {
                console.log('🎯 Trainer Tracker: Not on companies page, skipping');
                return;
            }

            // Check if document is ready
            if (document.readyState !== 'complete') {
                console.log('🎯 Trainer Tracker: Document not ready, retrying...');
                setTimeout(tryInit, CONFIG.RETRY_DELAY);
                return;
            }

            // Check if body exists
            if (!document.body) {
                console.log('🎯 Trainer Tracker: Body not found, retrying...');
                if (attempts < CONFIG.RETRY_ATTEMPTS) {
                    setTimeout(tryInit, CONFIG.RETRY_DELAY);
                }
                return;
            }

            // Try to create the tracker
            try {
                createTracker();
                console.log('🎯 Trainer Tracker: Successfully created!');
            } catch (error) {
                console.error('🎯 Trainer Tracker: Error creating tracker:', error);
                if (attempts < CONFIG.RETRY_ATTEMPTS) {
                    setTimeout(tryInit, CONFIG.RETRY_DELAY * 2);
                }
            }
        }

        // Start initialization
        tryInit();
    }

    function createTracker() {
        // Remove existing tracker
        const existing = document.getElementById(CONFIG.OVERLAY_ID);
        if (existing) {
            existing.remove();
            console.log('🎯 Trainer Tracker: Removed existing overlay');
        }

        // Create main container
        const overlay = document.createElement('div');
        overlay.id = CONFIG.OVERLAY_ID;

        // Apply styles directly to avoid CSP issues
        const styles = {
            position: 'fixed',
            top: '20px',
            right: '20px',
            width: '350px',
            minHeight: '200px',
            backgroundColor: '#1a1a1a',
            color: '#ffffff',
            border: '2px solid #444444',
            borderRadius: '8px',
            fontFamily: 'Arial, sans-serif',
            fontSize: '13px',
            zIndex: '2147483647', // Maximum z-index
            boxShadow: '0 4px 20px rgba(0,0,0,0.8)',
            overflow: 'hidden'
        };

        Object.assign(overlay.style, styles);

        // Create header
        const header = document.createElement('div');
        header.style.cssText = `
            background: #333333;
            padding: 12px 15px;
            border-bottom: 1px solid #555555;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
            user-select: none;
        `;

        const title = document.createElement('span');
        title.textContent = '🎯 Trainer Tracker';
        title.style.fontWeight = 'bold';

        const closeBtn = document.createElement('span');
        closeBtn.textContent = '✕';
        closeBtn.style.cssText = `
            cursor: pointer;
            font-size: 16px;
            padding: 2px 6px;
            border-radius: 3px;
            background: #ff4444;
            color: white;
        `;
        closeBtn.title = 'Close Tracker';

        header.appendChild(title);
        header.appendChild(closeBtn);

        // Create navigation
        const nav = document.createElement('div');
        nav.style.cssText = `
            display: flex;
            background: #2a2a2a;
            border-bottom: 1px solid #555555;
        `;

        const queueBtn = document.createElement('button');
        queueBtn.textContent = 'Queue';
        queueBtn.style.cssText = `
            flex: 1;
            padding: 10px;
            background: #0066cc;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 12px;
        `;

        const addBtn = document.createElement('button');
        addBtn.textContent = 'Add Employee';
        addBtn.style.cssText = `
            flex: 1;
            padding: 10px;
            background: #444444;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 12px;
        `;

        nav.appendChild(queueBtn);
        nav.appendChild(addBtn);

        // Create content area
        const content = document.createElement('div');
        content.id = 'tracker-content';
        content.style.cssText = `
            padding: 15px;
            max-height: 400px;
            overflow-y: auto;
            background: #1a1a1a;
        `;

        // Assemble the overlay
        overlay.appendChild(header);
        overlay.appendChild(nav);
        overlay.appendChild(content);

        // Add to page
        document.body.appendChild(overlay);
        console.log('🎯 Trainer Tracker: Overlay added to DOM');

        // Add event listeners
        closeBtn.addEventListener('click', () => {
            overlay.remove();
            console.log('🎯 Trainer Tracker: Closed by user');
        });

        queueBtn.addEventListener('click', () => {
            queueBtn.style.background = '#0066cc';
            addBtn.style.background = '#444444';
            showQueue(content);
        });

        addBtn.addEventListener('click', () => {
            queueBtn.style.background = '#444444';
            addBtn.style.background = '#0066cc';
            showAddForm(content);
        });

        // Make draggable
        makeDraggable(overlay, header);

        // Show queue by default
        showQueue(content);

        // Add a visible indicator that the script is working
        const indicator = document.createElement('div');
        indicator.style.cssText = `
            position: fixed;
            top: 5px;
            right: 5px;
            background: #00ff00;
            color: #000000;
            padding: 2px 6px;
            font-size: 10px;
            border-radius: 3px;
            z-index: 2147483646;
        `;
        indicator.textContent = 'TRACKER ACTIVE';
        document.body.appendChild(indicator);

        // Remove indicator after 3 seconds
        setTimeout(() => {
            if (indicator.parentNode) {
                indicator.remove();
            }
        }, 3000);
    }

    function makeDraggable(element, handle) {
        let isDragging = false;
        let startX, startY, startLeft, startTop;

        handle.addEventListener('mousedown', (e) => {
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;

            const rect = element.getBoundingClientRect();
            startLeft = rect.left;
            startTop = rect.top;

            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;

            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;

            const newLeft = Math.max(0, Math.min(startLeft + deltaX, window.innerWidth - element.offsetWidth));
            const newTop = Math.max(0, Math.min(startTop + deltaY, window.innerHeight - element.offsetHeight));

            element.style.left = newLeft + 'px';
            element.style.top = newTop + 'px';
            element.style.right = 'auto';
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
        });
    }

    function showQueue(content) {
        const employees = getEmployees();

        let html = '<div style="color: #cccccc;">';

        if (employees.length === 0) {
            html += '<div style="text-align: center; padding: 20px; color: #888888;">No employees added yet.<br>Use "Add Employee" to get started.</div>';
        } else {
            html += '<div style="margin-bottom: 10px; font-weight: bold;">Employee Queue (' + employees.length + ')</div>';

            employees.forEach((emp, index) => {
                const remaining = Math.max(0, emp.total_training_target - emp.trainings_completed);
                const progress = emp.total_training_target > 0 ? (emp.trainings_completed / emp.total_training_target * 100).toFixed(1) : 0;
                const statusColor = remaining === 0 ? '#00ff00' : emp.status === 'In Paid Training' ? '#ffaa00' : '#00aaff';

                html += `
                    <div style="background: #333333; margin-bottom: 8px; padding: 10px; border-radius: 5px; border-left: 3px solid ${statusColor};">
                        <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
                            <strong>${escapeHtml(emp.torn_name)}</strong>
                            <span style="color: ${statusColor}; font-weight: bold;">${remaining} left</span>
                        </div>
                        <div style="font-size: 11px; color: #aaaaaa; margin-bottom: 8px;">
                            ID: ${escapeHtml(emp.torn_id)} | Progress: ${progress}% (${emp.trainings_completed}/${emp.total_training_target})
                        </div>
                        <div style="margin-bottom: 8px;">
                            <select class="status-select" data-index="${index}" style="width: 100%; padding: 4px; background: #222222; color: white; border: 1px solid #555555;">
                                <option value="In Rotational Queue"${emp.status === 'In Rotational Queue' ? ' selected' : ''}>In Rotational Queue</option>
                                <option value="In Paid Training"${emp.status === 'In Paid Training' ? ' selected' : ''}>In Paid Training</option>
                                <option value="Completed"${emp.status === 'Completed' ? ' selected' : ''}>Completed</option>
                            </select>
                        </div>
                        <div style="display: flex; gap: 4px;">
                            <button class="add-train-btn" data-index="${index}" style="flex: 1; padding: 5px; background: #00aa00; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 10px;">+1 Train</button>
                            <button class="edit-total-btn" data-index="${index}" style="flex: 1; padding: 5px; background: #aa6600; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 10px;">Edit Total</button>
                            <button class="remove-employee-btn" data-index="${index}" style="flex: 1; padding: 5px; background: #aa0000; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 10px;">Remove</button>
                        </div>
                    </div>
                `;
            });
        }

        html += '</div>';
        content.innerHTML = html;

        // Attach event listeners for all action buttons and selects
        content.querySelectorAll('.add-train-btn').forEach(btn => {
            btn.addEventListener('click', function() {
                const index = parseInt(btn.getAttribute('data-index'));
                addTraining(index);
            });
        });
        content.querySelectorAll('.edit-total-btn').forEach(btn => {
            btn.addEventListener('click', function() {
                const index = parseInt(btn.getAttribute('data-index'));
                editEmployeeTotal(index);
            });
        });
        content.querySelectorAll('.remove-employee-btn').forEach(btn => {
            btn.addEventListener('click', function() {
                const index = parseInt(btn.getAttribute('data-index'));
                removeEmployee(index);
            });
        });
        content.querySelectorAll('.status-select').forEach(sel => {
            sel.addEventListener('change', function() {
                const index = parseInt(sel.getAttribute('data-index'));
                updateEmployeeStatus(index, sel.value);
            });
        });
    }

    function showAddForm(content) {
        const html = `
            <div style="color: #cccccc;">
                <div style="margin-bottom: 15px; font-weight: bold;">Add New Employee</div>
                <form id="add-employee-form">
                    <div style="margin-bottom: 10px;">
                        <label style="display: block; margin-bottom: 5px; color: #aaaaaa;">Torn ID:</label>
                        <input type="number" id="emp-torn-id" required style="width: 100%; padding: 8px; background: #333333; color: white; border: 1px solid #555555; border-radius: 3px;" placeholder="Enter player ID">
                    </div>
                    <div style="margin-bottom: 10px;">
                        <label style="display: block; margin-bottom: 5px; color: #aaaaaa;">Player Name:</label>
                        <input type="text" id="emp-torn-name" required style="width: 100%; padding: 8px; background: #333333; color: white; border: 1px solid #555555; border-radius: 3px;" placeholder="Enter player name">
                    </div>
                    <div style="margin-bottom: 15px;">
                        <label style="display: block; margin-bottom: 5px; color: #aaaaaa;">Training Target:</label>
                        <input type="number" id="emp-training-target" required min="1" style="width: 100%; padding: 8px; background: #333333; color: white; border: 1px solid #555555; border-radius: 3px;" placeholder="Number of trainings needed">
                    </div>
                    <button type="submit" style="width: 100%; padding: 10px; background: #0066cc; color: white; border: none; border-radius: 3px; cursor: pointer; font-weight: bold;">Add Employee</button>
                </form>
            </div>
        `;

        content.innerHTML = html;

        document.getElementById('add-employee-form').addEventListener('submit', function(e) {
            e.preventDefault();

            const tornId = document.getElementById('emp-torn-id').value.trim();
            const tornName = document.getElementById('emp-torn-name').value.trim();
            const trainingTarget = parseInt(document.getElementById('emp-training-target').value);

            if (!tornId || !tornName || !trainingTarget || trainingTarget < 1) {
                alert('Please fill in all fields correctly.');
                return;
            }

            const employees = getEmployees();

            // Check for duplicate
            if (employees.some(emp => emp.torn_id === tornId)) {
                alert('An employee with this Torn ID already exists.');
                return;
            }

            // Add new employee
            employees.push({
                torn_id: tornId,
                torn_name: tornName,
                total_training_target: trainingTarget,
                trainings_completed: 0,
                status: 'In Rotational Queue'
            });

            saveEmployees(employees);
            alert(`${tornName} has been added to the tracker!`);

            // Switch back to queue view
            const queueBtn = document.querySelector('#trainer-tracker-overlay button');
            if (queueBtn) queueBtn.click();
        });
    }

    function escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }

    // Replace global window functions with local ones
    function updateEmployeeStatus(index, status) {
        const employees = getEmployees();
        if (employees[index]) {
            employees[index].status = status;
            saveEmployees(employees);
            console.log(`Updated ${employees[index].torn_name} status to: ${status}`);
        }
    }

    function addTraining(index) {
        const employees = getEmployees();
        const emp = employees[index];
        if (!emp) return;

        const add = parseInt(prompt(`Add trainings for ${emp.torn_name}:`, '1'));
        if (isNaN(add) || add < 1) return;

        emp.trainings_completed = Math.min(emp.trainings_completed + add, emp.total_training_target);
        saveEmployees(employees);

        // Refresh the display
        const content = document.getElementById('tracker-content');
        if (content) showQueue(content);
    }

    function editEmployeeTotal(index) {
        const employees = getEmployees();
        const emp = employees[index];
        if (!emp) return;

        // Show options
        const action = prompt(
            `Edit Training Target for ${emp.torn_name}:\n` +
            `1. Add To Total\n` +
            `2. Subtract From Total\n` +
            `3. Change Total\n\n` +
            `Enter 1, 2, or 3:`
        );

        if (!['1', '2', '3'].includes(action)) return;

        let value;
        switch (action) {
            case '1':
                value = parseInt(prompt('How much to add to total?', '1'));
                if (isNaN(value) || value < 1) return;
                emp.total_training_target += value;
                break;
            case '2':
                value = parseInt(prompt('How much to subtract from total?', '1'));
                if (isNaN(value) || value < 1) return;
                emp.total_training_target = Math.max(1, emp.total_training_target - value);
                emp.trainings_completed = Math.min(emp.trainings_completed, emp.total_training_target);
                break;
            case '3':
                value = parseInt(prompt('Enter new total:', emp.total_training_target));
                if (isNaN(value) || value < 1) return;
                emp.total_training_target = value;
                emp.trainings_completed = Math.min(emp.trainings_completed, value);
                break;
        }

        saveEmployees(employees);

        // Refresh the display
        const content = document.getElementById('tracker-content');
        if (content) showQueue(content);
    }

    function removeEmployee(index) {
        const employees = getEmployees();
        const emp = employees[index];
        if (!emp) return;

        if (confirm(`Remove ${emp.torn_name} from the tracker?`)) {
            employees.splice(index, 1);
            saveEmployees(employees);

            // Refresh the display
            const content = document.getElementById('tracker-content');
            if (content) showQueue(content);
        }
    }

    // Start the initialization process
    console.log('🎯 Trainer Tracker: Script loaded, initializing...');

    // Multiple initialization strategies
    if (document.readyState === 'complete') {
        initializeTracker();
    } else {
        document.addEventListener('DOMContentLoaded', initializeTracker);
        window.addEventListener('load', initializeTracker);
    }

    // Fallback initialization after a delay
    setTimeout(initializeTracker, 2000);

})();