<?php
/**
 * Combo field - Field contains sub fields
 *
 * @package Formidable
 *
 * @since 4.11
 */

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

class FrmFieldCombo extends FrmFieldType {

	/**
	 * Does the html for this field label need to include "for"?
	 *
	 * @var bool
	 *
	 * @since 3.0
	 */
	protected $has_for_label = false;

	/**
	 * Sub fields.
	 *
	 * @var array
	 */
	protected $sub_fields = array();

	/**
	 * This is used to check if field is combo field.
	 *
	 * @var bool
	 */
	public $is_combo_field = true;

	/**
	 * Gets ALL subfields.
	 *
	 * @return array
	 */
	public function get_sub_fields() {
		return $this->sub_fields;
	}

	/**
	 * Registers sub fields.
	 *
	 * @param array $sub_fields Sub fields. Accepts array or array or array of string.
	 *
	 * @return void
	 */
	protected function register_sub_fields( array $sub_fields ) {
		$defaults = $this->get_default_sub_field();

		foreach ( $sub_fields as $name => $sub_field ) {
			if ( empty( $sub_field ) ) {
				continue;
			}

			if ( is_array( $sub_field ) ) {
				$sub_field                 = wp_parse_args( $sub_field, $defaults );
				$sub_field['name']         = $name;
				$this->sub_fields[ $name ] = $sub_field;
				continue;
			}

			if ( is_string( $sub_field ) ) {
				$this->sub_fields[ $name ] = wp_parse_args(
					array(
						'name'  => $name,
						'label' => $sub_field,
					),
					$defaults
				);
			}
		}//end foreach
	}

	/**
	 * Set the aria-invalid attribute for subfields.
	 *
	 * @since 6.16.3
	 *
	 * @param array $shortcode_atts
	 * @param array $args
	 *
	 * @return void
	 */
	public function set_aria_invalid_error( &$shortcode_atts, $args ) {
		foreach ( $this->get_sub_fields() as $sub_field ) {
			$shortcode_atts[ 'aria-invalid-' . $sub_field['name'] ] = isset( $args['errors'][ 'field' . $this->field_id . '-' . $sub_field['name'] ] ) ? 'true' : 'false';
		}
	}

	/**
	 * Gets default sub field.
	 *
	 * @return array
	 */
	protected function get_default_sub_field() {
		return array(
			'type'            => 'text',
			'label'           => '',
			'classes'         => '',
			'wrapper_classes' => '',
			'options'         => array(
				'default_value',
				'placeholder',
				'desc',
			),
			'optional'        => false,
			'atts'            => array(),
		);
	}

	/**
	 * Registers extra options for saving.
	 *
	 * @return array
	 */
	protected function extra_field_opts() {
		$extra_options = parent::extra_field_opts();

		// Register for sub field options.
		foreach ( $this->sub_fields as $key => $sub_field ) {
			if ( empty( $sub_field['options'] ) || ! is_array( $sub_field['options'] ) ) {
				continue;
			}

			foreach ( $sub_field['options'] as $option ) {
				if ( 'default_value' === $option ) {
					// We parse default value from field column.
					continue;
				}

				if ( is_string( $option ) ) {
					$extra_options[ $key . '_' . $option ] = '';
				} elseif ( ! empty( $option['name'] ) ) {
					$extra_options[ $key . '_' . $option['name'] ] = '';
				}
			}
		}

		return $extra_options;
	}

	/**
	 * Include the settings for placeholder, default value, and sub labels for each
	 * of the individual field labels.
	 *
	 * @since 4.0
	 *
	 * @param array $args Includes 'field', 'display'.
	 *
	 * @return void
	 */
	public function show_after_default( $args ) {
		$field                = (array) $args['field'];
		$default_value        = $this->get_default_value();
		$processed_sub_fields = $this->get_processed_sub_fields();

		foreach ( $this->sub_fields as $name => $sub_field ) {
			$sub_field['name'] = $name;
			$wrapper_classes   = 'frm_grid_container frm_sub_field_options frm_sub_field_options-' . $sub_field['name'];

			if ( ! isset( $processed_sub_fields[ $name ] ) ) {
				// Options for this subfield should be hidden.
				$wrapper_classes .= ' frm_hidden';
			}

			include FrmAppHelper::plugin_path() . '/classes/views/frm-fields/back-end/combo-field/sub-field-options.php';
		}
	}

	/**
	 * Gets default value of field.
	 * This should return an array of default value of sub fields.
	 *
	 * @return array
	 */
	protected function get_default_value() {
		$default_value = $this->get_field_column( 'default_value' );

		if ( is_array( $default_value ) ) {
			return $default_value;
		}

		if ( is_object( $default_value ) ) {
			return (array) $default_value;
		}

		if ( ! $default_value ) {
			$default_value = array();

			foreach ( $this->sub_fields as $name => $sub_field ) {
				$default_value[ $name ] = '';
			}

			return $default_value;
		}

		// We store default value as JSON string in db.
		return json_decode( $default_value, true );
	}

	/**
	 * Gets labels for built-in options of fields or sub fields.
	 *
	 * @return array
	 */
	protected function get_built_in_option_labels() {
		return array(
			'default_value' => __( 'Default Value', 'formidable' ),
			'placeholder'   => __( 'Placeholder Text', 'formidable' ),
			'desc'          => __( 'Description', 'formidable' ),
		);
	}

	/**
	 * Which built-in settings this field supports?
	 *
	 * @return array
	 */
	protected function field_settings_for_type() {
		$settings = array(
			'description'    => false,
			'default'        => false,
			// Don't use the regular placeholder option.
			'clear_on_focus' => false,
			'logic'          => true,
			'visibility'     => true,
		);

		return $settings;
	}

	/**
	 * Shows field on the form builder.
	 *
	 * @param string $name Field name.
	 *
	 * @return void
	 */
	public function show_on_form_builder( $name = '' ) {
		$field = FrmFieldsHelper::setup_edit_vars( $this->field );

		$field['default_value'] = $this->get_default_value();
		$field['value']         = $field['default_value'];

		$field_name = $this->html_name( $name );

		$this->load_field_output( compact( 'field', 'field_name' ) );
	}

	/**
	 * Gets processed sub fields.
	 * This should return the list of sub fields after sorting or show/hide based of some options.
	 *
	 * @return array
	 */
	protected function get_processed_sub_fields() {
		return $this->sub_fields;
	}

	/**
	 * Shows field in the frontend.
	 *
	 * @param array $args           Arguments.
	 * @param array $shortcode_atts Shortcode attributes.
	 *
	 * @return string
	 */
	public function front_field_input( $args, $shortcode_atts ) {
		$field = (array) $this->field;

		$field['default_value'] = $this->get_default_value();

		if ( empty( $field['value'] ) ) {
			$field['value'] = $field['default_value'];
		}

		$args['field']          = $field;
		$args['shortcode_atts'] = $shortcode_atts;

		ob_start();
		$this->load_field_output( $args );
		$input_html = ob_get_clean();

		return $input_html;
	}

	/**
	 * Loads field output.
	 *
	 * @param array $args {
	 *     Arguments.
	 *
	 *     @type array  $field          Field array.
	 *     @type string $html_id        HTML ID.
	 *     @type string $field_name     Field name attribute.
	 *     @type array  $shortcode_atts Shortcode attributes.
	 *     @type array  $errors         Field errors.
	 *     @type bool   $remove_names   Remove name attribute or not.
	 * }
	 *
	 * @return void
	 */
	protected function load_field_output( $args ) {
		if ( empty( $args['field'] ) ) {
			return;
		}

		$this->process_args_for_field_output( $args );

		$include_paths = array(
			FrmAppHelper::plugin_path() . "/classes/views/frm-fields/front-end/{$args['field']['type']}-field/{$args['field']['type']}-field.php",
			FrmAppHelper::plugin_path() . '/classes/views/frm-fields/front-end/combo-field/combo-field.php',
		);

		foreach ( $include_paths as $include_path ) {
			if ( file_exists( $include_path ) ) {
				include $include_path;
				return;
			}
		}
	}

	/**
	 * Loads processed args for field output.
	 *
	 * @param array $args {
	 *     Arguments.
	 *
	 *     @type array  $field          Field array.
	 *     @type string $html_id        HTML ID.
	 *     @type string $field_name     Field name attribute.
	 *     @type array  $shortcode_atts Shortcode attributes.
	 *     @type array  $errors         Field errors.
	 *     @type bool   $remove_names   Remove name attribute or not.
	 * }
	 *
	 * @return void
	 */
	protected function process_args_for_field_output( &$args ) {
		$args['field'] = (array) $args['field'];

		if ( ! isset( $args['html_id'] ) ) {
			$args['html_id'] = $this->html_id();
		}

		if ( ! isset( $args['field_name'] ) ) {
			$args['field_name'] = $this->html_name( $args['field']['name'] );
		}

		$args['sub_fields'] = $this->get_processed_sub_fields();

		if ( ! isset( $args['shortcode_atts'] ) ) {
			$args['shortcode_atts'] = array();
		}

		if ( ! isset( $args['errors'] ) ) {
			$args['errors'] = array();
		}
	}

	/**
	 * Prints sub field input atts.
	 *
	 * @param array $args Arguments. Includes `field`, `sub_field`.
	 *
	 * @return void
	 */
	protected function print_input_atts( $args ) {
		$field     = $args['field'];
		$sub_field = $args['sub_field'];

		// Placeholder.
		if ( in_array( 'placeholder', $sub_field['options'], true ) ) {
			$placeholders = FrmField::get_option( $field, 'placeholder' );

			if ( ! empty( $placeholders[ $sub_field['name'] ] ) ) {
				$field['placeholder'] = $placeholders[ $sub_field['name'] ];
			}
		}

		// Add optional class.
		$classes = $sub_field['classes'] ?? '';

		if ( is_array( $classes ) ) {
			$classes = implode( ' ', $classes );
		}

		if ( ! empty( $sub_field['optional'] ) ) {
			$classes .= ' frm_optional';
		}

		if ( $classes ) {
			$field['input_class'] = esc_attr( $classes );
		}

		// Fake it to avoid printing frm-val attribute.
		$field['default_value'] = '';

		if ( ! empty( $sub_field['name'] ) ) {
			$field['subfield_name'] = $sub_field['name'];
		}
		do_action( 'frm_field_input_html', $field );

		// Print custom attributes.
		if ( ! empty( $sub_field['atts'] ) && is_array( $sub_field['atts'] ) ) {
			foreach ( $sub_field['atts'] as $att_name => $att_value ) {
				echo esc_attr( trim( $att_name ) ) . '="' . esc_attr( trim( $att_value ) ) . '" ';
			}
		}
	}

	/**
	 * Validate field.
	 *
	 * @param array $args Arguments. Includes `errors`, `value`.
	 *
	 * @return array Errors array.
	 */
	public function validate( $args ) {
		$errors = $args['errors'] ?? array();

		if ( ! $this->field->required ) {
			return $errors;
		}

		if ( class_exists( 'FrmProEntryMeta' ) && FrmProEntryMeta::skip_required_validation( $this->field ) ) {
			return $errors;
		}

		if ( class_exists( 'FrmProFieldsHelper' ) && ! FrmProFieldsHelper::is_field_visible_to_user( $this->field ) ) {
			return $errors;
		}

		$blank_msg = FrmFieldsHelper::get_error_msg( $this->field, 'blank' );

		$sub_fields = $this->get_processed_sub_fields();

		// Validate not empty.
		foreach ( $sub_fields as $name => $sub_field ) {
			if ( empty( $sub_field['optional'] ) && empty( $args['value'][ $name ] ) ) {
				$errors[ 'field' . $args['id'] . '-' . $name ] = '';
				$errors[ 'field' . $args['id'] ]               = $blank_msg;
			}
		}

		return $errors;
	}

	/**
	 * Gets export headings.
	 *
	 * @return array
	 */
	public function get_export_headings() {
		$headings   = array();
		$field_id   = $this->field->id ?? $this->field['id'];
		$field_name = $this->field->name ?? $this->field['name'];
		$field_key  = $this->field->field_key ?? $this->field['field_key'];
		$sub_fields = $this->get_processed_sub_fields();

		foreach ( $sub_fields as $name => $sub_field ) {
			$headings[ $field_id . '_' . $name ] = $field_name . ' (' . $field_key . ') - ' . $sub_field['label'];
		}

		return $headings;
	}

	/**
	 * Get a list of all field settings that should be translated
	 * on a multilingual site.
	 *
	 * @since 3.06.01
	 *
	 * @return array
	 */
	public function translatable_strings() {
		$strings = parent::translatable_strings();

		foreach ( $this->sub_fields as $name => $sub_field ) {
			if ( in_array( 'desc', $sub_field['options'], true ) ) {
				$strings[] = $name . '_desc';
			}
		}

		return $strings;
	}

	/**
	 * Checks if should print hidden subfields and hide them. This is useful to use js to show or hide sub fields.
	 *
	 * @return bool
	 */
	protected function should_print_hidden_sub_fields() {
		return false;
	}

	/**
	 * Gets inputs container attributes.
	 *
	 * @return array
	 */
	protected function get_inputs_container_attrs() {
		return array(
			'class' => 'frm_combo_inputs_container',
			'id'    => 'frm_combo_inputs_container_' . $this->field_id,
		);
	}

	/**
	 * Gets subfield input attributes.
	 *
	 * @since 6.26
	 *
	 * @param array $sub_field Subfield data.
	 * @param array $args      Field output args. See {@see FrmFieldCombo::load_field_output()}.
	 *
	 * @return array
	 */
	protected function get_sub_field_input_attrs( $sub_field, $args ) {
		$attrs = array(
			'type'  => $sub_field['type'],
			'id'    => $args['html_id'] . '_' . $sub_field['name'],
			'value' => '',
		);

		if ( ! empty( $args['field']['value'][ $sub_field['name'] ] ) ) {
			$attrs['value']       = $args['field']['value'][ $sub_field['name'] ];
			$attrs['data-frmval'] = $args['field']['value'][ $sub_field['name'] ];
		}

		if ( empty( $args['remove_names'] ) ) {
			$attrs['name'] = $args['field_name'] . '[' . $sub_field['name'] . ']';
		}

		return $attrs;
	}
}
