(function ($) {
    "use strict";

    // ===== generic helpers =====

    function val( v, fallback ) {
        if ( v === '' || v == null ) {
            return fallback;
        }
        if ( typeof v === 'object' && 'size' in v ) {
            return ( v.size === '' || v.size == null ) ? fallback : v.size;
        }
        return v;
    }

    function toThreeColor( raw, fallback ) {
        var v = ( raw === '' || raw == null ) ? fallback : raw;
        if ( typeof v === 'string' && v.indexOf( '0x' ) === 0 ) {
            return new THREE.Color( parseInt( v ) );
        }
        return new THREE.Color( v );
    }

    // Maps Elementor setting keys → data-* attribute keys (where they differ).
    var dataKeyMap = {
        pp_animation_type: 'animation-type',
        vanta_bg_opacity:  'canvas-opacity',
        part_color:        'part-color',
        part_opacity:      'part-opacity',
        part_rand_opacity: 'rand-opacity',
        part_quantity:     'quantity',
        part_size:         'part-size',
        part_speed:        'part-speed',
        part_direction:    'part-direction',
        part_hover_effect: 'hover-effect',
        part_hover_size:   'hover-size',
        line_hover_color:  'line-h-color',
        part_custom_code:  'custom-code'
    };

    function dataGetter( $elem ) {
        return function ( key, fallback ) {
            var dataKey = dataKeyMap[ key ] || key.replace( /_/g, '-' );
            var v = $elem.data( dataKey );
            return ( v === '' || v == null ) ? fallback : v;
        };
    }

    function settingsGetter( s ) {
        return function ( key, fallback ) {
            return val( s[ key ], fallback );
        };
    }

    // ===== Vanta builders (settings -> options object) =====

    var vantaBuilders = {
        birds: function ( get, sectionId ) {
            return {
                el:              '#pp-background-' + sectionId,
                backgroundColor: toThreeColor( get( 'bird_bg_color', null ), '#07192f' ),
                color1:          toThreeColor( get( 'bird_color_1',  null ), '#ff0001' ),
                color2:          toThreeColor( get( 'bird_color_2',  null ), '#00d1ff' ),
                colorMode:  get( 'bird_color_mode',  'lerp' ),
                quantity:   get( 'bird_quantity',    4 ),
                birdSize:   get( 'bird_size',        1.5 ),
                wingSpan:   get( 'bird_wing_span',   30 ),
                speedLimit: get( 'bird_speed_limit', 5 ),
                separation: get( 'bird_separation',  20 ),
                alignment:  get( 'bird_alignment',   20 ),
                cohesion:   get( 'bird_cohesion',    30 )
            };
        },
        fog: function ( get, sectionId ) {
            return {
                el:             '#pp-background-' + sectionId,
                highlightColor: toThreeColor( get( 'fog_highlight_color', null ), '#ffc302' ),
                midtoneColor:   toThreeColor( get( 'fog_midtone_color',   null ), '#ff1d01' ),
                lowlightColor:  toThreeColor( get( 'fog_lowlight_color',  null ), '#2c07ff' ),
                baseColor:      toThreeColor( get( 'fog_base_color',      null ), '#ffebeb' ),
                blurFactor: get( 'fog_blur_factor', 0.6 ),
                zoom:       get( 'fog_zoom',        1 ),
                speed:      get( 'fog_speed',       1 )
            };
        },
        waves: function ( get, sectionId ) {
            return {
                el:         '#pp-background-' + sectionId,
                color:      toThreeColor( get( 'waves_color', null ), '#005588' ),
                shininess:  get( 'waves_shininess', 30 ),
                waveHeight: get( 'waves_height',    15 ),
                zoom:       get( 'waves_zoom',      1 ),
                waveSpeed:  get( 'waves_speed',     1 )
            };
        },
        net: function ( get, sectionId ) {
            return {
                el:              '#pp-background-' + sectionId,
                color:           toThreeColor( get( 'net_color',    null ), '#ff3f81' ),
                backgroundColor: toThreeColor( get( 'net_bg_color', null ), '#23153d' ),
                points:      get( 'net_points',       10 ),
                maxDistance: get( 'net_max_distance', 20 ),
                spacing:     get( 'net_spacing',      15 ),
                showDots:    !! get( 'net_show_dot',  false )
            };
        },
        dots: function ( get, sectionId ) {
            return {
                el:              '#pp-background-' + sectionId,
                color:           toThreeColor( get( 'dots_color_1',  null ), '#ff8721' ),
                color2:          toThreeColor( get( 'dots_color_2',  null ), '#ff8721' ),
                backgroundColor: toThreeColor( get( 'dots_bg_color', null ), '#222222' ),
                size:    get( 'dots_size',    3 ),
                spacing: get( 'dots_spacing', 35 )
            };
        }
    };

    // ===== Particles (particles.js) builder =====

    function buildParticlesData( type, get ) {
        var lineLinked, partShapeType, partMoveRand, partSizeRandom,
            lineColor, partQuantity, partOpacity, partRandOpacity,
            partDirection, partSpeed, partSize,
            showHoverEffect, partHoverEffect, partHoverSize, partColor;

        var part_hover_effect = get( 'part_hover_effect', 'noeffect' );
        partColor = get( 'part_color', '#BAB9B9' );

        if ( 'particles' === type ) {
            lineLinked      = true;
            partShapeType   = 'circle';
            partMoveRand    = false;
            partSizeRandom  = true;
            lineColor       = get( 'line_color', '#BAB9B9' );
            partQuantity    = get( 'part_quantity', 80 );
            partOpacity     = get( 'part_opacity', 0.5 );
            partRandOpacity = !! get( 'part_rand_opacity', false );
            partDirection   = get( 'part_direction', 'none' );
            partSpeed       = get( 'part_speed', 3 );
            partSize        = get( 'part_size', 3 );
            partHoverEffect = ( 'none' !== part_hover_effect ) ? part_hover_effect : 'repulse';
            partHoverSize   = get( 'part_hover_size', 0 );
        } else if ( 'nasa' === type ) {
            lineLinked      = false;
            partShapeType   = 'star';
            partMoveRand    = true;
            partSizeRandom  = true;
            partQuantity    = get( 'part_quantity', 110 );
            partOpacity     = get( 'part_opacity', 1 );
            partRandOpacity = !! get( 'part_rand_opacity', true );
            partDirection   = get( 'part_direction', 'none' );
            partSpeed       = get( 'part_speed', 1 );
            partSize        = get( 'part_size', 3 );
            partHoverEffect = ( 'none' !== part_hover_effect ) ? part_hover_effect : 'bubble';
            lineColor       = ( 'grab' === part_hover_effect ) ? get( 'line_hover_color', '#BAB9B9' ) : '#BAB9B9';
            partHoverSize   = get( 'part_hover_size', 0 );
        } else if ( 'bubble' === type ) {
            lineLinked      = false;
            partShapeType   = 'circle';
            partMoveRand    = true;
            partSizeRandom  = false;
            partQuantity    = get( 'part_quantity', 6 );
            partOpacity     = get( 'part_opacity', 0.6 );
            partRandOpacity = !! get( 'part_rand_opacity', false );
            partDirection   = get( 'part_direction', 'none' );
            partSpeed       = get( 'part_speed', 10 );
            partSize        = get( 'part_size', 160 );
            partHoverEffect = ( 'none' !== part_hover_effect ) ? part_hover_effect : 'none';
            lineColor       = ( 'grab' === part_hover_effect ) ? get( 'line_hover_color', '#BAB9B9' ) : '#BAB9B9';
            partHoverSize   = get( 'part_hover_size', 0 );
        } else if ( 'snow' === type ) {
            partColor       = get( 'part_color', '#DEF4FD' );
            lineLinked      = false;
            partShapeType   = 'circle';
            partMoveRand    = false;
            partSizeRandom  = true;
            partQuantity    = get( 'part_quantity', 300 );
            partOpacity     = get( 'part_opacity', 0.5 );
            partRandOpacity = !! get( 'part_rand_opacity', true );
            partDirection   = get( 'part_direction', 'bottom' );
            partSpeed       = get( 'part_speed', 5 );
            partSize        = get( 'part_size', 10 );
            partHoverEffect = ( 'none' !== part_hover_effect ) ? part_hover_effect : 'bubble';
            lineColor       = ( 'grab' === part_hover_effect ) ? get( 'line_hover_color', '#DEF4FD' ) : '#DEF4FD';
            partHoverSize   = get( 'part_hover_size', 4 );
        } else {
            return null;
        }

        showHoverEffect = ( 'noeffect' !== part_hover_effect );

        return {
            "particles": {
                "number": {
                    "value": partQuantity,
                    "density": { "enable": true, "value_area": 800 }
                },
                "color": { "value": partColor },
                "shape": {
                    "type": partShapeType,
                    "stroke":  { "width": 0, "color": "#000000" },
                    "polygon": { "nb_sides": 5 },
                    "image":   { "src": "", "width": 0, "height": 0 }
                },
                "opacity": {
                    "value":  partOpacity,
                    "random": partRandOpacity,
                    "anim":   { "enable": true, "speed": 1, "opacity_min": 0.1, "sync": false }
                },
                "size": {
                    "value":  partSize,
                    "random": partSizeRandom,
                    "anim":   { "enable": false, "speed": 40, "size_min": 0.1, "sync": false }
                },
                "line_linked": {
                    "enable":   lineLinked,
                    "distance": 150,
                    "color":    lineColor,
                    "opacity":  0.4,
                    "width":    1
                },
                "move": {
                    "enable":    true,
                    "speed":     partSpeed,
                    "direction": partDirection,
                    "random":    partMoveRand,
                    "straight":  false,
                    "out_mode":  "out",
                    "bounce":    false,
                    "attract":   { "enable": false, "rotateX": 600, "rotateY": 1200 }
                }
            },
            "interactivity": {
                "detect_on": "canvas",
                "events": {
                    "onhover":  { "enable": showHoverEffect, "mode": partHoverEffect },
                    "onclick":  { "enable": true, "mode": "repulse" },
                    "resize":   true
                },
                "modes": {
                    "grab":    { "distance": 400, "line_linked": { "opacity": 0.5 } },
                    "bubble":  { "distance": 400, "size": partHoverSize, "duration": 0.3, "opacity": 1, "speed": 3 },
                    "repulse": { "distance": 200, "duration": 0.4 },
                    "push":    { "particles_nb": 4 },
                    "remove":  { "particles_nb": 2 }
                }
            },
            "retina_detect": true
        };
    }

    // ===== Vanta lifecycle =====

    function destroyVanta( $wrapper ) {
        var existing = $wrapper.data( 'pp-vanta-instance' );
        if ( existing && typeof existing.destroy === 'function' ) {
            existing.destroy();
        }
        $wrapper.removeData( 'pp-vanta-instance' );
        $wrapper.removeData( 'pp-vanta-type' );
    }

    function vantaFactory( type ) {
        if ( typeof window.VANTA === 'undefined' ) {
            return null;
        }
        switch ( type ) {
            case 'birds': return VANTA.BIRDS;
            case 'fog':   return VANTA.FOG;
            case 'waves': return VANTA.WAVES;
            case 'net':   return VANTA.NET;
            case 'dots':  return VANTA.DOTS;
        }
        return null;
    }

    function runVanta( $wrapper, type, options ) {
        if ( typeof window.THREE === 'undefined' ) {
            return;
        }
        var factory = vantaFactory( type );
        if ( typeof factory !== 'function' ) {
            return;
        }
        destroyVanta( $wrapper );
        var instance = factory( options );
        $wrapper.data( 'pp-vanta-instance', instance );
        $wrapper.data( 'pp-vanta-type', type );
    }

    // ===== Particles lifecycle =====

    function destroyParticles( sectionId ) {
        if ( typeof window.pJSDom === 'undefined' ) {
            return;
        }
        var idStr = 'pp-background-' + sectionId;
        for ( var i = window.pJSDom.length - 1; i >= 0; i-- ) {
            var item = window.pJSDom[ i ];
            if ( item && item.pJS && item.pJS.canvas && item.pJS.canvas.el && item.pJS.canvas.el.id === idStr ) {
                try {
                    if ( item.pJS.fn && item.pJS.fn.vendors && typeof item.pJS.fn.vendors.destroypJS === 'function' ) {
                        item.pJS.fn.vendors.destroypJS();
                    }
                } catch ( e ) {}
                window.pJSDom.splice( i, 1 );
            }
        }
    }

    function runParticles( sectionId, particlesData ) {
        if ( typeof particlesJS !== 'function' || ! particlesData ) {
            return;
        }
        destroyParticles( sectionId );
        $( '#pp-background-' + sectionId ).find( 'canvas' ).remove();
        particlesJS( 'pp-background-' + sectionId, particlesData );
    }

    function runCustom( sectionId, customCode ) {
        if ( typeof particlesJS !== 'function' ) {
            return;
        }
        if ( customCode && typeof customCode === 'object' ) {
            destroyParticles( sectionId );
            $( '#pp-background-' + sectionId ).find( 'canvas' ).remove();
            particlesJS( 'pp-background-' + sectionId, customCode );
        }
    }

    // ===== Initial-render handler (runs from data-* attrs on element_ready) =====

    var BgHandler = function ( $scope ) {

        if ( ! $scope.hasClass( 'pp-bg-effects-yes' ) ) {
            return;
        }

        var bg_row_elem    = elementorFrontend.isEditMode() ? $scope.find( '.pp-background-wrapper' ) : $scope,
            section_id     = $scope.data( 'id' ),
            animation_type = bg_row_elem.data( 'animation-type' ),
            hideMaxWidth   = bg_row_elem.data( 'hide-max-width' ),
            hideMinWidth   = bg_row_elem.data( 'hide-min-width' ),
            winWidth       = $( window ).width();

        var prepend_content = function ( opacity ) {
            if ( ! elementorFrontend.isEditMode() ) {
                var content = $( '<div class="pp-background-wrapper" id="pp-background-' + section_id + '" style="opacity: ' + opacity + ';"></div>' );

                if ( $scope.find( '.elementor-background-overlay' ).length === 0 ) {
                    bg_row_elem.prepend( content );
                } else {
                    content.insertBefore( bg_row_elem.find( '.elementor-container' ) );
                }
            } else {
                var bg_overlay      = $scope.find( '.elementor-element-overlay ~ .elementor-background-overlay' ),
                    bg_effects_wrap = $scope.find( '#pp-background-' + section_id );

                if ( bg_overlay.next( '#pp-background-' + section_id ).length === 0 ) {
                    bg_overlay.after( bg_effects_wrap );
                }

                if ( 'particles' !== animation_type && 'nasa' !== animation_type && 'bubble' !== animation_type && 'snow' !== animation_type && 'custom' !== animation_type ) {
                    bg_row_elem.css( 'opacity', opacity );
                }
            }
        };

        var init_effect = function () {
            if ( ! ( winWidth > hideMaxWidth || winWidth < hideMinWidth || 'none' === hideMaxWidth ) ) {
                return;
            }

            var get = dataGetter( bg_row_elem );

            if ( 'particles' === animation_type || 'nasa' === animation_type || 'bubble' === animation_type || 'snow' === animation_type ) {
                prepend_content( '' );
                runParticles( section_id, buildParticlesData( animation_type, get ) );
            } else if ( 'custom' === animation_type ) {
                prepend_content( '' );
                runCustom( section_id, bg_row_elem.data( 'custom-code' ) );
            } else if ( vantaBuilders[ animation_type ] ) {
                prepend_content( bg_row_elem.data( 'canvas-opacity' ) );
                runVanta( bg_row_elem, animation_type, vantaBuilders[ animation_type ]( get, section_id ) );
            }
        };

        if ( elementorFrontend.isEditMode() ) {
            init_effect();
        } else {
            setTimeout( init_effect, 0 );
        }

    };

    // ===== Editor live-update path (runs on settings change, no DOM rebuild) =====

    function applyLiveUpdate( elementModel ) {
        var elId = elementModel.get( 'id' );
        var $scope = $( '.elementor-element[data-id="' + elId + '"]' ).first();
        if ( ! $scope.length || ! $scope.hasClass( 'pp-bg-effects-yes' ) ) {
            return;
        }
        var s = elementModel.get( 'settings' ).toJSON();
        if ( 'yes' !== s.pp_background_effects_enable ) {
            return;
        }

        var $wrapper = $scope.find( '#pp-background-' + elId );
        if ( ! $wrapper.length ) {
            return;
        }

        var type = s.pp_animation_type;
        var get  = settingsGetter( s );

        if ( vantaBuilders[ type ] ) {
            $wrapper.css( 'opacity', val( s.vanta_bg_opacity, 1 ) );
            runVanta( $wrapper, type, vantaBuilders[ type ]( get, elId ) );
        } else if ( 'particles' === type || 'nasa' === type || 'bubble' === type || 'snow' === type ) {
            runParticles( elId, buildParticlesData( type, get ) );
        } else if ( 'custom' === type ) {
            runCustom( elId, s.part_custom_code );
        }
    }

    function getEditorScope() {
        if ( typeof elementor !== 'undefined' && elementor.channels ) {
            return elementor;
        }
        try {
            if ( window.parent && window.parent !== window && window.parent.elementor && window.parent.elementor.channels ) {
                return window.parent.elementor;
            }
        } catch ( e ) {}
        return null;
    }

    var editorListenerRegistered = false;
    function ensureEditorListener() {
        if ( editorListenerRegistered ) {
            return;
        }
        var ed = getEditorScope();
        if ( ! ed || ! ed.channels.editor ) {
            return;
        }
        editorListenerRegistered = true;
        ed.channels.editor.on( 'change', function ( controlView, elementView ) {
            if ( ! elementView || ! elementView.model ) {
                return;
            }
            var elType = elementView.model.get( 'elType' );
            if ( 'section' !== elType && 'container' !== elType ) {
                return;
            }
            applyLiveUpdate( elementView.model );
        });
    }

    // ===== Wiring =====

    $( window ).on( 'elementor/frontend/init', function () {
        elementorFrontend.hooks.addAction( 'frontend/element_ready/global', function ( $scope ) {
            BgHandler( $scope );
            if ( elementorFrontend.isEditMode() ) {
                ensureEditorListener();
            }
        });
    });

}(jQuery));
