from fastapi import FastAPI, Request, BackgroundTasks
from fastapi.staticfiles import StaticFiles
from MockValveController import ValveController
from ClassCAN import ValveControllerCAN
from starlette.concurrency import run_in_threadpool
import asyncio
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from fastapi.templating import (
    Jinja2Templates,
)
import logging
from crankshaft_functions import *
import asyncio
import random
app = FastAPI()

logging.getLogger("uvicorn.access").setLevel(logging.WARNING)


valve = ValveControllerCAN()
templates = Jinja2Templates(directory="static")

@app.get("/", response_class=HTMLResponse)
def login_form(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

@app.post("/connect")
async def connect():
    try:
        status = valve.connect()
        # if status :
        #     valve.start_listener()
        return {"status": "connected" if status else "failed"}
    except Exception as e:
        import traceback; traceback.print_exc()
        return {"status": "error", "detail": str(e)}

@app.post("/pid")
async def set_setpoint(req: Request):
    data = await req.json()
    kp = data["kp"]
    ki = data["ki"]
    tol = data["tol"]

    valve.send_PID_coeffs(kp, ki, tol)
    return {"status": "ok"}


@app.post("/setpoint")
async def set_setpoint(req: Request):
    data = await req.json()
    sp = data["setpoint"]
    valve.send_setpoint(sp)
    return {"setpoint": sp}

@app.post("/speed")
async def set_speed(req: Request):
    data = await req.json()
    sp = data["speed_sp"]
    valve.send_speed(sp)
    return {"speed_sp": sp}


@app.get("/position")
async def get_position():
    # Return last 10s of data
    data = list(valve.data_window)

    return {
        "time": [d[0] for d in data],
        "position": [d[1] for d in data],
        "setpoint": [d[2] for d in data],
        "speed": [d[3] for d in data],
        "speed_sp": [d[4] for d in data],
        "Kp_term": [d[5] for d in data],
        "Ki_term": [d[6] for d in data],
        "Kd_term": [d[7] for d in data],
    }


@app.post("/start_recording")
async def start_recording():
    if valve.running:
        return {"status": "already recording"}
    valve._record_task = asyncio.create_task(valve.record_loop())
    return {"status": "started"}


@app.post("/stop_recording")
async def stop_recording():
    await valve.stop_recording()
    return {"status": "stopped", "file": str(valve.csv_path.resolve())}


@app.post("/prbs")
async def run_prbs():
    """
    Generate and send a true PRBS (Pseudo-Random Binary Sequence).
    Hardcoded params:
    - amplitude: 0 / 100
    - dwell time: 0.1 s
    - order n = 7 → sequence length = 127 steps
    """
    async def prbs_task():
        amplitude = 100
        dwell_time = 0.3   # system τ ~0.1s
        n = 6             # PRBS order
        seq_length = 2**n - 1

        # --- LFSR initialization ---
        state = [1] * n  # initial seed (nonzero)
        prbs_bits = []

        for _ in range(seq_length):
            # Example taps for n=7 (maximal length LFSR: taps [7,6])
            new_bit = state[-1] ^ state[-2]  # XOR feedback
            prbs_bits.append(state[-1])
            state = [new_bit] + state[:-1]

        # Convert bits → setpoints {0, amplitude}
        prbs_sequence = [amplitude if bit == 1 else 0 for bit in prbs_bits]

        for sp in prbs_sequence:
            valve.send_setpoint(sp)
            # valve.send_speed(sp)

            await asyncio.sleep(dwell_time)

        # return to 0 at the end
        valve.send_setpoint(0)

    asyncio.create_task(prbs_task())
    return {"status": "started PRBS"}


app.mount("/static", StaticFiles(directory="static"), name="static")

