<?php
/**
 * Block class.
 *
 * @package Media_Library_Organizer
 * @author Themeisle
 */

/**
 * Media Library Organizer Block definition for Gutenberg and Shortcode.
 *
 * @package Media_Library_Organizer
 * @author Themeisle
 */
class Media_Library_Organizer_Block {

	/**
	 * Registers this block with the Plugin.
	 *
	 * @since   1.4.9
	 *
	 * @param   array $blocks     Blocks to Register.
	 * @return  array               Blocks to Register
	 */
	public function register( $blocks ) {

		$blocks[ $this->get_name() ] = array_merge(
			$this->get_overview(),
			array(
				'name'           => $this->get_name(),
				'prefix'         => Media_Library_Organizer()->get_class( 'shortcode' )->get_prefix(), // e.g. media_library_organizer.
				'fields'         => $this->get_fields(),
				'attributes'     => $this->get_attributes(),
				'supports'       => $this->get_supports(),
				'tabs'           => $this->get_tabs(),
				'default_values' => $this->get_default_values(),
			)
		);

		return $blocks;
	}

	/**
	 * Returns this block's programmatic name, excluding the mlo- prefix.
	 *
	 * @since   1.4.9
	 */
	public function get_name() {

		return '';
	}

	/**
	 * Returns this block's Title, Icon, Categories, Keywords and properties.
	 *
	 * @since   1.4.9
	 *
	 * @return  array
	 */
	public function get_overview() {

		return array();
	}

	/**
	 * Returns this block's Attributes
	 *
	 * @since   1.4.9
	 *
	 * @return  array
	 */
	public function get_attributes() {

		return array();
	}

	/**
	 * Gutenberg: Returns supported built in attributes, such as
	 * className, color etc.
	 *
	 * @since   1.4.9
	 *
	 * @return  array   Supports
	 */
	public function get_supports() {

		return array(
			'className' => true,
		);
	}

	/**
	 * Returns this block's Fields
	 *
	 * @since   1.4.9
	 *
	 * @return  array
	 */
	public function get_fields() {

		return array();
	}

	/**
	 * Returns this block's UI panels / sections.
	 *
	 * @since   1.4.9
	 *
	 * @return  array
	 */
	public function get_tabs() {

		return array();
	}

	/**
	 * Returns this block's Default Values
	 *
	 * @since   1.4.9
	 *
	 * @return  array
	 */
	public function get_default_values() {

		return array();
	}

	/**
	 * Returns the given block's field's Default Value
	 *
	 * @since   1.4.9
	 *
	 * @param   string $field Field Name.
	 * @return  string
	 */
	public function get_default_value( $field ) {

		$defaults = $this->get_default_values();
		if ( isset( $defaults[ $field ] ) ) {
			return $defaults[ $field ];
		}

		return '';
	}

	/**
	 * Performs several transformation on a block's attributes, including:
	 * - sanitization
	 * - adding attributes with default values are missing but registered by the block
	 * - cast attribute values based on their defined type
	 *
	 * These steps are performed because the attributes may be defined by a shortcode,
	 * block or third party widget/page builder's block, each of which handle attributes
	 * slightly differently.
	 *
	 * Returns a standardised attributes array.
	 *
	 * @since   1.4.9
	 *
	 * @param   array $atts   Declared attributes.
	 * @return  array           All attributes, standardised.
	 */
	public function sanitize_and_declare_atts( $atts ) {

		// Sanitize attributes, merging with default values so that the array
		// of attributes contains all expected keys for this block.
		$atts = shortcode_atts(
			$this->get_default_values(),
			$this->sanitize_atts( $atts ),
			$this->get_name()
		);

		// Fetch attribute definitions.
		$atts_definitions = $this->get_attributes();

		// Iterate through attributes, casting them based on their attribute definition.
		foreach ( $atts as $att => $value ) {
			// Skip if no definition exists for this attribute.
			if ( ! array_key_exists( $att, $atts_definitions ) ) {
				continue;
			}

			// Skip if no type exists for this attribute.
			if ( ! array_key_exists( 'type', $atts_definitions[ $att ] ) ) {
				continue;
			}

			// Cast, depending on the attribute type.
			switch ( $atts_definitions[ $att ]['type'] ) {
				case 'number':
					$atts[ $att ] = (int) $value;
					break;

				case 'boolean':
					$atts[ $att ] = (bool) $value;
					break;
			}
		}

		// Build CSS class(es) that might need to be added to the top level element for this block.
		$atts['_css_classes'] = array( 'mlo-' . $this->get_name() );
		$atts['_css_styles']  = array();

		// If the block supports a text color, and a preset color was selected, add it to the
		// array of CSS classes.
		if ( $atts['textColor'] ) {
			$atts['_css_classes'][] = 'has-text-color';
			$atts['_css_classes'][] = 'has-' . $atts['textColor'] . '-color';
		}

		// If the block supports a text color, and a custom hex color was selected, add it to the
		// array of CSS inline styles.
		if ( isset( $atts['style']['color'] ) && isset( $atts['style']['color']['text'] ) ) {
			$atts['_css_classes'][]       = 'has-text-color';
			$atts['_css_styles']['color'] = 'color:' . $atts['style']['color']['text'];
		}

		// If the shortcode supports a text color, and a custom hex color was selected, add it to the
		// array of CSS inline styles.
		if ( isset( $atts['text_color'] ) && ! empty( $atts['text_color'] ) ) {
			$atts['_css_classes'][]       = 'has-text-color';
			$atts['_css_styles']['color'] = 'color:' . $atts['text_color'];
		}

		// If the block supports a background color, and a preset color was selected, add it to the
		// array of CSS classes.
		if ( $atts['backgroundColor'] ) {
			$atts['_css_classes'][] = 'has-background';
			$atts['_css_classes'][] = 'has-' . $atts['backgroundColor'] . '-background-color';
		}

		// If the block supports a background color, and a custom hex color was selected, add it to the
		// array of CSS inline styles.
		if ( isset( $atts['style']['color'] ) && isset( $atts['style']['color']['background'] ) ) {
			$atts['_css_classes'][]            = 'has-background';
			$atts['_css_styles']['background'] = 'background-color:' . $atts['style']['color']['background'];
		}

		// If the block supports a font size, and a preset font size was selected, add it to the
		// array of CSS classes.
		if ( isset( $atts['fontSize'] ) && ! empty( $atts['fontSize'] ) ) {
			$atts['_css_classes'][] = 'has-custom-font-size';
			$atts['_css_classes'][] = 'has-' . $atts['fontSize'] . '-font-size';
		}

		// If the block supports padding, and padding is set, add it to the
		// array of CSS inline styles.
		if ( isset( $atts['style']['spacing'] ) && isset( $atts['style']['spacing']['padding'] ) ) {
			foreach ( $atts['style']['spacing']['padding'] as $position => $value ) {
				$atts['_css_styles'][ 'padding-' . $position ] = 'padding-' . $position . ':' . $value;
			}
		}

		// If the shortcode supports a background color, and a custom hex color was selected, add it to the
		// array of CSS inline styles.
		if ( isset( $atts['background_color'] ) && ! empty( $atts['background_color'] ) ) {
			$atts['_css_classes'][]            = 'has-background';
			$atts['_css_styles']['background'] = 'background-color:' . $atts['background_color'];
		}

		// Remove some unused attributes, now they're declared above.
		unset( $atts['style'] );

		return $atts;
	}

	/**
	 * Removes any HTML that might be wrongly included in the shorcode attribute's values
	 * due to e.g. copy and pasting from Documentation or other examples.
	 *
	 * @since   1.4.9
	 *
	 * @param   array|mixed $atts Block or shortcode attributes.
	 * @return  array|mixed       Block or shortcode attributes
	 */
	public function sanitize_atts( $atts ) {

		if ( ! is_array( $atts ) ) {
			return $atts;
		}

		foreach ( $atts as $key => $value ) {
			if ( is_array( $value ) ) {
				continue;
			}

			$atts[ $key ] = wp_strip_all_tags( $value );
		}

		return $atts;
	}

	/**
	 * Returns the given block / shortcode attributes array as HTML data-* attributes, which can be output
	 * in a block's container.
	 *
	 * @since   1.4.9
	 *
	 * @param   array $atts   Block or shortcode attributes.
	 * @return  string        Block or shortcode attributes
	 */
	public function get_atts_as_html_data_attributes( $atts ) {

		// Define attributes provided by Gutenberg, which will be skipped, such as
		// styling.
		$skip_keys = array(
			'backgroundColor',
			'textColor',
			'_css_classes',
			'_css_styles',
		);

		// Define a blank string to build the data-* attributes in.
		$data = '';

		foreach ( $atts as $key => $value ) {
			// Skip built in attributes provided by Gutenberg.
			if ( in_array( $key, $skip_keys, true ) ) {
				continue;
			}

			// Append to data string, replacing underscores with hyphens in the key name.
			$data .= ' data-' . strtolower( str_replace( '_', '-', $key ) ) . '="' . esc_attr( $value ) . '"';
		}

		return trim( $data );
	}

	/**
	 * Determines if the request for the block is from the block editor or the frontend site.
	 *
	 * @since   1.4.9
	 *
	 * @return  bool
	 */
	public function is_block_editor_request() {

		// Return false if not a WordPress REST API request, which Gutenberg uses.
		if ( ! defined( 'REST_REQUEST' ) ) {
			return false;
		}
		if ( REST_REQUEST !== true ) {
			return false;
		}

		// Return false if the context parameter isn't edit.
		if ( filter_input( INPUT_GET, 'context', FILTER_SANITIZE_STRING ) !== 'edit' ) {
			return false;
		}

		// Request is for the block editor.
		return true;
	}
}
