<?php
/**
 * Responsive Visibility integration.
 *
 * @package CraftAddonsForWPBakery
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class CAFW_Responsive
 */
class CAFW_Responsive {

	const PARAM = 'cafw_hidden';

	/**
	 * @return void
	 */
	public static function init() {
		add_action( 'vc_before_init', array( __CLASS__, 'register_param_type' ) );
		add_action( 'vc_after_init', array( __CLASS__, 'add_param_to_all' ) );
		add_filter( 'do_shortcode_tag', array( __CLASS__, 'inject_classes' ), 10, 3 );
		add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_css' ), 20 );
	}

	/**
	 * @return void
	 */
	public static function register_param_type() {
		if ( function_exists( 'vc_add_shortcode_param' ) ) {
			vc_add_shortcode_param( 'cafw_visibility', array( __CLASS__, 'visibility_field' ) );
		}
	}

	/**
	 * @param array  $settings Param definition.
	 * @param string $value    Current value.
	 * @return string
	 */
	public static function visibility_field( $settings, $value ) {
		$name        = isset( $settings['param_name'] ) ? $settings['param_name'] : self::PARAM;
		$type        = isset( $settings['type'] ) ? $settings['type'] : 'cafw_visibility';
		$breakpoints = isset( $settings['breakpoints'] ) && is_array( $settings['breakpoints'] ) ? $settings['breakpoints'] : array();
		$selected    = array_filter( array_map( 'trim', explode( ',', (string) $value ) ) );

		$shown  = esc_attr__( 'Visible', 'craft-addons-for-wpbakery' );
		$hidden = esc_attr__( 'Hidden', 'craft-addons-for-wpbakery' );

		$html = '<div class="cafw-visibility" data-shown="' . $shown . '" data-hidden="' . $hidden . '">';
		foreach ( $breakpoints as $bp ) {
			$is_hidden = in_array( $bp['key'], $selected, true );
			$html     .= sprintf(
				'<div class="cafw-visibility-row%1$s" data-key="%2$s"><span class="cafw-visibility-row__info"><span class="cafw-visibility-row__label">%3$s</span><span class="cafw-visibility-row__range">%4$s</span></span><span class="cafw-visibility-row__state">%5$s</span><button type="button" class="cafw-visibility-switch" role="switch" aria-checked="%6$s" aria-label="%7$s"><span class="cafw-visibility-switch__thumb"></span></button></div>',
				$is_hidden ? ' is-hidden' : '',
				esc_attr( $bp['key'] ),
				esc_html( $bp['label'] ),
				esc_html( self::range_label( (int) $bp['min'], (int) $bp['max'] ) ),
				$is_hidden ? $hidden : $shown,
				$is_hidden ? 'false' : 'true',
				/* translators: %s: breakpoint label. */
				esc_attr( sprintf( __( 'Toggle visibility on %s', 'craft-addons-for-wpbakery' ), $bp['label'] ) )
			);
		}
		$html .= sprintf(
			'<input type="hidden" class="wpb_vc_param_value %1$s %2$s_field" name="%1$s" value="%3$s" />',
			esc_attr( $name ),
			esc_attr( $type ),
			esc_attr( (string) $value )
		);
		$html .= '</div>';

		return $html;
	}

	/**
	 * @param int $min Min width.
	 * @param int $max Max width.
	 * @return string
	 */
	private static function range_label( $min, $max ) {
		if ( $min > 0 && $max > 0 ) {
			return $min . '-' . $max . 'px';
		}
		if ( $min > 0 ) {
			return '≥' . $min . 'px';
		}
		if ( $max > 0 ) {
			return '≤' . $max . 'px';
		}
		return __( 'All sizes', 'craft-addons-for-wpbakery' );
	}

	/**
	 * @return void
	 */
	public static function add_param_to_all() {
		if ( ! function_exists( 'vc_add_param' ) || ! class_exists( 'WPBMap' ) ) {
			return;
		}

		$breakpoints = CAFW_Breakpoints::get();
		if ( empty( $breakpoints ) ) {
			return;
		}

		$param = array(
			'type'        => 'cafw_visibility',
			'heading'     => esc_html__( 'Hide on', 'craft-addons-for-wpbakery' ),
			'param_name'  => self::PARAM,
			'breakpoints' => $breakpoints,
			'group'       => esc_html__( 'Responsive Visibility', 'craft-addons-for-wpbakery' ),
			'description' => esc_html__( 'Select the screens this element should be hidden on.', 'craft-addons-for-wpbakery' ),
		);

		$shortcodes = WPBMap::getAllShortCodes();
		if ( ! is_array( $shortcodes ) ) {
			return;
		}

		foreach ( array_keys( $shortcodes ) as $base ) {
			vc_add_param( $base, $param );
		}
	}

	/**
	 * @param string $output Rendered HTML.
	 * @param string $tag    Shortcode tag.
	 * @param array  $attr   Shortcode attributes.
	 * @return string
	 */
	public static function inject_classes( $output, $tag, $attr ) {
		if ( ! is_array( $attr ) || empty( $attr[ self::PARAM ] ) ) {
			return $output;
		}

		$classes = array();
		foreach ( array_filter( array_map( 'trim', explode( ',', (string) $attr[ self::PARAM ] ) ) ) as $key ) {
			$classes[] = CAFW_Breakpoints::hide_class( $key );
		}

		return empty( $classes ) ? $output : self::apply_classes( $output, $classes );
	}

	/**
	 * @param string   $html    Markup.
	 * @param string[] $classes Classes to add to the first opening tag.
	 * @return string
	 */
	public static function apply_classes( $html, array $classes ) {
		$add = trim( implode( ' ', array_map( 'sanitize_html_class', $classes ) ) );
		if ( '' === $add ) {
			return $html;
		}

		if ( preg_match( '/<[a-z][a-z0-9]*\b[^>]*>/i', $html, $m, PREG_OFFSET_CAPTURE ) ) {
			$tag = $m[0][0];
			$pos = $m[0][1];

			if ( preg_match( '/\sclass\s*=\s*("|\')(.*?)\1/i', $tag, $cm ) ) {
				$new_tag = str_replace( $cm[0], ' class=' . $cm[1] . $add . ' ' . $cm[2] . $cm[1], $tag );
			} else {
				$new_tag = preg_replace( '/^(<[a-z][a-z0-9]*)/i', '$1 class="' . $add . '"', $tag, 1 );
			}

			return substr_replace( $html, $new_tag, $pos, strlen( $tag ) );
		}

		return '<span class="' . esc_attr( $add ) . '">' . $html . '</span>';
	}

	/**
	 * @return void
	 */
	public static function enqueue_css() {
		if ( function_exists( 'vc_is_inline' ) && vc_is_inline() ) {
			return;
		}

		$css = CAFW_Breakpoints::css();
		if ( '' === $css ) {
			return;
		}

		wp_register_style( 'cafw-responsive', false, array(), CAFW_VERSION );
		wp_enqueue_style( 'cafw-responsive' );
		wp_add_inline_style( 'cafw-responsive', $css );
	}
}
