<?php

if ( ! defined( 'ABSPATH' ) ) {
	die( 'You are not allowed to call this page directly.' );
}

/**
 * @since 4.06
 */
class FrmProImages {

	/**
	 * Checks if field has image options. This wraps FrmProImages:should_show_images() method.
	 *
	 * @param array $field Field data.
	 * @return bool
	 */
	public static function has_image_options( $field ) {
		return self::field_type_support_image_options( $field ) && self::should_show_images( $field );
	}

	/**
	 * @since 5.0.06
	 *
	 * @param array $field
	 * @return bool
	 */
	private static function field_type_support_image_options( $field ) {
		if ( FrmField::is_field_type( $field, 'radio' ) || FrmField::is_field_type( $field, 'checkbox' ) ) {
			return true;
		}

		/**
		 * @since 6.8.3
		 *
		 * @param bool   $supports_image_options
		 * @return array $field
		 */
		return (bool) apply_filters( 'frm_field_type_support_image_options', false, $field );
	}

	/**
	 * @param array|string $options
	 * @return bool
	 */
	public static function has_images_options_in_html( $options ) {
		$options = is_array( $options ) ? implode( ' ', $options ) : $options;
		return strpos( $options, 'frm_image_option' ) !== false;
	}

	/**
	 * @param array|string $value
	 * @return bool
	 */
	public static function has_image_option_markup( $value ) {
		return is_string( $value ) && strpos( $value, 'frm_image_option_container' ) !== false;
	}

	public static function get_image_icon_markup() {
		return '<div class="frm_image_placeholder_icon">' . FrmAppHelper::icon_by_class( 'frmfont frm_placeholder_image_icon', array( 'echo' => false ) ) . '</div>';
	}

	public static function get_default_size() {
		return 'small';
	}

	/**
	 * Load settings in builder
	 *
	 * @param array $args
	 * @return void
	 */
	public static function show_image_choices( $args ) {
		$field = $args['field'];
		if ( isset( $field['post_field'] ) && $field['post_field'] === 'post_category' ) {
			return;
		}

		$columns = array(
			'small'  => __( 'Small', 'formidable-pro' ),
			'medium' => __( 'Medium', 'formidable-pro' ),
			'large'  => __( 'Large', 'formidable-pro' ),
			'xlarge' => __( 'Extra Large', 'formidable-pro' ),
		);

		echo '<div class="frm_grid_container frm_priority_field_choices">';
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/image-options.php';
		echo '</div>';
		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/separate-values.php';
	}

	/**
	 * Called by hook in lite.
	 */
	public static function admin_options( $atts ) {
		$field = $atts['field'];

		if ( ! self::field_type_support_image_options( $field ) ) {
			return;
		}

		$opt_key = $atts['opt_key'];
		$opt     = isset( $field['options'][ $opt_key ] ) ? $field['options'][ $opt_key ] : '';
		$return  = array( 'filename' );
		$image   = self::single_option_details( compact( 'opt', 'opt_key', 'field', 'return' ) );
		$opt     = FrmFieldsHelper::get_label_from_array( $opt, $opt_key, $field );

		if ( ! isset( $field['image_options'] ) ) {
			$field['image_options'] = 0;
		}

		include self::get_backend_fields_path() . 'image-selector.php';
	}

	/**
	 * @return string
	 */
	private static function get_backend_fields_path() {
		return FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/';
	}

	/**
	 * Checks if field should show images. This is similar to FrmProImages:has_image_options() but does not contain
	 * field type check.
	 *
	 * @since 5.0
	 * @since 6.2 This method is public.
	 *
	 * @param array $field Field data.
	 * @return bool
	 */
	public static function should_show_images( $field ) {
		$image_options = FrmField::get_option( $field, 'image_options' );

		/**
		 * Allows show or hide choice field images using custom code.
		 *
		 * @since 5.0
		 *
		 * @param bool  $show Show images or not.
		 * @param array $args The arguments. Contains `field`.
		 */
		return apply_filters( 'frm_pro_field_should_show_images', ! empty( $image_options ), compact( 'field' ) );
	}

	/**
	 * @param array $atts - includes opt, opt_key, field, and return.
	 */
	public static function single_option_details( $atts ) {
		$id    = self::get_image_from_array( $atts['opt'], $atts['opt_key'], $atts['field'] );
		$image = array(
			'id'  => $id,
			'url' => self::get_url( $id ),
		);

		if ( in_array( 'filename', $atts['return'], true ) ) {
			$image['filename'] = self::get_filename( $id );
		}

		if ( in_array( 'label', $atts['return'], true ) ) {
			$image['label'] = self::get_label( $atts['field'], $atts['opt'], $image['url'] );
		}

		return $image;
	}

	private static function get_image_from_array( $opt, $opt_key, $field ) {
		$opt = apply_filters( 'frm_field_image_id', $opt, $opt_key, $field );

		return self::check_image( $opt, $field );
	}

	private static function check_image( $opt, $field ) {
		if ( is_array( $opt ) ) {
			if ( FrmField::is_option_true( $field, 'image_options' ) ) {
				$opt = isset( $opt['image'] ) ? $opt['image'] : 0;
			} else {
				$opt = 0;
			}
		}

		return $opt;
	}

	/**
	 * Called by self::single_option_details.
	 *
	 * @param mixed $image_id
	 * @return string
	 */
	private static function get_url( $image_id ) {
		if ( ! self::validate_image_id( $image_id ) ) {
			return '';
		}

		$image_id = (int) $image_id;
		$size     = self::get_default_size();
		$src      = wp_get_attachment_image_src( $image_id, $size );
		$url      = is_array( $src ) ? $src[0] : '';

		if ( ! $url ) {
			$url = wp_get_attachment_image_url( $image_id );
		}

		return $url ? $url : '';
	}

	/**
	 * Check if an image id isn't empty and is a number before trying to get the image.
	 *
	 * @param mixed $image_id
	 * @return bool true if valid.
	 */
	private static function validate_image_id( $image_id ) {
		return $image_id && is_numeric( $image_id );
	}

	/**
	 * Called by self::single_option_details.
	 */
	private static function get_filename( $image_id ) {
		if ( empty( $image_id ) ) {
			return '';
		}

		$filename = get_post_meta( (int) $image_id, '_wp_attached_file', true );

		$matches = array();
		preg_match( '/([A-Za-z0-9.\-_]+)$/', $filename, $matches );

		return isset( $matches[0] ) ? $matches[0] : '';
	}

	/**
	 * Called by self::single_option_details.
	 *
	 * @param string $image_url
	 */
	private static function get_label( $field, $opt, $image_url = '' ) {
		if ( ! self::should_show_images( $field ) ) {
			return $opt;
		}

		$show_label  = self::should_show_label( $field );
		$label_class = $show_label ? ' frm_label_with_image' : '';
		$text_label  = self::get_label_from_opt( $opt, $field );
		$field_type  = FrmField::get_option( $field, 'type' );

		$label  = '<div class="frm_image_option_container' . esc_attr( $label_class ) . '">';
		if ( empty( $image_url ) ) {
			$label .= '<div class="frm_empty_url">' . self::get_image_icon_markup() . '</div>';
		} else {
			$label .= '<img src="' . esc_url( $image_url ) . '" alt="' . esc_attr( $text_label ) . '" />';
		}

		if ( $show_label ) {
			$label .= '<span class="frm_text_label_for_image"><span class="frm_text_label_for_image_inner">' . $text_label . '</span></span>';
		}

		$label .= '</div>';

		return $label;
	}

	/**
	 * Checks if should show image label in given field.
	 *
	 * @since 5.0
	 *
	 * @param array $field Field data.
	 * @return bool
	 */
	private static function should_show_label( $field ) {
		/**
		 * Allows showing or hiding image label from custom code.
		 *
		 * @since 5.0
		 *
		 * @param bool  $show Set to `true` to show.
		 * @param array $args The arguments. Contains `field`.
		 */
		return apply_filters( 'frm_pro_field_should_show_label', empty( $field['hide_image_text'] ), compact( 'field' ) );
	}

	/**
	 * @param array|string $opt
	 * @param array|object $field This is passed so it can be used as an arg for the frm_choice_field_option_label filter.
	 * @return string
	 */
	private static function get_label_from_opt( $opt, $field ) {
		if ( is_array( $opt ) ) {
			$label = $opt['label'] ?? '';
		} else {
			$label = $opt;
		}

		$field = (array) $field;

		/**
		 * Allows changing the HTML of option label in choice field (radio, checkbox,...).
		 *
		 * @since 6.23 This filter was added in Lite in version 5.0.04, but was not supported for image options.
		 *
		 * @param string $label Label HTML.
		 * @param array  $args  The arguments. Contains `field`.
		 */
		$filtered_label = apply_filters( 'frm_choice_field_option_label', $label, compact( 'field' ) );

		if ( ! is_string( $filtered_label ) ) {
			_doing_it_wrong( __METHOD__, 'The frm_choice_field_option_label filter must return a string.', '6.23' );
			return $label;
		}

		return $filtered_label;
	}

	/**
	 * Called by hooks.
	 */
	public static function get_image_option_classes( $classes, $field ) {
		return $classes . self::get_option_classes_from_field( $field );
	}

	private static function get_option_classes_from_field( $field ) {
		if ( ! self::should_show_images( $field ) ) {
			return '';
		}

		$image_size = FrmField::get_option( $field, 'image_size' );

		$class = ' frm_image_options ';
		if ( ! empty( $image_size ) ) {
			$class .= 'frm_image_size_' . $image_size . ' ';
		}
		return $class;
	}

	/**
	 * @param array $atts
	 * @return bool
	 */
	public static function showing_images( $field, $atts ) {
		$is_image_field = self::has_image_options( $field );

		// Don't show images in frm-show-entry if show_image_option=0.
		$in_entry_table = ! isset( $atts['show_image_options'] ) || $atts['show_image_options'];

		// Don't show images in field shortcodes if show_image=0.
		$show_image = ! isset( $atts['show_image'] ) || $atts['show_image'];

		// Only show images with the frm-show-entry shortcode if format is set to text or using default.
		$format = empty( $atts['format'] ) || $atts['format'] === 'text';

		return $is_image_field && $in_entry_table && $show_image && $format && empty( $atts['plain_text'] );
	}

	/**
	 * @param stdClass $field
	 * @param mixed    $value
	 * @param array    $atts
	 * @return array|string
	 */
	public static function display( $field, $value, $atts ) {
		$multiple_values = is_array( $value );

		$f_values = array();
		$f_labels = array();
		$f_images = array();

		foreach ( $field->options as $opt_key => $opt ) {
			if ( ! is_array( $opt ) ) {
				continue;
			}

			$f_labels[ $opt_key ] = isset( $opt['label'] ) ? $opt['label'] : reset( $opt );
			$f_values[ $opt_key ] = isset( $opt['value'] ) ? $opt['value'] : $f_labels[ $opt_key ];
			$f_images[ $opt_key ] = isset( $opt['image'] ) ? $opt['image'] : 0;
			unset( $opt_key, $opt );
		}

		if ( is_array( $value ) ) {
			$value = FrmAppHelper::array_flatten( $value, 'reset' );
		}

		$has_separate_option = FrmField::is_option_true( $field, 'separate_value' );
		$values_to_check     = $has_separate_option ? $f_values : $f_labels;

		if ( empty( $values_to_check ) ) {
			return $value;
		}

		if ( isset( $atts['show'] ) && $atts['show'] === 'value' ) {
			return $values_to_check;
		}

		foreach ( (array) $value as $v_key => $val ) {
			$val = self::maybe_adjust_val( $val, $values_to_check );
			if ( in_array( $val, $values_to_check ) ) {
				$opt           = array_search( $val, $values_to_check );
				$display_value = self::option_array( $f_labels, $f_images, $opt );

				if ( is_array( $value ) ) {
					$value[ $v_key ] = $display_value;
				} else {
					$value = $display_value;
				}
			}
			unset( $v_key, $val );
		}

		$hide_image_label  = ! empty( $field->field_options['hide_image_text'] );
		$image_size_option = FrmField::get_option( $field, 'image_size' );
		$image_values      = array(
			'display_options' => $value,
			'showing_images'  => isset( $atts['show_image'] ) ? $atts['show_image'] : false,
			'show_label'      => isset( $atts['show_label'] ) ? $atts['show_label'] : ! $hide_image_label,
			'multiple_values' => $multiple_values,
			'image_size'      => $image_size_option ? $image_size_option : self::get_default_size(),
		);

		return self::get_image_value( $atts, $image_values );
	}

	/**
	 * Checkbox value ampersands get encoded, so check for a decoded match if there is no match.
	 *
	 * @param string $val
	 * @param array  $values_to_check
	 * @return string
	 */
	private static function maybe_adjust_val( $val, $values_to_check ) {
		if ( false === strpos( $val, '&amp;' ) || in_array( $val, $values_to_check ) ) {
			return $val;
		}
		return str_replace( '&amp;', '&', $val );
	}

	/**
	 * @param array $f_labels
	 * @param array $f_images
	 * @param mixed $opt
	 * @return array
	 */
	private static function option_array( $f_labels, $f_images, $opt ) {
		return array(
			'label' => $f_labels[ $opt ],
			'image' => $f_images[ $opt ],
		);
	}

	/**
	 * @param array $atts
	 * @param array $image_values
	 * @return array|string
	 */
	private static function get_image_value( $atts, $image_values ) {
		if ( empty( $image_values['display_options'] ) ) {
			return '';
		}

		$image_values['file_object'] = FrmFieldFactory::get_field_type( 'file' );

		if ( empty( $image_values['multiple_values'] ) ) {
			return self::get_value_for_display( $image_values['display_options'], $atts, $image_values );
		}

		$image_markup = array();
		foreach ( $image_values['display_options'] as $single_image_values ) {
			$image_markup[] = self::get_value_for_display( $single_image_values, $atts, $image_values );
		}

		return $image_markup;
	}

	private static function get_value_for_display( $value, $atts, $image_values ) {
		if ( ! is_array( $value ) ) {
			return $value;
		}

		if ( isset( $atts['show'] ) && trim( $atts['show'] ) === 'id' ) {
			return empty( $value['image'] ) ? '' : $value['image'];
		}

		$image_size = $image_values['image_size'] ? $image_values['image_size'] : self::get_default_size();

		$file_field_object                  = $image_values['file_object'];
		$new_atts                           = $file_field_object->set_file_atts( $atts );
		$new_atts['show_image']             = isset( $atts['show_image'] ) ? $atts['show_image'] : 1;
		$new_atts['add_link_for_non_image'] = false;

		// If image_option_size is set for frm-show-entry shortcode, use it.
		if ( ! empty( $atts['image_option_size'] ) ) {
			$atts['size'] = $atts['image_option_size'];
		}

		$new_atts['size'] = $file_field_object->set_size( $atts );

		$has_image = ! empty( $value['image'] ) && $new_atts['show_image'];

		$display_content = '';
		$label           = isset( $value['label'] ) ? $value['label'] : '';

		if ( $has_image ) {
			$image_id = $value['image'];
			$alt_tag  = strip_tags( get_post_meta( $image_id, '_wp_attachment_image_alt', true ) );
			if ( ! $alt_tag && $label !== '' ) {
				// If alt tag not set for image, set the label as the alt tag for the image.
				update_post_meta( $image_id, '_wp_attachment_image_alt', $label, $alt_tag );
			}

			$display_content .= $file_field_object->get_file_display( $value['image'], $new_atts );
		}

		$label_class = '';
		$show_label  = ( $label !== '' && ( $image_values['show_label'] || ! $has_image || strpos( $display_content, 'img' ) === false ) );

		if ( $show_label ) {
			// using FrmAppHelper::kses over esc_html to fix Pro issue #3933
			$display_content .= '<span class="frm_text_label_for_image"><span class="frm_text_label_for_image_inner">' . FrmAppHelper::kses( $label, 'all' ) . '</span></span>';
			$label_class      = ' frm_label_with_image';
		}

		$display_content = '<span class="frm_show_images frm_image_option_container frm_image_option_size_' . esc_attr( $image_size . $label_class ) . '">' . $display_content . '</span>';

		return $display_content;
	}

	/**
	 * By default, most HTML is stripped from a value.
	 * This includes the HTML that is added by this function.
	 * This image markup is added very early, before get_display_value is called.
	 * As a result it goes through all of the display filtering as well.
	 *
	 * @since 6.8
	 *
	 * @param array $allowed_html
	 * @return array
	 */
	public static function allow_image_option_html( $allowed_html ) {
		$allowed_html['div']  = array( 'class' => true );
		$allowed_html['span'] = array(
			'id'    => true,
			'class' => true,
		);
		$allowed_html['img']  = array(
			'src'   => true,
			'class' => true,
			'alt'   => true,
		);
		$allowed_html['a']    = array(
			'href'   => true,
			'class'  => true,
			'target' => true,
		);
		return $allowed_html;
	}
}
