<?php
/**
 * Form Builder Component Class.
 *
 * @package fusion-builder
 * @since 3.1
 */

// Do not allow directly accessing this file.
if ( ! defined( 'ABSPATH' ) ) {
	exit( 'Direct script access denied.' );
}


/**
 * Form Builder Component Class.
 *
 * @since 3.1
 */
abstract class Fusion_Form_Component extends Fusion_Element {

	/**
	 * Global form options.
	 *
	 * @access public
	 * @since 3.1
	 * @var array
	 */
	public $params = [];

	/**
	 * Shortcode handle.
	 *
	 * @access public
	 * @since 3.1
	 * @var string
	 */
	public $shortcode_handle = '';

	/**
	 * The number of elements.
	 *
	 * @var int
	 */
	public $counter = 0;

	/**
	 * Constructor.
	 *
	 * @since 3.1
	 * @param string $shortcode_handle Shortcode Handle.
	 */
	public function __construct( $shortcode_handle ) {
		parent::__construct();
		$this->shortcode_handle = $shortcode_handle;
		add_shortcode( $this->shortcode_handle, [ $this, 'render' ] );
	}

	/**
	 * Get the element default arguments.
	 *
	 * @return array
	 */
	abstract public static function get_element_defaults();

	/**
	 * Render the shortcode
	 *
	 * @access public
	 * @since 2.2
	 * @param  array  $args    Shortcode parameters.
	 * @param  string $content Content between shortcode.
	 * @return string          HTML output.
	 */
	public function render( $args, $content = '' ) {
		$this->counter++;

		$defaults = FusionBuilder::set_shortcode_defaults( $this->get_element_defaults(), $args, $this->shortcode_handle );
		$content  = apply_filters( 'fusion_shortcode_content', $content, $this->shortcode_handle, $args );

		$this->args = $defaults;

		$html = $this->get_form_field( $content );

		$this->on_render();

		return apply_filters( 'fusion_form_component_' . $this->shortcode_handle . '_content', $html, $args );
	}

	/**
	 * Creates all meta data for the form elements.
	 *
	 * @since 3.1
	 * @access public
	 * @param array $args All needed values for the form field.
	 * @return string The meta data of a form element.
	 */
	public function create_element_data( $args ) {
		$data = [
			'checked'              => '',
			'readonly'             => '',
			'disabled'             => '',
			'required'             => '',
			'invalid_notice'       => '',
			'empty_notice'         => '',
			'required_label'       => '',
			'required_placeholder' => '',
			'placeholder'          => '',
			'label'                => '',
			'label_class'          => '',
			'style'                => '',
			'holds_private_data'   => 'no',
			'upload_size'          => '',
			'pattern'              => '',
		];

		$is_hidden = isset( $args['hidden'] ) && $args['hidden'];

		if ( ! $args ) {
			return $data;
		}

		// TODO: move input specific code to own file.
		if ( 'fusion_form_checkbox' === $this->shortcode_handle && isset( $args['checked'] ) && $args['checked'] ) {
			$data['checked'] = ' checked="checked"';
		}

		if ( 'fusion_form_upload' === $this->shortcode_handle ) {
			if ( isset( $args['upload_size'] ) && $args['upload_size'] ) {
				$data['upload_size'] = ' data-size="' . esc_attr( $args['upload_size'] ) . '"';
			}
		}

		if ( isset( $args['required'] ) && ( 'yes' === $args['required'] || 'selection' === $args['required'] ) ) {
			if ( 'selection' !== $args['required'] ) {
				$data['required'] = ' required="true" aria-required="true"';
			}
			$data['required_label_text'] = __( 'required', 'fusion-builder' );
			if ( ! empty( $args['required_label_text'] ) ) {
				$data['required_label_text'] = $args['required_label_text'];
			}

			$data['required_label']       = ' <abbr class="fusion-form-element-required" title="' . esc_attr( $data['required_label_text'] ) . '">*</abbr>';
			$data['required_placeholder'] = '*';

			if ( isset( $args['empty_notice'] ) && '' !== $args['empty_notice'] ) {
				$data['empty_notice'] = $args['empty_notice'];
			}
		}

		if ( isset( $args['disabled'] ) && 'yes' === $args['disabled'] ) {
			$data['disabled'] = ' disabled';

			if ( isset( $args['placeholder'] ) && '' !== $args['placeholder'] ) {
				$data['value'] = $args['placeholder'];
			}
		} else if ( isset( $args['readonly'] ) && 'yes' === $args['readonly'] ) {
			$data['readonly'] = ' readonly';

			if ( isset( $args['placeholder'] ) && '' !== $args['placeholder'] ) {
				$data['value'] = $args['placeholder'];
			}
		}

		$additional_class = isset( $args['input_class'] ) ? ' ' . $args['input_class'] : '';
		$data['class'] = ' class="fusion-form-input' . esc_attr( $additional_class ) . '"';

		if ( isset( $args['placeholder'] ) && '' !== $args['placeholder'] ) {
			if ( 'fusion_form_select' === $this->shortcode_handle ) {
				$data['placeholder'] = [ $args['placeholder'] . ' ' . $data['required_placeholder'] ];
			} else {
				$data['placeholder'] = ' placeholder="' . esc_attr( $args['placeholder'] . ' ' . $data['required_placeholder'] ) . '"';
			}
		}

		if ( isset( $args['label'] ) && '' !== $args['label'] && ! $is_hidden ) {
			$non_label_elements = [ 'fusion_form_radio', 'fusion_form_checkbox', 'fusion_form_image_select', 'fusion_form_rating' ];

			if ( 'fusion_form_checkbox' === $this->shortcode_handle ) {
				$data['label_class'] = ' class="fusion-form-checkbox-label"';
			}

			$tag_open  = '<label for="' . esc_attr( $args['name'] ) . '"' . esc_attr( $data['label_class'] ) . '>';
			$tag_close = '</label>';

			if ( in_array( $this->shortcode_handle, $non_label_elements, true ) ) {
				$tag_open  = '<span class="label">';
				$tag_close = '</span>';
			}

			$data['label'] = $tag_open . esc_html( $args['label'] ) . $data['required_label'] . $tag_close;
		}

		$data['holds_private_data'] = ' data-holds-private-data="false"';
		if ( isset( $args['holds_private_data'] ) && $args['holds_private_data'] ) {
			$data['holds_private_data'] = ' data-holds-private-data="true"';
		}

		$data['input_attributes'] = '';

		// Number field min and max.
		if ( isset( $args['min'] ) && is_numeric( $args['min'] ) ) {
			$data['input_attributes'] .= ' min="' . $args['min'] . '"';
		}

		if ( isset( $args['max'] ) && is_numeric( $args['max'] ) ) {
			$data['input_attributes'] .= ' max="' . $args['max'] . '"';
		}

		if ( isset( $args['must_match'] ) && '' !== $args['must_match'] ) {
			$data['input_attributes'] = ' data-must-match="' . $args['must_match'] . '"';
		}

		// Text field minlength and maxlength.
		if ( isset( $args['minlength'] ) && is_numeric( $args['minlength'] ) ) {
			$data['input_attributes'] .= ' minlength="' . $args['minlength'] . '"';
		}
		if ( isset( $args['maxlength'] ) && ! empty( $args['maxlength'] ) ) {
			$data['input_attributes'] .= ' maxlength="' . $args['maxlength'] . '"';
		}

		if ( isset( $args['invalid_notice'] ) && '' !== $args['invalid_notice'] ) {
			$data['invalid_notice'] = $args['invalid_notice'];
		}

		return $data;
	}

	/**
	 * Generate and returns tooltip html for the input field.
	 *
	 * @since 3.1
	 * @access public
	 * @param array $args All needed values for the form field.
	 * @return string $html HTML for tooltip.
	 */
	public function get_field_tooltip( $args ) {
		global $fusion_form;
		$html = '';

		if ( '' !== $args['tooltip'] ) {
			$html .= '<div class="fusion-form-tooltip">';
			$html .= '<i class="awb-icon-question-circle"></i>';
			$html .= '<span class="fusion-form-tooltip-content">' . $args['tooltip'] . '</span>';
			$html .= '</div>';
		}

		return $html;
	}

	/**
	 * Renders normal text inputs.
	 *
	 * @since 3.1
	 * @access private
	 * @param array  $args All needed values for the form field.
	 * @param string $type The type of the input, e.g. text, number, email.
	 * @return string      The text input element.
	 */
	public function generate_input_field( $args, $type = 'text' ) {
		global $fusion_form;

		$html         = '';
		$element_data = $this->create_element_data( $args );

		if ( '' !== $args['tooltip'] ) {
			$element_data['label'] .= $this->get_field_tooltip( $args );
		}

		$args['value'] = isset( $args['value'] ) ? $args['value'] : ( isset( $element_data['value'] ) ? $element_data['value'] : '' );

		$element_html = '<input type="' . $type . '" ';

		$autocomplete  = 'custom' === $args['autocomplete'] ? $args['autocomplete_custom'] : $args['autocomplete'];
		$element_html .= 'autocomplete="' . esc_attr( $autocomplete ) . '" ';

		if ( 'hidden' !== $type && '' !== $this->args['tab_index'] ) {
			$element_html .= 'tabindex="' . $this->args['tab_index'] . '" ';
		}

		$args['name']  = esc_attr( $args['name'] );
		$args['value'] = esc_attr( $args['value'] );

		$element_html .= 'text' === $type && isset( $args['pattern'] ) && '' !== $args['pattern'] ? $this->add_pattern( $args ) : '';
		$element_html .= in_array( $type, [ 'email', 'password', 'tel' ], true ) && isset( $args['pattern'] ) && '' !== $args['pattern'] ? ' pattern="' . fusion_decode_if_needed( $args['pattern'] ) . '" ' : '';
		$element_html .= '' !== $element_data['invalid_notice'] ? ' data-invalid-notice="' . $element_data['invalid_notice'] . '" ' : '';
		$element_html .= '' !== $element_data['empty_notice'] ? ' data-empty-notice="' . $element_data['empty_notice'] . '" ' : '';
		$element_html .= 'name="' . $args['name'] . '" id="' . $args['name'] . '" value="' . $args['value'] . '" ' . $element_data['class'] . $element_data['required'] . $element_data['readonly'] . $element_data['disabled'] . $element_data['placeholder'] . $element_data['style'] . $element_data['holds_private_data'] . $element_data['input_attributes'] . $element_data['pattern'] . '/>';

		$icon_wrapper_open = '';
		if ( isset( $args['input_field_icon'] ) && '' !== $args['input_field_icon'] ) {
			$icon_wrapper_open = '<div class="fusion-form-input-with-icon">';
			$icon_html         = '<i class="awb-form-icon ' . fusion_font_awesome_name_handler( $args['input_field_icon'] ) . '"></i>';
			$element_html      = $icon_html . $element_html;
		}

		if ( 'password' === $type && 'yes' === $this->args['reveal_password'] ) {
			if ( '' === $icon_wrapper_open ) {
				$icon_wrapper_open = '<div class="fusion-form-input-with-icon awb-form-pw-reveal">';
			} else {
				$icon_wrapper_open = str_replace( 'fusion-form-input-with-icon', 'fusion-form-input-with-icon awb-form-pw-reveal awb-form-both-icons', $icon_wrapper_open );
			}
			$element_html .= '<i class="awb-form-pw-reveal-icon awb-icon-eye-slash" id="' . $args['name'] . '_' . $this->counter . '"></i>';
		}

		$element_html  = $icon_wrapper_open . $element_html;
		$element_html .= '' === $icon_wrapper_open ? '' : '</div>';

		if ( '' !== $element_data['label'] ) {
			$element_data['label'] = '<div class="fusion-form-label-wrapper">' . $element_data['label'] . '</div>';
		}

		if ( 'above' === $fusion_form['form_meta']['label_position'] ) {
			$html .= $element_data['label'] . $element_html;
		} else {
			$html .= $element_html . $element_data['label'];
		}

		return $html;
	}

	/**
	 * Adds pattern attribute to text field.
	 *
	 * @since 3.8
	 * @access public
	 * @param array $args All needed values for the form field.
	 * @return string The updated HTML.
	 */
	public function add_pattern( $args ) {
		$patterns = [
			'letters'            => '[a-zA-Z]+',
			'alpha_numeric'      => '[a-zA-Z0-9]+',
			'number'             => '[0-9]+',
			'credit_card_number' => '[0-9]{13,16}',
			'phone'              => '[0-9()#&+*-=.]+',
			'url'                => '(http(s)?:\/\/.)?(www\.)?[\-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([\-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)',
		];

		return isset( $patterns[ $args['pattern'] ] ) ? ' pattern="' . $patterns[ $args['pattern'] ] . '" ' : ' pattern="' . fusion_decode_if_needed( $args['custom_pattern'] ) . '" ';
	}

	/**
	 * Renders checkbox inputs.
	 *
	 * @since 3.1
	 * @access private
	 * @param array  $args All needed values for the form field.
	 * @param string $type The type of the input, e.g. radio, checkbox.
	 * @return string The checkbox input element.
	 */
	public function checkbox( $args, $type = 'checkbox' ) {
		global $fusion_form;

		$options = '';
		$html    = '';

		if ( empty( $args['options'] ) ) {
			return $html;
		} else {
			$args['options'] = json_decode( fusion_decode_if_needed( $args['options'] ), true );
		}

		$element_data = $this->create_element_data( $args );

		foreach ( $args['options'] as $key => $option ) {
			$checked = $option[0] ? ' checked ' : '';
			$label   = trim( $option[1] );
			$value   = isset( $option[2] ) && '' !== $option[2] ? trim( $option[2] ) : $label;

			$name         = empty( $args['name'] ) ? $args['label'] : $args['name'];
			$element_name = ( 'checkbox' === $type ) ? $name . '[]' : $name;

			$checkbox_class = ( 'floated' === $args['form_field_layout'] ) ? 'fusion-form-' . $type . ' option-inline' : 'fusion-form-' . $type;
			$label_id       = $type . '-' . str_replace( ' ', '-', strtolower( $name ) ) . '-' . $this->counter . '-' . $key;
			$options       .= '<div class="' . $checkbox_class . '">';
			$options       .= '<input ';
			$options       .= '' !== $element_data['empty_notice'] ? 'data-empty-notice="' . $element_data['empty_notice'] . '" ' : '';
			$options       .= 'tabindex="' . $args['tab_index'] . '" id="' . $label_id . '" type="' . $type . '" value="' . $value . '" name="' . $element_name . '"' . $element_data['class'] . $element_data['required'] . $checked . $element_data['holds_private_data'] . '/>';
			$options       .= '<label for="' . $label_id . '">';
			$options       .= $label . '</label>';
			$options       .= '</div>';
		}

		$fieldset_attr_string = '';
		if ( isset( $args['fieldset_attr_string'] ) && $args['fieldset_attr_string'] ) {
			$fieldset_attr_string = ' ' . $args['fieldset_attr_string'];
		}
		$element_open_html  = '<fieldset' . $fieldset_attr_string . '>';
		$element_close_html = '</fieldset>';
		$element_html       = $options;

		if ( '' !== $args['tooltip'] ) {
			$element_data['label'] .= $this->get_field_tooltip( $args );
		}

		if ( '' !== $element_data['label'] ) {
			$tag = 'above' === $fusion_form['form_meta']['label_position'] ? 'legend' : 'div';
			$element_data['label'] = '<' . $tag . ' class="fusion-form-label-wrapper">' . $element_data['label'] . '</' . $tag . '>';
		}

		if ( 'above' === $fusion_form['form_meta']['label_position'] ) {
			$html .= $element_open_html . $element_data['label'] . $element_html . $element_close_html;
		} else {
			$html .= $element_open_html . $element_html . $element_close_html . $element_data['label'];
		}

		return $html;
	}

	/**
	 * Adds field data to the form.
	 *
	 * @access public
	 * @since 3.1
	 * @return void
	 */
	public function add_field_data_to_form() {
		global $fusion_form;

		if ( ! isset( $fusion_form['form_fields'] ) ) {
			$fusion_form['form_fields'] = [];
		}

		$fusion_form['form_fields'][] = $this->shortcode_handle;

		if ( isset( $this->args['label'] ) ) {
			$fusion_form['field_labels'][ $this->args['name'] ] = $this->args['label'];
		}

		$field_name = str_replace( 'fusion_form_', '', $this->shortcode_handle );
		$name       = isset( $this->args['name'] ) ? $this->args['name'] : $field_name . '_' . $this->counter;

		if ( isset( $this->args['logics'] ) ) {
			$fusion_form['field_logics'][ $name ] = base64_decode( $this->args['logics'] );
		}
		$fusion_form['field_types'][ $name ] = $field_name;
	}

	/**
	 * Adds field wrapper html.
	 *
	 * @access public
	 * @since 3.1
	 * @return string
	 */
	public function add_field_wrapper_html() {
		$label_position = 'above';

		if ( $this->params['form_meta']['label_position'] ) {
			$label_position = $this->params['form_meta']['label_position'];
		}

		$html = '<div ';

		// Add custom ID if it's there.
		if ( isset( $this->args['id'] ) && '' !== $this->args['id'] ) {
			$html .= 'id="' . esc_attr( $this->args['id'] ) . '" ';
		}

		// Start building class.
		$html .= 'class="fusion-form-field ' . str_replace( '_', '-', $this->shortcode_handle ) . '-field fusion-form-label-' . $label_position;

		// Add custom class if it's there.
		if ( isset( $this->args['class'] ) && '' !== $this->args['class'] ) {
			$html .= ' ' . esc_attr( $this->args['class'] );
		}

		// Hide field if it has got logics.
		if ( isset( $this->args['logics'] ) && '' !== $this->args['logics'] && '[]' !== base64_decode( $this->args['logics'] ) ) {
			$html .= ' fusion-form-field-hidden';
		}

		// Close class quotes.
		$html .= '"';

		$html .= ' style="' . $this->get_style_variables() . '"';

		$html .= ' data-form-id="' . $this->params['form_number'] . '"';

		if ( isset( $this->args['logics'] ) && '' !== $this->args['logics'] && '[]' !== base64_decode( $this->args['logics'] ) ) {
			$field_name = str_replace( 'fusion_form_', '', $this->shortcode_handle ) . '_' . $this->counter;
			$html .= ' data-form-element-name="' . esc_attr( $field_name ) . '"';
		}

		$html .= '>';

		return $html;
	}

	/**
	 * Get the style variables.
	 *
	 * @access protected
	 * @since 3.9
	 * @return string
	 */
	public function get_style_variables() {
		return '';
	}

	/**
	 * Get the global $fusion_form value.
	 *
	 * @access public
	 * @since 3.1
	 * @return array
	 */
	public function get_form_data() {
		global $fusion_form;

		if ( ! isset( $fusion_form['id'] ) ) {
			if ( fusion_doing_ajax() && isset( $_POST['post_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
				$fusion_form = Fusion_Builder_Form_Helper::fusion_form_set_form_data( (int) sanitize_text_field( wp_unslash( $_POST['post_id'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
			} elseif ( ! fusion_doing_ajax() && isset( $_POST['post_ID'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
				$fusion_form = Fusion_Builder_Form_Helper::fusion_form_set_form_data( (int) sanitize_text_field( wp_unslash( $_POST['post_ID'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
			} elseif ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {

				// We need default form params, otherwise REST request triggers PHP notices when content is rendered.
				$fusion_form = Fusion_Builder_Form_Helper::fusion_form_set_form_data( 0 );
			}
		}

		return $fusion_form;
	}

	/**
	 * Render form field html.
	 *
	 * @access public
	 * @since 3.1
	 * @param string $content The content.
	 * @return string
	 */
	public function get_form_field( $content ) {
		global $fusion_form;

		// Add form data to form element.
		if ( empty( $this->params ) || $this->params['id'] !== $fusion_form['id'] ) {
			$this->params = $this->get_form_data();
		}

		// Add form element data to a form.
		$this->add_field_data_to_form();

		// Get form input html.
		$html  = $this->add_field_wrapper_html();
		$html .= $this->render_input_field( $content );
		$html .= '</div>';

		return $html;
	}

	/**
	 * Render form input field html.
	 *
	 * @access public
	 * @since 3.1
	 * @param string $content The content.
	 * @return false
	 */
	public function render_input_field( $content ) {
		return false;
	}
}
