#ifndef GRAPH_H
#define GRAPH_H

const char graph_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>CAN Signal Graph</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #43cea2 0%, #185a9d 100%);
            min-height: 100vh;
            padding: 10px;
        }
        .container {
            max-width: 1600px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #43cea2 0%, #185a9d 100%);
            color: white;
            padding: 20px;
            text-align: center;
        }
        .header h1 { font-size: 1.8em; margin-bottom: 5px; }
        .nav {
            background: #2c3e50;
            padding: 10px;
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }
        .nav a {
            color: white;
            text-decoration: none;
            padding: 10px 15px;
            border-radius: 5px;
            transition: all 0.3s;
            font-size: 0.9em;
        }
        .nav a:hover { background: #34495e; }
        .nav a.active { background: #3498db; }
        .content { padding: 15px; }
        
        .dbc-upload {
            background: #f8f9fa;
            padding: 20px;
            border-radius: 10px;
            margin-bottom: 15px;
        }
        .upload-area {
            border: 3px dashed #43cea2;
            border-radius: 10px;
            padding: 30px 20px;
            text-align: center;
            cursor: pointer;
            transition: all 0.3s;
        }
        .upload-area:hover { background: #f0f9ff; border-color: #185a9d; }
        .upload-area input { display: none; }
        
        .signal-selector {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 10px;
            margin-bottom: 15px;
        }
        .signal-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            gap: 10px;
            margin-top: 15px;
        }
        .signal-item {
            background: white;
            padding: 12px;
            border-radius: 8px;
            border: 2px solid #ddd;
            cursor: pointer;
            transition: all 0.3s;
        }
        .signal-item:hover { border-color: #43cea2; transform: translateY(-2px); }
        .signal-item.selected { border-color: #185a9d; background: #e3f2fd; }
        .signal-name { font-weight: 600; color: #333; margin-bottom: 5px; font-size: 0.9em; }
        .signal-info { font-size: 0.8em; color: #666; }
        
        .graph-container {
            background: white;
            padding: 15px;
            border-radius: 10px;
            margin-bottom: 15px;
            border: 2px solid #e0e0e0;
        }
        .graph-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
            padding-bottom: 8px;
            border-bottom: 2px solid #43cea2;
        }
        .graph-title {
            font-size: 1em;
            font-weight: 600;
            color: #333;
        }
        .graph-value {
            font-size: 1.2em;
            font-weight: 700;
            color: #185a9d;
            font-family: 'Courier New', monospace;
        }
        canvas {
            width: 100%;
            height: 250px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background: #fafafa;
        }
        
        .controls {
            display: flex;
            gap: 8px;
            margin-bottom: 15px;
            flex-wrap: wrap;
        }
        .btn {
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            font-size: 0.9em;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
        }
        .btn-primary { background: linear-gradient(135deg, #43cea2 0%, #185a9d 100%); color: white; }
        .btn-success { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); color: white; }
        .btn-danger { background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%); color: white; }
        .btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.3); }
        
        h2 {
            color: #333;
            margin: 20px 0 15px 0;
            padding-bottom: 8px;
            border-bottom: 3px solid #43cea2;
            font-size: 1.3em;
        }
        
        .status { padding: 12px; background: #fff3cd; border-radius: 5px; margin-bottom: 15px; font-size: 0.9em; }
        .status.success { background: #d4edda; color: #155724; }
        .status.error { background: #f8d7da; color: #721c24; }
        
        @media (max-width: 768px) {
            body { padding: 5px; }
            .header h1 { font-size: 1.5em; }
            .content { padding: 10px; }
            .signal-grid { grid-template-columns: 1fr; gap: 8px; }
            canvas { height: 200px; }
            h2 { font-size: 1.1em; }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>CAN Signal Graph</h1>
            <p>Real-time Signal Visualization (Offline Mode)</p>
        </div>
        
        <div class="nav">
            <a href="/">Monitor</a>
            <a href="/transmit">Transmit</a>
            <a href="/graph" class="active">Graph</a>
        </div>
        
        <div class="content">
            <div id="status" class="status" style="display:none;"></div>
            
            <h2>Upload DBC File</h2>
            <div class="dbc-upload">
                <div class="upload-area" onclick="document.getElementById('dbc-file').click()">
                    <input type="file" id="dbc-file" accept=".dbc" onchange="loadDBCFile(event)">
                    <p style="font-size: 1.1em; margin-bottom: 8px;">Click to upload DBC</p>
                    <p style="color: #666; font-size: 0.85em;" id="dbc-status">No file loaded</p>
                </div>
            </div>
            
            <div id="signal-section" style="display:none;">
                <h2>Select Signals (Max 6)</h2>
                <div class="controls">
                    <button class="btn btn-success" onclick="startGraphing()">Start</button>
                    <button class="btn btn-danger" onclick="stopGraphing()">Stop</button>
                    <button class="btn btn-primary" onclick="clearSelection()">Clear</button>
                </div>
                
                <div class="signal-selector">
                    <div id="signal-list" class="signal-grid"></div>
                </div>
                
                <div style="background: #e3f2fd; padding: 15px; border-radius: 8px; margin-top: 15px; border-left: 4px solid #185a9d;">
                    <p style="color: #333; font-size: 0.9em; margin: 0;">
                        <strong>ℹ️ Info:</strong> Click "Start" to open a new window with real-time graphs. 
                        Your selected signals will be saved automatically.
                    </p>
                </div>
            </div>
        </div>
    </div>
    
    <script>
        let ws;
        let dbcData = {};
        let selectedSignals = [];
        let charts = {};
        let graphing = false;
        let startTime = 0;
        const MAX_SIGNALS = 6;
        const MAX_DATA_POINTS = 60;
        const COLORS = [
            {line: '#FF6384', fill: 'rgba(255, 99, 132, 0.1)'},
            {line: '#36A2EB', fill: 'rgba(54, 162, 235, 0.1)'},
            {line: '#FFCE56', fill: 'rgba(255, 206, 86, 0.1)'},
            {line: '#4BC0C0', fill: 'rgba(75, 192, 192, 0.1)'},
            {line: '#9966FF', fill: 'rgba(153, 102, 255, 0.1)'},
            {line: '#FF9F40', fill: 'rgba(255, 159, 64, 0.1)'}
        ];
        
        // 커스텀 차트 클래스
        class SimpleChart {
            constructor(canvas, signal, colorIndex) {
                this.canvas = canvas;
                this.ctx = canvas.getContext('2d');
                this.signal = signal;
                this.data = [];
                this.labels = [];
                this.colors = COLORS[colorIndex % COLORS.length];
                this.currentValue = 0;
                
                // Canvas 크기 설정
                this.resizeCanvas();
                window.addEventListener('resize', () => this.resizeCanvas());
            }
            
            resizeCanvas() {
                const rect = this.canvas.getBoundingClientRect();
                this.canvas.width = rect.width * window.devicePixelRatio;
                this.canvas.height = rect.height * window.devicePixelRatio;
                this.ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
                this.width = rect.width;
                this.height = rect.height;
                this.draw();
            }
            
            addData(value, label) {
                this.data.push(value);
                this.labels.push(label);
                this.currentValue = value;
                
                if (this.data.length > MAX_DATA_POINTS) {
                    this.data.shift();
                    this.labels.shift();
                }
                
                this.draw();
            }
            
            draw() {
                if (this.data.length === 0) return;
                
                const ctx = this.ctx;
                const padding = 40;
                const graphWidth = this.width - padding * 2;
                const graphHeight = this.height - padding * 2;
                
                // 배경 클리어
                ctx.clearRect(0, 0, this.width, this.height);
                
                // 데이터 범위 계산
                const minValue = Math.min(...this.data);
                const maxValue = Math.max(...this.data);
                const range = maxValue - minValue || 1;
                
                // 축 그리기
                ctx.strokeStyle = '#ddd';
                ctx.lineWidth = 1;
                ctx.beginPath();
                ctx.moveTo(padding, padding);
                ctx.lineTo(padding, this.height - padding);
                ctx.lineTo(this.width - padding, this.height - padding);
                ctx.stroke();
                
                // Y축 라벨
                ctx.fillStyle = '#666';
                ctx.font = '11px Arial';
                ctx.textAlign = 'right';
                ctx.fillText(maxValue.toFixed(2), padding - 5, padding + 5);
                ctx.fillText(minValue.toFixed(2), padding - 5, this.height - padding);
                
                // X축 라벨 (시간 - 초 단위)
                ctx.textAlign = 'center';
                ctx.fillStyle = '#666';
                ctx.font = '10px Arial';
                if (this.labels.length > 0) {
                    // 첫 번째와 마지막 시간 표시
                    ctx.fillText(this.labels[0] + 's', padding, this.height - padding + 15);
                    if (this.labels.length > 1) {
                        const lastIdx = this.labels.length - 1;
                        ctx.fillText(this.labels[lastIdx] + 's', 
                            padding + (graphWidth / (MAX_DATA_POINTS - 1)) * lastIdx, 
                            this.height - padding + 15);
                    }
                }
                
                // X축 타이틀
                ctx.fillText('Time (sec)', this.width / 2, this.height - 5);
                
                // 그리드 라인
                ctx.strokeStyle = '#f0f0f0';
                ctx.lineWidth = 1;
                for (let i = 1; i < 5; i++) {
                    const y = padding + (graphHeight / 5) * i;
                    ctx.beginPath();
                    ctx.moveTo(padding, y);
                    ctx.lineTo(this.width - padding, y);
                    ctx.stroke();
                }
                
                if (this.data.length < 2) return;
                
                // 영역 채우기
                ctx.fillStyle = this.colors.fill;
                ctx.beginPath();
                ctx.moveTo(padding, this.height - padding);
                
                for (let i = 0; i < this.data.length; i++) {
                    const x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i;
                    const y = this.height - padding - ((this.data[i] - minValue) / range) * graphHeight;
                    if (i === 0) {
                        ctx.lineTo(x, y);
                    } else {
                        ctx.lineTo(x, y);
                    }
                }
                
                ctx.lineTo(padding + (graphWidth / (MAX_DATA_POINTS - 1)) * (this.data.length - 1), this.height - padding);
                ctx.closePath();
                ctx.fill();
                
                // 선 그리기
                ctx.strokeStyle = this.colors.line;
                ctx.lineWidth = 2;
                ctx.beginPath();
                
                for (let i = 0; i < this.data.length; i++) {
                    const x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i;
                    const y = this.height - padding - ((this.data[i] - minValue) / range) * graphHeight;
                    
                    if (i === 0) {
                        ctx.moveTo(x, y);
                    } else {
                        ctx.lineTo(x, y);
                    }
                }
                
                ctx.stroke();
                
                // 데이터 포인트
                ctx.fillStyle = this.colors.line;
                for (let i = 0; i < this.data.length; i++) {
                    const x = padding + (graphWidth / (MAX_DATA_POINTS - 1)) * i;
                    const y = this.height - padding - ((this.data[i] - minValue) / range) * graphHeight;
                    
                    ctx.beginPath();
                    ctx.arc(x, y, 2, 0, Math.PI * 2);
                    ctx.fill();
                }
            }
        }
        
        function initWebSocket() {
            ws = new WebSocket('ws://' + window.location.hostname + ':81');
            
            ws.onopen = function() {
                console.log('WebSocket connected');
                showStatus('Connected', 'success');
            };
            
            ws.onclose = function() {
                console.log('WebSocket disconnected');
                showStatus('Disconnected - Reconnecting...', 'error');
                setTimeout(initWebSocket, 3000);
            };
            
            ws.onmessage = function(event) {
                try {
                    const data = JSON.parse(event.data);
                    
                    if (data.type === 'canBatch' && graphing) {
                        processCANData(data.messages);
                    }
                } catch(e) {
                    console.error('Error parsing WebSocket data:', e);
                }
            };
        }
        
        function loadDBCFile(event) {
            const file = event.target.files[0];
            if (!file) return;
            
            const reader = new FileReader();
            reader.onload = function(e) {
                const content = e.target.result;
                parseDBCContent(content);
                document.getElementById('dbc-status').textContent = file.name;
                
                // DBC 파일 저장
                saveDBCToLocalStorage(content, file.name);
            };
            reader.readAsText(file);
        }
        
        // DBC 파일을 localStorage에 저장
        function saveDBCToLocalStorage(content, filename) {
            try {
                localStorage.setItem('dbc_content', content);
                localStorage.setItem('dbc_filename', filename);
                console.log('DBC saved to localStorage:', filename);
            } catch(e) {
                console.error('Failed to save DBC to localStorage:', e);
            }
        }
        
        // localStorage에서 DBC 파일 복원
        function loadDBCFromLocalStorage() {
            try {
                const content = localStorage.getItem('dbc_content');
                const filename = localStorage.getItem('dbc_filename');
                
                if (content && filename) {
                    parseDBCContent(content);
                    document.getElementById('dbc-status').textContent = filename + ' (restored)';
                    showStatus('DBC file restored: ' + filename, 'success');
                    console.log('DBC restored from localStorage:', filename);
                    return true;
                }
            } catch(e) {
                console.error('Failed to load DBC from localStorage:', e);
            }
            return false;
        }
        
        function parseDBCContent(content) {
            dbcData = {messages: {}};
            const lines = content.split('\n');
            let currentMessage = null;
            
            for (let line of lines) {
                line = line.trim();
                
                if (line.startsWith('BO_ ')) {
                    const match = line.match(/BO_\s+(\d+)\s+(\w+)\s*:/);
                    if (match) {
                        const id = parseInt(match[1]);
                        const name = match[2];
                        currentMessage = {id: id, name: name, signals: []};
                        dbcData.messages[id] = currentMessage;
                    }
                }
                else if (line.startsWith('SG_ ') && currentMessage) {
                    const match = line.match(/SG_\s+(\w+)\s*:\s*(\d+)\|(\d+)@([01])([+-])\s*\(([^,]+),([^)]+)\)\s*\[([^\]]+)\]\s*"([^"]*)"/);
                    if (match) {
                        const signal = {
                            name: match[1],
                            startBit: parseInt(match[2]),
                            bitLength: parseInt(match[3]),
                            byteOrder: match[4] === '0' ? 'motorola' : 'intel',
                            signed: match[5] === '-',
                            factor: parseFloat(match[6]),
                            offset: parseFloat(match[7]),
                            unit: match[9],
                            messageId: currentMessage.id,
                            messageName: currentMessage.name
                        };
                        currentMessage.signals.push(signal);
                    }
                }
            }
            
            displaySignals();
            showStatus('DBC loaded: ' + Object.keys(dbcData.messages).length + ' messages', 'success');
        }
        
        function displaySignals() {
            const signalList = document.getElementById('signal-list');
            signalList.innerHTML = '';
            
            for (let msgId in dbcData.messages) {
                const msg = dbcData.messages[msgId];
                msg.signals.forEach(signal => {
                    const item = document.createElement('div');
                    item.className = 'signal-item';
                    item.onclick = () => toggleSignal(signal, item);
                    item.innerHTML = 
                        '<div class="signal-name">' + signal.name + '</div>' +
                        '<div class="signal-info">' +
                        'ID: 0x' + signal.messageId.toString(16).toUpperCase() + ' | ' +
                        signal.bitLength + 'bit' +
                        (signal.unit ? ' | ' + signal.unit : '') +
                        '</div>';
                    signalList.appendChild(item);
                });
            }
            
            document.getElementById('signal-section').style.display = 'block';
            
            // 저장된 선택 복원
            setTimeout(() => loadSelectedSignals(), 100);
        }
        
        function toggleSignal(signal, element) {
            const index = selectedSignals.findIndex(s => 
                s.messageId === signal.messageId && s.name === signal.name);
            
            if (index >= 0) {
                selectedSignals.splice(index, 1);
                element.classList.remove('selected');
            } else {
                if (selectedSignals.length >= MAX_SIGNALS) {
                    showStatus('Max ' + MAX_SIGNALS + ' signals!', 'error');
                    return;
                }
                selectedSignals.push(signal);
                element.classList.add('selected');
            }
            
            // 선택한 신호 저장
            saveSelectedSignals();
        }
        
        function clearSelection() {
            selectedSignals = [];
            document.querySelectorAll('.signal-item').forEach(item => {
                item.classList.remove('selected');
            });
            
            // 저장된 선택 삭제
            saveSelectedSignals();
        }
        
        // 선택한 신호를 localStorage에 저장
        function saveSelectedSignals() {
            try {
                localStorage.setItem('selected_signals', JSON.stringify(selectedSignals));
                console.log('Saved', selectedSignals.length, 'signals');
            } catch(e) {
                console.error('Failed to save selected signals:', e);
            }
        }
        
        // localStorage에서 선택한 신호 복원
        function loadSelectedSignals() {
            try {
                const saved = localStorage.getItem('selected_signals');
                if (saved) {
                    const signals = JSON.parse(saved);
                    
                    // 신호 목록이 표시된 후에 선택 상태 복원
                    signals.forEach(savedSignal => {
                        // DBC에 해당 신호가 있는지 확인
                        let found = false;
                        for (let msgId in dbcData.messages) {
                            const msg = dbcData.messages[msgId];
                            const signal = msg.signals.find(s => 
                                s.messageId === savedSignal.messageId && s.name === savedSignal.name);
                            
                            if (signal) {
                                selectedSignals.push(signal);
                                found = true;
                                break;
                            }
                        }
                    });
                    
                    // UI 업데이트
                    document.querySelectorAll('.signal-item').forEach(item => {
                        const signalName = item.querySelector('.signal-name').textContent;
                        const signalInfo = item.querySelector('.signal-info').textContent;
                        const idMatch = signalInfo.match(/ID: 0x([0-9A-F]+)/);
                        
                        if (idMatch) {
                            const msgId = parseInt(idMatch[1], 16);
                            const isSelected = selectedSignals.some(s => 
                                s.messageId === msgId && s.name === signalName);
                            
                            if (isSelected) {
                                item.classList.add('selected');
                            }
                        }
                    });
                    
                    if (selectedSignals.length > 0) {
                        showStatus('Restored ' + selectedSignals.length + ' selected signals', 'success');
                    }
                }
            } catch(e) {
                console.error('Failed to load selected signals:', e);
            }
        }
        
        function startGraphing() {
            if (selectedSignals.length === 0) {
                showStatus('Select at least one signal!', 'error');
                return;
            }
            
            // 선택한 신호 저장 (새 창에서 사용)
            saveSelectedSignals();
            
            // 새 창 열기
            const width = 1200;
            const height = 800;
            const left = (screen.width - width) / 2;
            const top = (screen.height - height) / 2;
            
            window.open(
                '/graph-view',
                'CAN_Graph_Viewer',
                'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',resizable=yes,scrollbars=yes'
            );
            
            showStatus('Graph viewer opened in new window', 'success');
        }
        
        function stopGraphing() {
            showStatus('Use the stop button in the graph viewer window', 'error');
        }
        
        function createGraphs() {
            // 이 함수는 새 창에서 사용됨
        }
        
        function processCANData(messages) {
            // 이 함수는 새 창에서 사용됨
        }
        
        function decodeSignal(signal, hexData) {
            const bytes = [];
            
            if (typeof hexData === 'string') {
                const cleanHex = hexData.replace(/\s/g, '').toUpperCase();
                
                for (let i = 0; i < cleanHex.length && i < 16; i += 2) {
                    bytes.push(parseInt(cleanHex.substring(i, i + 2), 16));
                }
            }
            
            while (bytes.length < 8) {
                bytes.push(0);
            }
            
            let rawValue = 0;
            
            if (signal.byteOrder === 'intel') {
                for (let i = 0; i < signal.bitLength; i++) {
                    const bitPos = signal.startBit + i;
                    const byteIdx = Math.floor(bitPos / 8);
                    const bitIdx = bitPos % 8;
                    
                    if (byteIdx < bytes.length) {
                        const bit = (bytes[byteIdx] >> bitIdx) & 1;
                        rawValue |= (bit << i);
                    }
                }
            } else {
                for (let i = 0; i < signal.bitLength; i++) {
                    const bitPos = signal.startBit - i;
                    const byteIdx = Math.floor(bitPos / 8);
                    const bitIdx = 7 - (bitPos % 8);
                    
                    if (byteIdx < bytes.length && byteIdx >= 0) {
                        const bit = (bytes[byteIdx] >> bitIdx) & 1;
                        rawValue |= (bit << (signal.bitLength - 1 - i));
                    }
                }
            }
            
            if (signal.signed && (rawValue & (1 << (signal.bitLength - 1)))) {
                rawValue -= (1 << signal.bitLength);
            }
            
            const physicalValue = rawValue * signal.factor + signal.offset;
            
            return physicalValue;
        }
        
        function showStatus(message, type) {
            const status = document.getElementById('status');
            status.textContent = message;
            status.className = 'status ' + type;
            status.style.display = 'block';
            setTimeout(() => { status.style.display = 'none'; }, 5000);
        }
        
        const uploadArea = document.querySelector('.upload-area');
        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            uploadArea.addEventListener(eventName, e => {
                e.preventDefault();
                e.stopPropagation();
            });
        });
        
        ['dragenter', 'dragover'].forEach(eventName => {
            uploadArea.addEventListener(eventName, () => {
                uploadArea.style.borderColor = '#185a9d';
                uploadArea.style.background = '#f0f9ff';
            });
        });
        
        ['dragleave', 'drop'].forEach(eventName => {
            uploadArea.addEventListener(eventName, () => {
                uploadArea.style.borderColor = '#43cea2';
                uploadArea.style.background = '';
            });
        });
        
        uploadArea.addEventListener('drop', function(e) {
            const file = e.dataTransfer.files[0];
            if (file && file.name.endsWith('.dbc')) {
                const reader = new FileReader();
                reader.onload = function(ev) {
                    parseDBCContent(ev.target.result);
                    document.getElementById('dbc-status').textContent = file.name;
                    
                    // DBC 파일 저장
                    saveDBCToLocalStorage(ev.target.result, file.name);
                };
                reader.readAsText(file);
            }
        });
        
        // 페이지 로드 시 localStorage에서 DBC 복원
        window.addEventListener('load', function() {
            loadDBCFromLocalStorage();
        });
        
        initWebSocket();
    </script>
</body>
</html>
)rawliteral";

#endif