function planwiseWpInit() {
    const canvasWrapper = document.getElementById('canvas-wrapper');
    if (!canvasWrapper) {
        return;
    }

    const canvas = document.getElementById('floorPlanCanvas');
    let ctx = canvas.getContext('2d');

    const ajaxObj = typeof planwisewp_ajax_obj !== 'undefined' ? planwisewp_ajax_obj : {
        ajax_url: '',
        nonce: '',
        is_pro: false,
        unit_system: 'metric',
        l10n: {
            pro_feature_alert: 'This feature is available in PlanWiseWP PRO. Please upgrade to unlock.',
            confirm_delete: 'Are you sure you want to delete the selected item?',
            no_selection_delete: 'No item selected for deletion.',
            invalid_json: 'ERROR: The file is corrupt or has an invalid format. Check console for details.',
            file_read_error: 'ERROR: Failed to read the file.',
            nothing_to_export: 'There is nothing to export!',
            error_saving_background: 'An error occurred during background saving, but the download will start.',
            network_error_saving: 'A network error occurred during background saving, but the download will start.',
            opening_too_wide: 'Warning: The opening (%s) is too wide! Maximum width on this wall section is %s.',
            window_on_shared_wall: 'Warning: You cannot place a window on a shared wall!',
            opening_height_too_large: 'The opening height (%s) cannot be greater than the ceiling height (%s).',
        }
    };

const isMetric = ajaxObj.unit_system === 'metric';
    const l10n = ajaxObj.l10n;
    const isPro = ajaxObj.is_pro;

    const CM_PER_INCH = 2.54;
    const CM_PER_FT = 30.48;
    const SQM_PER_SQFT = 0.092903;
    const CUM_PER_CUFT = 0.0283168;

    const unitLength = isMetric ? 'm' : 'ft';
    const unitLengthSmall = isMetric ? 'cm' : 'in';
    const unitArea = isMetric ? 'm²' : 'sq ft';
    const unitVolume = isMetric ? 'm³' : 'cu ft';

    l10n.unitCm = 'cm';
    l10n.unitIn = 'in';
    l10n.unitFt = 'ft';
    l10n.unitM = 'm';
    l10n.unitSqM = 'm²';
    l10n.unitSqFt = 'sq ft';
    l10n.unitCuM = 'm³';
    l10n.unitCuFt = 'cu ft';

    l10n.ceilingHeight = l10n.ceilingHeight || 'Ceiling Height';
    l10n.no_rooms = l10n.no_rooms || 'No rooms on the floor plan yet.';
    l10n.generalData = l10n.generalData || 'General Data';
    l10n.roomCount = l10n.roomCount || 'Room Count';
    l10n.totalNetFloorArea = l10n.totalNetFloorArea || 'Total Net Floor Area';
    l10n.totalGrossFloorArea = l10n.totalGrossFloorArea || 'Total Gross Floor Area';
    l10n.totalAirVolume = l10n.totalAirVolume || 'Total Air Volume';
    l10n.netTotalSurface = l10n.netTotalSurface || 'Total Net Surface';
    l10n.grossTotalSurface = l10n.grossTotalSurface || 'Total Gross Surface';
    l10n.totalOpenings = l10n.totalOpenings || 'Total Openings';
    l10n.windows = l10n.windows || 'Windows';
    l10n.doors = l10n.doors || 'Doors';
    l10n.pcs = l10n.pcs || 'pcs';
    l10n.openings = l10n.openings || 'Openings';
    l10n.dimensions = l10n.dimensions || 'Dimensions';
    l10n.wallSurfaces = l10n.wallSurfaces || 'Wall Surfaces';
    l10n.grossWallSurface = l10n.grossWallSurface || 'Gross Wall Surface';
    l10n.totals = l10n.totals || 'Totals';
    l10n.close = l10n.close || 'Close';
    l10n.loadingTooltip = l10n.loadingTooltip || 'Loading...';
    l10n.watermarkText = l10n.watermarkText || 'PlanWise Floor Planner'; // Név frissítve

    let rooms = []; let markers = []; let fillers = [];
    let selectedRoomId = null; let selectedMarkerId = null; let selectedFillerId = null;
    let isDraggingRoom = false; let isDraggingMarker = false; let isDraggingFiller = false;
    let dragOffset = { x: 0, y: 0 };
    let lastMousePos = { x: 0, y: 0 };
    let viewTransform = { x: 250, y: 100, scale: 0.5 };
    let placementMode = null;
    let isPinching = false;
    let initialPinchDistance = null;
    let lastPanPos = { x: 0, y: 0 };
    let touchStartTime = 0;
    let touchStartPos = { x: 0, y: 0 };
    let longPressTimer = null;
    const TAP_THRESHOLD_MS = 200;
    const snapDistance = 6;
    const projectId = 'proj_' + Date.now().toString(36) + Math.random().toString(36).substr(2);
    let hasUnsavedChanges = false;
    let originalAddRoomTitle = 'Add Room';
    let originalAddFillerTitle = 'Add Pillar/Filler';

    const ceilingHeightInput = document.getElementById('ceilingHeightInput');
    const ceilingHeightDisplay = document.getElementById('ceilingHeightDisplay');
    const fileInput = document.getElementById('fileInput');
    const editWidthInput = document.getElementById('editWidthInput');
    const editHeightInput = document.getElementById('editHeightInput');
    const editTiledHeightInput = document.getElementById('editTiledHeightInput');
    const editColorInput = document.getElementById('editColorInput'); 
    const openingWidthInput = document.getElementById('openingWidthInput');
    const openingHeightInput = document.getElementById('openingHeightInput');
    const windowWidthInput = document.getElementById('windowWidthInput');
    const windowHeightInput = document.getElementById('windowHeightInput');
    const doorWidthInput = document.getElementById('doorWidthInput');
    const doorHeightInput = document.getElementById('doorHeightInput');
    const wallThicknessInput = document.getElementById('wallThicknessInput');
    const addRoomBtn = document.getElementById('addRoomBtn');
    const addFillerBtn = document.getElementById('addFillerBtn');

    if (addRoomBtn) {
        originalAddRoomTitle = addRoomBtn.title;
    }
    if (addFillerBtn) {
         originalAddFillerTitle = addFillerBtn.title;
    }

    function getInputType(elementId) {
        if (isMetric) return 'cm';

        switch(elementId) {
            case 'ceilingHeightInput':
            case 'roomWidthInput':
            case 'roomHeightInput':
            case 'fillerWidthInput':
            case 'fillerHeightInput':
            case 'editWidthInput':
            case 'editHeightInput':
                return 'ft';
            
            case 'wallThicknessInput':
            case 'windowWidthInput':
            case 'windowHeightInput':
            case 'parapetHeightInput':
            case 'doorWidthInput':
            case 'doorHeightInput':
            case 'openingWidthInput':
            case 'openingHeightInput':
            case 'editTiledHeightInput':
                return 'in'; 
                
            default:
                return 'cm'; 
        }
    }

    function getCmFromInput(value, inputType) {
        if (isNaN(value) || value <= 0) return 0;
        if (inputType === 'ft') return value * CM_PER_FT;
        if (inputType === 'in') return value * CM_PER_INCH;
        return value;
    }

    function getValueForInput(cmValue, inputType) {
        if (isNaN(cmValue)) return 0;
        let value = 0;
        if (inputType === 'ft') {
            value = cmValue / CM_PER_FT;
        } else if (inputType === 'in') {
            value = cmValue / CM_PER_INCH;
        } else {
            value = cmValue;
        }
        return Math.round(value * 100) / 100;
    }

    function formatSmartDecimal(num, precision, forcePrecision = false) {
        const number = parseFloat(num);
        if (isNaN(number)) return '0';

        let formatted = number.toFixed(precision);

        if (!forcePrecision && precision > 0) {
            formatted = formatted.replace(/\.?0+$/, '');
             if (formatted.endsWith('.')) formatted = formatted.slice(0, -1);
        } else if (forcePrecision && precision > 0 && !formatted.includes('.')) {
             formatted += '.' + '0'.repeat(precision);
        }

        if (number === 0 && precision > 0 && forcePrecision) {
             return '0.' + '0'.repeat(precision);
        }

        return formatted === '' ? '0' : formatted;
    }

    function formatLength(cmValue, forceSmallUnit = false) {
        if (isNaN(cmValue)) cmValue = 0;
        if (isMetric) {
            return `${formatSmartDecimal(cmValue, 1)} ${l10n.unitCm}`;
        } else {
            const totalInches = cmValue / CM_PER_INCH;
            if (totalInches >= 12 && !forceSmallUnit) {
                const feet = Math.floor(totalInches / 12);
                const inches = totalInches % 12;
                return `${feet}' ${formatSmartDecimal(inches, 2, true)}"`;
            }
            return `${formatSmartDecimal(totalInches, 2, true)}"`;
        }
    }

    function formatArea(sqCmValue) {
        if (isNaN(sqCmValue)) sqCmValue = 0;
        if (isMetric) {
            const sqM = sqCmValue / 10000;
            return `${formatSmartDecimal(sqM, 3)} ${l10n.unitSqM}`;
        } else {
            const sqM = sqCmValue / 10000;
            const sqFt = sqM / SQM_PER_SQFT;
            return `${formatSmartDecimal(sqFt, 2, true)} ${l10n.unitSqFt}`;
        }
    }

    function formatVolume(cuCmValue) {
        if (isNaN(cuCmValue)) cuCmValue = 0;
        if (isMetric) {
            const cuM = cuCmValue / 1000000;
            return `${formatSmartDecimal(cuM, 3)} ${l10n.unitCuM}`;
        } else {
            const cuM = cuCmValue / 1000000;
            const cuFt = cuM / CUM_PER_CUFT;
            return `${formatSmartDecimal(cuFt, 2, true)} ${l10n.unitCuFt}`;
        }
    }

    const addRoomBtnInitial = document.getElementById('addRoomBtn');
 
    const addSafeListener = (id, event, handler) => {
        const el = document.getElementById(id);
        if (el) {
            el.addEventListener(event, handler);
        }
    };

    addSafeListener('addRoomBtn', 'click', addRoom);

    addSafeListener('addFillerBtn', 'click', () => addFiller());
    
    addSafeListener('deleteBtn', 'click', deleteSelected);
    addSafeListener('rotateBtn', 'click', rotateSelected);
    addSafeListener('downloadBtn', 'click', () => downloadImage());
    addSafeListener('zoomInBtn', 'click', () => zoom(1.1));
    addSafeListener('zoomOutBtn', 'click', () => zoom(0.9));

    addSafeListener('addDotBtn', 'click', () => setPlacementMode('dot'));
    addSafeListener('addWindowBtn', 'click', () => setPlacementMode('window'));
    addSafeListener('addDoorBtn', 'click', () => setPlacementMode('door'));
    
    addSafeListener('closeEditPanelBtn', 'click', () => {
        selectedRoomId = null;
        selectedMarkerId = null;
        selectedFillerId = null;
        updateEditorInputs();
        draw();
    });

    if (ceilingHeightInput) ceilingHeightInput.addEventListener('input', () => { updateCeilingHeightDisplay(); updateCalculations(); });
    if (wallThicknessInput) wallThicknessInput.addEventListener('input', draw);
    if (editWidthInput) editWidthInput.addEventListener('input', handleDimEdit);
    if (editHeightInput) editHeightInput.addEventListener('input', handleDimEdit);

    canvas.addEventListener('mousedown', onMouseDown);
    canvas.addEventListener('mousemove', onMouseMove);
    canvas.addEventListener('mouseup', onMouseUp);
    canvas.addEventListener('mouseleave', onMouseUp);
    canvas.addEventListener('wheel', onWheel, { passive: false });
    canvas.addEventListener('contextmenu', e => e.preventDefault());
    canvas.addEventListener('touchstart', onTouchStart, { passive: false });
    canvas.addEventListener('touchmove', onTouchMove, { passive: false });
    canvas.addEventListener('touchend', onTouchEnd);
    canvas.addEventListener('touchcancel', onTouchEnd);

    function updateCeilingHeightDisplay() { 
        const ceilingCm = getCmFromInput(parseFloat(ceilingHeightInput.value.replace(',', '.')) || 0, getInputType('ceilingHeightInput'));
        ceilingHeightDisplay.textContent = `${l10n.ceilingHeight}: ${formatLength(ceilingCm)}`; 
    }
    
    function setPlacementMode(mode) { 
        placementMode = mode; 
        canvas.style.cursor = 'crosshair'; 
    }

    function addRoom() {
        const widthCm = getCmFromInput(parseFloat(document.getElementById('roomWidthInput').value.replace(',', '.')) || 0, getInputType('roomWidthInput'));
        const heightCm = getCmFromInput(parseFloat(document.getElementById('roomHeightInput').value.replace(',', '.')) || 0, getInputType('roomHeightInput'));
        const defaultWidthCm = getCmFromInput(isMetric ? 400 : 12, getInputType('roomWidthInput'));
        const defaultHeightCm = getCmFromInput(isMetric ? 500 : 15, getInputType('roomHeightInput'));
        const finalWidth = widthCm > 0 ? widthCm : defaultWidthCm;
        const finalHeight = heightCm > 0 ? heightCm : defaultHeightCm;

        if (finalWidth > 0 && finalHeight > 0) {
            const name = document.getElementById('roomNameSelect').value;
            const worldCenter = screenToWorld({ x: canvas.width / 2, y: canvas.height / 2 });
            const cascadeStep = 40; 
            const offsetX = (rooms.length % 3) * cascadeStep; 
            const offsetY = Math.floor(rooms.length / 3) * cascadeStep;
            rooms.push({ 
                id: Date.now(), 
                x: worldCenter.x - finalWidth / 2 + offsetX, 
                y: worldCenter.y - finalHeight / 2 + offsetY, 
                width: finalWidth,
                height: finalHeight,
                name, 
                color: '#add8e6',
                tiledHeight: 0
            });
            document.getElementById('roomWidthInput').value = ''; 
            document.getElementById('roomHeightInput').value = '';
            draw();
        }
        updateCalculations();
        hasUnsavedChanges = true;
    }

    function addFiller() {
        const widthCm = getCmFromInput(parseFloat(document.getElementById('fillerWidthInput').value.replace(',', '.')) || 0, getInputType('fillerWidthInput'));
        const heightCm = getCmFromInput(parseFloat(document.getElementById('fillerHeightInput').value.replace(',', '.')) || 0, getInputType('fillerHeightInput'));
        const defaultWidthCm = getCmFromInput(isMetric ? 30 : 1, getInputType('fillerWidthInput'));
        const defaultHeightCm = getCmFromInput(isMetric ? 100 : 3, getInputType('fillerHeightInput'));
        const finalWidth = widthCm > 0 ? widthCm : defaultWidthCm;
        const finalHeight = heightCm > 0 ? heightCm : defaultHeightCm;

        if (finalWidth > 0 && finalHeight > 0) {
            const worldCenter = screenToWorld({ x: canvas.width / 2, y: canvas.height / 2 });
            fillers.push({
                id: Date.now(),
                x: worldCenter.x - finalWidth / 2,
                y: worldCenter.y - finalHeight / 2,
                width: finalWidth,
                height: finalHeight
            });
            document.getElementById('fillerWidthInput').value = '';
            document.getElementById('fillerHeightInput').value = '';
            draw();
        }
        updateCalculations();
        hasUnsavedChanges = true;
    }
    
    function addMarker(pos) {
        const ceilingHeightCm = getCmFromInput(parseFloat(ceilingHeightInput.value.replace(',', '.')) || 0, getInputType('ceilingHeightInput'));
        const marker = { id: Date.now(), type: placementMode, linkedRoomId: null };

        let markerHeightCm = 0;
        let markerWidthCm;

        const defaults = ajaxObj.free_defaults || { window_w: 100, window_h: 120, door_w: 80, door_h: 210 };
        
        if (placementMode === 'window') {
            const wVal = parseFloat(document.getElementById('windowWidthInput').value.replace(',', '.')) || 0;
            const hVal = parseFloat(document.getElementById('windowHeightInput').value.replace(',', '.')) || 0;
            markerWidthCm = getCmFromInput(wVal, getInputType('windowWidthInput'));
            markerHeightCm = getCmFromInput(hVal, getInputType('windowHeightInput'));
        } else if (placementMode === 'door') {
            const wVal = parseFloat(document.getElementById('doorWidthInput').value.replace(',', '.')) || 0;
            const hVal = parseFloat(document.getElementById('doorHeightInput').value.replace(',', '.')) || 0;
            markerWidthCm = getCmFromInput(wVal, getInputType('doorWidthInput'));
            markerHeightCm = getCmFromInput(hVal, getInputType('doorHeightInput'));
        }else if (placementMode === 'dot') {
             marker.x = pos.x; marker.y = pos.y;
             markers.push(marker);
             draw();
             placementMode = null;
             canvas.style.cursor = 'default';
             return;
        } else {
             placementMode = null;
             canvas.style.cursor = 'default';
             return;
        }

        if (markerHeightCm > 0 && markerHeightCm >= ceilingHeightCm) {
            alert(l10n.opening_height_too_large.replace('%s', formatLength(markerHeightCm)).replace('%s', formatLength(ceilingHeightCm)));
            return;
        }

        const room = getNearestRoom(pos, 30 / viewTransform.scale);
        if (!room) {
            placementMode = null;
            canvas.style.cursor = 'default';
            return;
        }

        const snapped = getClosestPointOnRect(room, pos);
        marker.orientation = (snapped.edge === 'top' || snapped.edge === 'bottom') ? 'h' : 'v';
        const wallInfo = getWallSystemInfo({ parentRoomId: room.id, edge: snapped.edge, x: snapped.x, y: snapped.y, orientation: marker.orientation });

        switch (snapped.edge) {
            case 'top':    marker.y = room.y - wallInfo.thickness / 2; break;
            case 'bottom': marker.y = room.y + room.height + wallInfo.thickness / 2; break;
            case 'left':   marker.x = room.x - wallInfo.thickness / 2; break;
            case 'right':  marker.x = room.x + room.width + wallInfo.thickness / 2; break;
        }
        if(marker.orientation === 'h') marker.x = snapped.x; else marker.y = snapped.y;

        marker.parentRoomId = room.id;
        marker.edge = snapped.edge;
        marker.width = markerWidthCm;
        marker.height = markerHeightCm;
        
        if (placementMode === 'window') {
            const sill_val = parseFloat(document.getElementById('parapetHeightInput').value.replace(',', '.'));
            const default_sill_cm = getCmFromInput(isMetric ? 90 : 36, getInputType('parapetHeightInput'));
            marker.parapetHeight = getCmFromInput(sill_val || 0, getInputType('parapetHeightInput')) || default_sill_cm; 
        }

        markers.push(marker);
        linkSharedWallMarker(marker);

        if (placementMode === 'opening' || placementMode === 'door') {
            const wallLength = getAvailableWallLength(room, snapped.edge, {x: marker.x, y: marker.y});
            if (marker.width > wallLength) {
                alert(l10n.opening_too_wide.replace('%s', formatLength(marker.width)).replace('%s', formatLength(Math.floor(wallLength))));
                markers.pop();
            }
        } else if (marker.type === 'window' && marker.linkedRoomId) { 
            alert(l10n.window_on_shared_wall); 
            markers.pop();
        }

        placementMode = null;
        canvas.style.cursor = 'default';
        draw();
        updateCalculations();
    }

    function handleDimEdit(event) {
        const value = event.target.type === 'color' ? event.target.value : parseFloat(event.target.value.replace(',', '.'));
        if (isNaN(value) && event.target.type !== 'color') return;

        if (selectedRoomId) {
            const room = rooms.find(r => r.id === selectedRoomId);
            if (room) {
                if (event.target.id === 'editWidthInput' && value > 0) room.width = getCmFromInput(value, getInputType('editWidthInput'));
                if (event.target.id === 'editHeightInput' && value > 0) room.height = getCmFromInput(value, getInputType('editHeightInput'));
                draw();
            }
        } 
        else if (selectedFillerId) {
            const filler = fillers.find(f => f.id === selectedFillerId);
            if (filler) {
                if (event.target.id === 'editWidthInput' && value > 0) filler.width = getCmFromInput(value, getInputType('editWidthInput'));
                if (event.target.id === 'editHeightInput' && value > 0) filler.height = getCmFromInput(value, getInputType('editHeightInput'));
                draw();
            }
        }
        updateCalculations();
        hasUnsavedChanges = true;
    }

    function updateEditorInputs() {
        const editPanel = document.getElementById('edit-panel');
        const rotateBtn = document.getElementById('rotateBtn');
        const deleteBtn = document.getElementById('deleteBtn');

        const widthWrapper = document.getElementById('edit-width-wrapper');
        const heightWrapper = document.getElementById('edit-height-wrapper');
        const tiledWrapper = document.getElementById('edit-tiled-wrapper');
        const colorWrapper = document.getElementById('edit-color-wrapper');

        const room = rooms.find(r => r.id === selectedRoomId);
        const filler = fillers.find(f => f.id === selectedFillerId);
        const marker = markers.find(m => m.id === selectedMarkerId);
        const selectedObject = room || filler || marker; 

        if (!selectedObject) {
            editPanel.style.display = 'none';
            return;
        }

        let panelMarginLeft = -120;
        let panelMarginTop = 15;
        editPanel.style.left = `${panelMarginLeft}px`; 
        editPanel.style.top = `${panelMarginTop}px`;
        editPanel.style.right = 'auto'; 
        editPanel.style.bottom = 'auto';

        [widthWrapper, heightWrapper, tiledWrapper, colorWrapper, rotateBtn, deleteBtn].forEach(el => {
            if(el) el.style.display = 'none';
        });

        if (room) {
            widthWrapper.style.display = 'flex';
            heightWrapper.style.display = 'flex';
            rotateBtn.style.display = 'inline-flex';
            deleteBtn.style.display = 'inline-flex';

            editWidthInput.value = getValueForInput(room.width, getInputType('editWidthInput'));
            editHeightInput.value = getValueForInput(room.height, getInputType('editHeightInput'));
        } 
        else if (filler) { 
            widthWrapper.style.display = 'flex';
            heightWrapper.style.display = 'flex';
            rotateBtn.style.display = 'inline-flex';
            deleteBtn.style.display = 'inline-flex';

            editWidthInput.value = getValueForInput(filler.width, getInputType('editWidthInput'));
            editHeightInput.value = getValueForInput(filler.height, getInputType('editHeightInput'));
        } 
        else if (marker) {
            deleteBtn.style.display = 'inline-flex';
        }
        const actionsContainer = document.querySelector('#edit-panel .edit-actions');
        if (actionsContainer) {
            const visibleButtons = actionsContainer.querySelectorAll('button[style*="display: inline-flex"], button:not([style*="display: none"])'); 
            if (visibleButtons.length === 1) {
                actionsContainer.classList.add('single-button');
            } else {
                actionsContainer.classList.remove('single-button');
            }
        }
        editPanel.style.display = 'flex';
    }
    
    function updateAllMarkerLinks() {
        markers.forEach(linkSharedWallMarker);
    }
    function linkSharedWallMarker(marker) {
        if (marker.type === 'dot') return;
        const wallInfo = getWallSystemInfo(marker);
        marker.linkedRoomId = wallInfo.linkedRoomId;
    }

    function deleteSelected() { 
        if (!confirm(l10n.confirm_delete)) return; 

        if (selectedRoomId) {
            const roomToDeleteId = selectedRoomId; rooms = rooms.filter(r => r.id !== roomToDeleteId);
            markers.forEach(m => {
                if(m.parentRoomId === roomToDeleteId) { m.parentRoomId = m.linkedRoomId; m.linkedRoomId = null; } 
                else if (m.linkedRoomId === roomToDeleteId) { m.linkedRoomId = null; }
            });
            markers = markers.filter(m => m.parentRoomId != null || m.type === 'dot'); selectedRoomId = null;
        } else if (selectedMarkerId) { 
            markers = markers.filter(m => m.id !== selectedMarkerId); selectedMarkerId = null;
        } else if (selectedFillerId) {
            fillers = fillers.filter(f => f.id !== selectedFillerId); selectedFillerId = null;
        } else { 
            alert(l10n.no_selection_delete); 
        }
        updateEditorInputs();
        draw();
        updateCalculations();
        hasUnsavedChanges = true;
    }

    function rotateSelected() { 
        if (selectedRoomId) { 
            const room = rooms.find(r => r.id === selectedRoomId); 
            [room.width, room.height] = [room.height, room.width]; 
            updateEditorInputs(); 
            draw(); 
        } 
        if (selectedFillerId) { 
            const filler = fillers.find(f => f.id === selectedFillerId);
            [filler.width, filler.height] = [filler.height, filler.width];
            updateEditorInputs();
            draw();
        }
        updateCalculations();
        hasUnsavedChanges = true;
    }

    function zoom(factor, center) {
        if (!center) {
            center = { x: canvas.width / 2, y: canvas.height / 2 };
        }
        const worldPosBefore = screenToWorld(center);
        const newScale = Math.max(0.1, Math.min(2.0, viewTransform.scale * factor));
        viewTransform.scale = newScale;
        const worldPosAfter = screenToWorld(center);
        viewTransform.x += (worldPosAfter.x - worldPosBefore.x);
        viewTransform.y += (worldPosAfter.y - worldPosBefore.y);
        draw();
    }

    function downloadImage() {
        if (rooms.length === 0) {
            alert(l10n.nothing_to_export);
            return;
        }
        const allObjects = [...rooms, ...fillers];
        const wallThicknessCm = getCmFromInput(parseFloat(wallThicknessInput.value.replace(',', '.')) || 0, getInputType('wallThicknessInput'));
        const PADDING = 150 + wallThicknessCm; 
        let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
        allObjects.forEach(obj => {
            minX = Math.min(minX, obj.x);
            maxX = Math.max(maxX, obj.x + obj.width);
            minY = Math.min(minY, obj.y);
            maxY = Math.max(maxY, obj.y + obj.height);
        });

        minX -= PADDING; minY -= PADDING; maxX += PADDING; maxY += PADDING;
        const totalWidth = maxX - minX;
        const totalHeight = maxY - minY;

        const exportCanvas = document.createElement('canvas');
        exportCanvas.width = totalWidth;
        exportCanvas.height = totalHeight;
        const exportCtx = exportCanvas.getContext('2d');

        const originalTransform = { ...viewTransform };
        const originalCtx = ctx;

        viewTransform = { x: -minX, y: -minY, scale: 1 };
        ctx = exportCtx;

        ctx.fillStyle = 'white';
        ctx.fillRect(0, 0, totalWidth, totalHeight);

        draw(true);
            ctx.save();
            ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
            ctx.rotate(-Math.PI / 12);
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.font = `bold ${Math.max(80, totalWidth / 15)}px Poppins, sans-serif`;
            ctx.fillStyle = 'rgba(0, 0, 0, 0.08)';
            ctx.fillText(l10n.watermarkText || 'PlanWise Floor Planner', 0, 0);
            ctx.restore();

        function finishExport() {

            const link = document.createElement('a');
            link.download = 'planwisewp_floor_plan.png'; 
            link.href = exportCanvas.toDataURL('image/png');
            link.click();

            const projectData = {
                rooms: rooms,
                fillers: fillers,
                markers: markers,
                ceilingHeight: parseFloat(ceilingHeightInput.value.replace(',', '.')) || 0,
                wallThickness: parseFloat(wallThicknessInput.value.replace(',', '.')) || 0,
                view: { x: 0, y: 0, scale: 1 } 
            };

            const formData = new FormData();
            formData.append('action', 'planwisewp_save_project');
            formData.append('_ajax_nonce', ajaxObj.nonce);
            formData.append('projectId', projectId);
            formData.append('projectJson', JSON.stringify(projectData));

            fetch(ajaxObj.ajax_url, {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    console.log('Project saved via Export:', data);
                } else {
                    console.error('Save error:', data.data.message);
                }
            })
            .catch(error => {
                console.error('Network error during save:', error);
            });

            ctx = originalCtx;
            viewTransform = originalTransform;
            draw();
        }
        finishExport();
    }

    function getCanvasCoordinates(event) {
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        let clientX, clientY;
        if (event.type.startsWith('touch')) {
            const touch = event.touches[0] || event.changedTouches[0];
            if (!touch) return null;
            clientX = touch.clientX;
            clientY = touch.clientY;
        } else {
            clientX = event.clientX;
            clientY = event.clientY;
        }
        return {
            x: (clientX - rect.left) * scaleX,
            y: (clientY - rect.top) * scaleY
        };
    }
    function onMouseDown(e) {
        const pos = getCanvasCoordinates(e); if (!pos) return;
        lastMousePos = pos;
        const worldPos = screenToWorld(pos);
        if (e.button === 2) { 
            const clickedObject = getRoomAtPos(worldPos, false) || getMarkerAtPos(worldPos);
            if (clickedObject) {
                selectedRoomId = clickedObject.hasOwnProperty('name') ? clickedObject.id : null;
                selectedFillerId = (!clickedObject.hasOwnProperty('name') && !clickedObject.type) ? clickedObject.id : null;
                selectedMarkerId = clickedObject.type ? clickedObject.id : null;
                updateEditorInputs();
                draw();
            }
            return;
        }
        if (e.button === 0) { 
            if (placementMode) {
                addMarker(worldPos);
                return;
            }
            selectedRoomId = null; 
            selectedMarkerId = null; 
            selectedFillerId = null;
            selectedRoomId = null; selectedMarkerId = null; selectedFillerId = null;
            const clickedMarker = getMarkerAtPos(worldPos);
            const clickedFiller = getFillerAtPos(worldPos, false); 
            const clickedRoom = getRoomAtPos(worldPos, false);
            let selectedObject = null;
            if (clickedMarker) {
                selectedMarkerId = clickedMarker.id; selectedObject = clickedMarker; isDraggingMarker = true;
            } else if (clickedFiller) {
                selectedFillerId = clickedFiller.id; selectedObject = clickedFiller; isDraggingFiller = true;
            } else if (clickedRoom) {
                selectedRoomId = clickedRoom.id; selectedObject = clickedRoom; isDraggingRoom = true;
            }
            if(selectedObject) {
                dragOffset.x = worldPos.x - selectedObject.x;
                dragOffset.y = worldPos.y - selectedObject.y;
            }
            updateEditorInputs();
            draw();
        }
    }
    function onMouseMove(e) {
        const pos = getCanvasCoordinates(e); if (!pos) return;
        if (e.buttons === 2) { 
            canvas.style.cursor = 'grabbing';
            const dx = pos.x - lastMousePos.x;
            const dy = pos.y - lastMousePos.y;
            viewTransform.x += (dx / viewTransform.scale);
            viewTransform.y += (dy / viewTransform.scale);
            lastMousePos = pos;
            draw();
            return;
        }
        const isDragging = isDraggingRoom || isDraggingMarker || isDraggingFiller;
        if (isDragging) {
            const worldPos = screenToWorld(pos);
            let stateChanged = false;
            if (isDraggingRoom || isDraggingFiller) {
                const movingObj = isDraggingRoom ? rooms.find(r => r.id === selectedRoomId) : fillers.find(f => f.id === selectedFillerId);
                if (movingObj) {
                    const snapMargin = snapDistance / viewTransform.scale;
                    let targetX = worldPos.x - dragOffset.x; let targetY = worldPos.y - dragOffset.y;
                    let bestSnap = { deltaX: Infinity, deltaY: Infinity, x: targetX, y: targetY };
                    const staticObjects = rooms.concat(fillers);
                    const wallThicknessCm = getCmFromInput(parseFloat(wallThicknessInput.value.replace(',', '.')) || 0, getInputType('wallThicknessInput'));
                    for (const staticObj of staticObjects) {
                        if (movingObj.id === staticObj.id) continue;
                        const mov = { w: movingObj.width, h: movingObj.height }; const stat = { x: staticObj.x, y: staticObj.y, w: staticObj.width, h: staticObj.height };
                        const xTargets = [stat.x, stat.x + stat.w, stat.x - mov.w, stat.x + stat.w - mov.w, stat.x + stat.w / 2 - mov.w / 2, stat.x + stat.w + wallThicknessCm, stat.x - mov.w - wallThicknessCm];
                        const yTargets = [stat.y, stat.y + stat.h, stat.y - mov.h, stat.y + stat.h - mov.h, stat.y + stat.h / 2 - mov.h / 2, stat.y + stat.h + wallThicknessCm, stat.y - mov.h - wallThicknessCm];
                        for (const x of xTargets) { const delta = Math.abs(targetX - x); if (delta < bestSnap.deltaX) { bestSnap.deltaX = delta; bestSnap.x = x; } }
                        for (const y of yTargets) { const delta = Math.abs(targetY - y); if (delta < bestSnap.deltaY) { bestSnap.deltaY = delta; bestSnap.y = y; } }
                    }
                    movingObj.x = (bestSnap.deltaX < snapMargin) ? bestSnap.x : targetX;
                    movingObj.y = (bestSnap.deltaY < snapMargin) ? bestSnap.y : targetY;
                    stateChanged = true;
                }
            }
            if (isDraggingMarker) {
                const marker = markers.find(m => m.id === selectedMarkerId); 
                if (marker) {
                    if (marker.type === 'dot') {
                        marker.x = worldPos.x - dragOffset.x;
                        marker.y = worldPos.y - dragOffset.y;
                        stateChanged = true;
                    } else {
                        const wallInfo = getWallSystemInfo(marker);
                        const parentRoom = rooms.find(r => r.id === marker.parentRoomId);
                        if(parentRoom) {
                            let newX = worldPos.x - dragOffset.x; let newY = worldPos.y - dragOffset.y;
                            switch (marker.edge) {
                                case 'top': newY = parentRoom.y - wallInfo.thickness / 2; newX = Math.max(parentRoom.x, Math.min(newX, parentRoom.x + parentRoom.width)); break;
                                case 'bottom': newY = parentRoom.y + parentRoom.height + wallInfo.thickness / 2; newX = Math.max(parentRoom.x, Math.min(newX, parentRoom.x + parentRoom.width)); break;
                                case 'left': newX = parentRoom.x - wallInfo.thickness / 2; newY = Math.max(parentRoom.y, Math.min(newY, parentRoom.y + parentRoom.height)); break;
                                case 'right': newX = parentRoom.x + parentRoom.width + wallInfo.thickness / 2; newY = Math.max(parentRoom.y, Math.min(newY, parentRoom.y + parentRoom.height)); break;
                            }
                            marker.x = newX; marker.y = newY;
                            stateChanged = true;
                        }
                    }
                }
            }
            if (stateChanged) { draw(); }
        }
        lastMousePos = pos;
    }
    function onMouseUp(e) {
        if (isDraggingRoom || isDraggingMarker || isDraggingFiller) {
            updateAllMarkerLinks();
            draw();
            hasUnsavedChanges = true;
        }
        isDraggingRoom = false; isDraggingMarker = false; isDraggingFiller = false;
        canvas.style.cursor = placementMode ? 'crosshair' : 'default';
        updateCalculations();
    }
    
    function onTouchStart(e) {
        e.preventDefault();
        if (e.touches.length > 1) { return; } 
        const pos = getCanvasCoordinates(e); if (!pos) return;
        clearTimeout(longPressTimer);
        touchStartTime = new Date().getTime();
        touchStartPos = pos;
        const worldPos = screenToWorld(pos);
        if (placementMode) {
            addMarker(worldPos);
            return;
        }
        selectedRoomId = null; selectedMarkerId = null; selectedFillerId = null;
        const isTouch = true;
        const clickedMarker = getMarkerAtPos(worldPos);
        const clickedFiller = getFillerAtPos(worldPos, isTouch);
        const clickedRoom = getRoomAtPos(worldPos, isTouch);
        let selectedObject = null;
        if (clickedMarker) { selectedMarkerId = clickedMarker.id; selectedObject = clickedMarker; } 
        else if (clickedFiller) { selectedFillerId = clickedFiller.id; selectedObject = clickedFiller; } 
        else if (clickedRoom) { selectedRoomId = clickedRoom.id; selectedObject = clickedRoom; }
        if (selectedObject) {
            dragOffset.x = worldPos.x - selectedObject.x;
            dragOffset.y = worldPos.y - selectedObject.y;
            longPressTimer = setTimeout(() => { updateEditorInputs(); }, 700);
        } else {
            updateEditorInputs(); 
        }
        draw();
    }
    function onTouchMove(e) {
        e.preventDefault();
        const pos = getCanvasCoordinates(e); if (!pos) return;
        if (e.touches.length === 2) {
            clearTimeout(longPressTimer);
            const pos1 = getCanvasCoordinates({ touches: [e.touches[0]], type: 'touch' });
            const pos2 = getCanvasCoordinates({ touches: [e.touches[1]], type: 'touch' });
            const pinchCenter = { x: (pos1.x + pos2.x) / 2, y: (pos1.y + pos2.y) / 2 };
            if (!isPinching) {
                isPinching = true;
                initialPinchDistance = Math.hypot(pos1.x - pos2.x, pos1.y - pos2.y);
            }
            const currentPinchDistance = Math.hypot(pos1.x - pos2.x, pos1.y - pos2.y);
            const zoomFactor = currentPinchDistance / initialPinchDistance;

            zoom(zoomFactor, pinchCenter); 
            
            initialPinchDistance = currentPinchDistance;
            lastPanPos = pinchCenter; 
            return;
        }

        let isDragging = isDraggingRoom || isDraggingMarker || isDraggingFiller;
        const DRAG_THRESHOLD = 10;
        if (!isDragging && Math.hypot(pos.x - touchStartPos.x, pos.y - touchStartPos.y) > DRAG_THRESHOLD) {
            clearTimeout(longPressTimer);
            if (selectedRoomId) isDraggingRoom = true;
            if (selectedMarkerId) isDraggingMarker = true;
            if (selectedFillerId) isDraggingFiller = true;
            isDragging = isDraggingRoom || isDraggingMarker || isDraggingFiller;
        }
        if (isDragging) {
            const worldPos = screenToWorld(pos);
            let stateChanged = false;
            if (isDraggingRoom || isDraggingFiller) {
                 const movingObj = isDraggingRoom ? rooms.find(r => r.id === selectedRoomId) : fillers.find(f => f.id === selectedFillerId);
                 if (movingObj) {
                    const snapMargin = snapDistance / viewTransform.scale;
                    let targetX = worldPos.x - dragOffset.x; let targetY = worldPos.y - dragOffset.y;
                    let bestSnap = { deltaX: Infinity, deltaY: Infinity, x: targetX, y: targetY };
                    const staticObjects = rooms.concat(fillers);
                    const wallThicknessCm = getCmFromInput(parseFloat(wallThicknessInput.value.replace(',', '.')) || 0, getInputType('wallThicknessInput'));
                    for (const staticObj of staticObjects) {
                        if (movingObj.id === staticObj.id) continue;
                        const mov = { w: movingObj.width, h: movingObj.height }; const stat = { x: staticObj.x, y: staticObj.y, w: staticObj.width, h: staticObj.height };
                        const xTargets = [stat.x, stat.x + stat.w, stat.x - mov.w, stat.x + stat.w - mov.w, stat.x + stat.w / 2 - mov.w / 2, stat.x + stat.w + wallThicknessCm, stat.x - mov.w - wallThicknessCm];
                        const yTargets = [stat.y, stat.y + stat.h, stat.y - mov.h, stat.y + stat.h - mov.h, stat.y + stat.h / 2 - mov.h / 2, stat.y + stat.h + wallThicknessCm, stat.y - mov.h - wallThicknessCm];
                        for (const x of xTargets) { const delta = Math.abs(targetX - x); if (delta < bestSnap.deltaX) { bestSnap.deltaX = delta; bestSnap.x = x; } }
                        for (const y of yTargets) { const delta = Math.abs(targetY - y); if (delta < bestSnap.deltaY) { bestSnap.deltaY = delta; bestSnap.y = y; } }
                    }
                    movingObj.x = (bestSnap.deltaX < snapMargin) ? bestSnap.x : targetX;
                    movingObj.y = (bestSnap.deltaY < snapMargin) ? bestSnap.y : targetY;
                    stateChanged = true;
                 }
            }
            if (isDraggingMarker) {
                const marker = markers.find(m => m.id === selectedMarkerId); 
                if (marker) {
                    if (marker.type === 'dot') {
                        marker.x = worldPos.x - dragOffset.x;
                        marker.y = worldPos.y - dragOffset.y;
                        stateChanged = true;
                    } else {
                        const wallInfo = getWallSystemInfo(marker);
                        const parentRoom = rooms.find(r => r.id === marker.parentRoomId);
                        if(parentRoom) {
                            let newX = worldPos.x - dragOffset.x; let newY = worldPos.y - dragOffset.y;
                            switch (marker.edge) {
                                case 'top': newY = parentRoom.y - wallInfo.thickness / 2; newX = Math.max(parentRoom.x, Math.min(newX, parentRoom.x + parentRoom.width)); break;
                                case 'bottom': newY = parentRoom.y + parentRoom.height + wallInfo.thickness / 2; newX = Math.max(parentRoom.x, Math.min(newX, parentRoom.x + parentRoom.width)); break;
                                case 'left': newX = parentRoom.x - wallInfo.thickness / 2; newY = Math.max(parentRoom.y, Math.min(newY, parentRoom.y + parentRoom.height)); break;
                                case 'right': newX = parentRoom.x + parentRoom.width + wallInfo.thickness / 2; newY = Math.max(parentRoom.y, Math.min(newY, parentRoom.y + parentRoom.height)); break;
                            }
                            marker.x = newX; marker.y = newY;
                            stateChanged = true;
                        }
                    }
                }
            }
            if (stateChanged) { draw(); }
        }
    }
    function onTouchEnd(e) {
        e.preventDefault();
        clearTimeout(longPressTimer);
        if (isDraggingRoom || isDraggingMarker || isDraggingFiller) {
            updateAllMarkerLinks();
            hasUnsavedChanges = true;
        }
        isDraggingRoom = false; isDraggingMarker = false; isDraggingFiller = false; isPinching = false;
        updateCalculations();
    }
    function onWheel(e) {
        const pos = getCanvasCoordinates(e); if (!pos) return;
        if (e.ctrlKey) {
            e.preventDefault();
            const zoomFactor = e.deltaY < 0 ? 1.1 : 0.9;
            zoom(zoomFactor, pos);
        }
    }

    function screenToWorld(pos) { return { x: (pos.x / viewTransform.scale) - viewTransform.x, y: (pos.y / viewTransform.scale) - viewTransform.y }; }
    function getRoomAtPos(pos, isTouchEvent = false) {
        const padding = isTouchEvent ? (20 / viewTransform.scale) : 0; 
        return rooms.slice().reverse().find(r => 
            pos.x >= r.x - padding && pos.x <= r.x + r.width + padding && 
            pos.y >= r.y - padding && pos.y <= r.y + r.height + padding
        );
    }
    function getFillerAtPos(pos, isTouchEvent = false) {
        const padding = isTouchEvent ? (20 / viewTransform.scale) : 0;
        return fillers.slice().reverse().find(f => 
            pos.x >= f.x - padding && pos.x <= f.x + f.width + padding && 
            pos.y >= f.y - padding && pos.y <= f.y + f.height + padding
        );
    }
    function getMarkerAtPos(pos) { 
        const markerSize = 25 / viewTransform.scale; 
        return markers.slice().reverse().find(m => { 
            const dx = m.x - pos.x; 
            const dy = m.y - pos.y; 
            return (dx * dx + dy * dy) < (markerSize * markerSize); 
        }); 
    }
    function getNearestRoom(pos, tolerance) { let closestRoom = null; let minDistance = Infinity; for (const room of rooms) { const closestPoint = getClosestPointOnRect(room, pos); const distance = Math.sqrt((pos.x - closestPoint.x)**2 + (pos.y - closestPoint.y)**2); if (distance < minDistance && distance < tolerance) { minDistance = distance; closestRoom = room; } } return closestRoom; }

    function getClosestPointOnRect(rect, pos) {
        const points = [
            { 
                x: Math.max(rect.x, Math.min(pos.x, rect.x + rect.width)), 
                y: rect.y, 
                edge: 'top' 
            },
            { 
                x: Math.max(rect.x, Math.min(pos.x, rect.x + rect.width)), 
                y: rect.y + rect.height, 
                edge: 'bottom' 
            },
            { 
                x: rect.x, 
                y: Math.max(rect.y, Math.min(pos.y, rect.y + rect.height)), 
                edge: 'left' 
            },
            { 
                x: rect.x + rect.width, 
                y: Math.max(rect.y, Math.min(pos.y, rect.y + rect.height)), 
                edge: 'right' 
            }
        ];
        
        let closest = points[0];
        let minDistSq = Infinity;
        
        for (const p of points) {
            const distSq = (p.x - pos.x) ** 2 + (p.y - pos.y) ** 2;
            if (distSq < minDistSq) {
                minDistSq = distSq;
                closest = p;
            }
        }
        return closest;
    }

    function worldToScreen(pos) { return { x: (pos.x + viewTransform.x) * viewTransform.scale, y: (pos.y + viewTransform.y) * viewTransform.scale }; }
    function getSharedWallLength(roomA, roomB, orientation) { if (!roomA || !roomB) return 0; const yOverlap = Math.max(0, Math.min(roomA.y + roomA.height, roomB.y + roomB.height) - Math.max(roomA.y, roomB.y)); const xOverlap = Math.max(0, Math.min(roomA.x + roomA.width, roomB.x + roomB.width) - Math.max(roomA.x, roomB.x)); return orientation === 'h' ? xOverlap : yOverlap; }
    
    function getWallSystemInfo(marker) {
        const wallThicknessCm = getCmFromInput(parseFloat(wallThicknessInput.value.replace(',', '.')) || 0, getInputType('wallThicknessInput'));
        const epsilon = 5;
        const parentRoom = rooms.find(r => r.id === marker.parentRoomId);
        const defaultResult = { linkedRoomId: null, thickness: wallThicknessCm }; 
        if (!parentRoom) return defaultResult;
        const positionOnEdge = (marker.orientation === 'h') ? marker.x : marker.y;
        const candidates = [...rooms, ...fillers].filter(obj => { 
            if (obj.id === parentRoom.id) return false;
            if (marker.orientation === 'h') { return positionOnEdge >= obj.x && positionOnEdge <= obj.x + obj.width; } 
            else { return positionOnEdge >= obj.y && positionOnEdge <= obj.y + obj.height; }
        });
        if (candidates.length === 0) return defaultResult;
        let bestMatch = null;
        candidates.forEach(obj => {
            let dist = Infinity;
            if (marker.edge === 'right') dist = obj.x - (parentRoom.x + parentRoom.width);
            else if (marker.edge === 'left') dist = parentRoom.x - (obj.x + obj.width);
            else if (marker.edge === 'bottom') dist = obj.y - (parentRoom.y + parentRoom.height);
            else if (marker.edge === 'top') dist = parentRoom.y - (obj.y + obj.height);
            if (dist >= 0 && (!bestMatch || dist < bestMatch.dist)) {
                bestMatch = { obj, dist };
            }
        });
        if (!bestMatch) return defaultResult;
        const closestObj = bestMatch.obj;
        const closestDist = bestMatch.dist;
        const isClosestObjARoom = closestObj.hasOwnProperty('name');
        const maxAllowedGap = 2 * wallThicknessCm; 
        if (isClosestObjARoom && closestDist <= (maxAllowedGap + epsilon)) {
            return { linkedRoomId: closestObj.id, thickness: closestDist };
        }
        if (!isClosestObjARoom && Math.abs(closestDist - wallThicknessCm) < epsilon) { 
            const filler = closestObj;
            const roomBeyondFiller = rooms.find(r => {
                if (r.id === parentRoom.id) return false;
                let distFromFiller = Infinity;
                if (marker.edge === 'right') distFromFiller = r.x - (filler.x + filler.width);
                else if (marker.edge === 'left') distFromFiller = filler.x - (r.x + r.width);
                else if (marker.edge === 'bottom') distFromFiller = r.y - (filler.y + filler.height);
                else if (marker.edge === 'top') distFromFiller = filler.y - (r.y + r.height);
                return Math.abs(distFromFiller - wallThicknessCm) < epsilon;
            });
            if (roomBeyondFiller) {
                let finalThickness;
                if (marker.edge === 'right') finalThickness = roomBeyondFiller.x - (parentRoom.x + parentRoom.width);
                else if (marker.edge === 'left') finalThickness = parentRoom.x - (roomBeyondFiller.x + roomBeyondFiller.width);
                else if (marker.edge === 'bottom') finalThickness = roomBeyondFiller.y - (parentRoom.y + parentRoom.height);
                else finalThickness = parentRoom.y - (roomBeyondFiller.y + roomBeyondFiller.height);
                return { linkedRoomId: roomBeyondFiller.id, thickness: Math.abs(finalThickness) };
            }
        }
        return defaultResult;
    }
    function getMarkerBounds(marker) { const wallInfo = getWallSystemInfo(marker); const actualThickness = wallInfo.thickness; if(marker.type === 'dot') return {x: marker.x, y: marker.y, width: 0, height: 0}; const length = marker.width; const h_len = marker.orientation === 'h' ? length : actualThickness; const v_len = marker.orientation === 'v' ? length : actualThickness; return { x: marker.x - h_len / 2, y: marker.y - v_len / 2, width: h_len, height: v_len }; }
    function checkMarkerCollision(marker1, marker2) { if (!marker1.parentRoomId || !marker2.parentRoomId) return false; const bounds1 = getMarkerBounds(marker1); const bounds2 = getMarkerBounds(marker2); return ( bounds1.x < bounds2.x + bounds2.width && bounds1.x + bounds1.width > bounds2.x && bounds1.y < bounds2.y + bounds2.height && bounds1.y + bounds1.height > bounds2.y ); }

    function isSideOccupied(obj, side, allObjects) {
        const wallThicknessCm = getCmFromInput(parseFloat(wallThicknessInput.value.replace(',', '.')) || 0, getInputType('wallThicknessInput'));
        const offset = 40 / viewTransform.scale;
        let detectionRect = {};
        switch(side) {
            case 'top': detectionRect = { x: obj.x, y: obj.y - offset - wallThicknessCm, width: obj.width, height: offset }; break;
            case 'bottom': detectionRect = { x: obj.x, y: obj.y + obj.height + wallThicknessCm, width: obj.width, height: offset }; break;
            case 'left': detectionRect = { x: obj.x - offset - wallThicknessCm, y: obj.y, width: offset, height: obj.height }; break;
            case 'right': detectionRect = { x: obj.x + obj.width + wallThicknessCm, y: obj.y, width: offset, height: obj.height }; break;
        }
        return allObjects.some(other => {
            if (obj.id === other.id) return false;
            const rect2 = { x: other.x, y: other.y, width: other.width, height: other.height };
            return (detectionRect.x < rect2.x + rect2.width && detectionRect.x + detectionRect.width > rect2.x &&
                    detectionRect.y < rect2.y + rect2.height && detectionRect.y + detectionRect.height > rect2.y);
        });
    }
    function findPreferredSide(obj, sideA, sideB, allObjects) { const isSideAOccupied = isSideOccupied(obj, sideA, allObjects); const isSideBOccupied = isSideOccupied(obj, sideB, allObjects); if (!isSideAOccupied && !isSideBOccupied) { return sideA; } if (!isSideAOccupied) { return sideA; } if (!isSideBOccupied) { return sideB; } return null; }

    function draw(isExport = false) {
        const allObjects = [...rooms, ...fillers];

        if (!isExport) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        }
        ctx.save();
        ctx.scale(viewTransform.scale, viewTransform.scale);
        ctx.translate(viewTransform.x, viewTransform.y);

        drawWallsAndFloors();
        fillers.forEach(f => drawFiller(f)); 
        rooms.forEach(r => drawRoomOverlays(r));

        markers.forEach(drawMarker);
        ctx.restore();
    }
    
    function getAvailableWallLength(parentRoom, edge, position) {
        const wallInfo = getWallSystemInfo({ parentRoomId: parentRoom.id, edge: edge, x: position.x, y: position.y, orientation: (edge === 'top' || edge === 'bottom') ? 'h' : 'v' });
        const linkedRoom = rooms.find(r => r.id === wallInfo.linkedRoomId);
        if (linkedRoom) {
            const orientation = (edge === 'top' || edge === 'bottom') ? 'h' : 'v';
            return getSharedWallLength(parentRoom, linkedRoom, orientation);
        } else {
            return (edge === 'top' || edge === 'bottom') ? parentRoom.width : parentRoom.height;
        }
    }

    function drawWallsAndFloors() {
        const wallThicknessCm = getCmFromInput(parseFloat(wallThicknessInput.value.replace(',', '.')) || 0, getInputType('wallThicknessInput'));
        
        ctx.fillStyle = '#868e96'; 
        rooms.forEach(room => {
            ctx.fillRect(room.x - wallThicknessCm, room.y - wallThicknessCm, room.width + 2 * wallThicknessCm, room.height + 2 * wallThicknessCm);
        });
        
        rooms.forEach(room => {
            ctx.fillStyle = room.color || '#add8e6'; 
            ctx.fillRect(room.x, room.y, room.width, room.height);
        });
    }

    function drawFiller(filler) {
        ctx.fillStyle = '#6c757d';
        ctx.fillRect(filler.x, filler.y, filler.width, filler.height);
        ctx.strokeStyle = '#343a40';
        ctx.lineWidth = 1 / viewTransform.scale;
        ctx.strokeRect(filler.x, filler.y, filler.width, filler.height);
        if (filler.id === selectedFillerId) {
            ctx.strokeStyle = '#ff0000';
            ctx.lineWidth = 3 / viewTransform.scale;
            ctx.strokeRect(filler.x, filler.y, filler.width, filler.height);
        }
    }

    function drawRoomOverlays(room) {
        ctx.strokeStyle = '#333'; ctx.lineWidth = 1 / viewTransform.scale;
        ctx.strokeRect(room.x, room.y, room.width, room.height);
        if (room.id === selectedRoomId) {
            ctx.strokeStyle = '#ff0000'; ctx.lineWidth = 3 / viewTransform.scale;
            ctx.strokeRect(room.x, room.y, room.width, room.height);
        }
        const fontSize = Math.min(room.width / 10, room.height / 7, 25) / viewTransform.scale;
        if (fontSize * viewTransform.scale > 8) {
            ctx.fillStyle = '#000000'; ctx.font = `${fontSize}px Poppins, sans-serif`;
            ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
            wrapText(ctx, room.name, room.x + room.width / 2, room.y + room.height / 2, room.width - 20/viewTransform.scale, fontSize * 1.2);
        }
    }
    
    function wrapText(ctx, text, x, y, maxWidth, lineHeight) { 
        const words = text.split(' '); let line = ''; const lines = []; 
        for (let n = 0; n < words.length; n++) { 
            const testLine = line + words[n] + ' '; 
            const testWidth = ctx.measureText(testLine).width; 
            if (testWidth > maxWidth && n > 0) { lines.push(line); line = words[n] + ' '; } 
            else { line = testLine; } 
        } 
        lines.push(line); 
        const startY = y - (lines.length - 1) * lineHeight / 2; 
        for(let i = 0; i < lines.length; i++) { ctx.fillText(lines[i].trim(), x, startY + i * lineHeight); } 
        return lines.length * lineHeight; 
    }

    function drawMarker(marker) {
        if (marker.id === selectedMarkerId) {
            ctx.beginPath();
            const selectionSize = 25 / viewTransform.scale;
            ctx.arc(marker.x, marker.y, selectionSize, 0, 2 * Math.PI);
            ctx.fillStyle = 'rgba(255, 255, 0, 0.5)';
            ctx.fill();
        }

        if (marker.type === 'dot') { 
            ctx.beginPath();
            const radius = (canvas.width * 0.01) / viewTransform.scale;
            ctx.arc(marker.x, marker.y, radius, 0, 2 * Math.PI);
            ctx.fillStyle = ajaxObj.marker_color || '#dc3545';
            ctx.fill();
            return;
        }

        const epsilon = 1.5;
        const bounds = getMarkerBounds(marker);
        const parentRoom = rooms.find(r => r.id === marker.parentRoomId);

        if (parentRoom) {
            ctx.fillStyle = parentRoom.color || '#add8e6'; 
            ctx.fillRect(bounds.x - epsilon, bounds.y - epsilon, bounds.width + (epsilon*2), bounds.height + (epsilon*2));
        } else {
            ctx.clearRect(bounds.x - epsilon, bounds.y - epsilon, bounds.width + (epsilon*2), bounds.height + (epsilon*2));
        }

        if (marker.type === 'window') {
            const wallInfo = getWallSystemInfo(marker);
            const h_len = marker.orientation === 'h' ? marker.width / 2 : wallInfo.thickness / 2;
            const v_len = marker.orientation === 'v' ? marker.width / 2 : wallInfo.thickness / 2;
            ctx.fillStyle = '#664BC4'; 
            ctx.fillRect(marker.x - h_len, marker.y - v_len, h_len * 2, v_len * 2);
            ctx.fillStyle = 'white';
            ctx.fillRect(marker.x - h_len / 1.5, marker.y - v_len / 1.5, (h_len / 1.5) * 2, (v_len / 1.5) * 2);
        } else if (marker.type === 'door') {
            ctx.fillStyle = 'white';
            ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
            ctx.strokeStyle = 'black';
            ctx.lineWidth = 1 / viewTransform.scale;
            ctx.strokeRect(bounds.x, bounds.y, bounds.width, bounds.height);
        }

    }

    function updateCalculations() {
        const outputDiv = document.getElementById('results-output'); 
        outputDiv.innerHTML = '';

        if (rooms.length === 0) { 
            outputDiv.innerHTML = `<p style="text-align:center; color: #888;">${l10n.no_rooms}</p>`; 
            return;
        }

        if (typeof calculateRoomData !== 'function') {
            console.error("calculateRoomData function is not defined!");
            return;
        }

        const roomDataList = rooms.map(room => calculateRoomData(room, fillers)); 

        let grandTotalNetFloorAreaCM = 0; 
        let totalRooms = rooms.length;
        let grandTotalGrossSurfaceAreaCM = 0; 
        let totalWindows = 0, totalDoors = 0;
        let totalWindowAreaCM = 0, totalDoorAreaCM = 0;
        const countedMarkers = new Set();
        
        roomDataList.forEach(data => {
            grandTotalNetFloorAreaCM += data.floorAreaCM;
            grandTotalGrossSurfaceAreaCM += data.grossTotalSurfaceCM;
        });

        const allMarkers = [...markers];
        allMarkers.forEach(m => {
            if (m.type === 'dot' || countedMarkers.has(m.id)) return;
            const areaCM = (m.width * m.height);
            if (m.type === 'window') { totalWindows++; totalWindowAreaCM += areaCM; }
            if (m.type === 'door') { totalDoors++; totalDoorAreaCM += areaCM; }
            countedMarkers.add(m.id);
            if (m.linkedRoomId) {
                const linkedMarker = allMarkers.find(om => om.id !== m.id && om.parentRoomId === m.linkedRoomId && om.linkedRoomId === m.parentRoomId);
                if (linkedMarker) countedMarkers.add(linkedMarker.id);
            }
        });

        const globalDiv = document.createElement('div');
        globalDiv.className = 'results-global';

        const ceilingHeightCm = getCmFromInput(parseFloat(ceilingHeightInput.value.replace(',', '.')) || 0, getInputType('ceilingHeightInput'));

        let globalHtml = `
            <h3>${l10n.generalData}</h3>
            <ul>
                <li>${l10n.ceilingHeight}: <span>${formatLength(ceilingHeightCm)}</span></li>
                <li>${l10n.roomCount}: <span>${totalRooms} ${l10n.pcs}</span></li>
                <li>${l10n.totalNetFloorArea}: <span>${formatArea(grandTotalNetFloorAreaCM)}</span></li>
                <li>${l10n.grossTotalSurface}: <span>${formatArea(grandTotalGrossSurfaceAreaCM)}</span></li>
                <li class="subheader" style="margin-top: 10px;">${l10n.totalOpenings}</li>
                <li>${l10n.windows}: <span>${totalWindows} ${l10n.pcs}</span></li>
                <li>${l10n.doors}: <span>${totalDoors} ${l10n.pcs}</span></li>
            </ul>`;

        globalDiv.innerHTML = globalHtml;
        outputDiv.appendChild(globalDiv);

        rooms.forEach((room, index) => {
            const data = roomDataList[index];
            const roomDiv = document.createElement('div'); 
            roomDiv.className = 'room-results';
            roomDiv.style.borderLeft = `5px solid ${room.color || '#add8e6'}`;

            let roomHtml = `
                <h3>${data.name}</h3>
                <ul>
                    <li>${l10n.dimensions}: <span>${formatLength(room.width)} x ${formatLength(room.height)}</span></li>
                    <li>${l10n.netFloorArea}: <span>${formatArea(data.floorAreaCM)}</span></li>
                    <li class="subheader">${l10n.openings}</li>
                    <li>${l10n.doors}: <span>${data.numDoors} ${l10n.pcs}</span></li>
                    <li>${l10n.windows}: <span>${data.numWindows} ${l10n.pcs}</span></li>
                    <li class="subheader">${l10n.wallSurfaces}</li>
                    <li>${l10n.grossWallSurface}: <span>${formatArea(data.grossWallAreaCM)}</span></li>
                    <li class="subheader">${l10n.totals}</li>
                    <li>${l10n.grossTotalSurface}: <span>${formatArea(data.grossTotalSurfaceCM)}</span></li>
                </ul>`;
            
            roomDiv.innerHTML = roomHtml;
            outputDiv.appendChild(roomDiv);
        });
    }

    function calculateRoomData(room, allFillers) {
                const tiledHeight = 0;
                const ceilingHeight = getCmFromInput(parseFloat(ceilingHeightInput.value.replace(',', '.')) || 0, getInputType('ceilingHeightInput'));
                const roomMarkers = [...new Set(markers.filter(m => m.parentRoomId === room.id || m.linkedRoomId === room.id))];

                roomMarkers.forEach(m => {
                    if (!m.height || !m.width) return;

                    let edgeToUse = null;
                    if (m.parentRoomId === room.id) {
                        edgeToUse = m.edge;
                    } else if (m.linkedRoomId === room.id) {

                    }

                });

                const grossWidthWallAreaCM = room.width * ceilingHeight;
                const grossHeightWallAreaCM = room.height * ceilingHeight;
                const grossWallAreaCM = (2 * grossWidthWallAreaCM) + (2 * grossHeightWallAreaCM);
                let floorAreaCM = room.width * room.height;
                let freestandingPillarSurface = { totalCM: 0, tiledCM: 0, paintedCM: 0 };
                let attachedPillarSurface = { totalCM: 0, tiledCM: 0, paintedCM: 0 };

                if (allFillers.length > 0) {
                    const fillersInRoom = allFillers.filter(f => 
                        (f.x + f.width / 2) > room.x && (f.x + f.width / 2) < (room.x + room.width) &&
                        (f.y + f.height / 2) > room.y && (f.y + f.height / 2) < (room.y + room.height)
                    );

                    fillersInRoom.forEach(filler => {
                        floorAreaCM -= filler.width * filler.height;
                        const epsilon = 1.5;
                        const contacts = {
                            top: Math.abs(filler.y - room.y) < epsilon,
                            bottom: Math.abs((filler.y + filler.height) - (room.y + room.height)) < epsilon,
                            left: Math.abs(filler.x - room.x) < epsilon,
                            right: Math.abs((filler.x + filler.width) - (room.x + room.width)) < epsilon
                        };
                        const contactCount = Object.values(contacts).filter(Boolean).length;

                        if (contactCount === 0) {
                            const pillarPerimeter = 2 * (filler.width + filler.height);
                            freestandingPillarSurface.totalCM += pillarPerimeter * ceilingHeight;
                            freestandingPillarSurface.tiledCM += pillarPerimeter * tiledHeight;
                            freestandingPillarSurface.paintedCM += pillarPerimeter * (ceilingHeight - tiledHeight);
                        } else {
                            let areaChange = { totalCM: 0, tiledCM: 0, paintedCM: 0 };

                            if (contactCount === 1) {
                                let netPerimeterChange = 0;
                                if (contacts.top || contacts.bottom) {
                                    netPerimeterChange = 2 * filler.height;
                                } else {
                                    netPerimeterChange = 2 * filler.width;
                                }
                                areaChange.totalCM = netPerimeterChange * ceilingHeight;
                                areaChange.tiledCM = netPerimeterChange * tiledHeight;
                                areaChange.paintedCM = netPerimeterChange * (ceilingHeight - tiledHeight);
                            } else if (contactCount >= 2) {
                                const coveredPerimeter = (contacts.top || contacts.bottom ? filler.width : 0) + (contacts.left || contacts.right ? filler.height : 0);
                                const exposedPerimeter = (2 * (filler.width + filler.height)) - (2 * coveredPerimeter);
                                const netPerimeterChange = exposedPerimeter - coveredPerimeter;

                                areaChange.totalCM = netPerimeterChange * ceilingHeight;
                                areaChange.tiledCM = netPerimeterChange * tiledHeight;
                                areaChange.paintedCM = netPerimeterChange * (ceilingHeight - tiledHeight);
                            }
                            attachedPillarSurface.totalCM += areaChange.totalCM;
                            attachedPillarSurface.tiledCM += areaChange.tiledCM;
                            attachedPillarSurface.paintedCM += areaChange.paintedCM;
                        }
                    });
                }
                
                return {
                    name: room.name,
                    numWindows: roomMarkers.filter(m => m.type === 'window').length,
                    numDoors: roomMarkers.filter(m => m.type === 'door').length,
                    floorAreaCM: floorAreaCM,
                    grossWallAreaCM: grossWallAreaCM,
                    grossTotalSurfaceCM: (grossWallAreaCM + (room.width * room.height)),
                };
            }
    const tooltipTrigger = document.querySelector('.tooltip-trigger-text');
    if (tooltipTrigger) {
        const tooltip = document.createElement('div');
        tooltip.id = 'custom-tooltip';
        tooltip.innerHTML = `
            <button class="tooltip-close-btn" aria-label="${l10n.close}">&times;</button>
            ${tooltipTrigger.dataset.tooltipContent || `<p>${l10n.loadingTooltip}</p>`}
        `;
        document.body.appendChild(tooltip);

        const showTooltip = () => tooltip.classList.add('is-visible');
        const hideTooltip = () => tooltip.classList.remove('is-visible');

        tooltipTrigger.addEventListener('click', (e) => {
            e.stopPropagation();
            tooltip.classList.toggle('is-visible');
        });
        tooltip.addEventListener('click', (e) => {
            if (e.target.classList.contains('tooltip-close-btn')) {
                hideTooltip();
            }
        });
        document.addEventListener('click', (e) => {
            if (!tooltip.contains(e.target) && !tooltipTrigger.contains(e.target) && tooltip.classList.contains('is-visible')) {
                hideTooltip();
            }
        });

        if (window.innerWidth > 1024) {
            tooltipTrigger.addEventListener('mouseover', showTooltip);
            tooltipTrigger.addEventListener('mousemove', (e) => {
                let leftPos = e.clientX - (tooltip.offsetWidth / 2);
                let topPos = e.clientY + 20; 
                
                if (leftPos < 10) leftPos = 10;
                if (leftPos + tooltip.offsetWidth > window.innerWidth - 10) {
                    leftPos = window.innerWidth - tooltip.offsetWidth - 10;
                }
                if (topPos + tooltip.offsetHeight > window.innerHeight - 10) {
                    topPos = e.clientY - tooltip.offsetHeight - 10; 
                }
                
                tooltip.style.left = `${leftPos}px`;
                tooltip.style.top = `${topPos}px`;
                tooltip.style.transform = 'none'; 
            });
            tooltipTrigger.addEventListener('mouseout', hideTooltip);
        }
    }

    const hamburgerBtn = document.getElementById('hamburger-btn');
    const mobileControlsPanel = document.getElementById('mobile-controls-panel');
    const controlsDiv = document.getElementById('controls');

    if (hamburgerBtn && mobileControlsPanel && controlsDiv) {
        hamburgerBtn.addEventListener('click', (e) => {
            e.stopPropagation(); 
            mobileControlsPanel.classList.toggle('is-open');
        });
        document.addEventListener('click', (e) => {
            if (!mobileControlsPanel.contains(e.target) && !hamburgerBtn.contains(e.target) && mobileControlsPanel.classList.contains('is-open')) {
                mobileControlsPanel.classList.remove('is-open');
            }
        });
        controlsDiv.addEventListener('click', (e) => {
            if (window.innerWidth <= 1024 && mobileControlsPanel.classList.contains('is-open')) {
                const targetButton = e.target.closest('button');
                const ADD_BUTTON_IDS = ['addRoomBtn', 'addFillerBtn', 'addDotBtn', 'addWindowBtn', 'addDoorBtn', 'addOpeningBtn'];
                if (targetButton && ADD_BUTTON_IDS.includes(targetButton.id)) {
                    mobileControlsPanel.classList.remove('is-open');
                }
            }
        });
    }

    function initializeInputValues() {
        const inputsToConvert = [
            'ceilingHeightInput', 'wallThicknessInput',
            'roomWidthInput', 'roomHeightInput',
            'fillerWidthInput', 'fillerHeightInput',
            'windowWidthInput', 'windowHeightInput', 'parapetHeightInput',
            'doorWidthInput', 'doorHeightInput',
            'openingWidthInput', 'openingHeightInput'
        ];

        inputsToConvert.forEach(id => {
            const input = document.getElementById(id);
            if (!input) return;
            
            const inputType = getInputType(id);
            const defaultCmValue = parseFloat(input.value) || parseFloat(input.placeholder) || 0;
            
            if (defaultCmValue > 0) {
                const displayValue = getValueForInput(defaultCmValue, inputType);
                
                if (input.hasAttribute('value')) {
                    input.value = displayValue;
                } else if (input.hasAttribute('placeholder')) {
                    input.placeholder = Math.round(displayValue * 10) / 10;
                }
            }

            if (inputType === 'ft') {
                input.step = '0.01';
            } else if (inputType === 'in') {
                input.step = '0.1';
            } else { 
                input.step = '0.1';
            }
        });
    }

    initializeInputValues();
    updateCeilingHeightDisplay();
    updateEditorInputs();
    draw();
    updateCalculations();

}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', planwiseWpInit);
} else {
    planwiseWpInit();
}