<?php // https://divibooster.com/enhancing-divi-gallery-slider-with-cursor-following-arrows/

namespace DiviBooster\GalleryBooster\GalleryCursorArrows;

const DIVI4_MODULE_SLUG = 'et_pb_gallery';
const DIVI5_MODULE_SLUG = 'divi/gallery';
const DIVI4_SETTING_SLUG = 'dbdb_cursor_arrows';
const DIVI5_SETTING_SLUG = 'dbdbCursorArrows';

// === Divi 5 Attribute Registration (PHP) ===
add_filter('divi_module_library_register_module_attrs', function($module_attrs, $filter_args) {
    if (($filter_args['name'] ?? '') !== DIVI5_MODULE_SLUG) {
        return $module_attrs;
    }
    $module_attrs[DIVI5_SETTING_SLUG] = get_divi5_attribute_definition();
    return $module_attrs;
}, 10, 2);

function get_divi5_attribute_definition() {
    return [
        'type'     => 'object',
        'settings' => [
            'innerContent' => [
                'groupType' => 'group-items',
                'items'     => [
                    'cursorArrows' => [
                        'groupSlug'   => 'contentElements',
                        'attrName'    => DIVI5_SETTING_SLUG,
                        'label'       => esc_html__('Use Cursor-Following Arrow Effect', 'et_builder'),
                        'description' => esc_html__('Replace the static previous / next arrows with an arrow that follows the mouse. Depending which half of the gallery the mouse is in, the previous or next arrow will replace the mouse cursor and clicking will trigger that effect. On mobile, the arrows won\'t show but tapping on (or swiping from) the left half of the gallery will move to the previous slide and tapping on (or swiping from) the right half will move to the next slide. Note that images won\'t open in a lightbox when this feature is enabled.', 'divi-booster'),
                        'features'    => [
                            'hover'      => false,
                            'sticky'     => false,
                            'responsive' => false,
                            'preset'     => 'elements',
                        ],
                        'render'   => true,
                        'priority' => 99,
                        'component' => [
                            'type' => 'field',
                            'name' => 'divi/toggle',
                        ],
                        'defaultAttr' => [ 'desktop' => [ 'value' => 'off' ] ],
                        'options' => [
                            'on'  => esc_html__('Yes', 'et_builder'),
                            'off' => esc_html__('No', 'et_builder'),
                        ],
                    ],
                ],
            ],
        ],
    ];
}

// === Divi 5 JS: Attribute registration and conversion ===
add_action('wp_enqueue_scripts', __NAMESPACE__ . '\enqueue_divi5_script');
function enqueue_divi5_script() {
    if ((!function_exists('et_builder_d5_enabled') || !\et_builder_d5_enabled()) ||
        (!function_exists('et_core_is_fb_enabled') || !\et_core_is_fb_enabled())) {
        return;
    }
    $handle = sanitize_title('divi-booster-' . DIVI5_MODULE_SLUG . '-' . DIVI5_SETTING_SLUG);
    \dbdb_enqueue_inline_script_if_dependencies_available($handle, get_divi5_inline_js(), array('lodash', array('divi-vendor-wp-hooks', 'wp-hooks')));
}
function get_divi5_inline_js() {
    $attribute_json = wp_json_encode(get_divi5_attribute_definition());
    $divi4_slug = wp_json_encode(DIVI4_MODULE_SLUG);
    $divi5_setting_slug = wp_json_encode(DIVI5_SETTING_SLUG);
    $divi4_setting_slug = wp_json_encode(DIVI4_SETTING_SLUG);
    return <<<END
// Adds the custom attribute to the Gallery module.
window.vendor.wp.hooks.addFilter('divi.moduleLibrary.moduleAttributes.divi.gallery', 'divi', (attributes, metadata) => {
    attributes[{$divi5_setting_slug}] = $attribute_json;
    // Add visible callback to cursorArrows
    if (attributes[{$divi5_setting_slug}]?.settings?.innerContent?.items?.cursorArrows) {
        attributes[{$divi5_setting_slug}].settings.innerContent.items.cursorArrows.visible = function({ attrs }) {
            // Only show if fullwidth is on (slider mode)
            return attrs?.module?.advanced?.fullwidth?.desktop?.value === 'on';
        };
    }
    return attributes;
});
window.vendor.wp.hooks.addFilter('divi.moduleLibrary.conversion.moduleConversionOutline', 'divi', (conversionOutline, name) => {
    if (name !== {$divi4_slug}) return conversionOutline;
    conversionOutline.module[{$divi4_setting_slug}] = {$divi5_setting_slug} + '.*';
    return conversionOutline;
});
END;
}

// === Divi 4 Classic Builder Field Registration ===
add_filter('et_pb_all_fields_unprocessed_' . DIVI4_MODULE_SLUG, __NAMESPACE__ . '\add_field');
// === Divi 5: PHP conversion outline registration ===
function dbdb_register_divi5_cursor_arrows_conversion( $conversion_outline, $module_name ) {
    if ( DIVI5_MODULE_SLUG !== $module_name ) {
        return $conversion_outline;
    }
    if (!isset($conversion_outline['module']) || !is_array($conversion_outline['module'])) {
        $conversion_outline['module'] = array();
    }
    $conversion_outline['module'][DIVI4_SETTING_SLUG] = DIVI5_SETTING_SLUG . '.*';
    return $conversion_outline;
}
add_filter( 'divi.moduleLibrary.conversion.moduleConversionOutline', __NAMESPACE__ . '\dbdb_register_divi5_cursor_arrows_conversion', 10, 2 );
function add_field($fields) {
    if (!is_array($fields)) {
        return $fields;
    }
    $fields[DIVI4_SETTING_SLUG] = array(
        'label'             => esc_html__('Use Cursor-Following Arrow Effect', 'et_builder'),
        'type'              => 'yes_no_button',
        'option_category'   => 'configuration',
        'options'           => array(
            'on'  => esc_html__('Yes', 'et_builder'),
            'off' => esc_html__('No', 'et_builder'),
        ),
        'default'  => 'off',
        'toggle_slug'      => 'elements',
        'description'       => esc_html__('Replace the static previous / next arrows with an arrow that follows the mouse. Depending which half of the gallery the mouse is in, the previous or next arrow will replace the mouse cursor and clicking will trigger that effect. On mobile, the arrows won\'t show but tapping on (or swiping from) the left half of the gallery will move to the previous slide and tapping on (or swiping from) the right half will move to the next slide. Note that images won\'t open in a lightbox when this feature is enabled.', 'divi-booster'),
        'show_if' => array(
            'fullwidth' => 'on',
        ),
        'class'=>'hide-warning',
    );
    $fields['dbdb_cursor_arrows_warning'] = array(
        'type'              => 'warning',
        'tab_slug'          => 'general',
        'toggle_slug'      => 'elements',
        'message'       => esc_html__('This feature will only show on the front end, not in the Visual Builder preview.', 'divi-booster'),
        'show_if' => array('dbdb_cursor_arrows' => 'on', 'fullwidth' => 'on'),
        'value' => true,
        'display_if' => true,
    );
    return $fields;
}

// === Common Output Logic (shared by Divi 4 and Divi 5) ===
function output_cursor_arrows_script_once() {
    static $done = false;
    if ($done) return;
    $done = true;
    ?>
    <style>
        /* Hide the cursor */
        body:not(.et-fb) .et_pb_gallery.dbdb-cursor-arrows.et_pb_slider *,
        body:not(.et-fb) .et_pb_gallery.dbdb-cursor-arrows.et_pb_slider .et-pb-slider-arrows a {
            cursor: none !important;
        }
        /* Make the arrows fixed */
        .et_pb_gallery.dbdb-cursor-arrows.et_pb_slider .et-pb-arrow-prev,
        .et_pb_gallery.dbdb-cursor-arrows.et_pb_slider .et-pb-arrow-next {
            display: none;
            position: fixed;
            z-index: 9999;
            transition: none;
        }
    </style>
    <script>
        jQuery(document).ready(function($) {
            var isTouching = false;
            function moveArrow($arrow, e) {
                if (!isTouching) {
                    $arrow.css({
                        'display': 'block',
                        'left': (e.clientX - 24) + 'px',
                        'top': e.clientY + 'px'
                    });
                }
            }
            function triggerClick($arrow) {
                $arrow.click();
            }
            $(document).on('pointermove', '.et_pb_gallery.dbdb-cursor-arrows.et_pb_slider', function(e) {
                var $thisGallery = $(this);
                var galleryOffset = $thisGallery.offset();
                var galleryWidth = $thisGallery.width();
                var galleryHeight = $thisGallery.height();
                var mouseX = e.clientX - galleryOffset.left;
                var mouseY = (e.clientY + $(window).scrollTop()) - galleryOffset.top;
                var isInsideGallery = (mouseX > 0 && mouseX < galleryWidth && mouseY > 0 && mouseY < galleryHeight);
                $thisGallery.find('.et-pb-arrow-prev, .et-pb-arrow-next').hide();
                if (isInsideGallery) {
                    if (mouseX < galleryWidth / 2) {
                        moveArrow($thisGallery.find('.et-pb-arrow-prev'), e);
                    } else {
                        moveArrow($thisGallery.find('.et-pb-arrow-next'), e);
                    }
                }
            });
            $('.et_pb_gallery.dbdb-cursor-arrows.et_pb_slider').on('touchstart', function(e) {
                isTouching = true;
                var startTouch = e.originalEvent.touches[0];
                var startX = startTouch.pageX;
                var startY = startTouch.pageY;
                var scrolledVertically = false;
                var $thisGallery = $(this);
                $thisGallery.on('touchmove', function(moveEvent) {
                    var moveTouch = moveEvent.originalEvent.touches[0];
                    var changeX = moveTouch.pageX - startX;
                    var changeY = moveTouch.pageY - startY;
                    if (Math.abs(changeY) > Math.abs(changeX)) {
                        scrolledVertically = true;
                        return;
                    }
                    moveEvent.preventDefault();
                }).on('touchend', function(endEvent) {
                    $thisGallery.off('touchmove touchend');
                    if (scrolledVertically) {
                        scrolledVertically = false;
                        isTouching = false;
                        return;
                    }
                    var endTouch = endEvent.originalEvent.changedTouches[0];
                    var endX = endTouch.pageX;
                    var galleryWidth = $thisGallery.width();
                    var changeX = endX - startX;
                    if (Math.abs(changeX) < 10) {
                        var galleryOffset = $thisGallery.offset();
                        var touchX = startX - galleryOffset.left;
                        if (touchX < galleryWidth / 2) {
                            triggerClick($thisGallery.find('.et-pb-arrow-prev'));
                        } else {
                            triggerClick($thisGallery.find('.et-pb-arrow-next'));
                        }
                    } else if (changeX > 0) {
                        triggerClick($thisGallery.find('.et-pb-arrow-prev'));
                    } else {
                        triggerClick($thisGallery.find('.et-pb-arrow-next'));
                    }
                    var event = $.Event('divi-booster:gallery-slide-changed');
                    $thisGallery.trigger(event);
                    isTouching = false;
                });
                e.stopPropagation();
            });
        });
    </script>
    <?php
}

// === Divi 4 Output Filter ===
add_filter('et_module_shortcode_output', function($output, $render_slug, $module) {
    if (!is_string($output) || $render_slug !== DIVI4_MODULE_SLUG || !isset($module->props)) {
        return $output;
    }
    $props = $module->props;
    if (!isset($props['fullwidth']) || $props['fullwidth'] !== 'on') {
        return $output;
    }
    if (empty($props[DIVI4_SETTING_SLUG]) || $props[DIVI4_SETTING_SLUG] !== 'on') {
        return $output;
    }
    add_action('wp_footer', __NAMESPACE__ . '\output_cursor_arrows_script_once');
    return $output;
}, 10, 3);

// === Divi 4 Output Class Injection ===
add_filter('et_module_shortcode_output', function($output, $render_slug, $module) {
    if (!is_string($output) || $render_slug !== DIVI4_MODULE_SLUG || !isset($module->props) || !is_array($module->props)) {
        return $output;
    }
    $props = $module->props;
    // Only add class if fullwidth is on and feature is enabled
    if (
        isset($props['fullwidth']) && $props['fullwidth'] === 'on' &&
        !empty($props[DIVI4_SETTING_SLUG]) && $props[DIVI4_SETTING_SLUG] === 'on'
    ) {
        $output = preg_replace_callback(
            '/class="([^"]*?et_pb_module et_pb_gallery[^"]*?)"/',
            function ($matches) {
                $classes = explode(' ', $matches[1]);
                if (!in_array('dbdb-cursor-arrows', $classes, true)) {
                    $classes[] = 'dbdb-cursor-arrows';
                }
                return 'class="' . implode(' ', $classes) . '"';
            },
            $output
        );
    }
    return $output;
}, 9, 3);

// === Divi 5 Block Output Filter ===
add_filter('render_block_' . DIVI5_MODULE_SLUG, function($block_content, $parsed_block, $block) {
    $attrs = $parsed_block['attrs'] ?? [];
    $layout_is_slider = $attrs['module']['advanced']['fullwidth']['desktop']['value'] ?? 'off';
    $effect_enabled = $attrs[DIVI5_SETTING_SLUG]['desktop']['value'] ?? ($attrs[DIVI5_SETTING_SLUG]['value'] ?? 'off');
    if ($layout_is_slider !== 'on' || $effect_enabled !== 'on') {
        return $block_content;
    }
    // Inject dbdb-cursor-arrows class into the gallery block output for Divi 5 (class order may differ)
    $block_content = preg_replace_callback(
        '/class="([^"]*?\bet_pb_gallery\b[^"]*?)"/',
        function ($matches) {
            $classes = preg_split('/\s+/', $matches[1]);
            if (!in_array('dbdb-cursor-arrows', $classes, true)) {
                // Insert after et_pb_gallery if present, else append
                $pos = array_search('et_pb_gallery', $classes, true);
                if ($pos !== false) {
                    array_splice($classes, $pos + 1, 0, 'dbdb-cursor-arrows');
                } else {
                    $classes[] = 'dbdb-cursor-arrows';
                }
            }
            return 'class="' . implode(' ', $classes) . '"';
        },
        $block_content
    );
    add_action('wp_footer', __NAMESPACE__ . '\output_cursor_arrows_script_once');
    return $block_content;
}, 10, 3);

