<?php
session_start();
require_once 'get_time.php';

if (!function_exists('logMessage')) {
    function logMessage($message) {
        $logFile = __DIR__ . '/update.log';
        $timestamp = date('Y-m-d H:i');
        $logEntry = "[$timestamp] $message\n";

        $logContent = file_exists($logFile) ? file_get_contents($logFile) : '';
        if ($logContent !== false && strpos($logContent, $logEntry) !== false) {
            return;
        }
        file_put_contents($logFile, $logEntry, FILE_APPEND);
    }
}

// Проверка аутентификации
if (!isset($_SESSION['user_id'])) {
    header("Location: login.php");
    exit();
}

// Проверка роли
$db = new SQLite3('schedule.db');
$result = $db->query("SELECT role FROM users WHERE id = {$_SESSION['user_id']}");
$user = $result->fetchArray(SQLITE3_ASSOC);
$userRole = $user['role'];

if ($userRole !== 'admin') {
    logMessage("Попытка доступа к управлению через пользователь без прав (ID: {$_SESSION['user_id']}).");
    echo '<div class="alert alert-danger mt-5 text-center">У вас нет прав для выполнения этого действия.</div>';
    exit();
}

$message = '';

?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Управление</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />
<style>
.sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 100;
    padding: 48px 0 0;
    box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
    background-color: #343a40;
    color: white;
}
.sidebar-sticky {
    position: relative;
    top: 0;
    height: calc(100vh - 48px);
    padding-top: .5rem;
    overflow-x: hidden;
    overflow-y: auto;
}
.sidebar a {
    color: white;
}
.sidebar a:hover {
    color: #007bff;
}
.main-content {
    margin-left: 250px;
    padding: 48px 20px;
}
pre.log-output {
    background-color: #222;
    color: #eee;
    padding: 15px;
    border-radius: 4px;
    max-height: 60vh;
    overflow: auto;
    font-size: 0.9rem;
    line-height: 1.3;
    white-space: pre-wrap; /* если хочется переносы */
}
</style>
</head>
<body>
<?php include("./shab/nav.php"); ?> 
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 main-content">

<h2>Управление зонами 768×512</h2>

<div class="controls mb-3">
    <button id="clearZones" class="btn btn-sm btn-danger">Очистить зоны</button>
</div>

<!-- Форма для ручного изменения размеров выделенной зоны -->
<div class="size-manual mb-3">
    <label>
        Ширина (px):
        <input type="number" id="manualWidth" min="20" max="768" style="width:80px;">
    </label>
    <label class="ml-3">
        Высота (px):
        <input type="number" id="manualHeight" min="20" max="512" style="width:80px;">
    </label>
    <button id="applySize" class="btn btn-sm btn-primary ml-3">Применить</button>
    <span id="sizeMsg" style="color: red; margin-left: 1rem;"></span>
</div>

<div id="field" style="position: relative; width: 768px; height: 512px; border: 2px solid #333; user-select: none; background:#f9f9f9; box-sizing: border-box;">
    <!-- Зоны появятся здесь -->
</div>

<script>
(() => {
    const field = document.getElementById('field');
    const clearBtn = document.getElementById('clearZones');
    const manualWidth = document.getElementById('manualWidth');
    const manualHeight = document.getElementById('manualHeight');
    const applySizeBtn = document.getElementById('applySize');
    const sizeMsg = document.getElementById('sizeMsg');

    let zones = [];

    let selectionStart = null;
    let selectionDiv = null;
    let activeZone = null;
    let dragOffset = {x:0, y:0};
    let resizeDir = null;
    let selectedZone = null;

    function genId() {
        return 'zone-' + Math.random().toString(36).substr(2, 9);
    }

    async function saveZones() {
        try {
            await fetch('zones_api.php', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify(zones)
            });
        } catch(e) {
            console.error('Ошибка сохранения зон:', e);
        }
    }

    async function loadZones() {
        try {
            let res = await fetch('zones_api.php');
            if (!res.ok) throw new Error('Ошибка загрузки');
            zones = await res.json();
            renderZones();
        } catch (e) {
            console.error(e);
            zones = [];
            renderZones();
        }
    }

    function selectZone(zone) {
        selectedZone = zone;
        manualWidth.value = Math.round(zone.width);
        manualHeight.value = Math.round(zone.height);
        sizeMsg.textContent = '';
    }

function createZoneElement(zone) {
    let el = document.createElement('div');
    el.className = 'zone';
    el.dataset.id = zone.id;
    el.style.left = zone.x + 'px';
    el.style.top = zone.y + 'px';
    el.style.width = zone.width + 'px';
    el.style.height = zone.height + 'px';

    // Кнопка удаления зоны
    let delBtn = document.createElement('div');
    delBtn.className = 'zone-delete-btn';
    delBtn.title = 'Удалить зону';
    delBtn.textContent = '×';

    delBtn.style.position = 'absolute';
    delBtn.style.top = '2px';
    delBtn.style.left = '2px';
    delBtn.style.cursor = 'pointer';
    delBtn.style.color = '#fff';
    delBtn.style.background = '#dc3545';
    delBtn.style.fontWeight = 'bold';
    delBtn.style.borderRadius = '50%';
    delBtn.style.width = '18px';
    delBtn.style.height = '18px';
    delBtn.style.lineHeight = '18px';
    delBtn.style.textAlign = 'center';
    delBtn.style.userSelect = 'none';
    delBtn.style.zIndex = '50';

    delBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        zones = zones.filter(z => z.id !== zone.id);
        if (selectedZone && selectedZone.id === zone.id) {
            selectedZone = null;
            manualWidth.value = '';
            manualHeight.value = '';
            sizeMsg.textContent = '';
        }
        saveZones();
        renderZones();
    });

    el.appendChild(delBtn);

    // Инфо (название, размер)
    let infoContainer = document.createElement('div');
    infoContainer.style.display = 'flex';
    infoContainer.style.justifyContent = 'space-between';
    infoContainer.style.alignItems = 'center';
    infoContainer.style.width = 'calc(100% - 28px)';
    infoContainer.style.marginLeft = '28px';

    let label = document.createElement('div');
    label.className = 'zone-label';
    label.contentEditable = true;
    label.innerText = zone.name || 'Без названия';
    label.title = 'Двойной клик для редактирования имени';
    label.style.flexGrow = '1';
    label.style.paddingRight = '5px';
    label.style.whiteSpace = 'nowrap';
    label.style.overflow = 'hidden';
    label.style.textOverflow = 'ellipsis';

    label.addEventListener('blur', () => {
        zone.name = label.innerText.trim() || 'Без названия';
        saveZones();
    });
    label.addEventListener('keydown', e => {
        if(e.key === 'Enter') {
            e.preventDefault();
            label.blur();
        }
    });

    infoContainer.appendChild(label);

    let sizeDisplay = document.createElement('div');
    sizeDisplay.className = 'zone-size';
    sizeDisplay.style.flexShrink = '0';
    sizeDisplay.style.fontSize = '12px';
    sizeDisplay.style.color = '#004a99';
    sizeDisplay.style.whiteSpace = 'nowrap';
    infoContainer.appendChild(sizeDisplay);

    el.appendChild(infoContainer);

    function updateZoneSize() {
        sizeDisplay.textContent = `${Math.round(zone.width)}×${Math.round(zone.height)} px`;
    }
    updateZoneSize();

    // Видео контейнер
    let videoContainer = document.createElement('div');
    videoContainer.style.position = 'relative';
    videoContainer.style.width = '100%';
    videoContainer.style.height = 'calc(100% - 30px)';
    videoContainer.style.marginTop = '2px';
    videoContainer.style.overflow = 'hidden';
    videoContainer.style.display = 'flex';
    videoContainer.style.alignItems = 'center';
    videoContainer.style.justifyContent = 'center';
    videoContainer.style.backgroundColor = 'rgba(0,0,0,0.05)';
    videoContainer.style.borderTop = '1px solid #007bff';
    videoContainer.style.zIndex = '15';

    el.appendChild(videoContainer);

    async function renderVideo() {
        videoContainer.innerHTML = '';

        if (zone.videoSrc) {
            let videoElement = document.createElement('video');
            videoElement.src = zone.videoSrc;
            videoElement.controls = true;
            videoElement.style.maxWidth = '100%';
            videoElement.style.maxHeight = '100%';
            videoContainer.appendChild(videoElement);

            let delVideoBtn = document.createElement('button');
            delVideoBtn.textContent = '×';
            delVideoBtn.title = 'Удалить видео';
            delVideoBtn.style.position = 'absolute';
            delVideoBtn.style.top = '2px';
            delVideoBtn.style.right = '2px';
            delVideoBtn.style.background = '#dc3545';
            delVideoBtn.style.color = '#fff';
            delVideoBtn.style.border = 'none';
            delVideoBtn.style.borderRadius = '50%';
            delVideoBtn.style.width = '22px';
            delVideoBtn.style.height = '22px';
            delVideoBtn.style.cursor = 'pointer';
            delVideoBtn.style.fontWeight = 'bold';
            delVideoBtn.style.zIndex = '20';

            delVideoBtn.addEventListener('click', async e => {
                e.stopPropagation();

                try {
                    if (zone.videoSrc) {
                        const parts = zone.videoSrc.split('/');
                        const filename = parts[parts.length -1];

                        let res = await fetch('zones_api.php?deleteVideo=1', {
                            method: 'POST',
                            headers: {'Content-Type': 'application/json'},
                            body: JSON.stringify({file: filename})
                        });

                        if (!res.ok) {
                            const err = await res.json();
                            alert('Ошибка удаления файла: ' + (err.error || 'Unknown error'));
                            return;
                        }
                    }

                    zone.videoSrc = null;
                    await saveZones();
                    renderZones();

                } catch (err) {
                    alert('Ошибка при удалении видео: ' + err.message);
                }
            });

            videoContainer.appendChild(delVideoBtn);

        } else {
            let fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.accept = 'video/mp4';
            fileInput.title = 'Выбрать видео (только mp4)';
            fileInput.style.width = '90%';

            fileInput.addEventListener('change', async e => {
                let file = fileInput.files[0];
                if (!file) return;

                if (file.type !== 'video/mp4') {
                    alert('Допустим только формат MP4');
                    fileInput.value = '';
                    return;
                }

                let formData = new FormData();
                formData.append('video', file);

                try {
                    let res = await fetch('zones_api.php?uploadVideo=1', {
                        method: 'POST',
                        body: formData
                    });

                    if (!res.ok) {
                        let errText = await res.text();
                        let errMsg = errText;
                        try {
                            const errJson = JSON.parse(errText);
                            errMsg = errJson.error || JSON.stringify(errJson);
                        } catch {}
                        alert('Ошибка загрузки видео: ' + errMsg);
                        fileInput.value = '';
                        return;
                    }

                    let data = await res.json();
                    if (!data.url) {
                        alert('Ошибка загрузки видео: нет ссылки');
                        fileInput.value = '';
                        return;
                    }

                    zone.videoSrc = data.url;
                    await saveZones();
                    renderZones();

                } catch (error) {
                    alert('Ошибка загрузки видео: ' + error.message);
                    fileInput.value = '';
                }
            });

            videoContainer.appendChild(fileInput);
        }
    }

    renderVideo();

    // Ручки изменения размера
    const resizeHandlesPos = ['nw','n','ne','e','se','s','sw','w'];
    resizeHandlesPos.forEach(pos => {
        let handle = document.createElement('div');
        handle.className = 'resize-handle ' + pos;
        handle.dataset.dir = pos;
        el.appendChild(handle);
    });

    label.addEventListener('dblclick', () => label.focus());

    el.addEventListener('mousedown', e => {
        if(e.target.classList.contains('resize-handle')) {
            activeZone = zone;
            resizeDir = e.target.dataset.dir;
        } else if(e.target === label) {
            return;
        } else {
            activeZone = zone;
            resizeDir = null;
            dragOffset.x = e.clientX - el.getBoundingClientRect().left;
            dragOffset.y = e.clientY - el.getBoundingClientRect().top;
        }
        e.stopPropagation();
        e.preventDefault();
    });

    el.addEventListener('click', e => {
        e.stopPropagation();
        selectZone(zone);
    });

    el._updateZoneSize = updateZoneSize;

    return el;
}

    function renderZones() {
        field.querySelectorAll('.zone').forEach(z => z.remove());
        zones.forEach(zone => {
            let el = createZoneElement(zone);
            field.appendChild(el);
        });
    }

    function addZoneFromRect(x1, y1, x2, y2) {
        let left = Math.min(x1, x2);
        let top = Math.min(y1, y2);
        let right = Math.max(x1, x2);
        let bottom = Math.max(y1, y2);

        left = Math.max(left, 0);
        top = Math.max(top, 0);
        right = Math.min(right, field.clientWidth);
        bottom = Math.min(bottom, field.clientHeight);

        let width = right - left;
        let height = bottom - top;

        if(width < 20 || height < 20) return;

        const newZone = {
            id: genId(),
            x: left,
            y: top,
            width: width,
            height: height,
            name: 'Без названия'
        };

        zones.push(newZone);
        saveZones();
        renderZones();
    }

    field.addEventListener('mousedown', e => {
        if(e.target !== field) return;

        selectionStart = {x: e.offsetX, y: e.offsetY};
        selectionDiv = document.createElement('div');
        selectionDiv.className = 'selection-rect';
        selectionDiv.style.left = selectionStart.x + 'px';
        selectionDiv.style.top = selectionStart.y + 'px';
        selectionDiv.style.width = '0px';
        selectionDiv.style.height = '0px';
        field.appendChild(selectionDiv);
        e.preventDefault();
    });
    field.addEventListener('mousemove', e => {
        if (!selectionStart) return;

        let x = e.offsetX;
        let y = e.offsetY;
        let left = Math.min(selectionStart.x, x);
        let top = Math.min(selectionStart.y, y);
        let width = Math.abs(selectionStart.x - x);
        let height = Math.abs(selectionStart.y - y);

        selectionDiv.style.left = left + 'px';
        selectionDiv.style.top = top + 'px';
        selectionDiv.style.width = width + 'px';
        selectionDiv.style.height = height + 'px';

        e.preventDefault();
    });
    field.addEventListener('mouseup', e => {
        if (!selectionStart) return;

        addZoneFromRect(selectionStart.x, selectionStart.y, e.offsetX, e.offsetY);

        selectionDiv.remove();
        selectionDiv = null;
        selectionStart = null;

        e.preventDefault();
    });

    document.addEventListener('mouseup', e => {
        activeZone = null;
        resizeDir = null;
    });
    document.addEventListener('mousemove', e => {
        if (!activeZone) return;

        let zoneEl = field.querySelector(`.zone[data-id="${activeZone.id}"]`);
        if (!zoneEl) return;

        let fieldRect = field.getBoundingClientRect();

        if (resizeDir) {
            let dx = e.clientX - fieldRect.left;
            let dy = e.clientY - fieldRect.top;
            let minSize = 20;

            let newLeft = activeZone.x;
            let newTop = activeZone.y;
            let newWidth = activeZone.width;
            let newHeight = activeZone.height;

            if (resizeDir.includes('n')) {
                newTop = Math.min(dy, activeZone.y + activeZone.height - minSize);
                newHeight = activeZone.height - (newTop - activeZone.y);
            }
            if (resizeDir.includes('s')) {
                newHeight = Math.max(minSize, dy - activeZone.y);
            }
            if (resizeDir.includes('w')) {
                newLeft = Math.min(dx, activeZone.x + activeZone.width - minSize);
                newWidth = activeZone.width - (newLeft - activeZone.x);
            }
            if (resizeDir.includes('e')) {
                newWidth = Math.max(minSize, dx - activeZone.x);
            }

            if (newLeft < 0) {
                newWidth -= -newLeft;
                newLeft = 0;
            }
            if (newTop < 0) {
                newHeight -= -newTop;
                newTop = 0;
            }
            if (newLeft + newWidth > field.clientWidth) {
                newWidth = field.clientWidth - newLeft;
            }
            if (newTop + newHeight > field.clientHeight) {
                newHeight = field.clientHeight - newTop;
            }

            activeZone.x = newLeft;
            activeZone.y = newTop;
            activeZone.width = newWidth;
            activeZone.height = newHeight;

            zoneEl.style.left = newLeft + 'px';
            zoneEl.style.top = newTop + 'px';
            zoneEl.style.width = newWidth + 'px';
            zoneEl.style.height = newHeight + 'px';

            if (typeof zoneEl._updateZoneSize === 'function') {
                zoneEl._updateZoneSize();
            }

            saveZones();

        } else {
            let x = e.clientX - dragOffset.x - fieldRect.left;
            let y = e.clientY - dragOffset.y - fieldRect.top;

            x = Math.max(0, Math.min(x, field.clientWidth - activeZone.width));
            y = Math.max(0, Math.min(y, field.clientHeight - activeZone.height));

            activeZone.x = x;
            activeZone.y = y;

            zoneEl.style.left = x + 'px';
            zoneEl.style.top = y + 'px';

            if (typeof zoneEl._updateZoneSize === 'function') {
                zoneEl._updateZoneSize();
            }

            saveZones();
        }
    });

    clearBtn.addEventListener('click', () => {
        if (confirm('Очистить все зоны?')) {
            zones = [];
            selectedZone = null;
            manualWidth.value = '';
            manualHeight.value = '';
            sizeMsg.textContent = '';
            saveZones();
            renderZones();
        }
    });

    field.addEventListener('click', e => {
        if (e.target === field) {
            selectedZone = null;
            manualWidth.value = '';
            manualHeight.value = '';
            sizeMsg.textContent = '';
        }
    });

    applySizeBtn.addEventListener('click', () => {
        if (!selectedZone) {
            sizeMsg.textContent = 'Выберите зону для изменения.';
            return;
        }
        let w = parseInt(manualWidth.value);
        let h = parseInt(manualHeight.value);

        if (isNaN(w) || isNaN(h) || w < 20 || w > 768 || h < 20 || h > 512) {
            sizeMsg.textContent = 'Введите корректные размеры: ширина 20-768, высота 20-512.';
            return;
        }

        let old = {x: selectedZone.x, y: selectedZone.y, width: selectedZone.width, height:selectedZone.height};
        selectedZone.width = w;
        selectedZone.height = h;

        if(selectedZone.x + w > field.clientWidth) selectedZone.x = field.clientWidth - w;
        if(selectedZone.y + h > field.clientHeight) selectedZone.y = field.clientHeight - h;

        sizeMsg.textContent = '';

        saveZones();
        renderZones();

        manualWidth.value = Math.round(selectedZone.width);
        manualHeight.value = Math.round(selectedZone.height);
    });

    const style = document.createElement('style');
    style.textContent = `
      .zone {
        position: absolute;
        background-color: rgba(0,123,255,0.3);
        border: 2px solid #007bff;
        box-sizing: border-box;
        cursor: move;
        user-select: none;
        z-index: 10;
        display: flex;
        align-items: flex-start;
        justify-content: center;
        padding-top: 2px;
      }
      .zone-label {
        background: rgba(255,255,255,0.8);
        border-radius: 4px;
        padding: 0 5px;
        max-width: 100%;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        cursor: text;
        user-select: text;
        font-size: 14px;
      }
      .zone-size {
        font-weight: normal;
        flex-shrink: 0;
        font-size: 12px;
        color: #004a99;
        white-space: nowrap;
        padding-left: 6px;
        user-select: none;
      }
      .zone-delete-btn {
        position: absolute;
        top: 2px;
        left: 2px;
        cursor: pointer;
        color: #fff;
        background: #dc3545;
        font-weight: bold;
        border-radius: 50%;
        width: 18px;
        height: 18px;
        line-height: 18px;
        text-align: center;
        user-select: none;
        z-index: 30;
      }
      .zone-delete-btn:hover {
        background: #c82333;
      }
      .resize-handle {
        position: absolute;
        width: 10px;
        height: 10px;
        background: #007bff;
        border: 1px solid #004a99;
        box-sizing: border-box;
        z-index: 20;
        cursor: pointer;
        border-radius: 2px;
      }
      .resize-handle.nw { top: -5px; left: -5px; cursor: nwse-resize; }
      .resize-handle.n { top: -5px; left: 50%; transform: translateX(-50%); cursor: ns-resize; }
      .resize-handle.ne { top: -5px; right: -5px; cursor: nesw-resize; }
      .resize-handle.e { top: 50%; right: -5px; transform: translateY(-50%); cursor: ew-resize; }
      .resize-handle.se { bottom: -5px; right: -5px; cursor: nwse-resize; }
      .resize-handle.s { bottom: -5px; left: 50%; transform: translateX(-50%); cursor: ns-resize; }
      .resize-handle.sw { bottom: -5px; left: -5px; cursor: nesw-resize; }
      .resize-handle.w { top: 50%; left: -5px; transform: translateY(-50%); cursor: ew-resize; }
      .selection-rect {
        position: absolute;
        border: 2px dashed #007bff;
        background-color: rgba(0, 123, 255, 0.15);
        pointer-events: none;
        z-index: 1000;
      }
      .size-manual input {
        padding: 3px 6px;
        font-size: 14px;
      }
    `;
    document.head.appendChild(style);

    loadZones();

})();
</script>

</main>
</div>
</div>

<!-- Bootstrap JS и зависимости -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/feather-icons/4.9.0/feather.min.js"></script>
<script>
feather.replace()
</script>
</body>
</html> изучи код