#!/usr/bin/env python3
"""
Backend for Glass curtain wall adhesive test system (OpenCV MJPEG + CSV tail + WebSocket)
"""
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import StreamingResponse, FileResponse
from fastapi.staticfiles import StaticFiles
import asyncio, threading, time, glob, os, json
import cv2

# ---------- CONFIG ----------
CSV_DIR = "/home/pi/acc_data"
CSV_GLOB = "ai0_accel_*.csv"
POLL_INTERVAL = 0.05
QUEUE_MAXSIZE = 20000
CAMERA_INDEX = 0   # /dev/video0
# ----------------------------

app = FastAPI(title="Glass curtain wall adhesive test system")
app.mount("/", StaticFiles(directory="static", html=True), name="static")

data_queue: asyncio.Queue = asyncio.Queue(maxsize=QUEUE_MAXSIZE)
clients = set()

def find_latest_csv():
    try:
        files = glob.glob(os.path.join(CSV_DIR, CSV_GLOB))
        if not files:
            return None
        files.sort(key=os.path.getmtime, reverse=True)
        return files[0]
    except Exception as e:
        print("find_latest_csv error:", e)
        return None

def tail_file(path, loop, queue: asyncio.Queue, stop_event: threading.Event):
    try:
        f = open(path, "r", encoding="utf-8", errors="ignore")
    except Exception as e:
        print("tail_file open error:", e)
        return
    # seek to end (assume file already exists and will be appended)
    f.seek(0, os.SEEK_END)
    print(f"[tail_file] Tailing: {path}")
    while not stop_event.is_set():
        line = f.readline()
        if not line:
            time.sleep(POLL_INTERVAL)
            continue
        line = line.strip()
        if not line:
            continue
        parts = [p.strip() for p in line.split(",") if p.strip()!='']
        if len(parts) < 4:
            continue
        try:
            t = float(parts[0])
            volt = float(parts[1])
            accel_g = float(parts[2])
            accel_mps2 = float(parts[3])
            sample = {"t": round(t,6), "volt": round(volt,6), "accel_g": round(accel_g,6), "accel_mps2": round(accel_mps2,6)}
            loop.call_soon_threadsafe(asyncio.create_task, queue.put(sample))
        except Exception as e:
            print("parse error:", e, "line:", line)
            continue
    f.close()
    print(f"[tail_file] Stopped tailing: {path}")

async def csv_watcher_task():
    stop_event = None
    tail_thread = None
    current = None
    loop = asyncio.get_event_loop()
    while True:
        latest = find_latest_csv()
        if latest != current:
            if tail_thread and stop_event:
                stop_event.set()
                tail_thread.join(timeout=1.0)
            current = latest
            if current:
                stop_event = threading.Event()
                tail_thread = threading.Thread(target=tail_file, args=(current, loop, data_queue, stop_event), daemon=True)
                tail_thread.start()
                print("[csv_watcher] Started tail for", current)
            else:
                print("[csv_watcher] No CSV found; waiting...")
        await asyncio.sleep(1.0)

@app.on_event("startup")
async def startup_event():
    asyncio.create_task(csv_watcher_task())
    print("Backend started. Watching CSV in:", CSV_DIR)

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    clients.add(websocket)
    print("Client connected:", websocket.client)
    try:
        while True:
            sample = await data_queue.get()
            await websocket.send_text(json.dumps({"type":"sample","data": sample}, ensure_ascii=False))
    except WebSocketDisconnect:
        print("Client disconnected", websocket.client)
    except Exception as e:
        print("WS error:", e)
    finally:
        clients.discard(websocket)

# --- OpenCV MJPEG generator ---
def gen_frames():
    cap = cv2.VideoCapture(CAMERA_INDEX)
    if not cap.isOpened():
        print(f"[gen_frames] Cannot open camera index {CAMERA_INDEX}")
    while True:
        success, frame = cap.read()
        if not success:
            time.sleep(0.05)
            continue
        # resize for bandwidth if needed:
        # frame = cv2.resize(frame, (1280,720))
        ret, buffer = cv2.imencode('.jpg', frame)
        if not ret:
            continue
        frame_bytes = buffer.tobytes()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
    cap.release()

@app.get("/video_feed")
def video_feed():
    return StreamingResponse(gen_frames(), media_type='multipart/x-mixed-replace; boundary=frame')
