import {createContext, useContext, useEffect, useRef, useState} from "react";
import OBSWebSocket from "obs-websocket-js";
import {hex2rgb} from "../utils/Tools.js";

const OBSContext = createContext({connected: false, obs: null})

export function OBSProvider({children}) {
    const obs = useRef(null)
    const obsParm = useRef(null)
    const assets = useRef(null)
    const [connected, setConnected] = useState(false)
    const [doReconnect, setDoReconnect] = useState(false)

    useEffect(() => {
        if (!doReconnect)
            return;

        const timer = setInterval(() => {
            if (obs.current && !connected && doReconnect) {
                console.log("Reconnecting to OBS WebSocket...")
                obs.current.connect(obsParm.current.adresse, obsParm.current.password)
                    .then(data => {
                        console.log("Reconnected to OBS WebSocket", data)
                        setDoReconnect(false)
                    })
            }
        }, 5000);
        return () => clearInterval(timer);
    }, [connected, doReconnect]);

    const connect = (adresse, password = undefined, assets_dir = undefined) => {
        if (connected && obs.current)
            return;
        assets.current = assets_dir;

        const obs_ = new OBSWebSocket();
        obs_.connect(adresse, password)
            .then(data => {
                console.log("Connected to OBS WebSocket", data)
            })
            .catch(err => {
                console.error("Failed to connect to OBS WebSocket", err)
            });
        obs_.on('ConnectionOpened', () => {
            setConnected(true)
            console.log("OBS WebSocket connection opened")
        });
        obs_.on('ConnectionClosed', err => {
            setConnected(false)
            console.log("OBS WebSocket connection closed", err.code, err.message)
            if (err.code === 1000 || err.code === 4009) // 1000 = Normal Closure, 4009 = Authentication Failure
                return;
            obsParm.current = {adresse, password}
            setDoReconnect(true)
        });
        obs_.on('error', err => {
            console.error("OBS WebSocket error", err)
        });

        obs.current = obs_;
    }

    const disconnect = () => {
        if (obs.current && connected) {
            obs.current.disconnect();
            obs.current = null;
        }
    }

    const ret = {connected, obs: obs.current, connect, disconnect, assets: assets.current}
    return <OBSContext.Provider value={ret}>
        {children}
    </OBSContext.Provider>
}

function getElementName(element) {
    return `sub${sessionStorage.getItem("liceName") || 1}.${element}`
}

export function useOBS() {
    const {connected, obs, connect, disconnect, assets} = useContext(OBSContext)
    const setTextAndColor = (element, text, color) => {
        if (!connected)
            return;
        if (color.startsWith("#")){
            const tmp = hex2rgb(color);
            color = (tmp.b << 16) + (tmp.g << 8) + tmp.r;
        }

        obs.call('SetInputSettings', {
            inputName: getElementName(element),
            inputSettings: {
                text: text,
                color: color,
                overlay: true,
            }
        }).catch(err => {
            console.error("Failed to set text and color for OBS element", element, err)
        });
    }
    const setText = (element, text) => {
        if (!connected)
            return;
        obs.call('SetInputSettings', {
            inputName: getElementName(element),
            inputSettings: {
                text: text,
                overlay: true,
            }
        }).catch(err => {
            console.error("Failed to set text for OBS element", element, err)
        });
    }

    const setDiapo = (element, files) => {
        if (!connected)
            return;
        const arr = [];
        for (const file of files) {
            arr.push({
                hidden: false,
                selected: false,
                value: (assets.endsWith('/') ? assets : assets + '/') + file,
            })
        }
        obs.call('SetInputSettings', {
            inputName: getElementName(element),
            inputSettings: {
                files: arr,
                overlay: true,
            }
        }).catch(err => {
            console.error("Failed to set diapo for OBS element", element, err)
        });
    }

    return {
        connected,
        obs,
        connect,
        disconnect,
        setText,
        setTextAndColor,
        setDiapo,
    }
}

export function exportOBSConfiguration(adresse, password, assets_dir) {
    const config = {
        adresse: adresse,
        password: password,
        assets_dir: assets_dir,
    };
    const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(config, null, 2));
    const downloadAnchorNode = document.createElement('a');
    downloadAnchorNode.setAttribute("href", dataStr);
    downloadAnchorNode.setAttribute("download", "obs_configuration.json");
    document.body.appendChild(downloadAnchorNode); // required for firefox
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
}

export async function importOBSConfiguration() {
    return new Promise((resolve, reject) => {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = 'application/json';
        input.onchange = e => {
            const file = e.target.files[0];
            const reader = new FileReader();
            reader.onload = event => {
                try {
                    const config = JSON.parse(event.target.result);
                    resolve(config);
                } catch (err) {
                    reject(err);
                }
            };
            reader.readAsText(file);
        };
        input.click();
    });
}
