(function ($) {
    "use strict";

    const initLiquidBackground = ($scope) => {
        const wrapper = $scope.find('.sifency-liquid-wrapper')[0];
        if (!wrapper) return;

        const canvas = wrapper.querySelector('.sifency-liquid-canvas');
        if (!canvas) return;

        // Try to get WebGL context with extra options
        const glOptions = {
            alpha: true,
            depth: false,
            stencil: false,
            antialias: true,
            premultipliedAlpha: true,
            preserveDrawingBuffer: true
        };
        const gl = canvas.getContext('webgl', glOptions) || canvas.getContext('experimental-webgl', glOptions);

        if (!gl) {
            console.error('Sifency: WebGL not supported.');
            return;
        }

        const config = JSON.parse(wrapper.dataset.config || '{}');
        const type = config.type || 'image';
        const isVideo = (type === 'video');
        const imageUrl = config.image;
        const videoUrl = config.video;
        const displacementUrl = config.displacement_url;
        
        const rSize = parseFloat(config.size || 30.0);
        const rSpeed = parseFloat(config.speed || 1.5);
        const rIntensity = parseFloat(config.intensity || 0.05);

        let animationFrameId;
        let mouseX = 0.5, mouseY = 0.5;
        let targetMouseX = 0.5, targetMouseY = 0.5;
        let startTime = Date.now();
        let mediaElement;
        let isAppStarted = false;

        // 1. Shaders
        const vertexShaderSource = `
            attribute vec2 position;
            varying vec2 vUv;
            void main() {
                vUv = position * 0.5 + 0.5;
                gl_Position = vec4(position, 0.0, 1.0);
            }
        `;

        const fragmentShaderSource = `
            precision mediump float;
            varying vec2 vUv;
            uniform sampler2D uImage;
            uniform sampler2D uDisplacement;
            uniform float uTime;
            uniform vec2 uMouse;
            uniform float uSize;
            uniform float uSpeed;
            uniform float uIntensity;
            uniform bool uHasDisplacement;

            void main() {
                vec2 uv = vUv;
                uv.y = 1.0 - uv.y; // Standard WebGL flip

                // Mouse interaction ripple
                float dist = distance(uv, uMouse);
                float ripple = sin(dist * uSize - uTime * uSpeed);
                vec2 mouseOffset = normalize(uv - uMouse) * ripple * uIntensity * smoothstep(0.5, 0.0, dist);

                // Liquid displacement
                vec2 dispOffset = vec2(0.0);
                if(uHasDisplacement) {
                    vec4 disp = texture2D(uDisplacement, uv * 0.8 + uTime * 0.03);
                    dispOffset = (disp.rg - 0.5) * uIntensity * 0.7;
                }

                // Ambient wave
                vec2 ambient = vec2(
                    sin(uv.y * 10.0 + uTime) * 0.002,
                    cos(uv.x * 10.0 + uTime) * 0.002
                );

                vec2 finalUV = uv + mouseOffset + dispOffset + ambient;
                finalUV = clamp(finalUV, 0.0, 1.0);
                
                gl_FragColor = texture2D(uImage, finalUV);
            }
        `;

        // 2. Setup Program
        const compileShader = (type, source) => {
            const shader = gl.createShader(type);
            gl.shaderSource(shader, source);
            gl.compileShader(shader);
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                console.error('Sifency Shader Error:', gl.getShaderInfoLog(shader));
                return null;
            }
            return shader;
        };

        const vShader = compileShader(gl.VERTEX_SHADER, vertexShaderSource);
        const fShader = compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
        if (!vShader || !fShader) return;

        const program = gl.createProgram();
        gl.attachShader(program, vShader);
        gl.attachShader(program, fShader);
        gl.linkProgram(program);
        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            console.error('Sifency Link Error:', gl.getProgramInfoLog(program));
            return;
        }

        gl.useProgram(program);

        // 3. Geometry
        const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
        const buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

        const posLoc = gl.getAttribLocation(program, "position");
        gl.enableVertexAttribArray(posLoc);
        gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);

        // 4. Uniforms
        const uniforms = {
            time: gl.getUniformLocation(program, "uTime"),
            mouse: gl.getUniformLocation(program, "uMouse"),
            size: gl.getUniformLocation(program, "uSize"),
            speed: gl.getUniformLocation(program, "uSpeed"),
            intensity: gl.getUniformLocation(program, "uIntensity"),
            image: gl.getUniformLocation(program, "uImage"),
            displacement: gl.getUniformLocation(program, "uDisplacement"),
            hasDisplacement: gl.getUniformLocation(program, "uHasDisplacement")
        };

        // 5. Textures
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        const mainTexture = gl.createTexture();
        const dispTexture = gl.createTexture();

        const initTexture = (tex) => {
            gl.bindTexture(gl.TEXTURE_2D, tex);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([30, 30, 30, 255]));
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        };
        initTexture(mainTexture);
        initTexture(dispTexture);

        const startApp = () => {
            if (isAppStarted) return;
            isAppStarted = true;
            resize();
            startTime = Date.now();
            render();
        };

        // 6. Loading
        if (displacementUrl) {
            const img = new Image();
            img.crossOrigin = "anonymous";
            img.onload = () => {
                gl.useProgram(program);
                gl.activeTexture(gl.TEXTURE1);
                gl.bindTexture(gl.TEXTURE_2D, dispTexture);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
                gl.uniform1i(uniforms.hasDisplacement, 1);
                gl.uniform1i(uniforms.displacement, 1);
            };
            img.src = displacementUrl;
        }

        if (isVideo) {
            mediaElement = document.createElement('video');
            mediaElement.muted = true;
            mediaElement.autoplay = true;
            mediaElement.loop = true;
            mediaElement.playsInline = true;
            mediaElement.crossOrigin = "anonymous";
            mediaElement.style.display = 'none';
            wrapper.appendChild(mediaElement);

            const handleVideo = () => {
                if (mediaElement.readyState >= 2) {
                    mediaElement.play().then(startApp).catch(startApp);
                }
            };
            mediaElement.onloadeddata = handleVideo;
            mediaElement.onplaying = handleVideo;
            mediaElement.oncanplay = handleVideo;
            mediaElement.src = videoUrl;
            mediaElement.load();
            setTimeout(startApp, 3000);
        } else {
            mediaElement = new Image();
            mediaElement.crossOrigin = "anonymous";
            mediaElement.onload = () => {
                gl.useProgram(program);
                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, mainTexture);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mediaElement);
                gl.uniform1i(uniforms.image, 0);
                startApp();
            };
            mediaElement.src = imageUrl || 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
        }

        // 7. Render
        const resize = () => {
            const dpr = window.devicePixelRatio || 1;
            canvas.width = wrapper.clientWidth * dpr;
            canvas.height = wrapper.clientHeight * dpr;
            gl.viewport(0, 0, canvas.width, canvas.height);
        };
        window.addEventListener('resize', resize);

        wrapper.addEventListener('mousemove', (e) => {
            const r = wrapper.getBoundingClientRect();
            targetMouseX = (e.clientX - r.left) / r.width;
            targetMouseY = (e.clientY - r.top) / r.height;
        });

        const render = () => {
            mouseX += (targetMouseX - mouseX) * 0.1;
            mouseY += (targetMouseY - mouseY) * 0.1;

            gl.useProgram(program);
            gl.uniform1f(uniforms.time, (Date.now() - startTime) / 1000);
            gl.uniform2f(uniforms.mouse, mouseX, mouseY);
            gl.uniform1f(uniforms.size, rSize);
            gl.uniform1f(uniforms.speed, rSpeed);
            gl.uniform1f(uniforms.intensity, rIntensity);

            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, mainTexture);
            gl.uniform1i(uniforms.image, 0);

            if (isVideo && mediaElement.readyState >= 2) {
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, mediaElement);
            }

            gl.activeTexture(gl.TEXTURE1);
            gl.bindTexture(gl.TEXTURE_2D, dispTexture);

            gl.drawArrays(gl.TRIANGLES, 0, 6);
            animationFrameId = requestAnimationFrame(render);
        };

        $scope.on('remove', () => {
            cancelAnimationFrame(animationFrameId);
            window.removeEventListener('resize', resize);
            if (isVideo && mediaElement) {
                mediaElement.pause();
                mediaElement.remove();
            }
        });
    };

    $(window).on('elementor/frontend/init', function () {
        elementorFrontend.hooks.addAction(
            'frontend/element_ready/sifency-liquid-background.default',
            initLiquidBackground
        );
    });

})(jQuery);


