/**
 * SaltyFace Web App — Animated robot expression UI for Chromium kiosk
 *
 * Runs fullscreen in Cage Wayland compositor on MageDok 7" display
 * Replaces React desktop component with lightweight web app
 *
 * Features:
 * - 8 emotional states (happy, alert, confused, sleeping, excited, emergency, listening, talking)
 * - Canvas-based rendering (30fps target)
 * - ROS2 bridge integration via WebSocket (rosbridge)
 * - Touch-responsive status overlay
 * - 1024×600 fullscreen on MageDok display
 */

// Emotion configuration
const EMOTIONS = {
    HAPPY: 'happy',
    ALERT: 'alert',
    CONFUSED: 'confused',
    SLEEPING: 'sleeping',
    EXCITED: 'excited',
    EMERGENCY: 'emergency',
    LISTENING: 'listening',
    TALKING: 'talking',
};

const EMOTION_CONFIG = {
    [EMOTIONS.HAPPY]: {
        eyeScale: 1.0,
        pupilPos: { x: 0, y: 0 },
        blink: true,
        blinkRate: 3000,
        color: '#10b981',
    },
    [EMOTIONS.ALERT]: {
        eyeScale: 1.3,
        pupilPos: { x: 0, y: -3 },
        blink: false,
        color: '#ef4444',
    },
    [EMOTIONS.CONFUSED]: {
        eyeScale: 1.1,
        pupilPos: { x: 0, y: 0 },
        blink: true,
        blinkRate: 1500,
        eyeWander: true,
        color: '#f59e0b',
    },
    [EMOTIONS.SLEEPING]: {
        eyeScale: 0.3,
        pupilPos: { x: 0, y: 0 },
        blink: false,
        isClosed: true,
        color: '#6b7280',
    },
    [EMOTIONS.EXCITED]: {
        eyeScale: 1.2,
        pupilPos: { x: 0, y: 0 },
        blink: true,
        blinkRate: 800,
        bounce: true,
        color: '#22c55e',
    },
    [EMOTIONS.EMERGENCY]: {
        eyeScale: 1.4,
        pupilPos: { x: 0, y: -4 },
        blink: false,
        color: '#dc2626',
        flash: true,
    },
    [EMOTIONS.LISTENING]: {
        eyeScale: 1.0,
        pupilPos: { x: 0, y: -2 },
        blink: true,
        blinkRate: 2000,
        color: '#0ea5e9',
    },
    [EMOTIONS.TALKING]: {
        eyeScale: 1.0,
        pupilPos: { x: 0, y: 0 },
        blink: true,
        blinkRate: 2500,
        color: '#06b6d4',
    },
};

// Mouth animation frames
const MOUTH_FRAMES = [
    { open: 0.0, shape: 'closed' },
    { open: 0.3, shape: 'smile-closed' },
    { open: 0.5, shape: 'smile-open' },
    { open: 0.7, shape: 'oh' },
    { open: 0.9, shape: 'ah' },
    { open: 0.7, shape: 'ee' },
];

// ROS2 bridge WebSocket
let ros = null;
const WS_URL = 'ws://localhost:9090';
let rosConnected = false;

// Animation state
let state = {
    emotion: EMOTIONS.HAPPY,
    isTalking: false,
    isListening: false,
    audioLevel: 0,
    showOverlay: true,
    botState: {
        battery: 85,
        state: 'IDLE',
        distance: 0.0,
        speed: 0.0,
        health: 90,
        hasTarget: false,
        obstacles: 0,
    },
    frameCount: 0,
    isBlinking: false,
    mouthFrame: 0,
    eyeWanderOffset: { x: 0, y: 0 },
};

// Drawing helpers
function drawEye(ctx, x, y, radius, config, isBlinking) {
    ctx.fillStyle = '#1f2937';
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2);
    ctx.fill();

    if (isBlinking) {
        ctx.strokeStyle = config.color;
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.moveTo(x - radius * 0.7, y);
        ctx.lineTo(x + radius * 0.7, y);
        ctx.stroke();
    } else {
        const pupilRadius = radius * (config.eyeScale / 2);
        ctx.fillStyle = config.color;
        ctx.beginPath();
        ctx.arc(x + config.pupilPos.x, y + config.pupilPos.y, pupilRadius, 0, Math.PI * 2);
        ctx.fill();

        // Highlight
        ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
        ctx.beginPath();
        ctx.arc(x + config.pupilPos.x + pupilRadius * 0.4, y + config.pupilPos.y - pupilRadius * 0.4, pupilRadius * 0.3, 0, Math.PI * 2);
        ctx.fill();
    }
}

function drawMouth(ctx, x, y, width, frame) {
    ctx.strokeStyle = '#f59e0b';
    ctx.lineWidth = 3;
    ctx.lineCap = 'round';

    if (frame.shape === 'closed') {
        ctx.beginPath();
        ctx.moveTo(x - width * 0.4, y);
        ctx.lineTo(x + width * 0.4, y);
        ctx.stroke();
    } else if (frame.shape === 'smile-open' || frame.shape === 'smile-closed') {
        ctx.beginPath();
        ctx.arc(x, y, width * 0.5, 0, Math.PI, false);
        ctx.stroke();
    } else if (frame.shape === 'oh') {
        ctx.fillStyle = '#f59e0b';
        ctx.beginPath();
        ctx.arc(x, y, width * 0.35 * frame.open, 0, Math.PI * 2);
        ctx.fill();
    } else if (frame.shape === 'ah') {
        ctx.beginPath();
        ctx.moveTo(x - width * 0.3, y - width * 0.2 * frame.open);
        ctx.lineTo(x + width * 0.3, y - width * 0.2 * frame.open);
        ctx.lineTo(x + width * 0.2, y + width * 0.3 * frame.open);
        ctx.lineTo(x - width * 0.2, y + width * 0.3 * frame.open);
        ctx.closePath();
        ctx.stroke();
    }
}

function drawFace(ctx, W, H) {
    const config = EMOTION_CONFIG[state.emotion] || EMOTION_CONFIG[EMOTIONS.HAPPY];

    // Clear canvas
    ctx.fillStyle = 'rgba(5, 5, 16, 0.95)';
    ctx.fillRect(0, 0, W, H);

    const centerX = W / 2;
    const centerY = H / 2.2;
    const eyeRadius = 40;
    const eyeSpacing = 80;

    // Eye wandering (confused state)
    let eyeOffX = 0, eyeOffY = 0;
    if (config.eyeWander) {
        eyeOffX = Math.sin(state.frameCount * 0.02) * 8;
        eyeOffY = Math.cos(state.frameCount * 0.015) * 8;
    }

    // Bounce (excited state)
    let bounceOffset = 0;
    if (config.bounce) {
        bounceOffset = Math.sin(state.frameCount * 0.08) * 6;
    }

    // Draw eyes
    const eyeY = centerY + bounceOffset;
    drawEye(ctx, centerX - eyeSpacing, eyeY + eyeOffY, eyeRadius, config, state.isBlinking);
    drawEye(ctx, centerX + eyeSpacing, eyeY + eyeOffY, eyeRadius, config, state.isBlinking);

    // Draw mouth (if talking)
    if (state.isTalking && !config.isClosed) {
        drawMouth(ctx, centerX, centerY + 80, 50, MOUTH_FRAMES[state.mouthFrame]);
    }

    // Flash (emergency state)
    if (config.flash && Math.sin(state.frameCount * 0.1) > 0.7) {
        ctx.fillStyle = 'rgba(220, 38, 38, 0.3)';
        ctx.fillRect(0, 0, W, H);
    }

    state.frameCount++;
}

// ROS2 Bridge functions
function initRosBridge() {
    console.log('[SaltyFace] Connecting to ROS bridge at', WS_URL);

    const ROSLIB = window.ROSLIB;
    if (!ROSLIB) {
        console.error('[SaltyFace] ROSLIB not loaded');
        return false;
    }

    ros = new ROSLIB.Ros({
        url: WS_URL,
    });

    ros.on('connection', () => {
        console.log('[SaltyFace] Connected to ROS bridge');
        rosConnected = true;
        updateWSStatus(true);
        subscribeToRosTopics();
    });

    ros.on('error', (error) => {
        console.error('[SaltyFace] ROS error:', error);
        rosConnected = false;
        updateWSStatus(false);
    });

    ros.on('close', () => {
        console.log('[SaltyFace] ROS connection closed');
        rosConnected = false;
        updateWSStatus(false);
    });

    return true;
}

function subscribeToRosTopics() {
    if (!ros || !rosConnected) return;

    // Subscribe to robot state
    const stateTopic = new ROSLIB.Topic({
        ros,
        name: '/saltybot/state',
        messageType: 'std_msgs/String',
    });
    stateTopic.subscribe((msg) => {
        try {
            const data = JSON.parse(msg.data);
            state.botState.state = data.state || 'IDLE';

            if (data.state === 'EMERGENCY') {
                state.emotion = EMOTIONS.EMERGENCY;
            } else if (data.state === 'TRACKING') {
                state.emotion = EMOTIONS.HAPPY;
            } else if (data.state === 'SEARCHING') {
                state.emotion = EMOTIONS.CONFUSED;
            } else if (data.state === 'IDLE') {
                state.emotion = EMOTIONS.HAPPY;
            }
        } catch (e) {}
    });

    // Battery topic
    const batteryTopic = new ROSLIB.Topic({
        ros,
        name: '/saltybot/battery',
        messageType: 'std_msgs/Float32',
    });
    batteryTopic.subscribe((msg) => {
        state.botState.battery = Math.round(msg.data);
        updateUI();
    });

    // Target tracking
    const targetTopic = new ROSLIB.Topic({
        ros,
        name: '/saltybot/target_track',
        messageType: 'geometry_msgs/Pose',
    });
    targetTopic.subscribe((msg) => {
        state.botState.hasTarget = !!msg;
        if (msg) state.emotion = EMOTIONS.EXCITED;
    });

    // Obstacles
    const obstacleTopic = new ROSLIB.Topic({
        ros,
        name: '/saltybot/obstacles',
        messageType: 'sensor_msgs/LaserScan',
    });
    obstacleTopic.subscribe((msg) => {
        const obstacleCount = msg?.ranges?.filter((r) => r < 0.5).length || 0;
        state.botState.obstacles = obstacleCount;
        if (obstacleCount > 0) state.emotion = EMOTIONS.ALERT;
    });

    // Speech
    const speakingTopic = new ROSLIB.Topic({
        ros,
        name: '/social/speech/is_speaking',
        messageType: 'std_msgs/Bool',
    });
    speakingTopic.subscribe((msg) => {
        state.isTalking = msg.data || false;
        if (msg.data) state.emotion = EMOTIONS.TALKING;
    });

    const listeningTopic = new ROSLIB.Topic({
        ros,
        name: '/social/speech/is_listening',
        messageType: 'std_msgs/Bool',
    });
    listeningTopic.subscribe((msg) => {
        state.isListening = msg.data || false;
        if (msg.data) state.emotion = EMOTIONS.LISTENING;
    });

    // Audio level
    const audioTopic = new ROSLIB.Topic({
        ros,
        name: '/saltybot/audio_level',
        messageType: 'std_msgs/Float32',
    });
    audioTopic.subscribe((msg) => {
        state.audioLevel = msg.data || 0;
    });
}

function updateWSStatus(connected) {
    const dot = document.getElementById('ws-dot');
    const label = document.getElementById('ws-label');
    const container = document.getElementById('ws-status');

    if (connected) {
        dot.style.backgroundColor = '#10b981';
        label.textContent = 'CONNECTED';
        container.classList.add('connected');
        container.classList.remove('disconnected');
    } else {
        dot.style.backgroundColor = '#ef4444';
        label.textContent = 'DISCONNECTED';
        container.classList.remove('connected');
        container.classList.add('disconnected');
    }
}

function updateUI() {
    document.getElementById('battery').textContent = state.botState.battery;
    document.getElementById('state').textContent = state.botState.state;
    document.getElementById('distance').textContent = state.botState.distance.toFixed(1);
    document.getElementById('speed').textContent = state.botState.speed.toFixed(1) + ' m/s';
    document.getElementById('health').textContent = state.botState.health;
    document.getElementById('emotion').textContent = state.emotion.toUpperCase();
    document.getElementById('talking').textContent = state.isTalking ? 'YES' : 'NO';
    document.getElementById('audio-level').textContent = Math.round(state.audioLevel * 100) + '%';

    // Health color
    const healthIcon = document.getElementById('health-icon');
    if (state.botState.health > 75) {
        healthIcon.style.color = '#10b981';
    } else if (state.botState.health > 50) {
        healthIcon.style.color = '#f59e0b';
    } else {
        healthIcon.style.color = '#ef4444';
    }
}

// Animation loop
function animationLoop(canvas, ctx) {
    drawFace(ctx, canvas.width, canvas.height);
    requestAnimationFrame(() => animationLoop(canvas, ctx));
}

// Blinking animation
function startBlinking() {
    const config = EMOTION_CONFIG[state.emotion] || EMOTION_CONFIG[EMOTIONS.HAPPY];
    if (!config.blink || config.isClosed) return;

    setInterval(() => {
        state.isBlinking = true;
        setTimeout(() => {
            state.isBlinking = false;
        }, 150);
    }, config.blinkRate);
}

// Mouth animation
function startMouthAnimation() {
    setInterval(() => {
        if (!state.isTalking) {
            state.mouthFrame = 0;
            return;
        }
        state.mouthFrame = (state.mouthFrame + 1) % MOUTH_FRAMES.length;
    }, 100);
}

// Initialize
export function initSaltyFace() {
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const overlay = document.getElementById('overlay');

    // Start animation loop
    animationLoop(canvas, ctx);

    // Start blinking
    startBlinking();

    // Start mouth animation
    startMouthAnimation();

    // Tap to toggle overlay
    canvas.addEventListener('click', () => {
        state.showOverlay = !state.showOverlay;
        overlay.classList.toggle('hidden', !state.showOverlay);
    });

    // Prevent context menu on long press
    canvas.addEventListener('contextmenu', (e) => e.preventDefault());

    // Initialize ROS bridge
    // Load ROSLIB dynamically
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/roslib@1.3.0/build/roslib.min.js';
    script.onload = () => {
        initRosBridge();
    };
    script.onerror = () => {
        console.warn('[SaltyFace] Failed to load ROSLIB from CDN, trying localhost');
        setTimeout(() => {
            initRosBridge();
        }, 2000);
    };
    document.head.appendChild(script);

    // Update UI periodically
    setInterval(updateUI, 500);

    console.log('[SaltyFace] Web app initialized');
}
