import React, { useState, useEffect, useCallback, useRef } from 'react';
import { ToolType, SimulationMap, AgvState, AgvMotionState, LogEntry, AgvError, AgvSignal, SystemFlag0, SystemFlag1, CSharpMapData, MapNode, MapEdge, FloorMark, MagnetLine, NodeType } from './types';
import { INITIAL_MAP, MAX_SPEED } from './constants';
import EditorToolbar from './components/EditorToolbar';
import SimulationCanvas from './components/SimulationCanvas';
import SerialConsole from './components/SerialConsole';
import AgvManualControls from './components/AgvManualControls';
import BmsPanel from './components/BmsPanel';
import AcsControls from './components/AcsControls';
import AgvStatusPanel from './components/AgvStatusPanel';
import AgvAutoRunControls from './components/AgvAutoRunControls';
import SystemLogPanel from './components/SystemLogPanel';
import { SerialPortHandler } from './services/serialService';

// --- ACS CRC16 Table Generation (Matches C# Logic) ---
const ACS_CRC_TABLE = new Uint16Array(256);
(function initAcsCrcTable() {
    const polynomial = 0x7979;
    for (let i = 0; i < 256; i++) {
        let value = 0;
        let temp = i;
        for (let j = 0; j < 8; j++) {
            if (((value ^ temp) & 0x0001) !== 0) {
                value = (value >> 1) ^ polynomial;
            } else {
                value >>= 1;
            }
            temp >>= 1;
        }
        ACS_CRC_TABLE[i] = value & 0xFFFF;
    }
})();

const calculateAcsCrc16 = (data: number[] | Uint8Array): number => {
    let crc = 0xFFFF;
    for (let i = 0; i < data.length; i++) {
        const index = (crc ^ data[i]) & 0xFF;
        crc = (crc >> 8) ^ ACS_CRC_TABLE[index];
    }
    return crc & 0xFFFF;
};

const App: React.FC = () => {
    // --- State ---
    const [activeTool, setActiveTool] = useState<ToolType>(ToolType.SELECT);

    // Map Data
    const [mapData, setMapData] = useState<SimulationMap>(INITIAL_MAP);

    // AGV Logic State
    const [agvState, setAgvState] = useState<AgvState>({
        x: 100,
        y: 100,
        rotation: 0,
        targetRotation: null,
        speed: MAX_SPEED,
        liftHeight: 0,
        motionState: AgvMotionState.IDLE,
        runConfig: {
            direction: 'FWD',
            branch: 'STRAIGHT',
            speedLevel: 'M'
        },
        error: null,
        sensorStatus: '1', // Default per C# DevAGV.cs
        detectedRfid: null,
        sensorLineFront: false,
        sensorLineRear: false,
        sensorMark: false,

        // Lift & Magnet
        magnetOn: false,
        liftStatus: 'IDLE',
        lidarEnabled: false,
        isCharging: false, // Initial state

        // Protocol Flags Initial State
        system0: 0,
        system1: (1 << SystemFlag1.agv_stop), // Default STOP state
        errorFlags: 0,
        signalFlags: 0, // Will be updated by useEffect

        batteryLevel: 95,
        maxCapacity: 100,
        batteryTemps: [25.0, 26.5],
        cellVoltages: Array(8).fill(3.2),
    });

    // Ref to hold latest state for Serial Callbacks (prevents stale closures)
    const agvStateRef = useRef<AgvState>(agvState);
    useEffect(() => {
        agvStateRef.current = agvState;
    }, [agvState]);

    // Logs
    const [logs, setLogs] = useState<LogEntry[]>([]);

    // Auto-clear logic: Keep last 500 lines
    const addLog = useCallback((source: 'AGV' | 'BMS' | 'ACS' | 'SYSTEM', type: 'INFO' | 'RX' | 'TX' | 'ERROR', message: string) => {
        setLogs(prev => [...prev.slice(-499), {
            id: crypto.randomUUID(),
            timestamp: new Date().toLocaleTimeString([], { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" }),
            source,
            type,
            message
        }]);
    }, []);

    const clearLogs = useCallback((source: 'AGV' | 'BMS' | 'ACS' | 'SYSTEM') => {
        setLogs(prev => prev.filter(l => l.source !== source));
    }, []);

    // Serial Handlers
    const agvSerialRef = useRef<SerialPortHandler | null>(null);
    const bmsSerialRef = useRef<SerialPortHandler | null>(null);
    const acsSerialRef = useRef<SerialPortHandler | null>(null);

    const [agvConnected, setAgvConnected] = useState(false);
    const [bmsConnected, setBmsConnected] = useState(false);
    const [acsConnected, setAcsConnected] = useState(false);

    const [agvPortInfo, setAgvPortInfo] = useState<string | null>(null);
    const [bmsPortInfo, setBmsPortInfo] = useState<string | null>(null);
    const [acsPortInfo, setAcsPortInfo] = useState<string | null>(null);

    // Serial Configuration
    const [agvBaudRate, setAgvBaudRate] = useState(57600);
    const [bmsBaudRate, setBmsBaudRate] = useState(9600);
    const [acsBaudRate, setAcsBaudRate] = useState(9600);

    // --- Helpers to Set/Clear Bits ---
    const setAgvBit = (field: 'system0' | 'system1' | 'errorFlags' | 'signalFlags', bit: number, value: boolean) => {
        setAgvState(prev => {
            let current = prev[field];
            if (value) current |= (1 << bit);
            else current &= ~(1 << bit);

            if (current === prev[field]) return prev;
            return { ...prev, [field]: current };
        });
    };

    // --- AGV Protocol Parser ---
    const agvBufferRef = useRef<number[]>([]);

    // Format: [STX]ACK[CMD]**[ETX]
    const sendAck = useCallback((commandToAck: string) => {
        if (agvSerialRef.current) {
            const encoder = new TextEncoder();
            const cmdBytes = encoder.encode("ACK");
            const valBytes = encoder.encode(commandToAck);

            const payload = new Uint8Array(1 + cmdBytes.length + valBytes.length + 2 + 1);
            payload[0] = 0x02; // STX
            payload.set(cmdBytes, 1);
            payload.set(valBytes, 1 + cmdBytes.length);
            payload[payload.length - 3] = 0x2A; // '*'
            payload[payload.length - 2] = 0x2A; // '*'
            payload[payload.length - 1] = 0x03; // ETX

            agvSerialRef.current.send(payload);
            addLog('AGV', 'TX', `ACK ${commandToAck}`);
        }
    }, [addLog]);

    // Tag Transmission Logic
    const sendTag = useCallback((tagId: string) => {
        if (agvSerialRef.current) {
            const paddedId = tagId.padStart(6, '0').slice(-6);
            const encoder = new TextEncoder();
            const cmdBytes = encoder.encode("TAG");
            const valBytes = encoder.encode(paddedId);

            const payload = new Uint8Array(1 + cmdBytes.length + valBytes.length + 2 + 1);
            let offset = 0;
            payload[offset++] = 0x02; // STX
            payload.set(cmdBytes, offset); offset += cmdBytes.length;
            payload.set(valBytes, offset); offset += valBytes.length;
            payload[offset++] = 0x2A; // '*'
            payload[offset++] = 0x2A; // '*'
            payload[offset++] = 0x03; // ETX

            agvSerialRef.current.send(payload);
            addLog('AGV', 'TX', `TAG ${paddedId}`);
        }
    }, [addLog]);

    const handleAgvData = useCallback((data: Uint8Array) => {
        for (let i = 0; i < data.length; i++) {
            agvBufferRef.current.push(data[i]);
        }

        const buf = agvBufferRef.current;
        while (true) {
            const stxIdx = buf.indexOf(0x02);
            if (stxIdx === -1) {
                agvBufferRef.current = [];
                return;
            }

            if (stxIdx > 0) {
                buf.splice(0, stxIdx);
            }

            const etxIdx = buf.indexOf(0x03);
            if (etxIdx === -1) {
                return;
            }

            const frameBytes = buf.splice(0, etxIdx + 1);
            processAgvFrame(frameBytes);
        }
    }, []);

    const processAgvFrame = (frame: number[]) => {
        if (frame.length < 7) return;

        const textDecoder = new TextDecoder();
        const cmd = textDecoder.decode(new Uint8Array(frame.slice(1, 4)));
        const dataStr = textDecoder.decode(new Uint8Array(frame.slice(4, frame.length - 3))).trim();

        addLog('AGV', 'RX', `${cmd} ${dataStr}`);

        switch (cmd) {
            case 'CRN': // Run Command (Auto Run)
                if (dataStr.length >= 4) {
                    const dirChar = dataStr[0].toUpperCase();
                    const bunki = dataStr[1];
                    const speed = dataStr[2];
                    const sensor = dataStr[3];

                    const isBack = dirChar === 'B' || dirChar === 'R';
                    const direction = isBack ? 'BWD' : 'FWD';

                    setAgvState(s => {
                        const newBranch = (bunki === 'L' ? 'LEFT' : bunki === 'R' ? 'RIGHT' : bunki === 'S' ? 'STRAIGHT' : s.runConfig.branch);
                        const newSpeed = (['L', 'M', 'H'].includes(speed) ? speed : s.runConfig.speedLevel) as any;

                        // Treat '0' as placeholder for sensor if we want to preserve CBR logic, 
                        // BUT allow manual toggle via UI to override later.
                        // Here, we assume CRN sending '0' shouldn't turn off sensor if user set it via CBR.
                        const newSensor = (sensor === '0') ? s.sensorStatus : sensor;
                        const newLidar = newSensor !== '0';

                        return {
                            ...s,
                            sensorStatus: newSensor,
                            lidarEnabled: newLidar,
                            motionState: AgvMotionState.RUNNING,
                            runConfig: {
                                direction: direction,
                                branch: newBranch,
                                speedLevel: newSpeed
                            }
                        };
                    });
                    addLog('AGV', 'INFO', `Run: Dir=${direction} Branch=${bunki} Speed=${speed} Sensor=${sensor}`);
                } else {
                    setAgvState(s => ({ ...s, motionState: AgvMotionState.RUNNING }));
                }
                // Update Flags
                setAgvBit('system1', SystemFlag1.agv_stop, false);
                setAgvBit('system1', SystemFlag1.agv_run, true);
                break;

            case 'CST': // Stop Command
                if (dataStr.length > 0 && dataStr.charAt(0).toUpperCase() === 'M') {
                    // Mark Stop Command (Drive to next mark)
                    setAgvState(s => ({
                        ...s,
                        motionState: AgvMotionState.MARK_STOPPING,
                        runConfig: {
                            ...s.runConfig,
                            speedLevel: 'L' // Force low speed for precision
                        }
                    }));
                    // In Mark Stopping mode, AGV is still technically running until it hits the mark
                    setAgvBit('system1', SystemFlag1.agv_stop, false);
                    setAgvBit('system1', SystemFlag1.agv_run, true);
                    addLog('AGV', 'INFO', 'CMD: Mark Stop Mode Started');
                } else {
                    // Normal Stop
                    setAgvBit('system1', SystemFlag1.agv_run, false);
                    setAgvBit('system1', SystemFlag1.agv_stop, true);
                    setAgvState(s => ({ ...s, motionState: AgvMotionState.IDLE }));
                }
                break;

            case 'CBR': // Branch
                if (dataStr.length >= 3) {
                    const dirChar = dataStr[0].toUpperCase();
                    const bunki = dataStr[1];
                    const speed = dataStr[2];
                    // Default to '1' (ON) if not provided, assuming CBR implies a valid path
                    const sensor = dataStr.length > 3 ? dataStr[3] : '1';

                    const isBack = dirChar === 'B' || dirChar === 'R';
                    const direction = isBack ? 'BWD' : 'FWD';

                    setAgvState(s => {
                        // Logic: Update if specific char provided, else keep existing
                        const newBranch = (bunki === 'L' ? 'LEFT' : bunki === 'R' ? 'RIGHT' : bunki === 'S' ? 'STRAIGHT' : s.runConfig.branch);
                        const newSpeed = (['L', 'M', 'H'].includes(speed) ? speed : s.runConfig.speedLevel) as any;

                        const newLidar = sensor !== '0';

                        return {
                            ...s,
                            sensorStatus: sensor,
                            lidarEnabled: newLidar,
                            runConfig: {
                                direction: direction,
                                branch: newBranch,
                                speedLevel: newSpeed
                            }
                        };
                    });
                    addLog('AGV', 'INFO', `Branch Set: Dir=${direction} Branch=${bunki} Speed=${speed} Sensor=${sensor}`);
                }
                break;

            case 'CRT': // Manual Control (Remote / Jog)
                const op = dataStr.charAt(0).toUpperCase();
                let manualMotion = AgvMotionState.IDLE;
                let targetDir: 'FWD' | 'BWD' = 'FWD';

                if (op === 'F') {
                    manualMotion = AgvMotionState.FORWARD;
                    targetDir = 'FWD';
                }
                else if (op === 'B') {
                    manualMotion = AgvMotionState.BACKWARD;
                    targetDir = 'BWD';
                }
                else if (op === 'L') manualMotion = AgvMotionState.TURN_LEFT;
                else if (op === 'R') manualMotion = AgvMotionState.TURN_RIGHT;
                else if (op === 'S') manualMotion = AgvMotionState.IDLE;

                setAgvState(s => ({
                    ...s,
                    motionState: manualMotion,
                    runConfig: {
                        ...s.runConfig,
                        direction: targetDir
                    }
                }));

                const isMoving = manualMotion !== AgvMotionState.IDLE;
                setAgvBit('system1', SystemFlag1.agv_stop, !isMoving);
                setAgvBit('system1', SystemFlag1.agv_run, isMoving);

                sendAck(cmd);
                break;

            case 'SFR': // Reset Errors
                setAgvState(s => ({ ...s, error: null, errorFlags: 0 }));
                setAgvBit('system1', SystemFlag1.agv_run, false);
                setAgvBit('system1', SystemFlag1.agv_stop, true);
                break;

            case 'CBT': // Charging Command
                if (dataStr.length > 4) {
                    const chargeCmd = dataStr[4];
                    const isCharging = chargeCmd === 'I';
                    setAgvBit('system1', SystemFlag1.Battery_charging, isCharging);
                }
                break;

            case 'CLF': // Lift / Magnet Control
                const subCmd = dataStr.trim().toUpperCase();
                if (subCmd === 'UP0000') {
                    setAgvState(s => ({ ...s, liftStatus: 'UP' }));
                } else if (subCmd === 'DN0000') {
                    setAgvState(s => ({ ...s, liftStatus: 'DOWN' }));
                } else if (subCmd === 'STP000') {
                    setAgvState(s => ({ ...s, liftStatus: 'IDLE' }));
                } else if (subCmd === 'ON0000') {
                    setAgvState(s => ({ ...s, magnetOn: true }));
                } else if (subCmd === 'OFF000') {
                    setAgvState(s => ({ ...s, magnetOn: false }));
                }
                sendAck(cmd);
                break;

            case 'CTL': // Turn Left 180
                handleTurn180('LEFT');
                sendAck(cmd);
                break;

            case 'CTR': // Turn Right 180
                handleTurn180('RIGHT');
                sendAck(cmd);
                break;

            case 'SSH': case 'SSM': case 'SSL': case 'SSS': case 'SHS': case 'SLS': case 'SPK': case 'SPM':
            case 'SPL': case 'SPS': case 'SIK': case 'SIM': case 'SIH': case 'SIL': case 'SIS': case 'SDK':
            case 'SDW': case 'SDL': case 'SDS': case 'SRS': case 'SCK': case 'SSK': case 'STT': case 'SSI':
            case 'SMD': case 'SSC': case 'SPN': case 'SPH': case 'SCH': case 'SDH': case 'SDM': case 'SLB':
            case 'SGS':
                sendAck(cmd);
                break;

            case 'ACK':
                break;
        }
    };

    const handleTurn180 = (direction: 'LEFT' | 'RIGHT') => {
        setAgvState(s => ({
            ...s,
            motionState: direction === 'LEFT' ? AgvMotionState.TURN_LEFT_180 : AgvMotionState.TURN_RIGHT_180,
            targetRotation: direction === 'LEFT' ? s.rotation - 180 : s.rotation + 180
        }));
    };

    const autoSendData = useCallback(() => {
        if (!agvConnected || !agvSerialRef.current || !agvSerialRef.current.connected) return;
        // Use ref to get latest state without recreating function
        const s = agvStateRef.current;
        const barr = new Uint8Array(34);
        barr[0] = 0x02; barr[1] = 0x53; barr[2] = 0x54; barr[3] = 0x53;

        const encodeHex = (val: number, len: number) => {
            const hex = val.toString(16).toUpperCase().padStart(len, '0');
            return new TextEncoder().encode(hex);
        };

        const voltBytes = new TextEncoder().encode("255");
        barr.set(voltBytes, 4);
        barr.set(encodeHex(s.system0, 4), 7);
        barr.set(encodeHex(s.system1, 4), 11);
        barr.set(encodeHex(s.errorFlags, 4), 15);

        const spdChar = s.runConfig.speedLevel.charCodeAt(0);
        barr[19] = spdChar;

        const branchChar = s.runConfig.branch === 'LEFT' ? 'L'.charCodeAt(0) : s.runConfig.branch === 'RIGHT' ? 'R'.charCodeAt(0) : 'S'.charCodeAt(0);
        barr[20] = branchChar;

        const dirChar = s.runConfig.direction === 'FWD' ? 'F'.charCodeAt(0) : 'B'.charCodeAt(0);
        barr[21] = dirChar;

        barr[22] = s.sensorStatus.charCodeAt(0);
        barr.set(encodeHex(s.signalFlags, 2), 23);

        barr[31] = '*'.charCodeAt(0); barr[32] = '*'.charCodeAt(0); barr[33] = 0x03;

        agvSerialRef.current.send(barr);

        // Log Transmission (Throttled log could be nice, but simple for now)
        addLog('AGV', 'TX', `STS (AutoSend)`);
    }, [agvConnected, addLog]);

    useEffect(() => {
        if (agvConnected) {
            const timer = setInterval(() => {
                autoSendData();
            }, 500);
            return () => clearInterval(timer);
        }
    }, [autoSendData, agvConnected]);

    // --- Main Logic: Sensors, Signals & Lift Physics ---
    useEffect(() => {
        // 1. Calculate Signals
        let sig = 0;
        if (agvState.sensorMark) sig |= (1 << AgvSignal.mark_sensor_1);

        // Front Line Sensor & Gate Out
        if (!agvState.sensorLineFront) {
            // Gate Out: Active when line is NOT detected
            sig |= (1 << AgvSignal.front_gate_out);
        }

        // Rear Sensor Gate Out
        if (!agvState.sensorLineRear) {
            // Gate Out: Active when line is NOT detected
            sig |= (1 << AgvSignal.rear_sensor_out);
        }

        // Lift Sensors
        if (agvState.liftHeight <= 0) sig |= (1 << AgvSignal.lift_down_sensor);
        if (agvState.liftHeight >= 100) sig |= (1 << AgvSignal.lift_up_sensor);

        // Magnet Relay
        if (agvState.magnetOn) sig |= (1 << AgvSignal.magnet_relay);

        // 2. Calculate Error Flags
        let err = agvState.errorFlags;
        if (agvState.error === 'LINE_OUT') err |= (1 << AgvError.line_out_error);
        else err &= ~(1 << AgvError.line_out_error);

        // 3. Calculate System Flags
        let sys1 = agvState.system1;
        const isAutoRun = agvState.motionState === AgvMotionState.RUNNING || agvState.motionState === AgvMotionState.MARK_STOPPING;
        const isManualRun = [AgvMotionState.FORWARD, AgvMotionState.BACKWARD, AgvMotionState.TURN_LEFT, AgvMotionState.TURN_RIGHT, AgvMotionState.TURN_LEFT_180, AgvMotionState.TURN_RIGHT_180].includes(agvState.motionState);

        if (isAutoRun) {
            sys1 |= (1 << SystemFlag1.agv_run);
            sys1 &= ~(1 << SystemFlag1.agv_stop);
        } else if (isManualRun) {
            // Manual move: Ensure stop bit is OFF. Run bit can be ON to indicate active state (matches CRT logic)
            sys1 |= (1 << SystemFlag1.agv_run);
            sys1 &= ~(1 << SystemFlag1.agv_stop);
        } else if (agvState.motionState === AgvMotionState.IDLE) {
            sys1 &= ~(1 << SystemFlag1.agv_run);
            sys1 |= (1 << SystemFlag1.agv_stop);
        }

        if (sig !== agvState.signalFlags || err !== agvState.errorFlags || sys1 !== agvState.system1) {
            setAgvState(s => ({ ...s, signalFlags: sig, errorFlags: err, system1: sys1 }));
        }
    }, [
        agvState.sensorMark, agvState.sensorLineFront, agvState.sensorLineRear, agvState.error,
        agvState.motionState, agvState.signalFlags, agvState.errorFlags, agvState.system1,
        agvState.detectedRfid, agvState.liftHeight, agvState.magnetOn
    ]);

    // --- Lift Animation Physics Loop ---
    useEffect(() => {
        let animId: number;
        const animateLift = () => {
            setAgvState(prev => {
                if (prev.liftStatus === 'IDLE') return prev;

                let newHeight = prev.liftHeight;
                const step = 0.6; // Speed of lift (~3s for 0-100)

                if (prev.liftStatus === 'UP') {
                    newHeight += step;
                    if (newHeight >= 100) {
                        newHeight = 100;
                        return { ...prev, liftHeight: 100, liftStatus: 'IDLE' };
                    }
                } else if (prev.liftStatus === 'DOWN') {
                    newHeight -= step;
                    if (newHeight <= 0) {
                        newHeight = 0;
                        return { ...prev, liftHeight: 0, liftStatus: 'IDLE' };
                    }
                }

                return { ...prev, liftHeight: newHeight };
            });
            animId = requestAnimationFrame(animateLift);
        };

        animId = requestAnimationFrame(animateLift);
        return () => cancelAnimationFrame(animId);
    }, []); // Logic relies on functional state updates, so empty dependency is safe-ish, but let's be cleaner.
    // Actually, requestAnimationFrame runs continuously. We just need to ensure we don't leak.


    // --- BMS Protocol Logic ---
    const bmsBufferRef = useRef<number[]>([]);

    const handleBmsData = useCallback((data: Uint8Array) => {
        // Log raw data for debugging visibility
        const hex = Array.from(data).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
        addLog('BMS', 'RX', `RAW: ${hex}`);

        for (let i = 0; i < data.length; i++) {
            bmsBufferRef.current.push(data[i]);
        }

        const buf = bmsBufferRef.current;
        // Protocol: 7 bytes total. Start=0xDD, End=0x77.
        while (buf.length >= 7) {
            const startIdx = buf.indexOf(0xDD);
            if (startIdx === -1) {
                bmsBufferRef.current = [];
                return;
            }
            if (startIdx > 0) {
                buf.splice(0, startIdx);
            }

            // Wait for end byte (not fully safe if 77 is inside payload, but standard for this packet size)
            // Actually, fixed length is 34 for Status, 23 for Cells. 
            // But we receive potentially fragmented.
            // Let's rely on fixed length per known command if we had one.
            // Here we just look for 0x77.
            const endIdx = buf.indexOf(0x77);
            if (endIdx === -1) return;

            const packet = buf.splice(0, endIdx + 1);
            processBmsPacket(packet);
        }
    }, [addLog]);

    const processBmsPacket = (packet: number[]) => {
        const cmd = packet[2];
        // const hexStr = packet.map(b => b.toString(16).padStart(2,'0').toUpperCase()).join(' ');

        if (cmd === 0x03) {
            sendBmsStatus();
        } else if (cmd === 0x04) {
            sendBmsCellVoltage();
        }
    };

    const sendBmsStatus = () => {
        if (!bmsSerialRef.current) return;
        const s = agvStateRef.current; // Use Ref for latest state
        const resp = new Uint8Array(34);
        resp[0] = 0xDD; resp[1] = 0x03; resp[2] = 0x00; resp[3] = 0x1b;   //arrary 3value 0->1b

        const totalV = Math.floor(s.cellVoltages.reduce((a, b) => a + b, 0) * 100);
        resp[4] = (totalV >> 8) & 0xFF; resp[5] = totalV & 0xFF;

        // 6, 7: Current (mA)
        // Power (W) = (mA / 1000) * V  => mA = (W / V) * 1000.
        // Discharging: ~50W, Charging: ~-450W.
        const isCharging = s.isCharging; // Use manual state
        const powerW = isCharging ? -450 : 50;

        let currentMA = 0;
        if (totalV > 0) {
            const realV = totalV / 100; // Convert 0.01V to Volts
            currentMA = Math.floor((powerW / totalV) * 10);
        }

        // Write Int16 (Little Endian or Big Endian? BMS usually Big Endian for these usually? 
        // Code above uses Big Endian for Voltage (resp[4] = high, resp[5] = low).
        // Standard BMS protocols often BE. I will follow the pattern of Voltage.
        // Handle negative numbers via Two's Complement for manual byte splitting if needed, 
        // but bitwise ops on standard JS numbers (treated as 32bit int) work fine for & 0xFF.
        resp[6] = (currentMA >> 8) & 0xFF;
        resp[7] = currentMA & 0xFF;

        const remainAh = Math.floor(s.maxCapacity * (s.batteryLevel / 100));
        resp[8] = (remainAh >> 8) & 0xFF; resp[9] = remainAh & 0xFF;
        const maxAh = s.maxCapacity;
        resp[10] = (maxAh >> 8) & 0xFF; resp[11] = maxAh & 0xFF;

        resp[23] = Math.floor(s.batteryLevel);

        const t1 = Math.floor((s.batteryTemps[0] * 10) + 2731);
        const t2 = Math.floor((s.batteryTemps[1] * 10) + 2731);

        resp[27] = (t1 >> 8) & 0xFF; resp[28] = t1 & 0xFF;
        resp[29] = (t2 >> 8) & 0xFF; resp[30] = t2 & 0xFF;

        resp[31] = 0x00; //*magic numbee(skip)
        resp[32] = 0x00; //*magic numbee(skip)
        resp[33] = 0x77;

        bmsSerialRef.current.send(resp);
        addLog('BMS', 'TX', 'DD ... 77 (Status)');
    };

    const sendBmsCellVoltage = () => {
        if (!bmsSerialRef.current) return;
        const s = agvStateRef.current; // Use Ref for latest state
        const resp = new Uint8Array(23);
        resp[0] = 0xDD; resp[1] = 0x04; resp[2] = 0x00; resp[3] = 0x10;
        let checksumSum = resp[3];

        for (let i = 0; i < 8; i++) {
            const v = Math.floor((s.cellVoltages[i] || 0) * 1000);
            const idx = 4 + (i * 2);
            const high = (v >> 8) & 0xFF;
            const low = v & 0xFF;
            resp[idx] = high; resp[idx + 1] = low;
            checksumSum += high + low;
        }

        const checksum = (0xFFFF - checksumSum + 1) & 0xFFFF;
        resp[20] = (checksum >> 8) & 0xFF; resp[21] = checksum & 0xFF;
        resp[22] = 0x77;

        bmsSerialRef.current.send(resp);
        addLog('BMS', 'TX', 'DD ... 77 (Cells)');
    };

    // --- ACS Protocol Logic (Implementing C# Logic) ---
    const acsBufferRef = useRef<number[]>([]);
    const acsParseErrorsRef = useRef(0);

    const sendAcsPacket = useCallback((cmd: number, payload: number[] = []) => {
        if (!acsSerialRef.current) {
            addLog('ACS', 'ERROR', 'Not Connected');
            return;
        }

        const id = 1; // Default AGV ID
        const len = 1 + 1 + payload.length; // ID + CMD + DATA
        const buffer = [0x02, len, id, cmd, ...payload];

        // Calculate CRC over [LEN, ID, CMD, DATA] (indices 1 to end)
        const crcData = buffer.slice(1);
        const crc = calculateAcsCrc16(crcData);

        buffer.push(crc & 0xFF); // Low Byte
        buffer.push((crc >> 8) & 0xFF); // High Byte
        buffer.push(0x03); // ETX

        acsSerialRef.current.send(new Uint8Array(buffer));

        const hex = buffer.map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
        addLog('ACS', 'TX', `CMD:${cmd.toString(16).toUpperCase()} [${hex}]`);
    }, [addLog]);

    const handleAcsData = useCallback((data: Uint8Array) => {
        for (let i = 0; i < data.length; i++) {
            acsBufferRef.current.push(data[i]);
        }

        const buf = acsBufferRef.current;
        while (buf.length > 0) {
            // 1. Find STX
            const stxIndex = buf.indexOf(0x02); // STX
            if (stxIndex === -1) {
                buf.length = 0; // Clear junk
                return;
            }
            // Remove garbage before STX
            if (stxIndex > 0) {
                buf.splice(0, stxIndex);
            }

            // 2. Check minimal size (STX + Len + ID + Cmd + CRC + ETX) = 7 bytes if data is 0?
            // Per C#: Length = 1(ID)+1(Cmd)+DataLen.
            // Packet: STX(1) + Len(1) + Payload(Len) + CRC(2) + ETX(1) = 5 + Len.
            if (buf.length < 2) return; // Need Length byte

            const lengthByte = buf[1];
            const totalPacketSize = lengthByte + 5;

            if (buf.length < totalPacketSize) return; // Wait for more data

            // 3. Extract and Verify
            const packetData = buf.slice(0, totalPacketSize);

            // Verify ETX
            if (packetData[totalPacketSize - 1] !== 0x03) {
                addLog('ACS', 'ERROR', 'ETX Missing');
                buf.shift(); // Invalid packet, shift 1 and retry
                continue;
            }

            // Verify CRC
            // CRC covers: [Length, ID, Command, Data...] (Skip STX, Exclude CRC bytes)
            // Bytes to CRC: From index 1 to (1 + length) inclusive?
            // C# Logic: CalculateCRC16(packetData.Skip(1).Take(length + 1))
            // packetData indices: 0=STX, 1=Len, 2=ID, 3=Cmd...
            // We need indices 1 to (1 + length) inclusive.
            // Total size is length + 5.
            // CRC is at [total - 3, total - 2].

            const dataForCrc = packetData.slice(1, lengthByte + 2); // slice end is exclusive
            const calcCrc = calculateAcsCrc16(dataForCrc);

            const receivedCrc = (packetData[totalPacketSize - 2] << 8) | packetData[totalPacketSize - 3]; // Little Endian in C# BitConverter?
            // C# BitConverter.ToUInt16(rawData, rawData.Length - 3). 
            // rawData ends with [CRC_L, CRC_H, ETX] usually? 
            // Default Little Endian. So index len-3 is Low, len-2 is High.

            if (receivedCrc !== 0xFFFF && calcCrc !== receivedCrc) {
                const hex = packetData.map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
                addLog('ACS', 'ERROR', `CRC Fail: ${hex}`);
                acsParseErrorsRef.current++;
                if (acsParseErrorsRef.current > 3) buf.length = 0; // Reset on too many errors
                else buf.shift();
                continue;
            }

            acsParseErrorsRef.current = 0;

            // Parse Success
            const id = packetData[2];
            const cmd = packetData[3];
            const payload = packetData.slice(4, totalPacketSize - 3);

            const hexStr = packetData.map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' ');
            addLog('ACS', 'RX', `[ID:${id} CMD:${cmd}] ${hexStr}`);

            // Consume buffer
            buf.splice(0, totalPacketSize);
        }
    }, [addLog]);

    // --- Connect/Disconnect Handlers ---

    const connectAgv = useCallback(async () => {
        if (!agvSerialRef.current) {
            agvSerialRef.current = new SerialPortHandler(handleAgvData);
        }
        try {
            await agvSerialRef.current.connect(agvBaudRate);
            setAgvConnected(true);
            setAgvPortInfo(agvSerialRef.current.getPortInfo());
            addLog('AGV', 'INFO', `Connected at ${agvBaudRate} baud`);
        } catch (err: any) {
            if (err.message !== 'USER_CANCELLED') {
                console.error(err);
                addLog('AGV', 'ERROR', `Connection failed: ${err.message}`);
            }
        }
    }, [agvBaudRate, handleAgvData, addLog]);

    const disconnectAgv = useCallback(async () => {
        if (agvSerialRef.current) {
            await agvSerialRef.current.disconnect();
            setAgvConnected(false);
            setAgvPortInfo(null);
            addLog('AGV', 'INFO', 'Disconnected');
        }
    }, [addLog]);

    const connectBms = useCallback(async () => {
        if (!bmsSerialRef.current) {
            bmsSerialRef.current = new SerialPortHandler(handleBmsData);
        }
        try {
            await bmsSerialRef.current.connect(bmsBaudRate);
            setBmsConnected(true);
            setBmsPortInfo(bmsSerialRef.current.getPortInfo());
            addLog('BMS', 'INFO', `Connected at ${bmsBaudRate} baud`);
        } catch (err: any) {
            if (err.message !== 'USER_CANCELLED') {
                console.error(err);
                addLog('BMS', 'ERROR', `Connection failed: ${err.message}`);
            }
        }
    }, [bmsBaudRate, handleBmsData, addLog]);

    const disconnectBms = useCallback(async () => {
        if (bmsSerialRef.current) {
            await bmsSerialRef.current.disconnect();
            setBmsConnected(false);
            setBmsPortInfo(null);
            addLog('BMS', 'INFO', 'Disconnected');
        }
    }, [addLog]);

    const connectAcs = useCallback(async () => {
        if (!acsSerialRef.current) {
            acsSerialRef.current = new SerialPortHandler(handleAcsData);
        }
        try {
            await acsSerialRef.current.connect(acsBaudRate);
            setAcsConnected(true);
            setAcsPortInfo(acsSerialRef.current.getPortInfo());
            addLog('ACS', 'INFO', `Connected at ${acsBaudRate} baud`);
        } catch (err: any) {
            if (err.message !== 'USER_CANCELLED') {
                console.error(err);
                addLog('ACS', 'ERROR', `Connection failed: ${err.message}`);
            }
        }
    }, [acsBaudRate, handleAcsData, addLog]);

    const disconnectAcs = useCallback(async () => {
        if (acsSerialRef.current) {
            await acsSerialRef.current.disconnect();
            setAcsConnected(false);
            setAcsPortInfo(null);
            addLog('ACS', 'INFO', 'Disconnected');
        }
    }, [addLog]);

    // --- Map Loading Logic (Updated for New Structure) ---
    const loadMapFromCSharp = useCallback((csharpData: CSharpMapData) => {
        const newNodes: MapNode[] = [];
        const newEdges: MapEdge[] = [];
        const edgeSet = new Set<string>();

        // 1. Process Nodes
        if (csharpData.Nodes && Array.isArray(csharpData.Nodes)) {
            csharpData.Nodes.forEach((n) => {
                const [xStr, yStr] = n.Position.split(',').map(s => s.trim());
                const x = parseFloat(xStr) || 0;
                const y = parseFloat(yStr) || 0;

                // Map StationType to Internal NodeType
                let internalType = n.StationType;
                if (n.StationType === 2) internalType = NodeType.Charging;
                else if (n.StationType === 3) internalType = NodeType.UnLoader;

                newNodes.push({
                    id: n.Id,
                    x, y,
                    type: internalType,
                    name: n.Text || n.AliasName || "",
                    rfidId: n.RfidId,
                    connectedNodes: n.ConnectedNodes || [],
                    foreColor: n.NodeTextForeColor,
                    fontSize: n.NodeTextFontSize
                });

                if (n.ConnectedNodes && Array.isArray(n.ConnectedNodes)) {
                    n.ConnectedNodes.forEach((targetId) => {
                        const ids = [n.Id, targetId].sort();
                        const key = ids.join('-');
                        if (!edgeSet.has(key)) {
                            edgeSet.add(key);
                            newEdges.push({
                                id: `edge_${key}`,
                                from: ids[0],
                                to: ids[1]
                            });
                        }
                    });
                }
            });
        }

        // 2. Process Labels (Convert to Nodes Type=Label)
        if (csharpData.Labels && Array.isArray(csharpData.Labels)) {
            csharpData.Labels.forEach(l => {
                const [xStr, yStr] = l.Position.split(',').map(s => s.trim());
                newNodes.push({
                    id: l.Id,
                    x: parseFloat(xStr) || 0, y: parseFloat(yStr) || 0,
                    type: NodeType.Label,
                    name: "",
                    rfidId: "",
                    connectedNodes: [],
                    labelText: l.Text,
                    foreColor: l.ForeColor,
                    backColor: l.BackColor,
                    fontSize: l.FontSize
                });
            });
        }

        // 3. Process Images (Convert to Nodes Type=Image)
        if (csharpData.Images && Array.isArray(csharpData.Images)) {
            csharpData.Images.forEach(img => {
                const [xStr, yStr] = img.Position.split(',').map(s => s.trim());
                newNodes.push({
                    id: img.Id,
                    x: parseFloat(xStr) || 0, y: parseFloat(yStr) || 0,
                    type: NodeType.Image,
                    name: img.Name,
                    rfidId: "",
                    connectedNodes: [],
                    imageBase64: img.ImageBase64
                });
            });
        }

        // 4. Process Magnets
        const newMagnets: MagnetLine[] = [];
        if (csharpData.Magnets && Array.isArray(csharpData.Magnets)) {
            csharpData.Magnets.forEach(m => {
                const type = m.ControlPoint ? 'CURVE' : 'STRAIGHT';
                newMagnets.push({
                    id: m.Id,
                    type: type,
                    p1: { x: m.P1.X, y: m.P1.Y },
                    p2: { x: m.P2.X, y: m.P2.Y },
                    controlPoint: m.ControlPoint ? { x: m.ControlPoint.X, y: m.ControlPoint.Y } : undefined
                });
            });
        }

        // 5. Process Marks
        const newMarks: FloorMark[] = [];
        if (csharpData.Marks && Array.isArray(csharpData.Marks)) {
            csharpData.Marks.forEach(mk => {
                newMarks.push({
                    id: mk.Id,
                    x: mk.X,
                    y: mk.Y,
                    rotation: mk.Rotation
                });
            });
        }

        setMapData({
            nodes: newNodes,
            edges: newEdges,
            marks: newMarks,
            magnets: newMagnets
        });
        addLog('SYSTEM', 'INFO', `New Map loaded: ${newNodes.length} objects`);

    }, [addLog]);

    // --- Initialize Map from NewMap.json on Mount ---
    useEffect(() => {
        fetch('NewMap.json')
            .then(response => {
                if (!response.ok) throw new Error("Default map not found");
                return response.json();
            })
            .then((data: CSharpMapData) => {
                loadMapFromCSharp(data);
            })
            .catch(err => {
                console.log("Could not load default map:", err);
            });
    }, [loadMapFromCSharp]);

    // --- Map IO Logic ---
    const processMapFile = (file: File) => {
        const reader = new FileReader();
        reader.onload = (evt) => {
            try {
                const jsonStr = evt.target?.result as string;
                const csharpData = JSON.parse(jsonStr) as CSharpMapData;
                loadMapFromCSharp(csharpData);
            } catch (err) {
                console.error(err);
                alert('Failed to parse map file.');
            }
        };
        reader.readAsText(file);
    };

    const saveMap = () => {
        const outNodes: any[] = [];
        const outLabels: any[] = [];
        const outImages: any[] = [];

        mapData.nodes.forEach(n => {
            const pos = `${Math.round(n.x)}, ${Math.round(n.y)}`;

            if (n.type === NodeType.Label) {
                outLabels.push({
                    Id: n.id,
                    Type: 1,
                    Text: n.labelText || "Label",
                    Position: pos,
                    ForeColor: n.foreColor || "White",
                    BackColor: n.backColor || "Transparent",
                    FontFamily: "Arial",
                    FontSize: n.fontSize || 12,
                    FontStyle: 0,
                    Padding: 5
                });
            } else if (n.type === NodeType.Image) {
                outImages.push({
                    Id: n.id,
                    Type: 2,
                    Name: n.name || "Image",
                    Position: pos,
                    ImagePath: "",
                    ImageBase64: n.imageBase64 || "",
                    Scale: "1, 1",
                    Opacity: 1.0,
                    Rotation: 0.0
                });
            } else {
                // Standard Node
                // Map Internal NodeType back to StationType
                let stType = n.type;
                if (n.type === NodeType.Charging) stType = 2; // Cleaner
                else if (n.type === NodeType.UnLoader) stType = 3; // Unloader

                // Re-calculate connected nodes
                const connected = new Set<string>();
                mapData.edges.forEach(e => {
                    if (e.from === n.id) connected.add(e.to);
                    if (e.to === n.id) connected.add(e.from);
                });

                outNodes.push({
                    Id: n.id,
                    Text: n.name || "",
                    Position: pos,
                    Type: 0, // Always 0 for nodes
                    StationType: stType,
                    ConnectedNodes: Array.from(connected),
                    RfidId: n.rfidId || "",
                    NodeTextForeColor: n.foreColor,
                    NodeTextFontSize: n.fontSize || 7.0,
                    AliasName: "",
                    SpeedLimit: 0,
                    CanDocking: [1, 2, 3, 4, 5].includes(n.type),
                    DockDirection: n.type === NodeType.Charging || n.type === NodeType.ChargerStation ? 1 : 0,
                    CanTurnLeft: true,
                    CanTurnRight: true,
                    DisableCross: false,
                    IsActive: true
                });
            }
        });

        const outMagnets = mapData.magnets.map(m => ({
            Id: m.id,
            Type: 4,
            P1: { X: m.p1.x, Y: m.p1.y },
            P2: { X: m.p2.x, Y: m.p2.y },
            ControlPoint: m.controlPoint ? { X: m.controlPoint.x, Y: m.controlPoint.y } : null
        }));

        const outMarks = mapData.marks.map(mk => ({
            Id: mk.id,
            Type: 3,
            Position: `${Math.round(mk.x)}, ${Math.round(mk.y)}`,
            X: mk.x,
            Y: mk.y,
            Rotation: mk.rotation
        }));

        const exportData = {
            Nodes: outNodes,
            Labels: outLabels,
            Images: outImages,
            Magnets: outMagnets,
            Marks: outMarks,
            Settings: {
                BackgroundColorArgb: -14671840,
                ShowGrid: false
            },
            CreatedDate: new Date().toISOString(),
            Version: "1.3"
        };

        const json = JSON.stringify(exportData, null, 2);
        const blob = new Blob([json], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'NewMap.json';
        a.click();
    };

    const loadMap = () => {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = 'application/json';
        input.onchange = (e) => {
            const file = (e.target as HTMLInputElement).files?.[0];
            if (file) {
                processMapFile(file);
            }
        };
        input.click();
    };

    // --- Drag and Drop Handlers ---
    const handleDragOver = (e: React.DragEvent) => {
        e.preventDefault();
        e.stopPropagation();
    };

    const handleDrop = (e: React.DragEvent) => {
        e.preventDefault();
        e.stopPropagation();
        const file = e.dataTransfer.files[0];
        if (file && file.name.endsWith('.json')) {
            processMapFile(file);
        }
    };

    // --- Real-time updates to Serial (AGV Events) ---
    const prevSensors = useRef({ rfid: null as string | null, mark: false, line: false });
    useEffect(() => {
        if (!agvConnected || !agvSerialRef.current) return;

        // Check RFID change
        if (agvState.detectedRfid !== prevSensors.current.rfid) {
            if (agvState.detectedRfid) {
                sendTag(agvState.detectedRfid);
            }
        }

        prevSensors.current = {
            rfid: agvState.detectedRfid,
            mark: agvState.sensorMark,
            line: agvState.sensorLineFront
        };
    }, [agvState, agvConnected, sendTag]);

    // BMS Simulation
    useEffect(() => {
        const timer = setInterval(() => {
            setAgvState(s => {
                const isCharging = s.isCharging;
                let newLevel = s.batteryLevel;

                if (isCharging) {
                    // Charge: 1% per min (faster than discharge)
                    newLevel = Math.min(100, s.batteryLevel + (1.0 / 60));
                } else {
                    // Discharge: 5% per 10 min = 0.5% per min = 0.5 / 60 per sec
                    newLevel = Math.max(0, s.batteryLevel - (0.5 / 60));
                }

                // Fluctuate Voltages (+- 0.01V)
                const newVoltages = s.cellVoltages.map(v => {
                    const delta = (Math.random() - 0.5) * 0.02;
                    let val = v + delta;
                    if (val < 2.8) val = 2.8;
                    if (val > 3.6) val = 3.6;
                    return val;
                });

                return {
                    ...s,
                    batteryLevel: newLevel,
                    cellVoltages: newVoltages
                };
            });
        }, 1000);
        return () => clearInterval(timer);
    }, []);

    return (
        <div
            className="flex h-screen w-screen bg-gray-950 text-white overflow-hidden"
            onDragOver={handleDragOver}
            onDrop={handleDrop}
        >
            {/* 1. Far Left: Toolbar */}
            <div className="w-16 p-2 border-r border-gray-800 flex flex-col items-center shrink-0">
                <EditorToolbar
                    activeTool={activeTool}
                    setTool={setActiveTool}
                    onSave={saveMap}
                    onLoad={loadMap}
                />
            </div>

            {/* 2. Inner Left: Protocol Flags (Separated Sidebar) - Removed BMS from here */}
            <div className="w-60 border-r border-gray-800 bg-gray-900 flex flex-col shrink-0">
                <div className="flex-1 overflow-hidden">
                    <AgvStatusPanel agvState={agvState} />

                </div>

                {/* Auto Run Controls (Left Sidebar) */}
                <div className="w-60 border-r border-gray-800 bg-gray-900 flex flex-col shrink-0">
                    <div className="flex-1 overflow-hidden">
                        <AgvAutoRunControls
                            agvState={agvState}
                            updateRunConfig={(key, value) => {
                                if (agvState.error) return;
                                setAgvState(s => ({ ...s, runConfig: { ...s.runConfig, [key]: value } }));
                            }}
                            toggleRun={() => {
                                if (agvState.error) return;
                                if (agvState.motionState === AgvMotionState.RUNNING || agvState.motionState === AgvMotionState.MARK_STOPPING) {
                                    setAgvState(s => ({ ...s, motionState: AgvMotionState.IDLE }));
                                } else {
                                    const isFwd = agvState.runConfig.direction === 'FWD';
                                    const hasLine = isFwd ? agvState.sensorLineFront : agvState.sensorLineRear;
                                    if (!hasLine) {
                                        setAgvState(s => ({ ...s, error: 'LINE_OUT' }));
                                        return;
                                    }
                                    setAgvState(s => ({ ...s, motionState: AgvMotionState.RUNNING }));
                                }
                            }}
                            isRunning={agvState.motionState === AgvMotionState.RUNNING || agvState.motionState === AgvMotionState.MARK_STOPPING}
                            isError={agvState.error !== null}
                            setLidar={(isOn) => setAgvState(s => ({ ...s, lidarEnabled: isOn, sensorStatus: isOn ? '1' : '0' }))}
                        />
                    </div>
                </div>

                {/* Middle 2: ACS Panel (Fixed) */}
                <div className="flex-shrink-0">
                    <AcsControls onSend={sendAcsPacket} isConnected={acsConnected} mapData={mapData} />
                </div>


            </div>

            {/* 3. Center: Canvas & Bottom Panels */}
            <div className="flex-1 flex flex-col min-w-0 bg-gray-950 relative">
                <div className="flex-1 relative overflow-hidden">
                    <SimulationCanvas
                        activeTool={activeTool}
                        mapData={mapData}
                        setMapData={setMapData}
                        agvState={agvState}
                        setAgvState={setAgvState}
                        onLog={(msg) => addLog('SYSTEM', 'INFO', msg)}
                    />
                </div>

                {/* Fixed Communication Panels (Now inside center column) */}
                <div className="h-64 border-t border-gray-700 bg-black grid grid-cols-3 gap-0 shrink-0">
                    <div className="border-r border-gray-800 h-full min-h-0">
                        <SerialConsole
                            title="AGV"
                            logs={logs.filter(l => l.source === 'AGV')}
                            isConnected={agvConnected}
                            onConnect={connectAgv}
                            onDisconnect={disconnectAgv}
                            onClear={() => clearLogs('AGV')}
                            baudRate={agvBaudRate}
                            setBaudRate={setAgvBaudRate}
                            portInfo={agvPortInfo}
                            colorClass="text-blue-400"
                        />
                    </div>
                    <div className="border-r border-gray-800 h-full min-h-0">
                        <SerialConsole
                            title="BMS"
                            logs={logs.filter(l => l.source === 'BMS')}
                            isConnected={bmsConnected}
                            onConnect={connectBms}
                            onDisconnect={disconnectBms}
                            onClear={() => clearLogs('BMS')}
                            baudRate={bmsBaudRate}
                            setBaudRate={setBmsBaudRate}
                            portInfo={bmsPortInfo}
                            colorClass="text-yellow-400"
                        />
                    </div>
                    <div className="h-full min-h-0">
                        <SerialConsole
                            title="ACS"
                            logs={logs.filter(l => l.source === 'ACS')}
                            isConnected={acsConnected}
                            onConnect={connectAcs}
                            onDisconnect={disconnectAcs}
                            onClear={() => clearLogs('ACS')}
                            baudRate={acsBaudRate}
                            setBaudRate={setAcsBaudRate}
                            portInfo={acsPortInfo}
                            colorClass="text-green-400"
                        />
                    </div>
                </div>
            </div>

            {/* 4. Right: Controls & BMS & ACS */}
            <div className="w-72 border-l border-gray-800 bg-gray-900 flex flex-col shrink-0">

                {/* Top: Controls (Scrollable if needed) */}
                <div className="flex-1 overflow-y-auto">
                    {/* Manual Controls */}
                    <AgvManualControls
                        agvState={agvState}
                        setMotion={(state) => setAgvState(s => ({ ...s, motionState: state }))}
                        setRunConfig={(config) => setAgvState(s => ({ ...s, runConfig: config }))}
                        setError={(err) => setAgvState(s => ({ ...s, error: err }))}
                        onTurn180={handleTurn180}
                        setLiftStatus={(status) => setAgvState(s => ({ ...s, liftStatus: status }))}
                        setMagnet={(isOn) => setAgvState(s => ({ ...s, magnetOn: isOn }))}
                    />
                </div>

                {/* Middle 1: BMS Panel (Fixed) */}
                <div className="flex-shrink-0">
                    <BmsPanel
                        state={agvState}
                        setBatteryLevel={(l) => setAgvState(s => ({ ...s, batteryLevel: l }))}
                        setIsCharging={(isConnected) => {
                            setAgvState(s => ({ ...s, isCharging: isConnected }));
                        }}
                    />
                </div>

                {/* Bottom: System Logs (Fixed Height) */}
                <div className="h-48 flex-shrink-0">
                    <SystemLogPanel logs={logs.filter(l => l.source === 'SYSTEM')} onClear={() => clearLogs('SYSTEM')} />
                </div>
            </div>
        </div >
    );
};

export default App;