<?php
/**
 * Organizer Functions
 *
 * Display functions (template-tags) for use in WordPress templates.
 */

// Don't load directly
use Tribe\Events\Models\Post_Types\Organizer;

if ( ! defined( 'ABSPATH' ) ) {
	die( '-1' );
}

if ( class_exists( 'Tribe__Events__Main' ) ) {

	/**
	 * Organizer ID
	 *
	 * Returns the event Organizer ID.
	 *
	 * @param int $postId Can supply either event id or organizer id.
	 *                    If none specified, current post is used.
	 *                    If given an event with multiple organizers,
	 *                    the first organizer ID is returned.
	 *
	 * @return int Organizer
	 */
	function tribe_get_organizer_id( $postId = null ) {
		$postId       = Tribe__Events__Main::postIdHelper( $postId );
		$organizer_id = null;
		if ( is_numeric( $postId ) && $postId > 0 ) {
			$tribe_ecp = Tribe__Events__Main::instance();
			// check if $postId is an organizer id
			if ( $tribe_ecp->isOrganizer( $postId ) ) {
				$organizer_id = $postId;
			} else {
				$organizer_id = tribe_get_event_meta( $postId, '_EventOrganizerID', true );
			}
		}

		return apply_filters( 'tribe_get_organizer_id', $organizer_id, $postId );
	}

	/**
	 * Get the IDs of all organizers associated with an event.
	 *
	 * @param int $event_id The event post ID. Defaults to the current event.
	 *
	 * @return array
	 */
	function tribe_get_organizer_ids( $event_id = null ) {
		$event_id = Tribe__Events__Main::postIdHelper( $event_id );

		$organizer_ids = [];

		if ( Tribe__Events__Main::instance()->isEvent( $event_id ) ) {
			$organizer_ids = tribe_get_event_meta( $event_id, '_EventOrganizerID', false );

			// Protect against storing array items that render false, such as `0`.
			$organizer_ids = array_filter( (array) $organizer_ids );
		}

		return apply_filters( 'tribe_get_organizer_ids', $organizer_ids, $event_id );
	}

	/**
	 * An organizers can have two sources the list of ordered items and the meta field associated with organizers,
	 * where the meta field takes precedence we need to respect the order of the meta order only when the present items
	 * on the meta field.
	 *
	 * @deprecated 4.6.23
	 * @todo Remove on 4.7
	 *
	 * @since 4.6.15
	 *
	 * @param array $current
	 * @param array $ordered
	 *
	 * @return array
	 */
	function tribe_sanitize_organizers( $current = [], $ordered = [] ) {
		_deprecated_function( __METHOD__, '4.6.23', 'No longer needed after removing reliance on a separate postmeta field to store the ordering.' );

		if ( empty( $ordered ) ) {
			return $current;
		}

		$order    = [];
		$excluded = [];
		foreach ( (array) $current as $post_id ) {
			$key = array_search( $post_id, $ordered );
			if ( false === $key ) {
				$excluded[] = $post_id;
			} else {
				$order[ $key ] = $post_id;
			}
		}

		// Make sure before the merge the order is ordered by the keys.
		ksort( $order );

		return array_merge( $order, $excluded );
	}

	/**
	 * Get Organizer Label Singular.
	 * Returns the singular version of the Organizer Label.
	 *
	 * Note: the output of this function is not escaped.
	 * You should escape it wherever you use it!
	 *
	 * @since 3.7
	 * @since5.1.6 remove escaping.
	 *
	 * @return string The singular version of the Organizer Label.
	 */
	function tribe_get_organizer_label_singular() {
		/**
		 * Allows customization of the singular version of the Organizer Label.
		 * Note: the output of this filter is not escaped!
		 *
		 * @since 3.7
		 * @since5.1.6 Added docblock, remove escaping.
		 *
		 * @param string $label The singular version of the Organizer label, defaults to "Organizer" (uppercase)
		 */
		return apply_filters(
			'tribe_organizer_label_singular',
			__( 'Organizer', 'the-events-calendar' )
		);
	}

	/**
	 * Get Organizer Label Plural
	 * Returns the plural version of the Organizer Label.
	 *
	 * Note: the output of this function is not escaped.
	 * You should escape it wherever you use it!
	 *
	 * @since 3.7
	 * @since5.1.6 remove escaping.
	 *
	 * @return string The plural version of the Organizer Label.
	 */
	function tribe_get_organizer_label_plural() {
		/**
		 * Allows customization of the plural version of the Organizer Label.
		 * Note: the output of this filter is not escaped!
		 *
		 * @since 3.7
		 * @since5.1.6 Added docblock, remove escaping.
		 *
		 * @param string $label The plural version of the Organizer label, defaults to "Organizers" (uppercase).
		 */
		return apply_filters(
			'tribe_organizer_label_plural',
			__( 'Organizers', 'the-events-calendar' )
		);
	}

	/**
	 * Get the organizer label.
	 *
	 * Note: the output of this function is not escaped.
	 * You should escape it wherever you use it!
	 *
	 * @param bool $singular TRUE to return the singular label, FALSE to return plural.
	 *
	 * @return string
	 */
	function tribe_get_organizer_label( $singular = true ) {
		if ( $singular ) {
			return tribe_get_organizer_label_singular();
		} else {
			return tribe_get_organizer_label_plural();
		}
	}

	/**
	 * Returns a summary of key information for the specified organizer.
	 *
	 * Typically this is a pipe separated format containing the organizer's telephone
	 * number, email address and website where available.
	 *
	 * @param int $post_id Either the organizer or event ID, if none specified, current post is used.
	 *
	 * @return string
	 */
	function tribe_get_organizer_details( $post_id = null ) {
		$post_id      = Tribe__Events__Main::postIdHelper( $post_id );
		$organizer_id = (int) tribe_get_organizer_id( $post_id );
		$details      = [];

		if ( $organizer_id && $tel = tribe_get_organizer_phone() ) {
			$details[] = '<span class="tel">' . $tel . '</span>';
		}

		if ( $organizer_id && $email = tribe_get_organizer_email() ) {
			$details[] = '<span class="email"> <a href="mailto:' . esc_attr( $email ) . '">' . $email . '</a> </span>';
		}

		if ( $organizer_id && $link = tribe_get_organizer_website_link() ) {
			// $link is a full HTML string (<a>) whose components are already escaped, so we don't need create an anchor tag or escape again here
			$details[] = '<span class="link">' . $link . '</span>';
		}

		$html = join( '<span class="tribe-events-divider">|</span>', $details );

		if ( ! empty( $html ) ) {
			$html = '<address class="organizer-address">' . $html . '</address>';
		}

		/**
		 * Provides an opportunity to modify the organizer details HTML.
		 *
		 * @param string $html         Organizer details HTML.
		 * @param int    $post_id      Either the organizer or event ID.
		 * @param int    $organizer_id The organizer ID.
		 */
		return apply_filters( 'tribe_get_organizer_details', $html, $post_id, $organizer_id );
	}

	/**
	 * Get Organizer
	 *
	 * Returns the name of the Organizer
	 *
	 * @param int $postId Either event id or organizer id, if none specified, current post is used.
	 *
	 * @return string Organizer's Name
	 */
	function tribe_get_organizer( $postId = null ) {
		$postId       = Tribe__Events__Main::postIdHelper( $postId );
		$organizer_id = (int) tribe_get_organizer_id( $postId );
		$output       = '';
		if ( $organizer_id > 0 ) {
			$output = esc_html( get_the_title( $organizer_id ) );
		}

		return apply_filters( 'tribe_get_organizer', $output, $organizer_id );
	}

	/**
	 * Organizer Test
	 *
	 * Returns true or false depending on if the post id has/is an organizer
	 *
	 * @param int $postId Either event id or organizer id, if none specified, current post is used.
	 *
	 * @return bool
	 */
	function tribe_has_organizer( $postId = null ) {
		$postId        = Tribe__Events__Main::postIdHelper( $postId );
		$has_organizer = ( tribe_get_organizer_id( $postId ) > 0 ) ? true : false;

		return apply_filters( 'tribe_has_organizer', $has_organizer );
	}

	/**
	 * Organizer Email
	 *
	 * Returns the Organizer's Email.
	 *
	 * @param int  $postId      Either event id or organizer id, if none specified, current post is used.
	 * @param bool $antispambot Whether the email should pass through the `antispambot` function or not.
	 *
	 * @return string Organizer's Email
	 */
	function tribe_get_organizer_email( $postId = null, $antispambot = true ) {
		$postId = Tribe__Events__Main::postIdHelper( $postId );
		$unfiltered_email  = esc_html( tribe_get_event_meta( tribe_get_organizer_id( $postId ), '_OrganizerEmail', true ) );
		$filtered_email = $antispambot ? antispambot( $unfiltered_email ) : $unfiltered_email;

		/**
		 * Allows for the organizer email to be filtered.
		 *
		 * Please note that obfuscation of email is done in subsequent line using the `antispambot` function.
		 *
		 * @param string $filtered_email   The organizer email obfuscated using the `antispambot` function.
		 * @param string $unfiltered_email The organizer email as stored in the database before any filtering or obfuscation is applied.
		 */
		$filtered_email = apply_filters( 'tribe_get_organizer_email', $filtered_email, $unfiltered_email );

		return $filtered_email;
	}

	/**
	 * Organizer Page Link
	 *
	 * Returns the event Organizer Name with a link to their single organizer page.
	 *
	 * @param int  $post_id   Either event id or organizer id, if none specified, current post is used.
	 * @param bool $full_link If true outputs a complete HTML <a> link, otherwise only the URL is output.
	 * @param bool $echo      Deprecated. If true, echo the link, otherwise return.
	 *
	 * @return string Organizer Name and Url
	 */
	function tribe_get_organizer_link( $post_id = null, $full_link = true, $echo = false ) {

		// As of TEC 4.0 this argument is deprecated.
		// If needed precede the call to this function with echo.
		if ( false != $echo ) {
			_deprecated_argument( __FUNCTION__, '4.0', 'As of TEC 4.0 this argument is deprecated. If needed, precede the call to this function with echo' );
		}

		$org_id = tribe_get_organizer_id( $post_id );
		if ( class_exists( 'Tribe__Events__Pro__Main' ) && get_post_status( $org_id ) == 'publish' ) {

			$url = esc_url_raw( get_permalink( $org_id ) );
			/**
			 * Filter the organizer link target attribute.
			 *
			 * @since 5.1.0
			 *
			 * @param string   $target  The target attribute string. Defaults to "_self".
			 * @param string   $url     The link URL.
			 * @param int      $post_id Either event id or organizer id, if none specified, current post is used.
			 * @param int      $org_id  The organizer id.
			 */
			$target = apply_filters( 'tribe_get_event_organizer_link_target', '_self', $url, $post_id, $org_id );
			$rel    = ( '_blank' === $target ) ? 'noopener noreferrer' : '';

			if ( $full_link ) {
				$name = tribe_get_organizer( $org_id );

				if ( empty( $url ) || empty( $name ) ) {
					$link = false;
				} else {
					$link = sprintf(
						'<a href="%s" title="%s" target="%s" rel="%s">%s</a>',
						esc_url( $url ),
						the_title_attribute(
							[
								'post' => $org_id,
								'echo' => false,
							]
						),
						esc_attr( $target ),
						esc_attr( $rel ),
						esc_html( $name )
					);
				}
			} else {
				$link = $url;
			}

			/**
			 * Filter the organizer link HTML
			 *
			 * @since 4.0
			 *
			 * @param string the link HTML.
			 */
			return apply_filters( 'tribe_get_organizer_link', $link, $post_id, $full_link, $url );
		}

		//Return Organizer Name if Pro is not Active
		return tribe_get_organizer( $org_id );
	}

	/**
	 * Organizer Phone
	 *
	 * Returns the event Organizer's phone number
	 *
	 * @param int $postId Can supply either event id or organizer id, if none specified, current post is used
	 *
	 * @return string Organizer's Phone Number
	 */
	function tribe_get_organizer_phone( $postId = null ) {
		$postId = Tribe__Events__Main::postIdHelper( $postId );
		$output = esc_html( tribe_get_event_meta( tribe_get_organizer_id( $postId ), '_OrganizerPhone', true ) );

		return apply_filters( 'tribe_get_organizer_phone', $output );
	}

	/**
	 * Organizer website url
	 *
	 * Returns the url to the event Organizer's supplied website.
	 *
	 * @param $postId post ID for an event
	 *
	 * @return string
	 **/
	if ( ! function_exists( 'tribe_get_organizer_website_url' ) ) { // wrapped in if function exists to maintain compatibility with community events 3.0.x. wrapper not needed after 3.1.x.
		function tribe_get_organizer_website_url( $postId = null ) {
			$postId = Tribe__Events__Main::postIdHelper( $postId );
			$output = esc_url( esc_html( tribe_get_event_meta( tribe_get_organizer_id( $postId ), '_OrganizerWebsite', true ) ) );

			return apply_filters( 'tribe_get_organizer_website_url', $output );
		}
	}

	/**
	 * Organizer website link
	 *
	 * Returns the event Organizer Name with a link to their supplied website
	 *
	 * @param null|int    $post_id The post ID for an event.
	 * @param null|string $label   The text for the link.
	 *
	 * @return string
	 **/
	function tribe_get_organizer_website_link( $post_id = null, $label = null ) {
		$post_id = tribe_get_organizer_id( $post_id );
		$url     = tribe_get_event_meta( $post_id, '_OrganizerWebsite', true );

		/**
		 * Filter the organizer link target attribute.
		 *
		 * @since 5.1.0
		 *
		 * @param string   $target  The target attribute string. Defaults to "_self".
		 * @param string   $url     The link URL.
		 * @param null|int $post_id post ID for the organizer.
		 */
		$target = apply_filters( 'tribe_get_event_organizer_link_target', '_self', $url, $post_id );
		$rel    = ( '_blank' === $target ) ? 'noopener noreferrer' : 'external';

		/**
		 * Filter the organizer link label
		 *
		 * @since 5.1.0
		 *
		 * @param string the link label/text.
		 */
		$label = apply_filters( 'tribe_get_organizer_website_link_label', $label, $post_id );

		if ( ! empty( $url ) ) {
			$label = is_null( $label ) ? $url : $label;

			if ( ! empty( $url ) ) {
				$parseUrl = parse_url( $url );
				if ( empty( $parseUrl['scheme'] ) ) {
					$url = "http://$url";
				}
			}

			$html = sprintf(
				'<a href="%1$s" target="%2$s" rel="%3$s">%4$s</a>',
				esc_attr( esc_url( $url ) ),
				esc_attr( $target ),
				esc_attr( $rel ),
				esc_html( $label )
			);

		} else {
			$html = '';
		}

		/**
		 * Filter the organizer link HTML
		 *
		 * @since 3.0
		 *
		 * @param string the link HTML.
		 */
		return apply_filters( 'tribe_get_organizer_website_link', $html );
	}

	/**
	 * Get the link for the organizer website.
	 *
	 * @since 5.5.0
	 *
	 * @param null|int $post_id The event or organizer ID.
	 * @return string  Formatted title for the organizer website link
	 */
	function tribe_events_get_organizer_website_title( $post_id = null ) {
		$post_id = tribe_get_organizer_id( $post_id );

		/**
		 * Allows customization of a organizer's website title link.
		 *
		 * @since 5.5.0
		 *
		 * @param string $title The title of the organizer's website link.
		 * @param int 	 $post_id The organizer ID.
		 */
		return apply_filters( 'tribe_events_get_organizer_website_title', __( 'Website:', 'the-events-calendar' ), $post_id );
	}

	/**
	 * Get all the organizers
	 *
	 * @param bool  $only_with_upcoming Only return organizers with upcoming events attached to them.
	 * @param int   $posts_per_page
	 * @param bool  $suppress_filters
	 * @param array $args {
	 *		Optional. Array of Query parameters.
	 *
	 *		@type int  $event       Only organizers linked to this event post ID.
	 *		@type bool $has_events  Only organizers that have events.
	 *		@type bool $found_posts Return the number of found organizers.
	 * }
	 *
	 * @return array|int An array of organizer post objects or an integer value if `found_posts` is set to a truthy value.
	 */
	function tribe_get_organizers( $only_with_upcoming = false, $posts_per_page = -1, $suppress_filters = true, array $args = [] ) {
		// filter out the `null` values
		$args = array_diff_key( $args, array_filter( $args, 'is_null' ) );

		if ( tribe_is_truthy( $only_with_upcoming ) ) {
			$args['only_with_upcoming'] = true;
		}

		$filter_args = [
			'event'              => 'find_for_event',
			'has_events'         => 'find_with_events',
			'only_with_upcoming' => 'find_with_upcoming_events',
		];

		foreach ( $filter_args as $filter_arg => $method ) {
			if ( ! isset( $args[ $filter_arg ] ) ) {
				continue;
			}

			if ('only_with_upcoming' !== $filter_arg) {
				$found = tribe( 'tec.linked-posts.organizer' )->$method( $args[ $filter_arg ] );
			} else {
				$found = tribe( 'tec.linked-posts.organizer' )->find_with_upcoming_events(
					$args[ $filter_arg ],
					isset( $args['post_status'] ) ? $args['post_status'] : null
				);
			}

			if ( empty( $found ) ) {
				return [];
			}

			$args['post__in'] = ! empty( $args['post__in'] )
				? array_intersect( (array) $args['post__in'], $found )
				: $found;

			if ( empty( $args['post__in'] ) ) {
				return [];
			}
		}

		$parsed_args = wp_parse_args(
			$args,
			[
				'post_type'        => Tribe__Events__Main::ORGANIZER_POST_TYPE,
				'posts_per_page'   => $posts_per_page,
				'suppress_filters' => $suppress_filters,
			]
		);

		$return_found_posts = ! empty( $args['found_posts'] );

		if ( $return_found_posts ) {
			$parsed_args['posts_per_page'] = 1;
			$parsed_args['paged']          = 1;
		}

		$query = new WP_Query( $parsed_args );

		if ( $return_found_posts ) {
			if ( $query->have_posts() ) {

				return $query->found_posts;
			}

			return 0;
		}

		return $query->have_posts() ? $query->posts : [];
	}

	/**
	 * Fetches and returns a decorated post object representing a Organizer.
	 *
	 * @since 5.3.0
	 *
	 * @param null|int|WP_Post $organizer  The organizer ID or post object or `null` to use the global one.
	 * @param string|null      $output The required return type. One of `OBJECT`, `ARRAY_A`, or `ARRAY_N`, which
	 *                                 correspond to a WP_Post object, an associative array, or a numeric array,
	 *                                 respectively. Defaults to `OBJECT`.
	 * @param string           $filter Type of filter to apply. Accepts 'raw'.
	 * @param bool             $force  Whether to force a re-fetch ignoring cached results or not.
	 *
	 * @return array|mixed|void|WP_Post|null {
	 *                              The Organizer post object or array, `null` if not found.
	 *
	 *                              @type string $phone The organizer phone number NOT filtered, apply anti-spambot filters if required.
	 *                              @type string $website The organizer full website URL.
	 *                              @type string $email The organizer email address NOT filtered, apply anti-spambot filters if required.
	 *                          }
	 */
	function tribe_get_organizer_object( $organizer = null, $output = OBJECT, $filter = 'raw', $force = false ) {
		/**
		 * Filters the organizer result before any logic applies.
		 *
		 * Returning a non `null` value here will short-circuit the function and return the value.
		 * Note: this value will not be cached and the caching of this value is a duty left to the filtering function.
		 *
		 * @since 5.3.0
		 *
		 * @param mixed       $return      The organizer object to return.
		 * @param mixed       $organizer       The organizer object to fetch.
		 * @param string|null $output      The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which
		 *                                 correspond to a `WP_Post` object, an associative array, or a numeric array,
		 *                                 respectively. Defaults to `OBJECT`.
		 * @param string      $filter      Type of filter to apply. Accepts 'raw'.
		 */
		$return = apply_filters( 'tribe_get_organizer_object_before', null, $organizer, $output, $filter );

		if ( null !== $return ) {
			return $return;
		}

		$post = false;
		if ( ! $force ) {
			$cache_key = 'tribe_get_organizer_object_' . md5( json_encode( [ $organizer, $output, $filter ] ) );
			/** @var Tribe__Cache $cache */
			$cache = tribe( 'cache' );
			$post  = $cache->get( $cache_key, Tribe__Cache_Listener::TRIGGER_SAVE_POST );
		}

		if ( false === $post ) {
			$post = Organizer::from_post( $organizer )->to_post( $output, $filter );

			if ( empty( $post ) ) {
				return null;
			}

			/**
			 * Filters the organizer post object before caching it and returning it.
			 *
			 * Note: this value will be cached; as such this filter might not run on each request.
			 * If you need to filter the output value on each call of this function then use the `tribe_get_organizer_object_before`
			 * filter.
			 *
			 * @since 5.3.0
			 *
			 * @param WP_Post $post   The organizer post object, decorated with a set of custom properties.
			 * @param string  $output The output format to use.
			 * @param string  $filter The filter, or context of the fetch.
			 */
			$post = apply_filters( 'tribe_get_organizer_object', $post, $output, $filter );

			$cache->set( $cache_key, $post, WEEK_IN_SECONDS, Tribe__Cache_Listener::TRIGGER_SAVE_POST );
		}

		if ( OBJECT !== $output ) {
			$post = ARRAY_A === $output ? (array) $post : array_values( (array) $post );
		}

		return $post;
	}
}
