<?php
/**
 * ACFE Form Class.
 *
 * Handles compatibility with ACFE Forms.
 *
 * @package Conditional_Form_Actions_For_ACFE
 * @since 0.1
 */

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;



/**
 * CiviCRM Profile Sync ACFE Form Class.
 *
 * A class that handles compatibility with ACFE Forms.
 *
 * @since 0.1
 */
class CFAFA_Form {

	/**
	 * Plugin object.
	 *
	 * @since 0.1
	 * @access public
	 * @var object $plugin The plugin object.
	 */
	public $plugin;

	/**
	 * ACF Loader object.
	 *
	 * @since 0.1
	 * @access public
	 * @var object $acf_loader The ACF Loader object.
	 */
	public $acf_loader;

	/**
	 * CiviCRM object.
	 *
	 * @since 0.1
	 * @access public
	 * @var object $civicrm The CiviCRM object.
	 */
	public $civicrm;

	/**
	 * ACF object.
	 *
	 * @since 0.1
	 * @access public
	 * @var object $acf The ACF object.
	 */
	public $acf;

	/**
	 * Parent (calling) object.
	 *
	 * @since 0.1
	 * @access public
	 * @var object $acfe The parent object.
	 */
	public $acfe;

	/**
	 * Supported Location Rule name.
	 *
	 * @since 0.1
	 * @access public
	 * @var string $rule_name The supported Location Rule name.
	 */
	public $rule_name = 'form_civicrm';



	/**
	 * Constructor.
	 *
	 * @since 0.1
	 *
	 * @param object $parent The parent object reference.
	 */
	public function __construct( $parent ) {

		// Store references to objects.
		$this->plugin = $parent->acf_loader->plugin;
		$this->acf_loader = $parent->acf_loader;
		$this->civicrm = $this->acf_loader->civicrm;
		$this->acf = $this->acf_loader->acf;
		$this->acfe = $parent;

		// Init when this plugin is loaded.
		add_action( 'cfafa/acf/acfe/loaded', [ $this, 'initialise' ] );

	}



	/**
	 * Initialise this object.
	 *
	 * @since 0.1
	 */
	public function initialise() {

		// Include files.
		$this->include_files();

		// Register Location Types.
		$this->register_location_types();

		// Register hooks.
		$this->register_hooks();

	}



	/**
	 * Include files.
	 *
	 * @since 0.1
	 */
	public function include_files() {

	}



	/**
	 * Register WordPress hooks.
	 *
	 * @since 0.1
	 */
	public function register_hooks() {

		// Listen for queries from the ACF Field Group class.
		add_filter( 'cfafa/acf/query_field_group_mapped', [ $this, 'query_field_group_mapped' ], 10, 2 );
		add_filter( 'cfafa/acf/field_group/query_supported_rules', [ $this, 'query_supported_rules' ], 10, 4 );

		// Listen for queries from the ACF Field class.
		add_filter( 'cfafa/acf/query_settings_field', [ $this, 'query_settings_field' ], 200, 3 );

		// Register ACFE Form Actions.
		add_filter( 'acfe/include_form_actions', [ $this, 'register_form_actions' ] );

		// Add Form Actions Javascript.
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_form_action_scripts' ] );

		// Add Form Actions Javascript.
		add_action( 'acfe/form/submit', [ $this, 'form_action_query_vars_clear' ] );

		// Set a better Form Wrapper class.
		add_filter( 'acfe/form/load', [ $this, 'form_wrapper' ], 10, 2 );

	}



	/**
	 * Clear the Form Action Query Vars.
	 *
	 * This means we get a fresh set of Query Vars during the load process after
	 * a Form has been submitted.
	 *
	 * @since 0.1
	 */
	public function form_action_query_vars_clear() {

		// Clear the array of Action results.
		set_query_var( 'acfe_form_actions', [] );

	}



	/**
	 * Alters the default "Success Wrapper" class.
	 *
	 * @since 0.1
	 *
	 * @param array $form The ACF Form data array.
	 * @param integer $post_id The numeric ID of the WordPress Post.
	 * @return array $form The modified ACF Form data array.
	 */
	public function form_wrapper( $form, $post_id ) {

		// Alter the default "Success Wrapper".
		if ( $form['html_updated_message'] === '<div id="message" class="updated">%s</div>' ) {
			$form['html_updated_message'] = '<div id="message" class="acfe-success">%s</div>';
		}

		// --<
		return $form;

	}



	/**
	 * Register Location Types.
	 *
	 * @since 0.1
	 */
	public function register_location_types() {

		// Bail if less than ACF 5.9.0.
		if ( ! function_exists( 'acf_register_location_type' ) ) {
			return;
		}

		// Include Location Rule class files.
		include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/locations/cfafa-acf-acfe-location-bypass.php';

		// Register Location Types with ACF.
		acf_register_location_type( 'CiviCRM_Profile_Sync_ACF_Location_Type_Bypass' );

	}



	/**
	 * Register Form Actions.
	 *
	 * @since 0.1
	 */
	public function register_form_actions() {

		// Include Form Action base class.
		include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-base.php';

		// Include Form Action classes.
		include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-contact.php';
		include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-activity.php';
		include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-participant.php';
		include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-redirect.php';

		// Init Form Actions.
		new CFAFA_Form_Action_Contact( $this );
		new CFAFA_Form_Action_Activity( $this );
		new CFAFA_Form_Action_Participant( $this );
		new CFAFA_Form_Action_Redirect( $this );

		// Init Case Action if the CiviCase component is active.
		$case_active = $this->plugin->civicrm->is_component_enabled( 'CiviCase' );
		if ( $case_active ) {
			include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-case.php';
			new CFAFA_Form_Action_Case( $this );
		}

		// Init Email Action if the "Email API" Extension is active.
		$email_active = $this->plugin->civicrm->is_extension_enabled( 'org.civicoop.emailapi' );
		if ( $email_active ) {
			include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-email.php';
			new CFAFA_Form_Action_Email( $this );
		}

		if ( function_exists( 'WC' ) ) {
			include CIVICRM_WP_PROFILE_SYNC_PATH . 'includes/acf/acfe/form-actions/cfafa-acf-acfe-form-action-product.php';
			new CFAFA_Form_Action_Product( $this );
		}

	}



	/**
	 * Enqueue Form Action Javascript.
	 *
	 * @since 0.1
	 */
	public function enqueue_form_action_scripts() {

		// Bail if the current screen is not an Edit ACFE Form screen.
		$screen = get_current_screen();
		if ( ! ( $screen instanceof WP_Screen ) ) {
			return;
		}
		if ( $screen->base != 'post' || $screen->id != 'acfe-form' ) {
			return;
		}

		// Add JavaScript plus dependencies.
		wp_enqueue_script(
			'cfafa-acfe-form-actions',
			plugins_url( 'assets/js/acf/acfe/form-actions/cfafa-form-action-model.js', CIVICRM_WP_PROFILE_SYNC_FILE ),
			[ 'acf-extended' ],
			CIVICRM_WP_PROFILE_SYNC_VERSION // Version.
		);

		// Init the Contact Reference Field actions array.
		$contact_action_refs = [
			// Contact Actions must always be present.
			'new_field/key=field_cfafa_contact_action_custom_alias' => 'newContactActionAlias',
			'remove_field/key=field_cfafa_contact_action_custom_alias' => 'removeContactActionAlias',
		];

		/**
		 * Query Form Action classes to build the Contact Reference Fields ACF Model actions array.
		 *
		 * @since 0.1
		 *
		 * @param array $contact_action_refs The ACF Model actions array to be populated.
		 */
		$contact_actions = apply_filters( 'cfafa/acf/acfe/form_actions/reference_fields/contact', $contact_action_refs );

		// Init the Case Reference Field actions array.
		$case_action_refs = [
			// Case Actions must always be present.
			'new_field/key=field_cfafa_case_action_custom_alias' => 'newCaseActionAlias',
			'remove_field/key=field_cfafa_case_action_custom_alias' => 'removeCaseActionAlias',
		];

		/**
		 * Query Form Action classes to build the Case Reference Fields ACF Model actions array.
		 *
		 * @since 0.1
		 *
		 * @param array $case_action_refs The ACF Model actions array to be populated.
		 */
		$case_actions = apply_filters( 'cfafa/acf/acfe/form_actions/reference_fields/case', $case_action_refs );

		// Init the Participant Reference Field actions array.
		$participant_action_refs = [
			// Participant Actions must always be present.
			'new_field/key=field_cfafa_participant_action_custom_alias' => 'newParticipantActionAlias',
			'remove_field/key=field_cfafa_participant_action_custom_alias' => 'removeParticipantActionAlias',
		];

		/**
		 * Query Form Action classes to build the Participant Reference Fields ACF Model actions array.
		 *
		 * @since 0.1
		 *
		 * @param array $participant_action_refs The ACF Model actions array to be populated.
		 */
		$participant_actions = apply_filters( 'cfafa/acf/acfe/form_actions/reference_fields/participant', $participant_action_refs );

		// Build data array.
		$vars = [
			'localisation' => [],
			'settings' => [
				'contact_actions_reference' => $contact_actions,
				'case_actions_reference' => $case_actions,
				'participant_actions_reference' => $participant_actions,
			],
		];

		// Localize our script.
		wp_localize_script(
			'cfafa-acfe-form-actions',
			'CWPS_ACFE_Form_Action_Vars',
			$vars
		);

	}



	// -------------------------------------------------------------------------



	/**
	 * Listen for queries from the Field Group class.
	 *
	 * This method responds with a Boolean if it detects that this Field Group
	 * should bypass ACF.
	 *
	 * @since 0.1
	 *
	 * @param bool $mapped The existing mapping flag.
	 * @param array $field_group The array of ACF Field Group data.
	 * @param bool $mapped True if the Field Group should bypass ACF, or pass through if not.
	 */
	public function query_field_group_mapped( $mapped, $field_group ) {

		// Bail if a Mapping has already been found.
		if ( $mapped !== false ) {
			return $mapped;
		}

		// Bail if this is not a Bypass Field Group.
		$is_bypass_field_group = $this->is_bypass_field_group( $field_group );
		if ( $is_bypass_field_group === false ) {
			return $mapped;
		}

		// --<
		return true;

	}



	/**
	 * Check if this Field Group should bypass ACF.
	 *
	 * @since 0.1
	 *
	 * @param array $field_group The Field Group to check.
	 * @return array|bool The array of Entities if the Field Group should bypass ACF, or false otherwise.
	 */
	public function is_bypass_field_group( $field_group ) {

		// Bail if there's no Field Group ID.
		if ( empty( $field_group['ID'] ) ) {
			return false;
		}

		// Only do this once per Field Group.
		static $pseudocache;
		if ( isset( $pseudocache[ $field_group['ID'] ] ) ) {
			return $pseudocache[ $field_group['ID'] ];
		}

		// Assume not visible.
		$is_visible = false;

		// Bail if no Location Rules exist.
		if ( ! empty( $field_group['location'] ) ) {

			// We only need the key to test for an ACF Bypass location.
			$params = [
				$this->rule_name => 'foo',
			];

			// Do the check.
			$is_visible = $this->acf_loader->acf->field_group->is_visible( $field_group, $params );

		}

		// Maybe add to pseudo-cache.
		if ( ! isset( $pseudocache[ $field_group['ID'] ] ) ) {
			$pseudocache[ $field_group['ID'] ] = $is_visible;
		}

		// --<
		return $is_visible;

	}



	/**
	 * Listen for queries for supported Location Rules.
	 *
	 * @since 0.1
	 *
	 * @param bool $supported The existing supported Location Rules status.
	 * @param array $rule The Location Rule.
	 * @param array $params The query params array.
	 * @param array $field_group The ACF Field Group data array.
	 * @return bool $supported The modified supported Location Rules status.
	 */
	public function query_supported_rules( $supported, $rule, $params, $field_group ) {

		// Bail if already supported.
		if ( $supported === true ) {
			return $supported;
		}

		// Test for this Location Rule.
		if ( $rule['param'] == $this->rule_name && ! empty( $params[$this->rule_name] ) ) {
			$supported = true;
		}

		// --<
		return $supported;

	}



	// -------------------------------------------------------------------------



	/**
	 * Returns a Setting Field from this Entity when found.
	 *
	 * @since 0.1
	 *
	 * @param array $setting_field The existing Setting Field array.
	 * @param array $field The ACF Field data array.
	 * @param array $field_group The ACF Field Group data array.
	 * @return array|bool $setting_field The Setting Field array if populated, false if conflicting.
	 */
	public function query_settings_field( $setting_field, $field, $field_group ) {

		// Pass if conflicting Fields have been found.
		if ( $setting_field === false ) {
			return false;
		}

		// Pass if this is not a Bypass Field Group.
		$is_visible = $this->is_bypass_field_group( $field_group );
		if ( $is_visible === false ) {
			return $setting_field;
		}

		// If already populated, then this is a conflicting Field.
		if ( ! empty( $setting_field ) ) {
			return false;
		}

		// Get the array of Entities and IDs.
		$entity_array = $this->entity_mapping_extract( $field_group['location'] );

		/**
		 * Request an array of Setting Field choices from Entity classes.
		 *
		 * @since 0.1
		 *
		 * @param array The empty default Setting Field choices array.
		 * @param array $field The ACF Field data array.
		 * @param array $field_group The ACF Field Group data array.
		 * @param array $entity_array The array of Entities and IDs.
		 */
		$choices = apply_filters( 'cfafa/acf/bypass/query_settings_choices', [], $field, $field_group, $entity_array );

		// Bail if there aren't any.
		if ( empty( $choices ) ) {
			return false;
		}

		// Define Setting Field.
		$setting_field = [
			'key' => $this->civicrm->acf_field_key_get(),
			'label' => __( 'CiviCRM Field', 'conditional-form-actions-for-acfe' ),
			'name' => $this->civicrm->acf_field_key_get(),
			'type' => 'select',
			'instructions' => __( 'Choose the CiviCRM Field that this ACF Field should sync with. (Optional)', 'conditional-form-actions-for-acfe' ),
			'default_value' => '',
			'placeholder' => '',
			'allow_null' => 1,
			'multiple' => 0,
			'ui' => 0,
			'required' => 0,
			'return_format' => 'value',
			'parent' => $this->acf->field_group->placeholder_group_get(),
			'choices' => $choices,
		];

		// Return populated array.
		return $setting_field;

	}



	/**
	 * Returns an array containing the Entities and their IDs.
	 *
	 * @since 0.1
	 *
	 * @param array $location_rules The Location Rules for the Field.
	 * @return array $entity_mapping The array containing the Entities and IDs.
	 */
	public function entity_mapping_extract( $location_rules ) {

		// Init an empty Entity mapping array.
		$entity_mapping = [];

		// The Location Rules outer array is made of "grouos".
		foreach ( $location_rules as $group ) {

			// Skip group if it has no rules.
			if ( empty( $group ) ) {
				continue;
			}

			// The Location Rules inner array is made of "rules".
			foreach ( $group as $rule ) {

				// Is this a Bypass rule?
				if ( $rule['param'] === $this->rule_name ) {

					// Extract the Entity and ID.
					//$entity_map = [];
					$tmp = explode( '-', $rule['value'] );
					//$entity_map[] = [ 'entity' => $tmp[0], 'entity_id' => (int) $tmp[1] ];

					// Add to return.
					$entity_mapping[ $tmp[0] ][] = (int) $tmp[1];

				}

			}

		}

		// --<
		return $entity_mapping;

	}



} // Class ends.