import json
import os
import html

def generate_html(source_code, trace_data, output_filename="trace.html"):
    
    # Pre-process source code into lines for easier highlighting
    code_lines = source_code.splitlines()
    formatted_code = ""
    for i, line in enumerate(code_lines):
        # ID format: line-1, line-2, etc.
        safe_line = html.escape(line)
        formatted_code += f'<div id="code-line-{i+1}" class="code-line"><span class="line-num">{i+1}</span><span class="code-content">{safe_line}</span></div>'

    json_trace = json.dumps(trace_data)

    html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PyViz Tutor</title>
    <style>
        :root {{
            --bg-dark: #1e1e1e;
            --bg-panel: #252526;
            --border: #3e3e42;
            --accent: #007acc;
            --text: #d4d4d4;
            --highlight: #ffff0033;
            --var-name: #9cdcfe;
            --var-type: #4ec9b0;
            --var-val: #ce9178;
        }}
        body {{
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: var(--bg-dark);
            color: var(--text);
            margin: 0;
            height: 100vh;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }}
        header {{
            background: #333333;
            padding: 10px 20px;
            font-weight: bold;
            border-bottom: 1px solid var(--border);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }}
        .main-container {{
            display: flex;
            flex: 1;
            overflow: hidden;
        }}
        /* Code Panel */
        .code-panel {{
            flex: 2;
            border-right: 1px solid var(--border);
            overflow-y: auto;
            background: var(--bg-dark);
            font-family: 'Consolas', 'Monaco', monospace;
            padding-top: 10px;
        }}
        .code-line {{
            display: flex;
            padding: 2px 0;
            min-height: 1.2em;
        }}
        .code-line.active {{
            background-color: var(--highlight);
            border-left: 3px solid var(--accent);
        }}
        .line-num {{
            width: 40px;
            text-align: right;
            padding-right: 15px;
            color: #858585;
            user-select: none;
        }}
        .code-content {{
            white-space: pre;
        }}

        /* Info Panel */
        .info-panel {{
            flex: 1;
            display: flex;
            flex-direction: column;
            background: var(--bg-panel);
        }}
        .section-title {{
            background: #333;
            padding: 5px 10px;
            font-size: 0.85rem;
            text-transform: uppercase;
            letter-spacing: 1px;
            border-top: 1px solid var(--border);
            border-bottom: 1px solid var(--border);
        }}
        
        /* Variables Table */
        .vars-container {{
            flex: 1;
            overflow-y: auto;
            padding: 10px;
        }}
        table {{
            width: 100%;
            border-collapse: collapse;
            font-size: 0.9rem;
        }}
        th, td {{
            text-align: left;
            padding: 5px;
            border-bottom: 1px solid #333;
        }}
        .v-name {{ color: var(--var-name); }}
        .v-type {{ color: var(--var-type); font-size: 0.8rem; }}
        .v-val {{ color: var(--var-val); }}

        /* Output Console */
        .console-container {{
            height: 30%;
            background: #111;
            color: #ddd;
            font-family: monospace;
            padding: 10px;
            overflow-y: auto;
            white-space: pre-wrap;
            border-top: 1px solid var(--border);
        }}

        /* Controls */
        .controls {{
            padding: 15px;
            background: #333333;
            border-top: 1px solid var(--border);
            display: flex;
            gap: 10px;
            align-items: center;
        }}
        button {{
            background: var(--accent);
            border: none;
            color: white;
            padding: 5px 15px;
            border-radius: 3px;
            cursor: pointer;
            font-weight: bold;
        }}
        button:disabled {{
            background: #555;
            cursor: not-allowed;
        }}
        input[type=range] {{
            flex: 1;
        }}
        .step-counter {{
            min-width: 80px;
            text-align: center;
            font-family: monospace;
        }}
    </style>
</head>
<body>

<header>
    <div>PyViz Tutor</div>
</header>

<div class="main-container">
    <div class="code-panel" id="code-container">
        {formatted_code}
    </div>
    
    <div class="info-panel">
        <div class="section-title">Variables</div>
        <div class="vars-container">
            <table id="vars-table">
                <thead><tr><th>Name</th><th>Type</th><th>Value</th></tr></thead>
                <tbody id="vars-body"></tbody>
            </table>
        </div>

        <div class="section-title">Standard Output</div>
        <div class="console-container" id="console-output"></div>
    </div>
</div>

<div class="controls">
    <button id="btn-prev">Prev</button>
    <button id="btn-play">Play/Pause</button>
    <button id="btn-next">Next</button>
    <input type="range" id="timeline" min="0" max="0" value="0">
    <div class="step-counter" id="step-display">0 / 0</div>
</div>

<script>
    // Embedded Trace Data
    const traceData = {json_trace};
    
    // State
    let currentStep = 0;
    let isPlaying = false;
    let playInterval = null;
    const totalSteps = traceData.length - 1;

    // DOM Elements
    const codeContainer = document.getElementById('code-container');
    const varsBody = document.getElementById('vars-body');
    const consoleOutput = document.getElementById('console-output');
    const stepDisplay = document.getElementById('step-display');
    const timeline = document.getElementById('timeline');
    
    // Setup Timeline
    timeline.max = totalSteps;

    function updateUI() {{
        const stepData = traceData[currentStep];
        
        // 1. Highlight Code Line
        // Remove old active class
        document.querySelectorAll('.code-line.active').forEach(el => el.classList.remove('active'));
        
        if (stepData.line > 0) {{
            const lineEl = document.getElementById(`code-line-${{stepData.line}}`);
            if (lineEl) {{
                lineEl.classList.add('active');
                lineEl.scrollIntoView({{behavior: "smooth", block: "center"}});
            }}
        }}

        // 2. Update Variables
        varsBody.innerHTML = '';
        const locals = stepData.locals || {{}};
        for (const [name, details] of Object.entries(locals)) {{
            const row = document.createElement('tr');
            row.innerHTML = `
                <td class="v-name">${{name}}</td>
                <td class="v-type">${{details.type}}</td>
                <td class="v-val">${{details.value}}</td>
            `;
            varsBody.appendChild(row);
        }}

        // 3. Update Console
        consoleOutput.textContent = stepData.stdout;
        consoleOutput.scrollTop = consoleOutput.scrollHeight;

        // 4. Update Controls
        timeline.value = currentStep;
        stepDisplay.textContent = `${{currentStep + 1}} / ${{totalSteps + 1}}`;
        document.getElementById('btn-prev').disabled = currentStep <= 0;
        document.getElementById('btn-next').disabled = currentStep >= totalSteps;
    }}

    // Event Listeners
    timeline.addEventListener('input', (e) => {{
        currentStep = parseInt(e.target.value);
        updateUI();
    }});

    document.getElementById('btn-prev').addEventListener('click', () => {{
        if (currentStep > 0) {{ currentStep--; updateUI(); }}
    }});

    document.getElementById('btn-next').addEventListener('click', () => {{
        if (currentStep < totalSteps) {{ currentStep++; updateUI(); }}
    }});

    document.getElementById('btn-play').addEventListener('click', () => {{
        if (isPlaying) {{
            clearInterval(playInterval);
            isPlaying = false;
        }} else {{
            isPlaying = true;
            playInterval = setInterval(() => {{
                if (currentStep < totalSteps) {{
                    currentStep++;
                    updateUI();
                }} else {{
                    clearInterval(playInterval);
                    isPlaying = false;
                }}
            }}, 500); // 500ms per step
        }}
    }});

    // Initial Render
    updateUI();

</script>
</body>
</html>
    """
    
    with open(output_filename, "w", encoding="utf-8") as f:
        f.write(html_content)
    
    return output_filename