<?php

namespace EmbedPress;

use Embera\Embera;
use Embera\ProviderCollection\DefaultProviderCollection;
use EmbedPress\Includes\Classes\Helper;
use WP_oEmbed;

(defined('ABSPATH') && defined('EMBEDPRESS_IS_LOADED')) or die("No direct script access allowed.");

/**
 * Entity responsible to handle the plugin's shortcode events and behaviors.
 *
 * @package     EmbedPress
 * @author      EmbedPress <help@embedpress.com>
 * @copyright   Copyright (C) 2023 WPDeveloper. All rights reserved.
 * @license     GPLv3 or later
 * @since       1.0.0
 */

class Shortcode
{
    /**
     * The WP_oEmbed class instance.
     *
     * @since   1.0.0
     * @access  private
     * @static
     *
     * @var     WP_oEmbed $oEmbedInstance
     */


    public static $y = 0;

    private static $oEmbedInstance = null;

    /**
     * The Embera class instance.
     *
     * @since   2.7.4
     * @access  private
     * @static
     *
     * @var     Embera $embera_instance
     */
    private static $embera_instance = null;
    /**
     * The DefaultProviderCollection class instance.
     *
     * @since   1.0.0
     * @access  private
     * @static
     *
     * @var     DefaultProviderCollection $collection
     */
    private static $collection = null;

    private static $emberaInstanceSettings = [];
    private static $ombed_attributes;
    public static $attributes_data;

    public static function shortcode_scripts()
    {
        // Assets are now handled by AssetManager
        // This function is kept for backward compatibility but does nothing
        // AssetManager automatically handles shortcode assets when EmbedPress shortcodes are detected
    }

    /**
     * Register the plugin's shortcode into WordPress.
     *
     * @return  void
     * @since   1.0.0
     * @static
     *
     */
    public static function register()
    {
        // Register the new shortcode for embeds.
        add_shortcode(EMBEDPRESS_SHORTCODE, ['\\EmbedPress\\Shortcode', 'do_shortcode']);
        add_shortcode('embed_oembed_html', ['\\EmbedPress\\Shortcode', 'do_shortcode']);
        add_shortcode('embedpress', ['\\EmbedPress\\Shortcode', 'do_shortcode']);
        add_shortcode('embedpress_pdf', ['\\EmbedPress\\Shortcode', 'do_shortcode_pdf']);
        add_shortcode('embedpress_doc', ['\\EmbedPress\\Shortcode', 'do_shortcode_doc']);
        add_shortcode('embedpress_pdf_gallery', ['\\EmbedPress\\Shortcode', 'do_shortcode_pdf_gallery']);
    }

    /**
     * Method that converts the plugin shortcoded-string into its complex content.
     *
     * @param array $attributes Array of attributes
     * @param string $subject The given string
     *
     * @return  string
     * @since   1.0.0
     * @static
     *
     */



    public static function content_protection_content($client_id = '', $protection_message = '', $allowed_roles = [])
    {
        $default_message = "This content is protected. Please log in or contact the administrator for access.";
        $protection_message = $protection_message ?: $default_message;

        $current_user = wp_get_current_user();
        $user_roles = $current_user->roles;

        if (!empty($user_roles) && array_intersect($user_roles, $allowed_roles)) {
            echo '';
            return;
        }

        if (strpos($protection_message, '[user_roles]') !== false) {
            $role_list = implode(', ', $allowed_roles);
            $protection_message = str_replace('[user_roles]', $role_list, $protection_message);
        }

        return sprintf('<div class="protected-message">%s</div>', esc_html($protection_message));
    }

    public static function display_password_form($client_id = '', $embedHtml = '', $pass_hash_key = '', $attributes = [])
    {
        $lock_heading = !empty($attributes['lockHeading']) ? sanitize_text_field($attributes['lockHeading']) : 'Content Locked';
        $lock_subheading = !empty($attributes['lockSubHeading']) ? sanitize_text_field($attributes['lockSubHeading']) : 'Content is locked and requires password to access it.';
        $lock_error_message = !empty($attributes['lockErrorMessage']) ? sanitize_text_field($attributes['lockErrorMessage']) : "Oops, that wasn't the right password. Try again.";
        $footer_message = !empty($attributes['footerMessage']) ? sanitize_text_field($attributes['footerMessage']) : "In case you don't have the password, kindly reach out to content owner or administrator to request access.";
        $password_placeholder = !empty($attributes['passwordPlaceholder']) ? sanitize_text_field($attributes['passwordPlaceholder']) : 'Password';
        $button_text = !empty($attributes['submitButtonText']) ? sanitize_text_field($attributes['submitButtonText']) : 'Unlock';
        $unlocking_text = !empty($attributes['submitUnlockingText']) ? sanitize_text_field($attributes['submitUnlockingText']) : 'Unlocking';
        $enable_footer_message = !empty($attributes['enableFooterMessage']);

        $key = Helper::get_hash();
        $salt = wp_salt(32);
        $wp_hash_key = hash('sha256', $salt . $pass_hash_key);
        $iv = substr($wp_hash_key, 0, 16);

        $cipher = openssl_encrypt($embedHtml, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
        $encrypted_data = base64_encode($cipher);

        update_post_meta(get_the_ID(), 'ep_base_' . $client_id, $encrypted_data);
        update_post_meta(get_the_ID(), 'hash_key_' . $client_id, $wp_hash_key);

        $lock_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><g fill="#6354a5" class="color134563 svgShape"><path d="M46.3 28.7h-3v-6.4C43.3 16.1 38.2 11 32 11c-6.2 0-11.3 5.1-11.3 11.3v6.4h-3v-6.4C17.7 14.4 24.1 8 32 8s14.3 6.4 14.3 14.3v6.4" fill="#6354a5" class="color000000 svgShape"></path><path d="M44.8 55.9H19.2c-2.6 0-4.8-2.2-4.8-4.8V31.9c0-2.6 2.2-4.8 4.8-4.8h25.6c2.6 0 4.8 2.2 4.8 4.8v19.2c0 2.7-2.2 4.8-4.8 4.8zM19.2 30.3c-.9 0-1.6.7-1.6 1.6v19.2c0 .9.7 1.6 1.6 1.6h25.6c.9 0 1.6-.7 1.6-1.6V31.9c0-.9-.7-1.6-1.6-1.6H19.2z" fill="#6354a5" class="color000000 svgShape"></path><path d="M35.2 36.7c0 1.8-1.4 3.2-3.2 3.2s-3.2-1.4-3.2-3.2 1.4-3.2 3.2-3.2 3.2 1.5 3.2 3.2" fill="#6354a5" class="color000000 svgShape"></path><path d="M32.8 36.7h-1.6l-1.6 9.6h4.8l-1.6-9.6" fill="#6354a5" class="color000000 svgShape"></path></g></svg>';

        return '
        <div id="ep-shortcode-content-' . $client_id . '" class="ep-shortcode-content">	
            <div class="ep-embed-content-wraper">
                <div class="password-form-container">
                    <h2>' . esc_html($lock_heading) . '</h2>
                    <p>' . esc_html($lock_subheading) . '</p>
                    <form class="password-form" method="post" data-unlocking-text="' . esc_attr($unlocking_text) . '">
                        <div class="password-field">
                            <span class="lock-icon">' . $lock_icon . '</span>
                            <input type="password" name="pass_' . esc_attr($client_id) . '" placeholder="' . esc_attr($password_placeholder) . '" required>
                        </div>
                        <input type="hidden" name="ep_client_id" value="' . esc_attr($client_id) . '">
                        <input type="hidden" name="post_id" value="' . esc_attr(get_the_ID()) . '">
                        <input type="submit" name="password_submit" value="' . esc_attr($button_text) . '">
                        <div class="error-message hidden">' . esc_html($lock_error_message) . '</div>
                    </form>
                    ' . ($enable_footer_message ? '<p class="need-access-message">' . esc_html($footer_message) . '</p>' : '') . '
                </div>
            </div>
        </div>';
    }


    public static function do_shortcode($attributes = [], $subject = null)
    {
        $plgSettings = Core::getSettings();


        $default = [];
        if ($plgSettings->enableGlobalEmbedResize) {
            $default = [
                'width'  => esc_attr($plgSettings->enableEmbedResizeWidth),
                'height' => esc_attr($plgSettings->enableEmbedResizeHeight),
                'powered_by' => !empty($plgSettings->embedpress_document_powered_by) ? esc_attr($plgSettings->embedpress_document_powered_by) : esc_attr('no'),
            ];
        }

        if (is_array($attributes)) {
            $attributes = array_map('esc_attr', $attributes);
        }

        $attributes = wp_parse_args($attributes, $default);

        // ACF dynamic field support: [embedpress]embedpress_my_field[/embedpress]
        if (!empty($subject)) {
            $raw = trim(preg_replace(
                '/(\[' . EMBEDPRESS_SHORTCODE . '(?:\]|.+?\])|\[\/' . EMBEDPRESS_SHORTCODE . '\])/i',
                "",
                $subject
            ));
            if (!empty($raw) && strpos($raw, 'embedpress_') === 0) {
                $field_name = substr($raw, strlen('embedpress_'));
                $resolved = self::resolveAcfFieldUrl($field_name);
                if (!empty($resolved)) {
                    $subject = $resolved;
                }
            }
        }

        $embed = self::parseContent($subject, true, $attributes);


        $client_id = is_object($embed) ? md5($embed->embed)  : '';

        $hash_pass = isset($attributes['protection_password'])
            ? hash('sha256', wp_salt(32) . md5($attributes['protection_password']))
            : '';

        $pass_hash_key = isset($attributes['protection_password'])
            ? md5($attributes['protection_password'])
            : '';

        $password_correct = $_COOKIE['password_correct_' . $client_id] ?? '';

        $protection_type = $attributes['protection_type'] ?? 'user-role';
        $protection_password = $attributes['protection_password'] ?? '';
        $protection_content = isset($attributes['protection_content']) ? $attributes['protection_content'] === 'true' : false;
        $user_role = isset($attributes['user_roles']) ? explode(',', preg_replace('/\s*,\s*/', ',', $attributes['user_roles'])) : '';
        $protection_message = $attributes['protection_message'] ?? '';


        // Conditions for content protection
        $password_protected = $protection_type == 'password' && !empty($protection_password);

        $password_verified = $password_protected && !empty(Helper::is_password_correct($client_id)) && ($hash_pass === $password_correct);
        $user_role_protected = $protection_type === 'user-role' && Helper::has_allowed_roles($user_role);

        if (
            apply_filters('embedpress/is_allow_rander', false) && ($protection_content == 'true') && (($protection_type == 'password' && !$password_verified) || ($protection_type == 'user-role' && !Helper::has_allowed_roles($user_role)))
        ) {
            return $password_protected
                ? self::display_password_form($client_id, $embed->embed, $pass_hash_key, $attributes)
                : self::content_protection_content($client_id, $protection_message, $user_role);
        }


        if (is_object($embed)) {
            $array = get_object_vars($embed);
            if (!empty($array[$embed->url]['provider_name']) && $array[$embed->url]['provider_name'] === 'Instagram Feed') {
                // shortcode_parse_atts() lowercases keys, so `instaLayout` arrives as `instalayout`.
                $attr_ci = array_change_key_case((array) $attributes, CASE_LOWER);
                $layout = isset($attr_ci['instalayout']) ? $attr_ci['instalayout'] : 'insta-grid';
                $allowed_layouts = ['insta-grid', 'insta-masonry', 'insta-carousel', 'insta-justify'];
                if (!in_array($layout, $allowed_layouts, true)) {
                    $layout = 'insta-grid';
                }

                // For carousel layout, the front-end JS (assets/js/instafeed.js) looks
                // up `data-carouselid` + `data-carousel-options` on the wrapper to
                // initialize CgCarousel and unhide the nav buttons. Without these the
                // shortcode-rendered carousel has no arrows and no scroll behavior.
                $carousel_attrs = '';
                if ($layout === 'insta-carousel') {
                    $boolish = function ($v, $default) {
                        if ($v === null) return $default;
                        $v = is_string($v) ? strtolower($v) : $v;
                        return in_array($v, ['true', '1', 1, true], true);
                    };
                    $options = [
                        'layout'          => 'insta-carousel',
                        'slideshow'       => isset($attr_ci['slidesshow']) ? intval($attr_ci['slidesshow']) : 3,
                        'autoplay'        => $boolish($attr_ci['carouselautoplay'] ?? null, false),
                        'autoplayspeed'   => isset($attr_ci['autoplayspeed']) ? intval($attr_ci['autoplayspeed']) : 3000,
                        'transitionspeed' => isset($attr_ci['transitionspeed']) ? intval($attr_ci['transitionspeed']) : 650,
                        'loop'            => $boolish($attr_ci['carouselloop'] ?? null, false),
                        'arrows'          => $boolish($attr_ci['carouselarrows'] ?? null, true),
                        'spacing'         => isset($attr_ci['carouselspacing']) ? intval($attr_ci['carouselspacing']) : 10,
                    ];
                    $carousel_attrs = ' data-carouselid="' . esc_attr('epsc-' . substr(md5($embed->embed . microtime(true)), 0, 10))
                        . '" data-carousel-options="' . esc_attr(wp_json_encode($options)) . '"';
                }

                $embed->embed = '<div class="ep-embed-content-wraper ' . esc_attr($layout) . '"' . $carousel_attrs . '>' . $embed->embed . '</div>';
            };
        }

        return is_object($embed) ? $embed->embed : $embed;
    }

    /**
     * Replace a given content with its embeded HTML code.
     *
     * @param string      The raw content that will be replaced.
     * @param bool $stripNewLine
     * @param array $customAttributes
     * @return  string|object
     * @since   1.0.0
     * @static
     */
    public static function parseContent($subject, $stripNewLine = false, $customAttributes = [])
    {
        // Dynamic-source shortcodes carry no body — the URL lives in a custom
        // field, e.g. [embedpress dynamic_source="metabox" dynamic_field="pdf"].
        // Resolve it up front so it becomes the $subject and flows through the
        // normal render path below (the in-body resolution at the bottom only
        // runs when $subject is already non-empty). Pro-gated for parity with
        // the block + Elementor widget paths.
        if (
            empty($subject)
            && !empty($customAttributes['dynamic_source'])
            && !empty($customAttributes['dynamic_field'])
            && \EmbedPress\Includes\Classes\Helper::is_pro_features_enabled()
        ) {
            $resolved = \EmbedPress\Includes\Classes\DynamicFieldResolver::resolve_field(
                $customAttributes['dynamic_source'],
                $customAttributes['dynamic_field']
            );
            if ($resolved !== '') {
                $subject = $resolved;
            }
        }

        if (!empty($subject)) {
            if (empty($customAttributes)) {
                $customAttributes = self::parseContentAttributesFromString($subject);
            }
            self::set_default_size($customAttributes);
            $url = preg_replace(
                '/(\[' . EMBEDPRESS_SHORTCODE . '(?:\]|.+?\])|\[\/' . EMBEDPRESS_SHORTCODE . '\])/i',
                "",
                $subject
            );

            if (strpos($url, 'youtube.com/embed') !== false) {
                preg_match("/embed\/([a-zA-Z0-9_-]+)/", $url, $matches);

                if (isset($matches[1])) {
                    $videoId = $matches[1];
                    $url = 'https://www.youtube.com/watch?v=' . $videoId;
                }
            }


            $uniqid = 'ose-uid-' . md5($url);
            $subject = esc_url($subject);


            // Converts any special HTML entities back to characters.
            $url = htmlspecialchars_decode($url);
            $url = esc_url($url);

            // Dynamic-source resolution: blocks and shortcodes may declare a
            // custom-field source (ACF/MetaBox/Pods/Toolset/JetEngine/meta) via
            // attributes, so the URL is looked up per-post at render time. The
            // resolved URL replaces the (often-empty) saved one.
            // Shortcode form: [embedpress dynamic_source="metabox" dynamic_field="pdf_file"]
            // Pro-gated — see Helper::is_pro_features_enabled() for parity with
            // the block + Elementor widget paths.
            if (
                !empty($customAttributes['dynamic_source'])
                && !empty($customAttributes['dynamic_field'])
                && \EmbedPress\Includes\Classes\Helper::is_pro_features_enabled()
            ) {
                $resolved = \EmbedPress\Includes\Classes\DynamicFieldResolver::resolve_field(
                    $customAttributes['dynamic_source'],
                    $customAttributes['dynamic_field']
                );
                if ($resolved !== '') {
                    $url = esc_url($resolved);
                }
            }

            $url = apply_filters('embedpress/resolve_url', $url, $customAttributes);

            $content_uid = md5($url);

            self::$ombed_attributes = self::parseContentAttributes($customAttributes, $content_uid);

            self::set_embera_settings(self::$ombed_attributes);

            // Identify what service provider the shortcode's link belongs to
            $is_embra_provider = apply_filters('embedpress:isEmbra', false, $url, self::get_embera_settings());

            if ($is_embra_provider || (strpos($url, 'meetup.com') !== false) || (strpos($url, 'sway.office.com') !== false) || (strpos($url, 'wistia.com') !== false) || self::is_problematic_provider($url) ) {
                $serviceProvider = '';
            } else {
                $serviceProvider = self::get_oembed()->get_provider($url);
            }

            // FIX FOR MEETUP as MEETUP API is OFF, use OUR custom embed
            if ('https://api.meetup.com/oembed' === $serviceProvider) {
                $serviceProvider = '';
            }

            // Prevent self-referencing loop: if the provider URL points to our own
            // REST endpoint, skip WP oembed and let Embera handle it directly
            if (!empty($serviceProvider) && strpos($serviceProvider, 'embedpress/v1/oembed/') !== false) {
                $serviceProvider = '';
            }


            $urlData = self::get_url_data($url, self::$ombed_attributes, $serviceProvider);

            // Sanitize the data
            $urlData = self::sanitizeUrlData($urlData, $url);



            // Stores the original content
            if (is_object($urlData)) {
                $urlData->originalContent = $url;
            }

            $embedResults = apply_filters('embedpress:onBeforeEmbed', $urlData, $subject);

            if (empty($embedResults)) {
                return $subject;
            }

            // Transform all shortcode attributes into html form. I.e.: {foo: "joe"} -> foo="joe"
            $attributesHtml = ['class="ose-{provider_alias} ' . $uniqid . ' ose-embedpress-responsive"'];
            //$attributesHtml = [];
            //foreach ( self::$ombed_attributes as $attrName => $attrValue ) {
            //    $attributesHtml[] = $attrName . '="' . $attrValue . '"';
            //}

            // Check if $url is a google shortened url and tries to extract from it which Google service it refers to.
            self::check_for_google_url($url);
            $provider_name = self::get_provider_name($urlData, $url);

            // Get excluded height sources
            $excludedHeightSources = self::get_excluded_height_sources();
            $shouldExcludeHeight = in_array($provider_name, $excludedHeightSources);

            if (isset($customAttributes['height'])) {
                $height = esc_attr($customAttributes['height']);
            }

            if (isset($customAttributes['width'])) {
                $width = esc_attr($customAttributes['width']);
                // Only apply height to wrapper if provider is not excluded
                if ($shouldExcludeHeight) {
                    $attributesHtml[] = "style=\"width:{$width}px; max-width:100%; display:inline-block;\"";
                } else {
                    $attributesHtml[] = "style=\"width:{$width}px; height:{$height}px; max-height:{$height}px; max-width:100%; display:inline-block;\"";
                }
            }
            $provider_name = sanitize_text_field($provider_name);

            // $html = '{html}';
            // if (strpos($url, 'youtube') !== false) {
            //     $html = '<div class="youtube-video">{html}</div>';
            // }

            // Add data-embed-type attribute based on provider
            $embedType = Helper::get_provider_name($url);
            $embedTypeAttr = $embedType ? ' data-embed-type="' . esc_attr($embedType) . '"' : '';

            $embedTemplate = '<div ' . implode(' ', $attributesHtml) . $embedTypeAttr . '>{html}</div>';

            $parsedContent = self::get_content_from_template($url, $embedTemplate, $serviceProvider);
            // Replace all single quotes to double quotes. I.e: foo='joe' -> foo="joe"
            $parsedContent = str_replace("'", '"', $parsedContent);
            $parsedContent = str_replace("{provider_alias}", esc_html($provider_name), $parsedContent);

            // YT playlist layouts fill their container, so strip the
            // hard-coded width/height/inline-block from the outer wrapper
            // and tag it for CSS targeting. Detect the layout from the
            // wrapper we just emitted ourselves.
            $playlist_layout_class = '';
            $playlist_layouts_known = ['queue', 'theatre', 'library', 'spotlight', 'cinema', 'magazine'];
            foreach ($playlist_layouts_known as $pl) {
                if (strpos($parsedContent, 'ep-player-wrap layout-' . $pl) !== false) {
                    $playlist_layout_class = 'has-layout-' . $pl;
                    break;
                }
            }
            if (!empty($playlist_layout_class)) {
                $parsedContent = preg_replace(
                    '~<div class="ose-youtube ([^"]+)"\s+style="[^"]*"~',
                    '<div class="ose-youtube $1 ' . $playlist_layout_class . '"',
                    $parsedContent,
                    1
                );
            }
            $parsedContent = str_replace('sandbox="allow-scripts"', 'sandbox="allow-modals allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"', $parsedContent);
            $parsedContent = str_replace('<iframe ', '<iframe allowFullScreen="true" ', $parsedContent);

            self::purify_html_content($parsedContent);
            self::modify_content_for_fb_and_canada($provider_name, $parsedContent);
            unset($embedTemplate, $serviceProvider);

            // This assure that the iframe has the same dimensions the user wants to
            if (isset(self::$emberaInstanceSettings['maxwidth']) || isset(self::$emberaInstanceSettings['maxheight'])) {
                // Get excluded sources for height attribute
                $excludedHeightSources = self::get_excluded_height_sources();
                $shouldExcludeHeight = in_array($provider_name, $excludedHeightSources);

                if (isset(self::$emberaInstanceSettings['maxwidth']) && isset(self::$emberaInstanceSettings['maxheight'])) {
                    $customWidth = (int) self::$emberaInstanceSettings['maxwidth'];
                    $customHeight = (int) self::$emberaInstanceSettings['maxheight'];
                } else {
                    if (preg_match('~width="(\d+)"|width\s+:\s+(\d+)~i', $parsedContent, $matches)) {
                        $iframeWidth = (int) $matches[1];
                    }

                    if (preg_match('~height="(\d+)"|height\s+:\s+(\d+)~i', $parsedContent, $matches)) {
                        $iframeHeight = (int) $matches[1];
                    }

                    if (isset($iframeWidth) && isset($iframeHeight) && $iframeWidth > 0 && $iframeHeight > 0) {
                        $iframeRatio = ceil($iframeWidth / $iframeHeight);

                        if (isset(self::$emberaInstanceSettings['maxwidth'])) {
                            $customWidth = (int) self::$emberaInstanceSettings['maxwidth'];
                            $customHeight = ceil($customWidth / $iframeRatio);
                        } else {
                            $customHeight = (int) self::$emberaInstanceSettings['maxheight'];
                            $customWidth = $iframeRatio * $customHeight;
                        }
                    }
                }

                if (isset($customWidth) && isset($customHeight)) {
                    // Always apply width changes
                    if (preg_match('~width="(\d+)"~i', $parsedContent)) {
                        $parsedContent = preg_replace(
                            '~width="(\d+)"~i',
                            'width="' . esc_attr($customWidth) . '"',
                            $parsedContent
                        );
                    } elseif (preg_match('~width="({.+})"~i', $parsedContent)) {
                        // this block was needed for twitch that has width="{width}" in iframe
                        $parsedContent = preg_replace(
                            '~width="({.+})"~i',
                            'width="' . esc_attr($customWidth) . '"',
                            $parsedContent
                        );
                    }

                    // Only apply height changes if provider is not excluded
                    if (!$shouldExcludeHeight) {
                        if (preg_match('~height="(\d+)"~i', $parsedContent)) {
                            $parsedContent = preg_replace(
                                '~height="(\d+)"~i',
                                'height="' . esc_attr($customHeight) . '"',
                                $parsedContent
                            );
                        } elseif (preg_match('~height="({.+})"~i', $parsedContent)) {
                            $parsedContent = preg_replace(
                                '~height="({.+})"~i',
                                'height="' . esc_attr($customHeight) . '"',
                                $parsedContent
                            );
                        }
                    }

                    if (preg_match('~width\s+:\s+(\d+)~i', $parsedContent)) {
                        $parsedContent = preg_replace(
                            '~width\s+:\s+(\d+)~i',
                            'width: ' . esc_attr($customWidth),
                            $parsedContent
                        );
                    }

                    // Only apply height CSS changes if provider is not excluded
                    if (!$shouldExcludeHeight) {
                        if (preg_match('~height\s+:\s+(\d+)~i', $parsedContent)) {
                            $parsedContent = preg_replace(
                                '~height\s+:\s+(\d+)~i',
                                'height: ' . esc_attr($customHeight),
                                $parsedContent
                            );
                        }
                    }

                    if ('gfycat' === $provider_name && preg_match('~height\s*:\s*auto\s*;~i', $parsedContent) && !$shouldExcludeHeight) {
                        $parsedContent = preg_replace(
                            '~height\s*:\s*auto\s*~i',
                            'height: ' . esc_attr($customHeight) . 'px',
                            $parsedContent
                        );
                        $parsedContent = preg_replace(
                            '~style="position:relative;padding-bottom(.+?)"~i',
                            '',
                            $parsedContent
                        );
                        $styles = "<style>
                        .ose-gfycat.ose-embedpress-responsive{
                            position: relative;
                        }
                        </style>";
                        $parsedContent = $styles . $parsedContent;
                    }
                }
            }





            if ('the-new-york-times' === $provider_name && isset($customAttributes['height']) && isset($customAttributes['width'])) {
                $height = $customAttributes['height'];
                $width = $customAttributes['width'];
                $styles = <<<KAMAL
<style>
.ose-the-new-york-times iframe{
	min-height: auto;
	height: {height}px;
	width: {width}px;
	max-width:100%
	max-height: 100%;
}
</style>
KAMAL;
                $styles = str_replace(['{height}', '{width}'], [esc_attr($height), esc_attr($width)], $styles);
                $parsedContent = $styles . $parsedContent;
            }

            if ($stripNewLine) {
                $parsedContent = preg_replace('/\n/', '', $parsedContent);
            }


            $parsedContent = apply_filters('pp_embed_parsed_content', $parsedContent, $urlData,  self::get_oembed_attributes());

            if (!empty($parsedContent)) {
                // Get provider name using Helper method
                $provider_name = Helper::get_provider_name($url);

                $embed = (object) array_merge((array) $urlData, [
                    'attributes' => (object) self::get_oembed_attributes(),
                    'embed'      => $parsedContent,
                    'url'        => $url,
                    'provider_name' => $provider_name,
                ]);

                $embed = self::modify_spotify_content($embed);
                $embed = apply_filters('embedpress:onAfterEmbed', $embed);

                // Attributes to remove
                $attributesToRemove = 'autoplay;';

                // New attribute to add
                $newAttribute = 'encrypted-media;' . 'accelerometer;' . 'autoplay;' . 'clipboard-write;' . 'gyroscope;' . 'picture-in-picture';

                // Remove existing attributes
                $embed->embed = str_replace($attributesToRemove, $newAttribute, $embed->embed);

                return $embed;
            }
        }

        return $subject;
    }

    protected static function get_oembed()
    {
        if (!self::$oEmbedInstance) {
            global $wp_version;
            if (version_compare($wp_version, '5.3.0', '>=')) {
                require_once ABSPATH . 'wp-includes/class-wp-oembed.php';
            } else {
                require_once ABSPATH . 'wp-includes/class-oembed.php';
            }
            self::$oEmbedInstance = _wp_oembed_get_object();
        }
        return self::$oEmbedInstance;
    }

    protected static function set_default_size(&$customAttributes)
    {

        $plgSettings = Core::getSettings();


        if (empty($customAttributes['width'])) {
            $customAttributes['width'] = !empty($plgSettings->enableEmbedResizeWidth) ? esc_attr($plgSettings->enableEmbedResizeWidth) : 600;
        }
        if (empty($customAttributes['height'])) {
            $customAttributes['height'] = !empty($plgSettings->enableEmbedResizeHeight) ? esc_attr($plgSettings->enableEmbedResizeHeight) : 550;
        }
    }

    protected static function get_url_data($url, $attributes = [], $serviceProvider = '')
    {
        if (!empty($serviceProvider)) {
            $urlData = self::get_oembed()->fetch($serviceProvider, $url, $attributes);
        } else {
            if (self::is_problematic_provider($url)) {
                // Force fake response for problematic providers
                $urlData = self::get_fake_response_for_url($url);
            } else {
                $urlData = self::get_embera_instance()->getUrlData($url);
            }
        }

        return $urlData;
    }

    // self::$attributes_data = self::$ombed_attributes;

    public static function getAttributesData()
    {
        self::$attributes_data = self::get_oembed_attributes();
        // return self::get_oembed_attributes();
        return self::$attributes_data;
    }


    /**
     * @return null|Embera
     */
    public static function get_embera_instance()
    {
        if (!self::$embera_instance) {
            $additionalServiceProviders = Core::getAdditionalServiceProviders();
            if (!empty($additionalServiceProviders)) {
                foreach ($additionalServiceProviders as $serviceProviderClassName => $serviceProviderUrls) {
                    self::addServiceProvider($serviceProviderClassName, $serviceProviderUrls);
                }
                unset($serviceProviderUrls, $serviceProviderClassName);
            }
            self::$embera_instance = new Embera(self::get_embera_settings(), self::get_collection());
        } else {
            // Re-instantiate Embera with new settings since setConfig does not exist
            self::$embera_instance = new Embera(self::get_embera_settings(), self::get_collection());
        }
        return self::$embera_instance;
    }

    /**
     * Method that adds support to a given new service provider (SP).
     *
     * @param string $className The new SP class name.
     * @param string $reference The new SP reference name.*
     * @return  DefaultProviderCollection|bool
     * @since   1.0.0
     * @static
     *
     */
    public static function addServiceProvider($className, $reference)
    {
        if (empty($className) || empty($reference)) {
            return false;
        }

        if (is_null(self::$collection)) {
            self::$collection = new DefaultProviderCollection();
        }
        if (is_string($reference)) {
            self::$collection->addProvider($reference, $className);
            return self::$collection;
        } elseif (is_array($reference)) {
            foreach ($reference as $serviceProviderUrl) {
                self::addServiceProvider($className, $serviceProviderUrl);
            }
            return self::$collection;
        } else {
            return false;
        }
    }

    /**
     * Method that retrieves all custom parameters from a shortcoded string.
     *
     * @param string $subject The given shortcoded string.
     *
     * @return  array
     * @since   1.0.0
     * @static
     *
     */
    public static function parseContentAttributesFromString($subject)
    {
        $customAttributes = [];
        if (preg_match('/\[embed\s*(.*?)\]/i', stripslashes($subject), $m)) {
            if (preg_match_all('/(\!?\w+-?\w*)(?:="(.+?)")?/i', stripslashes($m[1]), $matches)) {
                $attributes = $matches[1];
                $attrValues = $matches[2];

                foreach ($attributes as $attrIndex => $attrName) {
                    $customAttributes[$attrName] = $attrValues[$attrIndex];
                }
            }
        }
        return $customAttributes;
    }

    /**
     * Method that parses and adds the "data-" prefix to the given custom shortcode attributes.
     *
     * @param array $customAttributes The array containing the embed attributes.
     * @param string $content_uid An optional string specifying a unique ID for the embed
     *
     * @return  array
     * @since   1.0.0
     * @access  private
     * @static
     *
     */

    private static function parseContentAttributes(array $customAttributes, $content_uid = null)
    {

        $attributes = [
            'class' => ["embedpress-wrapper ose-embedpress-responsive"],
        ];

        $embedShouldBeResponsive = true;
        $embedShouldHaveCustomDimensions = false;
        if (!empty($customAttributes)) {
            if (isset($customAttributes['class'])) {
                if (!empty($customAttributes['class'])) {
                    $customAttributes['class'] = explode(' ', $customAttributes['class']);

                    $attributes['class'] = array_merge($attributes['class'], $customAttributes['class']);
                }

                unset($customAttributes['class']);
            }

            if (isset($customAttributes['width'])) {
                if (!empty($customAttributes['width'])) {
                    $attributes['width'] = (int) $customAttributes['width'];
                    $embedShouldHaveCustomDimensions = true;
                }
            }

            if (isset($customAttributes['height'])) {
                if (!empty($customAttributes['height'])) {
                    $attributes['height'] = (int) $customAttributes['height'];
                    $embedShouldHaveCustomDimensions = true;
                }
            }

            if (!empty($customAttributes)) {
                $attrNameDefaultPrefix = "data-";
                foreach ($customAttributes as $attrName => $attrValue) {
                    if (is_numeric($attrName)) {
                        $attrName = $attrValue;
                        $attrValue = "";
                    }

                    $attrName = str_replace($attrNameDefaultPrefix, "", $attrName);

                    if (is_bool($attrValue)) {
                        $attrValue = $attrValue ? "true" : "false";
                    } else if (is_numeric($attrValue)) {
                        // Preserve numeric values as strings
                        $attrValue = (string) $attrValue;
                    } else if (!is_string($attrValue) || !strlen($attrValue)) {
                        if ($attrName[0] === "!") {
                            $attrValue = "false";
                            $attrName = substr($attrName, 1);
                        } else {
                            $attrValue = "true";
                        }
                    }


                    $attributes[$attrNameDefaultPrefix . $attrName] = $attrValue;
                }
            }

            // Check if there's any "responsive" parameter
            $responsiveAttributes = ["responsive", "data-responsive"];
            foreach ($responsiveAttributes as $responsiveAttr) {
                if (isset($attributes[$responsiveAttr])) {
                    if (!strlen($attributes[$responsiveAttr])) { // If the parameter is passed but have no value, it will be true by default
                        $embedShouldBeResponsive = true;
                    } else {
                        $embedShouldBeResponsive = !self::valueIsFalse($attributes[$responsiveAttr]);
                    }

                    break;
                }
            }
            unset($responsiveAttr, $responsiveAttributes);
        }

        $attributes['class'][] = 'ose-{provider_alias}';

        if (!empty($content_uid)) {
            $attributes['class'][] = 'ose-uid-' . $content_uid;
        }

        if ($embedShouldBeResponsive && !$embedShouldHaveCustomDimensions) {
            $attributes['class'][] = 'responsive';
        } else {
            $attributes['data-responsive'] = "false";
        }

        $attributes['class'] = implode(' ', array_unique(array_filter($attributes['class'])));
        if (isset($attributes['width'])) {
            $height = esc_attr($attributes['height']);
            $width = esc_attr($attributes['width']);

            $attributes['style'] = "width:{$width}px;height:{$height}px;";
        }



        return $attributes;
    }

    protected static function set_embera_settings(&$attributes)
    {

        if (isset($attributes['width']) || isset($attributes['height'])) {
            if (isset($attributes['width'])) {
                self::$emberaInstanceSettings['maxwidth'] = esc_attr($attributes['width']);
                self::$emberaInstanceSettings['width'] = esc_attr($attributes['width']);
                unset($attributes['width']);
            }

            if (isset($attributes['height'])) {
                self::$emberaInstanceSettings['maxheight'] = esc_attr($attributes['height']);
                self::$emberaInstanceSettings['height'] = esc_attr($attributes['height']);
                unset($attributes['height']);
            }
        }

        // Add Meetup-specific attributes to Embera settings
        $meetup_attributes = ['orderby', 'order', 'per_page', 'enable_pagination', 'timezone', 'date_format', 'time_format'];
        foreach ($meetup_attributes as $attr) {
            if (isset($attributes[$attr])) {
                self::$emberaInstanceSettings[$attr] = $attributes[$attr];
            }
        }

        foreach ($attributes as $key => $value) {
            if (strpos($key, 'data-') === 0) {
                $key = str_replace('data-', '', $key);
                self::$emberaInstanceSettings[$key] = $value;
            }
        }
    }

    protected static function get_embera_settings()
    {
        return self::$emberaInstanceSettings;
    }

    public static function get_block_controls_data()
    {
        var_dump(self::$emberaInstanceSettings);
    }

    /**
     * Method that checks if a given value is/can be identified as (bool)false.
     *
     * @param mixed $subject The value to be checked.
     *
     * @return  boolean
     * @since   1.0.0
     * @static
     *
     */

    public static function valueIsFalse($subject)
    {
        $subject = strtolower(trim((string) $subject));
        switch ($subject) {
            case "0":
            case "false":
            case "off":
            case "no":
            case "n":
            case "nil":
            case "null":
                return true;
            default:
                return false;
        }
    }

    /**
     * Return the value from a header which is in an array resulted from a get_headers() call.
     * If the header cannot be found, this method will return null instead.
     *
     * @param string $headerPattern Regex pattern the header and its value must match.
     * @param array $headersList A list of headers resulted from a get_headers() call.
     *
     * @return  mixed
     * @since   1.1.0
     * @access  private
     * @static
     *
     */
    private static function extractContentFromHeaderAsArray($headerPattern, $headersList)
    {
        $headerValue = null;

        foreach ($headersList as $header) {
            if (preg_match($headerPattern, $header, $matches)) {
                $headerValue = $matches[1];
                break;
            }
        }

        return $headerValue;
    }

    /**
     * Sanitize the object returned by the embed source. Sometimes we need to convert
     * attributes from "dash" separated to "underline" separated to be able to access
     * those attributes from the object, without having to convert it to an array.
     *
     * @param object $data
     *
     * @return object
     * @since   1.6.1
     * @access  private
     * @static
     *
     */
    private static function sanitizeUrlData($data, $url = '')
    {
        if (is_object($data)) {
            $attributes = get_object_vars($data);

            foreach ($attributes as $key => $value) {
                if (substr_count($key, '-') && $key !== $url) {
                    unset($data->$key);

                    $key = str_replace('-', '_', $key);
                    $data->$key = $value;
                }
            }
        } elseif (is_array($data)) {
            foreach ($data as $key => $value) {
                if (substr_count($key, '-') && $key !== $url) {
                    unset($data[$key]);

                    $key = str_replace('-', '_', $key);
                    $data[$key] = $value;
                }
            }
        }

        return $data;
    }

    public static function get_collection()
    {
        return self::$collection;
    }

    protected static function purify_html_content(&$html)
    {
        if (!class_exists('\simple_html_dom')) {
            include_once EMBEDPRESS_PATH_CORE . 'simple_html_dom.php';
        }

        $dom = str_get_html($html);
        $ifDom = $dom->find('iframe', 0);
        if (!empty($ifDom) && is_object($ifDom)) {
            $ifDom->removeAttribute('sandbox');
        }

        ob_start();
        echo $dom;
        return ob_get_clean();
    }

    protected static function get_content_from_template($url, $template, $serviceProvider)
    {

        $html = apply_filters('embed_apple_podcast', '', $url, $template, $serviceProvider);

        if (!empty($html)) {
            return $html;
        }

        // YouTube playlist + watch?…&list=… need the Embera path so the
        // Youtube provider's getPlaylistGallery() can render the queue UI.
        // WP oembed would return a videoseries/single-video iframe and
        // short-circuit us. esc_url() encodes & → &#038;, so accept either form.
        $is_yt_playlist_url =
            preg_match('~^https?://(?:www\.)?youtube\.com/playlist\?(?:[^#]*(?:&|&\#038;))?list=[\w-]+~i', $url) ||
            preg_match('~^https?://(?:www\.)?youtube\.com/watch\?(?:[^#]*(?:&|&\#038;))?list=[\w-]+~i', $url);
        if ($is_yt_playlist_url) {
            $serviceProvider = '';
        }

        if (empty($serviceProvider)) {
            $html = '';
        } else {
            $html = self::get_oembed()->get_html($url, self::get_oembed_attributes());
        }

        if (!$html) {
            if (self::is_problematic_provider($url)) {
                // Force fake response for problematic providers
                $urlData = self::get_fake_response_for_url($url);
                if (!empty($urlData[$url]) && !isset($urlData[$url]['error'])) {
                    $html = $urlData[$url]['html'];
                }
            } else {
                $html = self::get_embera_instance()->autoEmbed($url);
            }

        }

        return str_replace('{html}', $html, $template);
    }

    /**
     * Get list of providers that have known API issues and should use fake responses
     *
     * @return array
     */
    protected static function get_problematic_providers()
    {
        return apply_filters('embedpress_problematic_providers', [
            'commaful.com',
            'flourish.studio',
            'dailymotion.com',
            'dai.ly',
        ]);
    }

    /**
     * Check if a URL belongs to a problematic provider
     *
     * @param string $url
     * @return bool
     */
    public static function is_problematic_provider($url)
    {
        $problematic_providers = self::get_problematic_providers();

        foreach ($problematic_providers as $provider) {
            if (strpos($url, $provider) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get fake response for problematic providers
     *
     * @param string $url
     * @return array
     */
    protected static function get_fake_response_for_url($url)
    {
        // Initialize Embera instance to ensure collection is available
        $embera = self::get_embera_instance();
        $collection = self::get_collection();

        if ($collection) {
            $providers = $collection->findProviders($url);

            if (!empty($providers[$url])) {
                $providerClass = get_class($providers[$url]);
                // Create new instance with the URL
                $provider = new $providerClass($url);
                return [$url => $provider->getFakeResponse()];
            }
        }

        return [$url => ['error' => 'No provider found for URL']];
    }

    protected static function get_oembed_attributes()
    {

        return self::$ombed_attributes;
    }
    protected static function set_oembed_attributes($atts)
    {
        self::$ombed_attributes = $atts;
    }

    protected static function check_for_google_url(&$url)
    {
        if (preg_match('/http[s]?:\/\/goo\.gl\/(?:([a-z]+)\/)?[a-z0-9]+\/?$/i', $url, $matches)) {
            // Fetch all headers from the short-url so we can know how to handle its original content depending on the service.
            $headers = get_headers($url);

            $supportedServicesHeadersPatterns = [
                'maps' => '/^Location:\s+(http[s]?:\/\/.+)$/i',
            ];

            $service = isset($matches[1]) ? strtolower($matches[1]) : null;
            // No specific service was found in the url.
            if (empty($service)) {
                // Let's try to guess which service the original url belongs to.
                foreach ($headers as $header) {
                    // Check if the short-url reffers to a Google Maps url.
                    if (preg_match($supportedServicesHeadersPatterns['maps'], $header, $matches)) {
                        // Replace the shortened url with its original url.
                        $url = $matches[1];
                        break;
                    }
                }
                unset($header);
            } else {
                // Check if the Google service is supported atm.
                if (isset($supportedServicesHeadersPatterns[$service])) {
                    // Tries to extract the url based on its headers.
                    $originalUrl = self::extractContentFromHeaderAsArray(
                        $supportedServicesHeadersPatterns[$service],
                        $headers
                    );
                    // Replace the shortened url with its original url if the specific header was found.
                    if (!empty($originalUrl)) {
                        $url = $originalUrl;
                    }
                    unset($originalUrl);
                }
            }
            unset($service, $supportedServicesHeadersPatterns, $headers, $matches);
        }
    }

    protected static function get_provider_name($urlData, $url)
    {
        $provider_name = '';
        if (isset($urlData->provider_name)) {
            $provider_name = str_replace([' ', ','], '-', strtolower($urlData->provider_name));
        } elseif (is_array($urlData) && !empty($urlData)) {
            $data = array_shift($urlData);
            if (isset($data['provider_name'])) {
                $provider_name = str_replace([' ', ','], '-', strtolower($data['provider_name']));
            }
        }
        return $provider_name;
    }

    /**
     * Get list of sources that should be excluded from height attribute application
     *
     * @return array Array of provider names that should not have height applied
     * @since 1.0.0
     * @static
     */
    protected static function get_excluded_height_sources()
    {
        // Default excluded sources - you can add more here
        $defaultExcluded = [
            'meetup' // Meetup events should not have fixed height
        ];

        // Allow filtering of excluded sources
        $excludedSources = apply_filters('embedpress_excluded_height_sources', $defaultExcluded);

        // Ensure it's always an array
        if (!is_array($excludedSources)) {
            $excludedSources = [];
        }

        return $excludedSources;
    }

    protected static function modify_content_for_fb_and_canada($provider_name, &$html)
    {
        if (!empty($provider_name)) {
            // NFB seems to always return their embed code with all HTML entities into their applicable characters string.
            $PROVIDER_NAME_IN_CAP = strtoupper($provider_name);
            if ($PROVIDER_NAME_IN_CAP  === "NATIONAL FILM BOARD OF CANADA") {
                $html = html_entity_decode($html);
            } elseif ($PROVIDER_NAME_IN_CAP === "FACEBOOK") {
                $plgSettings = Core::getSettings();

                // Check if the user wants to force a certain language into Facebook embeds.
                $locale = isset($plgSettings->fbLanguage) && !empty($plgSettings->fbLanguage) ? $plgSettings->fbLanguage : false;
                if (!!$locale) {
                    // Replace the automatically detected language by Facebook's API with the language chosen by the user.
                    $html = preg_replace(
                        '/\/[a-z]{2}\_[a-z]{2}\/sdk\.js/i',
                        "/{$locale}/sdk.js",
                        $html
                    );
                }

                // Make sure `adapt_container_width` parameter is set to false. Setting to true, as it is by default, might cause Facebook to render embeds inside editors (in admin) with only 180px wide.
                if (is_admin()) {
                    $html = preg_replace(
                        '~data\-adapt\-container\-width=\"(?:true|1)\"~i',
                        'data-adapt-container-width="0"',
                        $html
                    );
                }

                unset($locale, $plgSettings);
            }
        }
    }

    public static function modify_spotify_content($embed)
    {
        $should_modify = apply_filters('embedpress_should_modify_spotify', true);
        $isSpotify = (isset($embed->provider_name) && strtoupper($embed->provider_name) === 'SPOTIFY') || (isset($embed->url) && isset($embed->{$embed->url}) && isset($embed->{$embed->url}['provider_name']) && strtoupper($embed->{$embed->url}['provider_name']) === 'SPOTIFY');
        if (
            $should_modify && $isSpotify && isset($embed->embed)
            && preg_match('/src=\"(.+?)\"/', $embed->embed, $match)
        ) {
            $options = (array) get_option(EMBEDPRESS_PLG_NAME . ':spotify');
            // Parse the url to retrieve all its info like variables etc.
            $url_full = $match[1];
            $modified_url = str_replace('playlist-v2', 'playlist', $url_full);
            if (isset($options['theme'])) {
                if (strpos($modified_url, '?') !== false) {
                    $modified_url .= '&theme=' . sanitize_text_field($options['theme']);
                } else {
                    $modified_url .= '?theme=' . sanitize_text_field($options['theme']);
                }
            }

            // Replaces the old url with the new one.
            $embed->embed = str_replace($url_full, $modified_url, $embed->embed);
        }
        return $embed;
    }

    public static function getParamData($attributes)
    {

        $urlParamData = array(
            'themeMode' => isset($attributes['theme_mode']) ? esc_attr($attributes['theme_mode']) : 'default',
            'toolbar' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['toolbar']) ? esc_attr($attributes['toolbar']) : 'true',
            'lazyLoad' => isset($attributes['lazyLoad']) ? esc_attr($attributes['lazyLoad']) : 'false',
            'position' => isset($attributes['toolbar_position']) ? esc_attr($attributes['toolbar_position']) : 'top',
            'presentation' => isset($attributes['presentation']) ? esc_attr($attributes['presentation']) : 'true',
            'download' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['download']) ? esc_attr($attributes['download']) : 'true',
            'copy_text' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['copy_text']) ? esc_attr($attributes['copy_text']) : 'true',
            'add_text' => isset($attributes['add_text']) ? esc_attr($attributes['add_text']) : 'true',
            'draw' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['draw']) ? esc_attr($attributes['draw']) : 'true',
            'doc_rotation' => isset($attributes['doc_rotation']) ? esc_attr($attributes['doc_rotation']) : 'true',
            'add_image' => isset($attributes['add_image']) ? esc_attr($attributes['add_image']) : 'true',
            'doc_details' => isset($attributes['doc_details']) ? esc_attr($attributes['doc_details']) : 'true',
            'selection_tool' => isset($attributes['selection_tool']) ? esc_attr($attributes['selection_tool']) : '0',
            'scrolling' => isset($attributes['scrolling']) ? esc_attr($attributes['scrolling']) : '-1',
            'spreads' => isset($attributes['spreads']) ? esc_attr($attributes['spreads']) : '-1',
            'zoom_in' =>  isset($attributes['zoom_in'])  ? $attributes['zoom_in'] : 'true',
            'zoom_out' => isset($attributes['zoom_out'])  ? $attributes['zoom_out'] : 'true',
            'fit_view' => isset($attributes['fit_view'])  ? $attributes['fit_view'] : 'true',
            'bookmark' => isset($attributes['bookmark'])  ? $attributes['bookmark'] : 'true',
            'sound' => isset($attributes['sound'])  ? $attributes['sound'] : 'true',
            'flipbook_toolbar_position' => !empty($attributes['toolbar_position'])  ? $attributes['toolbar_position'] : 'bottom',
            'pageNumber' => isset($attributes['page_number']) ? absint($attributes['page_number']) : 1,
            'watermark_text' => defined('EMBEDPRESS_SL_ITEM_SLUG') && isset($attributes['watermarkText']) ? esc_attr($attributes['watermarkText']) : '',
            'watermark_font_size' => defined('EMBEDPRESS_SL_ITEM_SLUG') && isset($attributes['watermarkFontSize']) ? esc_attr($attributes['watermarkFontSize']) : '48',
            'watermark_color' => defined('EMBEDPRESS_SL_ITEM_SLUG') && isset($attributes['watermarkColor']) ? esc_attr($attributes['watermarkColor']) : '#000000',
            'watermark_opacity' => defined('EMBEDPRESS_SL_ITEM_SLUG') && isset($attributes['watermarkOpacity']) ? esc_attr($attributes['watermarkOpacity']) : '15',
            'watermark_style' => defined('EMBEDPRESS_SL_ITEM_SLUG') && isset($attributes['watermarkStyle']) ? esc_attr($attributes['watermarkStyle']) : 'center',
        );

        if ($urlParamData['themeMode'] == 'custom') {
            $urlParamData['customColor'] = isset($attributes['custom_color']) ? esc_attr($attributes['custom_color']) : '#333333';
        }

        $queryString = http_build_query($urlParamData);

        // Ensure UTF-8 encoding with fallback for when mbstring is not available
        if (function_exists('mb_convert_encoding')) {
            $queryString = \mb_convert_encoding($queryString, 'UTF-8');
        } 
        // If neither mbstring nor iconv is available, use the string as-is
        // since http_build_query() already produces valid output

        if (isset($attributes['viewer_style']) && $attributes['viewer_style'] == 'flip-book') {
            return "&key=" . base64_encode($queryString);
        }

        return "#key=" . base64_encode($queryString);
    }

    public static function getUnit($value)
    {
        if (preg_match('/[a-zA-Z%]+$/', $value, $matches)) {
            return '';
        }
        return 'px';
    }

    /**
     * Resolve a URL from an ACF field.
     *
     * Handles ACF "File URL" return format (string) and
     * "File Array" return format (array with 'url' key).
     *
     * @param string      $field_name The ACF field name.
     * @param int|null    $post_id    Optional post ID. Defaults to current post.
     * @return string The resolved URL, or empty string.
     */
    private static function resolveAcfFieldUrl($field_name, $post_id = null)
    {
        if (!function_exists('get_field')) {
            return '';
        }

        $field_name = sanitize_key($field_name);
        if (empty($field_name)) {
            return '';
        }

        $post_id = $post_id ?: get_the_ID();
        $field_value = get_field($field_name, $post_id);

        if (empty($field_value)) {
            return '';
        }

        // ACF "File URL" return format
        if (is_string($field_value)) {
            return trim($field_value);
        }

        // ACF "File Array" return format
        if (is_array($field_value) && !empty($field_value['url'])) {
            return trim($field_value['url']);
        }

        return '';
    }

    public static function do_shortcode_pdf($attributes = [], $subject = null)
    {
        $plgSettings = Core::getSettings();

        $default = [
            'width'  => esc_attr($plgSettings->enableEmbedResizeWidth),
            'height' => esc_attr($plgSettings->enableEmbedResizeHeight),
            'powered_by' => !empty($plgSettings->embedpress_document_powered_by) ? esc_attr($plgSettings->embedpress_document_powered_by) : esc_attr('no'),
            'display_mode' => 'inline',
            'trigger_text' => 'View PDF',
        ];

        if (!empty($plgSettings->pdf_custom_color_settings)) {
            $default['theme_mode'] = 'custom';
        }
        if (isset($default['theme_mode']) && $default['theme_mode'] == 'custom') {
            $default['custom_color'] = esc_attr($plgSettings->custom_color);
        }

        $attributes = wp_parse_args($attributes, $default);

        $url = preg_replace(
            '/(\[' . EMBEDPRESS_SHORTCODE . '(?:\]|.+?\])|\[\/' . EMBEDPRESS_SHORTCODE . '\])/i',
            "",
            $subject
        );

        $url = trim($url);

        // ACF dynamic field support: [embedpress_pdf]embedpress_my_field[/embedpress_pdf]
        if (!empty($url) && strpos($url, 'embedpress_') === 0) {
            $field_name = substr($url, strlen('embedpress_'));
            $url = self::resolveAcfFieldUrl($field_name);
        }

        $url = esc_url($url);

        ob_start();

        $id = 'embedpress-pdf-shortcode';

        $widthUnit = self::getUnit($attributes['width']);
        $heightUnit = self::getUnit($attributes['height']);

        $dimension = "width: {$attributes['width']}{$widthUnit}; height: {$attributes['height']}{$heightUnit};";


        $client_id = is_object('$embed') ? md5('$embed->embed')  : '';

        $hash_pass = isset($attributes['protection_password'])
            ? hash('sha256', wp_salt(32) . md5($attributes['protection_password']))
            : '';

        $pass_hash_key = isset($attributes['protection_password'])
            ? md5($attributes['protection_password'])
            : '';

        $password_correct = $_COOKIE['password_correct_' . $client_id] ?? '';

        $protection_type = $attributes['protection_type'] ?? 'user-role';
        $protection_password = $attributes['protection_password'] ?? '';
        $protection_content = isset($attributes['protection_content']) ? $attributes['protection_content'] === 'true' : false;
        $user_role = isset($attributes['user_roles']) ? explode(',', preg_replace('/\s*,\s*/', ',', $attributes['user_roles'])) : '';

        $protection_message = $attributes['protection_message'] ?? '';

        // Conditions for content protection
        $password_protected = $protection_type == 'password' && !empty($protection_password);

        $password_verified = $password_protected && !empty(Helper::is_password_correct($client_id)) && ($hash_pass === $password_correct);
        $user_role_protected = $protection_type === 'user-role' && Helper::has_allowed_roles($user_role);

        if (
            apply_filters('embedpress/is_allow_rander', false) && ($protection_content == 'true') && (($protection_type == 'user-role' && !Helper::has_allowed_roles($user_role)))
        ) {

            return self::content_protection_content($client_id, $protection_message, $user_role);
        }

        // Lightbox mode: render thumbnail + lightbox instead of inline viewer
        $display_mode = isset($attributes['display_mode']) ? $attributes['display_mode'] : 'inline';
        if (in_array($display_mode, ['button', 'link', 'text']) && self::is_pdf($url) && !self::is_external_url($url)) {
            $viewer_style = isset($attributes['viewer_style']) ? $attributes['viewer_style'] : 'modern';
            $param_string = self::getParamData($attributes);
            $viewer_params = '';
            if (preg_match('/key=(.+)$/', $param_string, $matches)) {
                $viewer_params = $matches[1];
            }
            $trigger_text = isset($attributes['trigger_text']) ? $attributes['trigger_text'] : 'View PDF';
            ?>
            <div class="embedpress-document-embed ep-doc-<?php echo esc_attr(md5($id)); ?>">
                <div class="ep-pdf-thumbnail-wrap"
                     data-pdf-url="<?php echo esc_url($url); ?>"
                     data-viewer-style="<?php echo esc_attr($viewer_style); ?>"
                     data-viewer-params="<?php echo esc_attr($viewer_params); ?>">
                    <span class="ep-pdf-trigger ep-pdf-trigger--<?php echo esc_attr($display_mode); ?>"><?php echo esc_html($trigger_text); ?></span>
                </div>
            </div>
            <?php
            return ob_get_clean();
        }
        if ($display_mode === 'lightbox' && self::is_pdf($url) && !self::is_external_url($url)) {
            $viewer_style = isset($attributes['viewer_style']) ? $attributes['viewer_style'] : 'modern';
            $param_string = self::getParamData($attributes);
            $viewer_params = '';
            if (preg_match('/key=(.+)$/', $param_string, $matches)) {
                $viewer_params = $matches[1];
            }
            $max_width = $attributes['width'] . self::getUnit($attributes['width']);
            if (empty(self::getUnit($attributes['width']))) {
                $max_width .= 'px';
            }
            $pdf_title = Helper::get_file_title($url);
            ?>
            <div class="embedpress-document-embed ep-doc-<?php echo esc_attr(md5($id)); ?>" style="max-width: <?php echo esc_attr($max_width); ?>;">
                <div class="ep-pdf-thumbnail-card">
                    <div class="ep-pdf-thumbnail-wrap"
                         data-pdf-url="<?php echo esc_url($url); ?>"
                         data-viewer-style="<?php echo esc_attr($viewer_style); ?>"
                         data-viewer-params="<?php echo esc_attr($viewer_params); ?>">
                        <div class="ep-pdf-thumbnail-inner">
                            <canvas class="ep-pdf-thumbnail-canvas" data-pdf-url="<?php echo esc_url($url); ?>" data-loading="true"></canvas>
                            <div class="ep-pdf-thumbnail-overlay">
                                <div class="ep-pdf-thumbnail-icon-circle">
                                    <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8 5v14l11-7z"/></svg>
                                </div>
                            </div>
                        </div>
                    </div>

                </div>
                <?php
                if (!empty($attributes['powered_by']) && $attributes['powered_by'] === 'yes') {
                    printf('<p class="embedpress-el-powered">%s</p>', __('Powered By EmbedPress', 'embedpress'));
                }
                ?>
            </div>
            <?php
            return ob_get_clean();
        }

?>
        <div class="embedpress-document-embed ose-document <?php echo 'ep-doc-' . md5($id); ?>" style="<?php echo esc_attr($dimension); ?>; max-width:100%; display: block">
            <?php if ($url != '') {
                if (self::is_pdf($url) && !self::is_external_url($url)) {
                    $renderer = Helper::get_pdf_renderer();

                    $src = $renderer . ((strpos($renderer, '?') == false) ? '?' : '&') . 'file=' . urlencode($url) . self::getParamData($attributes);


                    if (isset($attributes['viewer_style']) && $attributes['viewer_style'] === 'flip-book') {
                        $src = urlencode($url) . self::getParamData($attributes); // Assuming generate_pdf_params is meant to be getParamData
                        $renderer = Helper::get_flipbook_renderer();
                        $src_url = $renderer . ((strpos($renderer, '?') === false) ? '?' : '&') . 'file=' . $src;
                        echo '<iframe title="' . esc_attr(Helper::get_file_title($url)) . '" class="embedpress-embed-document-pdf ' . esc_attr($id) . '" style="' . esc_attr($dimension) . '; max-width:100%; display: inline-block" src="' . esc_url($src_url) . '" frameborder="0" oncontextmenu="return false;"></iframe>';
                    } else {
                    ?>
                        <iframe title="<?php echo esc_attr(Helper::get_file_title($url)); ?>" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="<?php echo esc_attr($dimension); ?>; max-width:100%; display: inline-block" data-emsrc="<?php echo esc_url($url); ?>" data-emid="<?php echo esc_attr($id); ?>" class="embedpress-embed-document-pdf <?php echo esc_attr($id); ?>" src="<?php echo esc_url($src); ?>" frameborder="0">
                        </iframe>
                    <?php
                    }
                } else {
                    ?>
                    <div>
                        <iframe title="<?php echo esc_attr(Helper::get_file_title($url)); ?>" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="<?php echo esc_attr($dimension); ?>; max-width:100%;" src="<?php echo esc_url($url); ?>" data-emsrc="<?php echo esc_url($url); ?>" data-emid="<?php echo esc_attr($id); ?>" class="embedpress-embed-document-pdf <?php echo esc_attr($id); ?>"></iframe>
                    </div>
            <?php

                }

                if (!empty($attributes['powered_by']) && $attributes['powered_by'] === 'yes') {
                    printf('<p class="embedpress-el-powered">%s</p>', __('Powered By EmbedPress', 'embedpress'));
                }
            }
            ?>
        </div>

<?php


        return ob_get_clean();
    }


    public static function do_shortcode_doc($attributes = [], $subject = null)
    {
        $plgSettings = Core::getSettings();
        $url = preg_replace(
            '/(\[' . EMBEDPRESS_SHORTCODE . '(?:\]|.+?\])|\[\/' . EMBEDPRESS_SHORTCODE . '\])/i',
            "",
            $subject
        );

        $url = trim($url);

        // ACF dynamic field support: [embedpress_doc]embedpress_my_field[/embedpress_doc]
        if (!empty($url) && strpos($url, 'embedpress_') === 0) {
            $field_name = substr($url, strlen('embedpress_'));
            $resolved = self::resolveAcfFieldUrl($field_name);
            if (!empty($resolved)) {
                $url = $resolved;
            }
        }

        $url = esc_url($url);


        $default = [
            'url' => $url,
            'width' => !empty($plgSettings->enableEmbedResizeWidth) ? esc_attr($plgSettings->enableEmbedResizeWidth) : '100%',
            'height' => !empty($plgSettings->enableEmbedResizeHeight) ? esc_attr($plgSettings->enableEmbedResizeHeight) : '500px',
            'viewer' => !empty($plgSettings->embedpress_document_viewer) ? esc_attr($plgSettings->embedpress_document_viewer) : 'custom',
            'powered_by' => (!isset($plgSettings->embedpress_document_powered_by) || $plgSettings->embedpress_document_powered_by === 'yes') ? 'yes' : 'no',
        ];


        $atts = shortcode_atts($default, $attributes);


        $url = esc_url($atts['url']);
        if (empty($url)) return '';

        $dimension = "width: {$atts['width']}px; height: {$atts['height']}px";

        $embed_content = '';

        // PDF Handling
        if (self::is_pdf($url)) {
            $embed_content .= '<div class="embedpress-document-embed ose-document embedpress-doc-wrap ep-doc-' . md5($url) . '" style="' . esc_attr($dimension) . '; max-width: 100%; display: block">';
            $embed_content .= '<iframe src="' . esc_url($url) . '" style="' . esc_attr($dimension) . '; max-width: 100%;" frameborder="0" allowfullscreen></iframe>';
            if ($atts['powered_by'] === 'yes') {
                $embed_content .= '<p class="embedpress-el-powered" style="text-align: center">Powered By EmbedPress</p>';
            }
            $embed_content .= '</div>';
            return $embed_content;
        }

        // Office or Google Viewer Handling
        if (self::is_file_url($url)) {
            $viewer_url = 'https://view.officeapps.live.com/op/embed.aspx?src=' . urlencode($url) . '&embedded=true';
        } else {
            $viewer_url = 'https://drive.google.com/viewerng/viewer?url=' . urlencode($url) . '&embedded=true&chrome=false';
        }

        if ($atts['viewer'] === 'google') {
            $viewer_url = '//docs.google.com/gview?embedded=true&url=' . urlencode($url);
        } elseif ($atts['viewer'] === 'custom') {
            $hostname = parse_url($url, PHP_URL_HOST);
            $domain = implode('.', array_slice(explode('.', $hostname), -2));
            if ($domain === 'google.com') {
                $viewer_url = $url . '?embedded=true';
                if (strpos($viewer_url, '/presentation/')) {
                    $viewer_url = Helper::get_google_presentation_url($url);
                }
            }
        }

        // Determine embed type from URL
        $embed_type = Helper::get_provider_name($url) ?: 'document';

        $embed_content .= '<div class="embedpress-document-embed ose-document embedpress-doc-wrap ep-doc-' . md5($url) . '" style="' . esc_attr($dimension) . '; max-width: 100%; display: block" data-embed-type="' . esc_attr($embed_type) . '">';

        $embed_content .= '<iframe src="' . esc_url($viewer_url) . '" style="' . esc_attr($dimension) . '; max-width: 100%;" frameborder="0" allowfullscreen ></iframe>';

        if ($atts['powered_by'] === 'yes') {
            $embed_content .= '<p class="embedpress-el-powered" style="text-align: center">Powered By EmbedPress</p>';
        }

        $embed_content .= '</div>';

        return $embed_content;
    }

    protected static function is_file_url($url)
    {
        $pattern = '/\.([0-9a-z]+)(?=[?#])|(\.)(?:[\w]+)$/i';
        return preg_match($pattern, $url) === 1;
    }

    protected static function is_external_url($url)
    {
        return strpos($url, get_site_url()) === false;
    }

    protected static function is_pdf($url)
    {
        $arr = explode('.', $url);
        return end($arr) === 'pdf';
    }

    /**
     * PDF Gallery shortcode handler
     *
     * Usage: [embedpress_pdf_gallery urls="url1.pdf,url2.pdf" thumbnails="thumb1.jpg,,thumb3.jpg"
     *         layout="grid" columns="3" gap="20" viewer_style="modern" ...]
     */
    public static function do_shortcode_pdf_gallery($attributes = [], $subject = null)
    {
        $defaults = [
            'urls'              => '',
            'thumbnails'        => '',
            'layout'            => 'grid',
            'columns'           => '3',
            'columns_tablet'    => '2',
            'columns_mobile'    => '1',
            'gap'               => '20',
            'aspect_ratio'      => '4:3',
            'border_radius'     => '8',
            'viewer_style'      => 'modern',
            'theme_mode'        => 'default',
            'custom_color'      => '#403A81',
            'toolbar'           => 'true',
            'toolbar_position'  => 'top',
            'flipbook_toolbar_position' => 'bottom',
            'presentation'      => 'true',
            'download'          => 'true',
            'copy_text'         => 'true',
            'draw'              => 'true',
            'add_text'          => 'true',
            'add_image'         => 'true',
            'doc_rotation'      => 'true',
            'doc_details'       => 'true',
            'zoom_in'           => 'true',
            'zoom_out'          => 'true',
            'fit_view'          => 'true',
            'bookmark'          => 'true',
            'watermark_text'    => '',
            'watermark_font_size' => '48',
            'watermark_color'   => '#000000',
            'watermark_opacity' => '15',
            'watermark_style'   => 'center',
            'carousel_autoplay' => 'false',
            'carousel_speed'    => '3000',
            'carousel_loop'     => 'true',
            'carousel_arrows'   => 'true',
            'carousel_dots'     => 'false',
            'slides_per_view'   => '3',
        ];

        $attributes = shortcode_atts($defaults, $attributes, 'embedpress_pdf_gallery');

        $urls = array_filter(array_map('trim', explode(',', $attributes['urls'])));
        if (empty($urls)) {
            return '';
        }

        $thumbnails = array_map('trim', explode(',', $attributes['thumbnails']));

        // Build items array
        $items = [];
        foreach ($urls as $index => $url) {
            $items[] = [
                'url' => esc_url($url),
                'fileName' => basename(parse_url($url, PHP_URL_PATH)),
                'customThumbnailUrl' => !empty($thumbnails[$index]) ? esc_url($thumbnails[$index]) : '',
            ];
        }

        // Build viewer params
        $viewer_params = self::getGalleryParamData($attributes);

        $layout = esc_attr($attributes['layout']);

        // Pro gate: bookshelf requires Pro
        if ($layout === 'bookshelf' && !defined('EMBEDPRESS_SL_ITEM_SLUG')) {
            $layout = 'grid';
        }

        $columns = intval($attributes['columns']);
        $columns_tablet = intval($attributes['columns_tablet']);
        $columns_mobile = intval($attributes['columns_mobile']);
        $gap = intval($attributes['gap']);
        $border_radius = intval($attributes['border_radius']);
        $aspect_ratio = esc_attr($attributes['aspect_ratio']);
        $viewer_style = esc_attr($attributes['viewer_style']);
        $gallery_id = 'ep-gallery-' . substr(md5($attributes['urls']), 0, 8);

        $carousel_options = '';
        if ($layout === 'carousel' || $layout === 'bookshelf') {
            $carousel_options = wp_json_encode([
                'autoplay' => $attributes['carousel_autoplay'] === 'true',
                'autoplaySpeed' => intval($attributes['carousel_speed']),
                'loop' => $attributes['carousel_loop'] !== 'false',
                'arrows' => $attributes['carousel_arrows'] !== 'false',
                'dots' => $attributes['carousel_dots'] === 'true',
                'slidesPerView' => intval($attributes['slides_per_view']),
            ]);
        }

        $style = sprintf(
            '--ep-gallery-columns:%d;--ep-gallery-columns-tablet:%d;--ep-gallery-columns-mobile:%d;--ep-gallery-gap:%dpx;--ep-gallery-radius:%dpx;',
            $columns, $columns_tablet, $columns_mobile, $gap, $border_radius
        );

        ob_start();
        ?>
        <div class="ep-pdf-gallery"
             data-layout="<?php echo $layout; ?>"
             data-columns="<?php echo $columns; ?>"
             data-columns-tablet="<?php echo $columns_tablet; ?>"
             data-columns-mobile="<?php echo $columns_mobile; ?>"
             data-gap="<?php echo $gap; ?>"
             data-border-radius="<?php echo $border_radius; ?>"
             data-viewer-style="<?php echo $viewer_style; ?>"
             data-viewer-params="<?php echo esc_attr($viewer_params); ?>"
             data-gallery-id="<?php echo esc_attr($gallery_id); ?>"
             <?php if ($carousel_options): ?>data-carousel-options="<?php echo esc_attr($carousel_options); ?>"<?php endif; ?>
             style="<?php echo esc_attr($style); ?>">

            <?php if ($layout === 'carousel' || $layout === 'bookshelf'): ?>
            <div class="ep-pdf-gallery__carousel">
                <div class="ep-pdf-gallery__carousel-track">
            <?php else: ?>
            <div class="ep-pdf-gallery__grid">
            <?php endif; ?>

                <?php foreach ($items as $index => $item): ?>
                <div class="ep-pdf-gallery__item"
                     data-pdf-url="<?php echo $item['url']; ?>"
                     data-pdf-index="<?php echo $index; ?>"
                     data-pdf-name="<?php echo esc_attr($item['fileName']); ?>">
                    <div class="ep-pdf-gallery__thumbnail-wrap" data-ratio="<?php echo $aspect_ratio; ?>">
                        <?php
                            $sc_thumb = !empty($item['customThumbnailUrl']) ? $item['customThumbnailUrl'] : (!empty($item['autoThumbnailUrl']) ? $item['autoThumbnailUrl'] : '');
                        ?>
                        <?php if ($sc_thumb): ?>
                            <img src="<?php echo esc_url($sc_thumb); ?>" alt="<?php echo esc_attr($item['fileName']); ?>" />
                        <?php else: ?>
                            <canvas class="ep-pdf-gallery__canvas" data-pdf-src="<?php echo esc_url($item['url']); ?>" data-loading="true"></canvas>
                        <?php endif; ?>
                        <div class="ep-pdf-gallery__overlay">
                            <svg class="ep-pdf-gallery__view-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                                <path d="M15 3l2.3 2.3-2.89 2.87 1.42 1.42L18.7 6.7 21 9V3h-6zM3 9l2.3-2.3 2.87 2.89 1.42-1.42L6.7 5.3 9 3H3v6zm6 12l-2.3-2.3 2.89-2.87-1.42-1.42L5.3 17.3 3 15v6h6zm12-6l-2.3 2.3-2.87-2.89-1.42 1.42 2.89 2.87L15 21h6v-6z"/>
                            </svg>
                        </div>
                    </div>
                    <div class="ep-pdf-gallery__book-title"><?php echo esc_html($item['fileName']); ?></div>
                </div>
                <?php endforeach; ?>

            <?php if ($layout === 'carousel' || $layout === 'bookshelf'): ?>
                </div>
                <button class="ep-pdf-gallery__carousel-prev" aria-label="Previous">
                    <svg viewBox="0 0 24 24"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>
                </button>
                <button class="ep-pdf-gallery__carousel-next" aria-label="Next">
                    <svg viewBox="0 0 24 24"><path d="M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z"/></svg>
                </button>
                <div class="ep-pdf-gallery__carousel-dots"></div>
            </div>
            <?php else: ?>
            </div>
            <?php endif; ?>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * Generate base64-encoded viewer params for PDF gallery shortcode
     */
    private static function getGalleryParamData($attributes)
    {
        $theme_mode = esc_attr($attributes['theme_mode']);

        $params = [
            'themeMode' => $theme_mode,
            'toolbar' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['toolbar']) ? esc_attr($attributes['toolbar']) : 'true',
            'position' => esc_attr($attributes['toolbar_position']),
            'flipbook_toolbar_position' => esc_attr($attributes['flipbook_toolbar_position']),
            'presentation' => esc_attr($attributes['presentation']),
            'download' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['download']) ? esc_attr($attributes['download']) : 'true',
            'copy_text' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['copy_text']) ? esc_attr($attributes['copy_text']) : 'true',
            'add_text' => esc_attr($attributes['add_text']),
            'draw' => apply_filters('embedpress/is_allow_rander', false) && isset($attributes['draw']) ? esc_attr($attributes['draw']) : 'true',
            'doc_rotation' => esc_attr($attributes['doc_rotation']),
            'doc_details' => esc_attr($attributes['doc_details']),
            'add_image' => esc_attr($attributes['add_image']),
            'zoom_in' => esc_attr($attributes['zoom_in']),
            'zoom_out' => esc_attr($attributes['zoom_out']),
            'fit_view' => esc_attr($attributes['fit_view']),
            'bookmark' => esc_attr($attributes['bookmark']),
            'watermark_text' => defined('EMBEDPRESS_SL_ITEM_SLUG') ? esc_attr($attributes['watermark_text']) : '',
            'watermark_font_size' => defined('EMBEDPRESS_SL_ITEM_SLUG') ? esc_attr($attributes['watermark_font_size']) : '48',
            'watermark_color' => defined('EMBEDPRESS_SL_ITEM_SLUG') ? esc_attr($attributes['watermark_color']) : '#000000',
            'watermark_opacity' => defined('EMBEDPRESS_SL_ITEM_SLUG') ? esc_attr($attributes['watermark_opacity']) : '15',
            'watermark_style' => defined('EMBEDPRESS_SL_ITEM_SLUG') ? esc_attr($attributes['watermark_style']) : 'center',
        ];

        if ($theme_mode === 'custom') {
            $params['customColor'] = esc_attr($attributes['custom_color']);
        }

        $query_string = http_build_query($params);
        if (function_exists('mb_convert_encoding')) {
            $query_string = mb_convert_encoding($query_string, 'UTF-8');
        }

        return base64_encode($query_string);
    }
}
