import React, {useEffect, useRef, useState} from 'react';
import {useTablesState} from "../../pages/competition/editor/StateWindow.jsx";
import {timePrint} from "../../utils/Tools.js";

let initialized = false;
const AudioEncoder = () => {
    const audioContextRef = useRef(null);
    const qpskProcessorRef = useRef(null);
    const [isReady, setIsReady] = useState(false);
    const [table, setTable] = useState('1');

    const lastSend = useRef({id: 0});
    const {state} = useTablesState();

    // Initialisation de l'AudioContext et du AudioWorklet
    useEffect(() => {
        const initAudio = async () => {
            if (initialized)
                return;
            console.log("Initialisation de l'audio après interaction utilisateur");
            initialized = true;

            try {
                const audioContext = new (window.AudioContext || window.webkitAudioContext)();
                console.log("AudioContext state:", audioContext.state);

                if (audioContext.state === 'suspended') {
                    await audioContext.resume(); // Nécessaire pour démarrer le contexte audio
                    console.log("AudioContext resumed");
                }

                await audioContext.audioWorklet.addModule('/processor-dtmf.js');
                await new Promise(r => setTimeout(r, 100));
                const processor = new AudioWorkletNode(audioContext, 'dtmf-processor');
                processor.connect(audioContext.destination);
                qpskProcessorRef.current = processor;
                audioContextRef.current = audioContext;

                await new Promise(r => setTimeout(r, 100));
                setIsReady(true);
            } catch (err) {
                initialized = false;
                console.error("Erreur d'initialisation AudioWorklet:", err);
            }
        };

        // Initialiser après un clic utilisateur (pour contourner les restrictions des navigateurs)
        const handleUserInteraction = () => {
            document.removeEventListener('click', handleUserInteraction);
            initAudio();
        };

        document.addEventListener('click', handleUserInteraction);
        return () => {
            if (audioContextRef.current?.state !== 'closed') {
                audioContextRef.current?.close();
            }
        };
    }, []);

    // Fonction pour encoder et envoyer un message
    const encodeAndSend = (data, lowPrio = false) => {
        if (!isReady) return;

        const symbols = Array.from(data).flatMap(byte => [byte >> 4, byte & 0x0F]);
        console.log("Bits :", symbols);

        // 5. Envoyer les symboles au processeur audio
        if (lowPrio) {
            qpskProcessorRef.current.port.postMessage({type: 'encodeLowPrio', symbols});
        } else {
            qpskProcessorRef.current.port.postMessage({type: 'encode', symbols});
        }
    }

    useEffect(() => {
        const t = state.find(o => o.liceName === table)
        if (!t)
            return

        // console.log("Data for table 1:", t, t.selectedMatch)
        const last = lastSend.current;
        if (t.selectedMatch !== last.id) {
            clearTimeout(last.time_id)
            last.time_id = setTimeout(() => {
                last.time_id = null
                if (t.selectedMatch === null) {
                    encodeAndSend(new Uint8Array([0, 0, 0, 0, 0, 0, 0]));
                } else {
                    const data = [];
                    for (let i = 0; i < 7; i++) { // MaxSafeInteger est sur 7 bytes (53 bits de précision)
                        data.unshift(Number((BigInt(t.selectedMatch) >> BigInt(i * 8)) & 0xFFn))
                    }
                    data[0] = data[0] & 0x1F // 3 premiers bits à 0 pour différencier des autres types de messages (ex: score, chrono, etc.)
                    // console.log("Data to send (selectedMatch):", data)
                    encodeAndSend(new Uint8Array(data), false);
                }
            }, 250)

            last.id = t.selectedMatch
        }

        const isRunning = (c) => c.startTime !== 0
        const getTime = (c) => {
            if (c.startTime === 0)
                return c.time
            return c.time + Date.now() - c.startTime
        }

        const timeStr = last.chronoState ? timePrint((last.chronoState.state === 2) ? last.chronoState.configPause : last.chronoState.configTime - getTime(last.chronoState)) : "-"
        const timeStr2 = timePrint((t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState))

        if (timeStr !== timeStr2) {
            clearInterval(lastSend.current.time_chronoInter)
            clearTimeout(lastSend.current.time_chronoText)
            lastSend.current.time_chronoText = setTimeout(() => {
                let time = (t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState)
                const ms = time % 1000
                time = (time - ms) / 1000

                const data = [((time >> 8) & 0x1F) + 0x20, time & 0xFF];
                // console.log("Data to send (time):", data)
                encodeAndSend(new Uint8Array(data));

                lastSend.current.time_chronoInter = setInterval(() => {
                    let time = (t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState)
                    const ms = time % 1000
                    time = (time - ms) / 1000

                    const data = [((time >> 8) & 0x1F) + 0x20, time & 0xFF];
                    // console.log("Data to send (time-auto):", data)
                    encodeAndSend(new Uint8Array(data), true);
                }, 10000);
            }, 150)
        }

        if (!last.chronoState || last.chronoState.state !== t.chronoState.state || isRunning(last.chronoState) !== isRunning(t.chronoState)) {
            let time = (t.chronoState.state === 2) ? t.chronoState.configPause : t.chronoState.configTime - getTime(t.chronoState)
            const ms = Math.round((time % 1000) / 250)

            const data = [0x40 + (t.chronoState.state << 3) + (isRunning(t.chronoState) << 2) + (ms & 0x03)];
            // console.log("Data to send (chrono state):", data)
            encodeAndSend(new Uint8Array(data));
        }

        last.chronoState = {...t.chronoState}

        // console.log(timeStr, timeStr2)
        // console.log(last.chronoState, t.chronoState)

        if (last.scoreRouge !== t.scoreState.scoreRouge) {
            clearTimeout(last.time_sr)
            last.time_sr = setTimeout(() => {
                if (last.scoreRouge !== t.scoreState.scoreRouge) {
                    const b = t.scoreState.scoreRouge < 0
                    const s = b ? -t.scoreState.scoreRouge : t.scoreState.scoreRouge
                    const data = [0x60 + (b << 3) + ((s >> 8) & 0x07), (s & 0xFF)];
                    console.log("Data to send (score r):", data)
                    encodeAndSend(new Uint8Array(data), true);
                    last.scoreRouge = t.scoreState.scoreRouge
                }
            }, 250)
        }

        if (last.scoreBleu !== t.scoreState.scoreBleu) {
            clearTimeout(last.time_sb)
            last.time_sb = setTimeout(() => {
                if (last.scoreBleu !== t.scoreState.scoreBleu) {
                    const b = t.scoreState.scoreBleu < 0
                    const s = b ? -t.scoreState.scoreBleu : t.scoreState.scoreBleu
                    const data = [0x60 + 0x10 + (b << 3) + ((s >> 8) & 0x07), (s & 0xFF)];
                    console.log("Data to send (score b):", data)
                    encodeAndSend(new Uint8Array(data), true);
                    last.scoreBleu = t.scoreState.scoreBleu
                }
            }, 250)
        }

    }, [state])

    useEffect(() => {
        const last = lastSend.current;
        clearTimeout(last.scoreBleu)
        clearTimeout(last.scoreRouge)
        clearTimeout(last.time_id)
        clearTimeout(last.time_chronoText)
        clearInterval(last.time_chronoInter)
        last.id = 0
        last.scoreBleu = 0
        last.scoreRouge = 0
        last.chronoState = null
    }, [table])

    return (
        <div>
            <input
                type="text"
                value={table}
                onChange={(e) => setTable(e.target.value)}
                placeholder="Nom de la zone"
            />
            <span>{isReady ? 'Actif' : "Zone non configurée"}</span>
        </div>
    );
};

export default AudioEncoder;
