javascript```
import React, { useState, useEffect, useRef } from 'react';
import { Document, Page } from 'react-pdf';
import ViewerChat from './ViewerChat';
import { getPdfUrl, addPdfSelectionToChat } from '../api';
import { pdfjs } from 'react-pdf';
import { useLocation, useNavigate } from 'react-router-dom';
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
import "react-pdf/dist/esm/Page/TextLayer.css";
import { FaArrowLeft, FaDownload, FaRegSquare, FaComments } from 'react-icons/fa';

// Determine device type as early as possible (used in initial state)
const isMobile = /Mobi|Android/i.test(navigator.userAgent);

pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;

export default function Viewer({ viewMode, setViewMode }) {
    const location = useLocation();
    const navigate = useNavigate();
    const [paperObj, setPaperObj] = useState(null);
    const [numPages, setNumPages] = useState(null);
    const [sidebarOpen, setSidebarOpen] = useState(() => !isMobile);
    const [chatExpanded, setChatExpanded] = useState(false);
    const [ocrTextQP, setOcrTextQP] = useState('');
    const [ocrTextMS, setOcrTextMS] = useState('');
    const [ocrTextIN, setOcrTextIN] = useState('');
    const pdfViewerRef = useRef(null);
    const [pageWidth, setPageWidth] = useState(0);
    const [zoom, setZoom] = useState(() => {
        // Initialize zoom from localStorage or default to 0.9
        const savedZoom = localStorage.getItem('viewer-zoom');
        return savedZoom ? parseFloat(savedZoom) : 0.9;
    });
    const initialDistanceRef = useRef(null);
    const initialZoomRef = useRef(zoom);

    // Select tool states
    const [selectToolActive, setSelectToolActive] = useState(false);
    const [isSelecting, setIsSelecting] = useState(false);
    const [selectionStart, setSelectionStart] = useState({ x: 0, y: 0 });
    const [selectionEnd, setSelectionEnd] = useState({ x: 0, y: 0 });
    const [showSelection, setShowSelection] = useState(false);
    const selectionRef = useRef(null);

    useEffect(() => {
        const params = new URLSearchParams(location.search);
        let paperFromSource = null;
        let viewModeFromSource = null;

        // Handle full paper object (preferred method)
        if (location.state && location.state.paper) {
            paperFromSource = location.state.paper;
            viewModeFromSource = location.state.viewMode || 'qp';
            console.log('Paper object from state:', paperFromSource); // Debug log
            setPaperObj(paperFromSource);
            setViewMode(viewModeFromSource);
            // Don't auto-convert URL for now to avoid issues
        } 
        // Handle legacy URL format with full paper object (for backward compatibility)
        else if (params.get('paper')) {
            try {
                paperFromSource = JSON.parse(decodeURIComponent(params.get('paper')));
                viewModeFromSource = params.get('viewMode') || 'qp';
                console.log('Paper object from URL:', paperFromSource); // Debug log
                setPaperObj(paperFromSource);
                setViewMode(viewModeFromSource);
            } catch (error) {
                console.error('Error parsing paper from URL:', error);
                navigate('/');
                return;
            }
        } 
        // Handle legacy filename-only format (backward compatibility)
        else if (location.state && location.state.filename) {
            const filename = location.state.filename;
            viewModeFromSource = location.state.viewMode || 'qp';
            // Create a basic paper object
            paperFromSource = { 
                qp: filename, 
                ms: filename.replace('_qp_', '_ms_').replace('_er_', '_ms_'), 
                in: filename.replace('_qp_', '_in_').replace('_er_', '_in_'), 
                code: filename.split('/').pop().replace('.pdf', ''),
                name: filename.split('/').pop().replace('.pdf', '')
            };
            setPaperObj(paperFromSource);
            setViewMode(viewModeFromSource);
        } 
        // Handle legacy URL filename format
        else if (params.get('filename')) {
            const filename = params.get('filename');
            viewModeFromSource = params.get('viewMode') || 'qp';
            // Create a basic paper object
            paperFromSource = { 
                qp: filename, 
                ms: filename.replace('_qp_', '_ms_').replace('_er_', '_ms_'), 
                in: filename.replace('_qp_', '_in_').replace('_er_', '_in_'), 
                code: filename.split('/').pop().replace('.pdf', ''),
                name: filename.split('/').pop().replace('.pdf', '')
            };
            setPaperObj(paperFromSource);
            setViewMode(viewModeFromSource);
        }

        if (!paperFromSource) {
            navigate('/');
        }
    }, [location.state, location.search, navigate]);

    // New function to extract OCR from a PDF and upload them to the backend, returning the OCR text.
    async function extractOCRFromUrl(url) {
        try {
            const loadingTask = pdfjs.getDocument(url);
            const pdf = await loadingTask.promise;
            let ocrText = "";
            for (let i = 1; i <= pdf.numPages; i++) {
                const page = await pdf.getPage(i);
                const textContent = await page.getTextContent();
                const pageText = textContent.items.map(item => item.str).join(' ');
                ocrText += `Page ${i}: ${pageText}\n`;
            }
            return ocrText;
        } catch (error) {
            console.error('Error extracting OCR from', url, error);
            return 'OCR not available';
        }
    }

    // useEffect to extract OCR text for all relevant documents
    useEffect(() => {
        if (paperObj) {
            if (paperObj.qp) {
                extractOCRFromUrl(getPdfUrl(paperObj.qp)).then(data => setOcrTextQP(data));
            } else {
                setOcrTextQP('Not available');
            }
            if (paperObj.ms) {
                extractOCRFromUrl(getPdfUrl(paperObj.ms)).then(data => setOcrTextMS(data));
            } else {
                setOcrTextMS('Not available');
            }
            if (paperObj.in) {
                extractOCRFromUrl(getPdfUrl(paperObj.in)).then(data => setOcrTextIN(data));
            } else {
                setOcrTextIN('Not available');
            }
        }
    }, [paperObj]);

    useEffect(() => {
        if (pdfViewerRef.current) {
            pdfViewerRef.current.scrollTop = 0;
        }
    }, [paperObj, viewMode]);

    // Updated onDocumentLoadSuccess to recalc pageWidth immediately
    function onDocumentLoadSuccess(pdf) {
        setNumPages(pdf.numPages);
        if (pdfViewerRef.current) {
            setPageWidth(pdfViewerRef.current.clientWidth);
        }
    }

    // Select tool functions
    const handleSelectToolToggle = () => {
        setSelectToolActive(!selectToolActive);
        setShowSelection(false);
        setIsSelecting(false);
    };

    const handleMouseDown = (e) => {
        if (!selectToolActive) return;
        
        const rect = pdfViewerRef.current.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        setSelectionStart({ x, y });
        setSelectionEnd({ x, y });
        setIsSelecting(true);
        setShowSelection(true);
    };

    const handleMouseMove = (e) => {
        if (!selectToolActive || !isSelecting) return;
        
        const rect = pdfViewerRef.current.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        setSelectionEnd({ x, y });
    };

    const handleMouseUp = () => {
        if (!selectToolActive || !isSelecting) return;
        setIsSelecting(false);
    };

    const captureSelection = async () => {
        if (!showSelection) return;

        try {
            // Get the selection rectangle dimensions
            const rect = {
                x: Math.min(selectionStart.x, selectionEnd.x),
                y: Math.min(selectionStart.y, selectionEnd.y),
                width: Math.abs(selectionEnd.x - selectionStart.x),
                height: Math.abs(selectionEnd.y - selectionStart.y)
            };

            // Temporarily hide the selection overlay to exclude it from capture
            const overlay = selectionRef.current;
            const originalDisplay = overlay ? overlay.style.display : null;
            if (overlay) {
                overlay.style.display = 'none';
            }

            // Use html2canvas to capture the PDF viewer area
            const html2canvas = await import('html2canvas');
            const screenshot = await html2canvas.default(pdfViewerRef.current, {
                x: rect.x,
                y: rect.y,
                width: rect.width,
                height: rect.height,
                scale: 1,
                useCORS: true,
                allowTaint: true,
                ignoreElements: (element) => {
                    // Ignore the selection overlay element
                    return element === overlay;
                }
            });

            // Restore the overlay visibility
            if (overlay && originalDisplay !== null) {
                overlay.style.display = originalDisplay;
            }

            // Convert to blob and get data URL for chat
            screenshot.toBlob(async (blob) => {
                try {
                    // Convert blob to data URL
                    const reader = new FileReader();
                    reader.onload = async (e) => {
                        try {
                            // Add to chat context
                            await addPdfSelectionToChat(e.target.result);
                            alert('Selection added to chat context!');
                            
                            // Clear the selection
                            clearSelection();
                        } catch (error) {
                            console.error('Failed to add selection to chat:', error);
                            // Fallback: copy to clipboard
                            try {
                                await navigator.clipboard.write([
                                    new ClipboardItem({ 'image/png': blob })
                                ]);
                                alert('Selection copied to clipboard!');
                            } catch (clipboardErr) {
                                console.error('Failed to copy to clipboard:', clipboardErr);
                                alert('Failed to capture selection');
                            }
                        }
                    };
                    reader.readAsDataURL(blob);
                } catch (err) {
                    console.error('Failed to process selection:', err);
                    alert('Failed to capture selection');
                }
            });
        } catch (error) {
            console.error('Error capturing selection:', error);
            alert('Failed to capture selection');
        }
    };

    const clearSelection = () => {
        setShowSelection(false);
        setIsSelecting(false);
    };

    // Handle adding PDF selection to chat
    const handleAddSelectionToChat = () => {
        if (showSelection) {
            captureSelection();
        } else {
            alert('Please make a selection first using the Select tool');
        }
    };

    const getFilePath = (mode) => {
        if (!paperObj || !paperObj[mode]) {
            console.error(`Paper or paper file for mode ${mode} is undefined`);
            return '';
        }
        return paperObj[mode];
    };

    useEffect(() => {
        function handleResize() {
            if (pdfViewerRef.current) {
                setPageWidth(pdfViewerRef.current.clientWidth);
            }
        }
        handleResize();
        window.addEventListener('resize', handleResize);
        return () => window.removeEventListener('resize', handleResize);
    }, []);

    const handleZoomIn = () => setZoom(prev => Math.min(prev + 0.1, 3));
    const handleZoomOut = () => setZoom(prev => Math.max(prev - 0.1, 0.5));
    const handleResetZoom = () => setZoom(0.9);
    const handleDownload = () => {
        const fileUrl = getPdfUrl(getFilePath(viewMode));
        const link = document.createElement('a');
        link.href = fileUrl;
        // Use paperObj.code if available, else default name
        link.download = (paperObj && paperObj.code ? paperObj.code : 'document') + '_' + viewMode + '.pdf';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    };
    const handleBack = () => {
        if (window.history.length > 1) {
            navigate(-1);
        } else if(window.opener) {
            window.close();
        } else {
            navigate('/');
        }
    };

    useEffect(() => {
        const viewer = pdfViewerRef.current;
        if (!viewer) return;
        const getDistance = (touch1, touch2) => {
            const dx = touch2.pageX - touch1.pageX;
            const dy = touch2.pageY - touch1.pageY;
            return Math.sqrt(dx * dx + dy * dy);
        };
        
        const handleTouchStart = (e) => {
            if (e.touches.length === 2) {
                initialDistanceRef.current = getDistance(e.touches[0], e.touches[1]);
                initialZoomRef.current = zoom; // capture current zoom
            }
        };
        
        const handleTouchMove = (e) => {
            if (e.touches.length === 2 && initialDistanceRef.current) {
                const currentDistance = getDistance(e.touches[0], e.touches[1]);
                const scaleFactor = currentDistance / initialDistanceRef.current;
                setZoom(Math.min(Math.max(initialZoomRef.current * scaleFactor, 0.5), 3));
            }
        };
        
        const handleTouchEnd = (e) => {
            if (e.touches.length < 2) {
                initialDistanceRef.current = null;
            }
        };
        
        viewer.addEventListener('touchstart', handleTouchStart);
        viewer.addEventListener('touchmove', handleTouchMove);
        viewer.addEventListener('touchend', handleTouchEnd);
        viewer.addEventListener('touchcancel', handleTouchEnd);
        
        return () => {
            viewer.removeEventListener('touchstart', handleTouchStart);
            viewer.removeEventListener('touchmove', handleTouchMove);
            viewer.removeEventListener('touchend', handleTouchEnd);
            viewer.removeEventListener('touchcancel', handleTouchEnd);
        };
    }, [zoom]);

    // Save zoom level to localStorage whenever it changes
    useEffect(() => {
        localStorage.setItem('viewer-zoom', zoom.toString());
    }, [zoom]);

    return !paperObj ? null : (
        <div className="viewer-wrapper" style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
            <div className="viewer-navbar">
                <select value={viewMode} onChange={(e) => {
                    const newMode = e.target.value;
                    // Check if the requested document type exists
                    if (!paperObj[newMode]) {
                        alert(`${newMode.toUpperCase()} document not available for this paper`);
                        return;
                    }
                    
                    // Open new window with same paper object but different view mode
                    const params = new URLSearchParams();
                    params.set('paper', encodeURIComponent(JSON.stringify(paperObj)));
                    params.set('viewMode', newMode);
                    const newUrl = `${window.location.pathname}?${params.toString()}`;
                    window.open(newUrl, '_blank');
                }}>
                    <option value="qp">Question Paper</option>
                    <option value="ms" disabled={!paperObj.ms}>Marking Scheme</option>
                    {paperObj.in && <option value="in">Reading Insert</option>}
                </select>
                <button onClick={handleBack} title="Back">
                    {isMobile ? <FaArrowLeft /> : 'Back'}
                </button>
                {!isMobile && (
                    <>
                        <button onClick={handleZoomOut}>-</button>
                        <button onClick={handleZoomIn}>+</button>
                        <button onClick={handleResetZoom}>Reset</button>
                    </>
                )}
                <button onClick={handleDownload} title="Download">
                    {isMobile ? <FaDownload /> : 'Download'}
                </button>
                <button 
                    onClick={handleSelectToolToggle}
                    title="Select area"
                    style={{ 
                        backgroundColor: selectToolActive ? '#007bff' : 'transparent',
                        color: selectToolActive ? 'white' : 'inherit'
                    }}
                >
                    {isMobile ? <FaRegSquare /> : 'Select'}
                </button>
                {showSelection && (
                    <>
                        <button onClick={captureSelection}>Add to Chat</button>
                        <button onClick={clearSelection}>Clear</button>
                    </>
                )}
                <button 
                    onClick={() => setSidebarOpen(!sidebarOpen)}
                    title="Chat"
                    style={{ 
                        backgroundColor: sidebarOpen ? '#007bff' : 'transparent',
                        color: sidebarOpen ? 'white' : 'inherit'
                    }}
                >
                    {isMobile ? <FaComments /> : 'Chat'}
                </button>
            </div>
            <div className="viewer-body" style={{ display: 'flex', flex: 1 }}>
                <div 
                    className="pdf-viewer" 
                    style={{ 
                        flex: 1, 
                        overflowY: 'auto',
                        position: 'relative',
                        cursor: selectToolActive ? 'crosshair' : 'default'
                    }} 
                    ref={pdfViewerRef}
                    onMouseDown={handleMouseDown}
                    onMouseMove={handleMouseMove}
                    onMouseUp={handleMouseUp}
                >
                    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                        <Document
                            file={getPdfUrl(getFilePath(viewMode))}
                            onLoadSuccess={onDocumentLoadSuccess}
                        >
                            {Array.from(new Array(numPages), (el, index) => (
                                <Page 
                                    key={`page_${index + 1}`} 
                                    pageNumber={index + 1} 
                                    width={pageWidth * zoom} 
                                    renderTextLayer={!selectToolActive} 
                                    style={{ 
                                        display: 'block', 
                                        marginBottom: 0,
                                        userSelect: selectToolActive ? 'none' : 'auto',
                                        WebkitUserSelect: selectToolActive ? 'none' : 'auto',
                                        MozUserSelect: selectToolActive ? 'none' : 'auto'
                                    }} 
                                />
                            ))}
                        </Document>
                    </div>
                    
                    {/* Selection overlay */}
                    {showSelection && (
                        <div
                            ref={selectionRef}
                            style={{
                                position: 'absolute',
                                left: Math.min(selectionStart.x, selectionEnd.x),
                                top: Math.min(selectionStart.y, selectionEnd.y),
                                width: Math.abs(selectionEnd.x - selectionStart.x),
                                height: Math.abs(selectionEnd.y - selectionStart.y),
                                border: '2px dashed #007bff',
                                backgroundColor: 'rgba(0, 123, 255, 0.1)',
                                pointerEvents: 'none',
                                zIndex: 1000
                            }}
                        />
                    )}
                </div>
                
                {/* Chat Sidebar */}
                {sidebarOpen && (
                    <ViewerChat
                        paperObj={paperObj}
                        ocrTexts={{
                            qp: ocrTextQP,
                            ms: ocrTextMS,
                            in: ocrTextIN
                        }}
                        onAddSelection={handleAddSelectionToChat}
                        isExpanded={chatExpanded}
                        onToggleExpanded={() => setChatExpanded(!chatExpanded)}
                    />
                )}
            </div>
        </div>
    );
}
```

Look through this page. What does it do?