#!/usr/bin/env python3
"""本地起 HTTP 服务播放教案幻灯片（离线可用）。
用法：python3 docs/teaching/serve.py [端口，默认 8000]
浏览器打开 http://localhost:<端口>/docs/teaching/slides/index.html

只绑定 127.0.0.1（不对局域网暴露）——仓库根目录含 .env（wiki 登录凭据），
必须避免整个仓库被同一 Wi-Fi/局域网上的其他设备访问到。
"""
import html
import http.server
import mimetypes
import os
import socketserver
import sys
import urllib.parse

# 幻灯片里点击的源码链接（.c/.cpp/.py/.md 等）需要以纯文本显示，而不是被当成
# 未知二进制触发下载——默认 mimetypes 对 .cpp/.c 等会猜成 text/x-csrc 之类，
# 浏览器不会内联渲染，这里统一覆盖成 text/plain。
for ext in (".c", ".cpp", ".h", ".hpp", ".cc", ".qrc", ".py", ".md"):
    mimetypes.add_type("text/plain", ext)

MARKED_JS_URL = "/docs/teaching/vendor/marked.min.js"

MARKDOWN_TEMPLATE = """<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>{title}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
  body {{ font-family: -apple-system, "Helvetica Neue", "PingFang SC", "Microsoft YaHei",
         sans-serif; max-width: 900px; margin: 40px auto; padding: 0 20px;
         line-height: 1.6; color: #24292f; }}
  pre {{ background: #f6f8fa; padding: 12px 16px; overflow-x: auto; border-radius: 6px; }}
  code {{ background: #f6f8fa; padding: 1px 5px; border-radius: 3px; font-size: 0.9em; }}
  pre code {{ background: none; padding: 0; }}
  table {{ border-collapse: collapse; }}
  th, td {{ border: 1px solid #d0d7de; padding: 6px 12px; }}
  blockquote {{ color: #57606a; border-left: 4px solid #d0d7de; margin-left: 0; padding-left: 16px; }}
  a {{ color: #0969da; }}
  .raw-link {{ float: right; font-size: 0.85em; }}
</style>
</head>
<body>
<div class="raw-link"><a href="{path_qs}?raw=1">查看原始 Markdown</a></div>
<div id="content">渲染中...</div>
<script type="text/plain" id="raw-md">{raw}</script>
<script src="{marked_url}"></script>
<script>
  document.getElementById('content').innerHTML =
      marked.parse(document.getElementById('raw-md').textContent);
</script>
</body>
</html>
"""

repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.chdir(repo_root)

try:
    port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
except ValueError:
    print(f"用法：python3 {sys.argv[0]} [端口号]")
    sys.exit(1)


class ReuseAddrTCPServer(socketserver.TCPServer):
    allow_reuse_address = True


class Utf8RequestHandler(http.server.SimpleHTTPRequestHandler):
    # http.server 的 guess_type() 从不附带 charset——中文源码/文档被当成
    # 无编码信息的 text/plain 返回，浏览器直接导航时会猜错编码变成乱码。
    def guess_type(self, path):
        ctype = super().guess_type(path)
        if ctype.startswith("text/") and "charset=" not in ctype:
            ctype += "; charset=utf-8"
        return ctype

    def end_headers(self):
        # 讲义/幻灯片会在课前反复改——不加 no-store 的话，浏览器可能对同一 URL
        # 展示改之前缓存的（曾经乱码的）旧响应，看起来像是"修复没生效"。
        self.send_header("Cache-Control", "no-store")
        super().end_headers()

    def do_GET(self):
        parsed = urllib.parse.urlsplit(self.path)
        # .md 直接导航时渲染成 HTML；?raw=1 保留原始文本入口（复制/diff 用）
        if parsed.path.endswith(".md") and "raw=1" not in parsed.query:
            self.serve_markdown(parsed.path)
        else:
            super().do_GET()

    def serve_markdown(self, url_path):
        fs_path = self.translate_path(url_path)
        try:
            with open(fs_path, "r", encoding="utf-8") as f:
                raw = f.read()
        except OSError:
            self.send_error(404, "File not found")
            return
        page = MARKDOWN_TEMPLATE.format(
            title=html.escape(os.path.basename(fs_path)),
            path_qs=html.escape(url_path),
            raw=raw.replace("</script", "<\\/script"),
            marked_url=MARKED_JS_URL,
        )
        body = page.encode("utf-8")
        self.send_response(200)
        self.send_header("Content-type", "text/html; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)


with ReuseAddrTCPServer(("127.0.0.1", port), Utf8RequestHandler) as httpd:
    print(f"Serving {repo_root} at http://localhost:{port}/docs/teaching/slides/index.html")
    httpd.serve_forever()
