#ifndef GRAPH_H
#define GRAPH_H

const char graph_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html><html lang="ko"><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>그래프 - CANFD Logger</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',sans-serif;background:#0d1117;color:#c9d1d9;font-size:13px}
nav{background:#161b22;border-bottom:1px solid #30363d;padding:8px 16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
nav a{color:#58a6ff;text-decoration:none;padding:4px 10px;border-radius:6px;font-size:12px}
nav a:hover{background:#21262d} nav a.active{background:#1f6feb;color:#fff}
.title{color:#e6edf3;font-weight:700;font-size:15px}
.container{padding:12px}
.card{background:#161b22;border:1px solid #30363d;border-radius:8px;padding:14px;margin-bottom:10px}
.card h3{font-size:12px;color:#8b949e;margin-bottom:12px;text-transform:uppercase}
input[type=text],select{background:#21262d;border:1px solid #30363d;color:#c9d1d9;padding:5px 8px;border-radius:6px;font-size:12px}
.btn{padding:5px 12px;border:none;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600}
.btn-blue{background:#1f6feb;color:#fff} .btn-green{background:#238636;color:#fff}
.btn-gray{background:#21262d;color:#c9d1d9;border:1px solid #30363d}
.row{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;margin-bottom:8px}
.field{margin-bottom:4px} label{display:block;font-size:11px;color:#8b949e;margin-bottom:3px}
canvas{width:100%;height:300px;background:#0d1117;border:1px solid #30363d;border-radius:6px}
.sig-list{display:flex;gap:8px;flex-wrap:wrap;margin:8px 0}
.sig-chip{background:#21262d;border:1px solid #30363d;border-radius:12px;padding:3px 10px;font-size:11px;display:flex;align-items:center;gap:6px}
.sig-color{width:10px;height:10px;border-radius:50%;display:inline-block}
</style>
</head><body>
<nav>
  <span class="title">⚡ CANFD Logger</span>
  <a href="/">대시보드</a><a href="/transmit">송신</a>
  <a href="/graph" class="active">그래프</a><a href="/settings">설정</a>
</nav>
<div class="container">
  <div class="card">
    <h3>실시간 신호 그래프 (CAN FD)</h3>
    <div class="row">
      <div class="field"><label>CAN ID (hex)</label><input type="text" id="sigId" value="0x123" style="width:90px"></div>
      <div class="field"><label>시작 비트</label><input type="text" id="sigStart" value="0" style="width:60px"></div>
      <div class="field"><label>비트 길이</label><input type="text" id="sigLen" value="8" style="width:60px"></div>
      <div class="field"><label>배율</label><input type="text" id="sigScale" value="1" style="width:60px"></div>
      <div class="field"><label>이름</label><input type="text" id="sigName" value="Signal 1" style="width:90px"></div>
      <div class="field" style="margin-bottom:4px"><button class="btn btn-blue" onclick="addSignal()">+ 추가</button></div>
    </div>
    <div class="sig-list" id="sigList"></div>
    <canvas id="chart"></canvas>
    <div style="margin-top:8px;display:flex;gap:8px">
      <button class="btn btn-gray" onclick="clearSignals()">신호 초기화</button>
      <span style="font-size:11px;color:#8b949e;margin-top:8px">최대 8개 신호, 500 포인트 표시</span>
    </div>
  </div>
</div>
<script>
const COLORS=['#58a6ff','#3fb950','#d29922','#f85149','#a371f7','#ffa657','#79c0ff','#56d364'];
const DLC_LEN=[0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64];
let signals=[], ws, reconnTimer;
const MAX_PTS=500;

function addSignal(){
  if(signals.length>=8) return alert('최대 8개');
  let id=parseInt(document.getElementById('sigId').value,16);
  let s=parseInt(document.getElementById('sigStart').value);
  let l=parseInt(document.getElementById('sigLen').value);
  let sc=parseFloat(document.getElementById('sigScale').value)||1;
  let nm=document.getElementById('sigName').value||('Signal '+(signals.length+1));
  let color=COLORS[signals.length%COLORS.length];
  signals.push({id,startBit:s,bitLen:l,scale:sc,name:nm,color,pts:[],lastVal:null});
  renderChips(); draw();
}

function clearSignals(){ signals=[]; renderChips(); draw(); }

function renderChips(){
  let html='';
  signals.forEach((s,i)=>{ html+=`<div class="sig-chip"><span class="sig-color" style="background:${s.color}"></span>${s.name} (0x${s.id.toString(16).toUpperCase()})<span onclick="removeSignal(${i})" style="cursor:pointer;color:#8b949e">✕</span></div>`; });
  document.getElementById('sigList').innerHTML=html;
}
function removeSignal(i){ signals.splice(i,1); renderChips(); draw(); }

function extractBits(data,start,len){
  let v=0;
  for(let i=0;i<len;i++){
    let bit=start+i; let b=Math.floor(bit/8); let bit2=7-(bit%8);
    if(b<data.length&&(data[b]&(1<<bit2))) v|=(1<<(len-1-i));
  }
  return v;
}

function processMsg(m){
  signals.forEach(s=>{
    if(s.id===m.id){
      let v=extractBits(m.data||[],s.startBit,s.bitLen)*s.scale;
      let ts=Date.now();
      s.pts.push({x:ts,y:v}); s.lastVal=v;
      if(s.pts.length>MAX_PTS) s.pts.shift();
    }
  });
  draw();
}

const cv=document.getElementById('chart');
const ctx=cv.getContext('2d');
function draw(){
  let W=cv.offsetWidth, H=cv.offsetHeight;
  cv.width=W; cv.height=H;
  ctx.fillStyle='#0d1117'; ctx.fillRect(0,0,W,H);
  if(signals.length===0){
    ctx.fillStyle='#8b949e'; ctx.font='13px sans-serif'; ctx.textAlign='center';
    ctx.fillText('신호를 추가하면 실시간 그래프가 표시됩니다.',W/2,H/2); return;
  }
  let allPts=signals.flatMap(s=>s.pts);
  if(allPts.length<2) return;
  let now=Date.now();
  let minX=now-30000, maxX=now;
  let allY=allPts.map(p=>p.y);
  let minY=Math.min(...allY), maxY=Math.max(...allY);
  if(minY===maxY){minY-=1;maxY+=1;}
  let pad=30;
  function toX(x){return pad+(x-minX)/(maxX-minX)*(W-pad*2);}
  function toY(y){return H-pad-(y-minY)/(maxY-minY)*(H-pad*2);}
  // 격자
  ctx.strokeStyle='#21262d'; ctx.lineWidth=1;
  for(let i=0;i<=4;i++){let y=pad+i*(H-pad*2)/4;ctx.beginPath();ctx.moveTo(pad,y);ctx.lineTo(W-pad,y);ctx.stroke();}
  // 축 레이블
  ctx.fillStyle='#8b949e'; ctx.font='10px sans-serif'; ctx.textAlign='right';
  for(let i=0;i<=4;i++){let y=pad+i*(H-pad*2)/4;let v=(maxY-(maxY-minY)*i/4).toFixed(2);ctx.fillText(v,pad-4,y+4);}
  // 신호
  signals.forEach(s=>{
    if(s.pts.length<2) return;
    ctx.strokeStyle=s.color; ctx.lineWidth=1.5; ctx.beginPath();
    s.pts.forEach((p,i)=>{let x=toX(p.x),y=toY(p.y);i===0?ctx.moveTo(x,y):ctx.lineTo(x,y);});
    ctx.stroke();
    // 현재값
    let last=s.pts[s.pts.length-1];
    ctx.fillStyle=s.color; ctx.font='11px monospace'; ctx.textAlign='left';
    ctx.fillText(s.name+': '+(s.lastVal!==null?s.lastVal.toFixed(2):'--'), toX(last.x)+4, toY(last.y)-4);
  });
}

function connect(){
  ws=new WebSocket('ws://'+location.hostname+':81/');
  ws.onopen=()=>clearTimeout(reconnTimer);
  ws.onclose=()=>reconnTimer=setTimeout(connect,3000);
  ws.onmessage=e=>{
    try{
      let d=JSON.parse(e.data);
      if(d.type==='update'&&d.messages) d.messages.forEach(m=>processMsg(m));
    }catch(ex){}
  };
}
connect();
setInterval(draw,100);
</script>
</body></html>
)rawliteral";
#endif