<?php

namespace SeriouslySimplePodcasting\Controllers;

use SeriouslySimplePodcasting\Handlers\CPT_Podcast_Handler;
use SeriouslySimplePodcasting\Repositories\Episode_Repository;
use SeriouslySimplePodcasting\Traits\Useful_Variables;
use stdClass;
use WP_Query;

use SeriouslySimplePodcasting\Handlers\Options_Handler;
use WP_Term;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Frontend Controller
 *
 * @author      Hugh Lashbrooke
 * @category    Class
 * @package     SeriouslySimplePodcasting/Controllers
 * @since       1.0
 */
class Frontend_Controller {

	use Useful_Variables;

	/**
	 * @var Episode_Controller
	 */
	public $episode_controller;


	/**
	 * @var Players_Controller
	 * */
	public $players_controller;

	/**
	 * @var Episode_Repository
	 * */
	public $episode_repository;


	/**
	 * @var array
	 * */
	protected $removed_filters;


	/**
	 * Frontend_Controller constructor.
	 *
	 * @param Episode_Controller $episode_controller
	 * @param Players_Controller $players_controller
	 * @param Episode_Repository $episode_repository
	 */
	public function __construct( $episode_controller, $players_controller, $episode_repository ) {
		$this->init_useful_variables();

		$this->episode_controller = $episode_controller;
		$this->players_controller = $players_controller;
		$this->episode_repository = $episode_repository;

		$this->register_hooks_and_filters();
		$this->register_ajax_actions();
		$this->protect_private_podcast_episodes();
	}

	/**
	 * Register all relevant front end hooks and filters
	 */
	public function register_hooks_and_filters() {

		// Register HTML5 player scripts and styles
		add_action( 'wp_enqueue_scripts', array( $this, 'register_html5_player_assets' ) );

		// Add meta data to start of podcast content
		$locations = get_option( 'ss_podcasting_player_locations', array( 'content' ) );

		if ( in_array( 'content', (array) $locations, true ) ) {
			add_filter( 'the_content', array( $this, 'content_meta_data' ), 10, 1 );
		}

		if ( in_array( 'excerpt', (array) $locations, true ) ) {
			add_filter( 'the_excerpt', array( $this, 'get_excerpt_meta_data' ), 10, 1 );
		}

		if ( in_array( 'excerpt_embed', (array) $locations, true ) ) {
			add_filter( 'the_excerpt_embed', array( $this, 'get_embed_meta_data' ), 10, 1 );
		}

		// Add SSP label and version to generator tags
		add_action( 'get_the_generator_html', array( $this, 'generator_tag' ), 10, 2 );
		add_action( 'get_the_generator_xhtml', array( $this, 'generator_tag' ), 10, 2 );

		// Add RSS meta tag to site header
		add_action( 'wp_head', array( $this, 'rss_meta_tag' ) );

		// Disable default RSS feed link for podcast archive page (/podcast)
		add_filter( 'post_type_archive_feed_link', array( $this, 'disable_default_podcast_rss_feed_link' ) );

		// Add podcast episode to main query loop if setting is activated
		add_action( 'pre_get_posts', array( $this, 'add_to_home_query' ) );

		// Make sure to fetch all relevant post types when viewing series archive
		add_action( 'pre_get_posts', array( $this, 'add_all_post_types' ) );

		// Make sure to fetch all relevant post types when viewing a tag archive
		add_action( 'pre_get_posts', array( $this, 'add_all_post_types_for_tag_archive' ) );

		// Download podcast episode
		add_action( 'wp', array( $this, 'download_file' ), 1 );

		// Trigger import podcast process (if active)
		add_action( 'wp_loaded', array( $this, 'import_existing_podcast_to_podmotor' ) );

		add_filter( 'feed_content_type', array( $this, 'feed_content_type' ), 10, 2 );

		// Handle localisation
		add_action( 'plugins_loaded', array( $this, 'load_localisation' ) );

		add_filter( "archive_template_hierarchy", array( $this, 'fix_template_hierarchy' ) );
	}

	/**
	 * Remove class filters.
	 *
	 * @since 2.11.0
	 * @param $filter_name
	 * @param $function_name
	 * @param int $priority
	 *
	 * @return bool
	 */
	public function remove_filter( $filter_name, $function_name, $priority = 10 ) {
		$removed = remove_filter( $filter_name, array( $this, $function_name ), $priority );

		if ( $removed ) {
			$this->removed_filters[] = array(
				'filter_name'   => $filter_name,
				'function_name' => $function_name,
				'priority'      => $priority,
			);
		}

		return true;
	}

	/**
	 * Restore filters removed previously with $this->remove_filter().
	 * @since 2.11.0
	 * */
	public function restore_filters() {
		foreach ( $this->removed_filters as $filter ) {
			add_filter( $filter['filter_name'], array( $this, $filter['function_name'] ), $filter['priority'] );
		}
	}

	/**
	 * Disable default podcast RSS feed link. We add the correct feed link manually @see rss_meta_tag()
	 *
	 * @param string $link
	 *
	 * @return string|null
	 */
	public function disable_default_podcast_rss_feed_link( $link ) {
		if ( is_post_type_archive( SSP_CPT_PODCAST ) && strpos( $link, SSP_CPT_PODCAST . '/feed' ) ) {
			return null;
		}

		return $link;
	}

	/**
	 * Adds filter for private podcast episodes content.
	 */
	protected function protect_private_podcast_episodes() {
		$filter = array( $this, 'show_private_content_message' );
		add_filter( 'the_content', $filter, 20 );
		add_filter( 'the_content_rss', $filter, 20 );
		add_filter( 'comment_text_rss', $filter, 20 );
	}

	/**
	 * Show a message that episode belongs to private podcast.
	 *
	 * @param $content
	 *
	 * @return mixed|string|void
	 */
	public function show_private_content_message( $content ) {

		$post = get_post();

		$ssp_post_types = ssp_post_types();

		if ( empty( $post->post_type ) || ! in_array( $post->post_type, $ssp_post_types ) ) {
			return $content;
		}

		$terms = wp_get_post_terms( $post->ID, CPT_Podcast_Handler::TAXONOMY_SERIES );

		if ( ! is_array( $terms ) ) {
			return $content;
		}

		$message =  __( 'This content is Private. To access this podcast, contact the site owner.', 'seriously-simple-podcasting' );

		// Protect default feed episodes.
		if ( empty( $terms ) && 'yes' === ssp_get_option( 'is_podcast_private' ) ) {
			return $message;
		}

		/**
		 * Protect episodes that belong to series.
		 *
		 * @var WP_Term[] $terms
		 * */
		foreach ( $terms as $term ) {
			if ( 'yes' === ssp_get_option( 'is_podcast_private', '', $term->term_id ) ) {
				return $message;
			}
		}

		return $content;
	}

	/**
	 * Unfortunately, WP core doesn't search for archive-podcast.php automatically (though it should).
	 * So add the template to search list manually.
	 *
	 * @param array $templates
	 *
	 * @return array
	 */
	public function fix_template_hierarchy( $templates ) {
		$use_post_tag = apply_filters( 'ssp_use_post_tags', true );

		// Use queried object because is_tax('post_tag') doesn't work ( is_tax is false ).
		$queried = get_queried_object();

		if ( is_tax( 'series' ) || ( $use_post_tag && isset( $queried->taxonomy ) && 'post_tag' === $queried->taxonomy ) ) {
			$templates = array_merge( array( 'archive-' . SSP_CPT_PODCAST . '.php' ), $templates );
		}

		return $templates;
	}

	public function register_ajax_actions() {
		add_action( 'wp_ajax_get_playlist_items', array( $this, 'get_ajax_playlist_items' ) );
		add_action( 'wp_ajax_nopriv_get_playlist_items', array( $this, 'get_ajax_playlist_items' ) );
	}

	public function get_ajax_playlist_items() {
		$items = $this->players_controller->get_ajax_playlist_items();
		wp_send_json_success( $items );
	}

	/**
	 * Used to load the HTML5 player scripts and styles
	 * Only load this if the HTML5 player is enabled in the plugin
	 * Additionally, if we're rendering a post or page which includes a player block, enqueue the player assets
	 */
	public function register_html5_player_assets() {
		/**
		 * If we're rendering a SSP Block, which includes the HTML5 player, also enqueue the player scripts
		 */
		if ( has_block( 'seriously-simple-podcasting/castos-player' ) || has_block( 'seriously-simple-podcasting/podcast-list' ) ) {
			wp_enqueue_script( 'ssp-castos-player' );
			wp_enqueue_style( 'ssp-castos-player' );
		}
	}

	/**
	 * Add episode meta data to the full content
	 * @param  string $content Existing content
	 * @return string          Modified content
	 * @deprecated Use embed_player_in_content() instead
	 */
	public function content_meta_data( $content = '' ) {
		return $this->embed_player_in_content( $content );
	}

	/**
	 * Add episode meta data to the full content
	 * @param  string $content Existing content
	 * @return string          Modified content
	 */
	public function embed_player_in_content( $content = '' ) {

		global $post, $wp_current_filter, $episode_context;

		// Don't do anything if we don't have a valid post object
		if ( ! is_a( $post, 'WP_Post' ) ) {
			return $content;
		}

		// Don't output unformatted data on excerpts
		if ( in_array( 'get_the_excerpt', (array) $wp_current_filter, true ) ) {
			return $content;
		}

		// Don't output player if Elementor Player widget was embedded manually
		if ( false !== strpos( $content, 'data-widget_type="Castos Player.default"' ) ) {
			return $content;
		}

		// Don't output episode meta in shortcode or widget
		if ( isset( $episode_context ) && in_array( $episode_context, array( 'shortcode', 'widget' ), true ) ) {
			return $content;
		}

		if ( post_password_required( $post->ID ) ) {
			return $content;
		}

		// Don't output episode meta in a REST Request
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return $content;
		}

		if ( ! apply_filters( 'ssp_show_media_player_in_content', true, $post, $content ) ) {
			return $content;
		}

		$podcast_post_types = ssp_post_types( true );

		$player_visibility = get_option( 'ss_podcasting_player_content_visibility', 'all' );

		switch ( $player_visibility ) {
			case 'membersonly':
				$show_player = is_user_logged_in();
				break;
			default:
				$show_player = true;
				break;
		}

		if ( $show_player && in_array( $post->post_type, $podcast_post_types ) && ! is_feed() && ! isset( $_GET['feed'] ) ) {

			// Get episode player
			$player = $this->get_episode_player( $post->ID, 'content' );

			// Get specified player position
			$player_position = get_option( 'ss_podcasting_player_content_location', 'above' );

			switch ( $player_position ) {
				case 'above':
					$content = $player . $content;
					break;
				case 'below':
					$content = $content . $player;
					break;
			}
		}

		return $content;
	}

	/**
	 * Runs checks to see if the player should be rendered or not
	 *
	 * @param $episode_id
	 *
	 * @param bool $show_player default true
	 *
	 * @return boolean
	 */
	public function validate_media_player( $episode_id, $show_player = true ) {
		/**
		 * If the show_player variable was already set false (specifically for the ss_podcast shortcode)
		 */
		if ( false === $show_player ) {
			return false;
		}
		/**
		 * Check if the user is using the ss_player shortcode anywhere in this post
		 */
		if ( ssp_check_if_podcast_has_shortcode( $episode_id, 'ss_player' ) ) {
			return false;
		}

		/**
		 * Check if this post is using the HTML5 player block
		 */
		if ( has_block( 'seriously-simple-podcasting/castos-player' ) || has_block( 'seriously-simple-podcasting/castos-html-player' ) ) {
			return false;
		}

		/**
		 * Check if the user is using an elementor widget version of the player.
		 */
		if ( ssp_is_elementor_ok() && ssp_check_if_podcast_has_elementor_player( $episode_id ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Get episode meta data
	 *
	 * @param  integer $episode_id ID of episode post
	 * @param  string $context Context for display
	 *
	 * @return string               Episode meta
	 */
	public function get_episode_player( $episode_id = 0, $context = 'content' ) {

		$player = '';

		if ( ! $episode_id ) {
			return $player;
		}

		$file = $this->get_enclosure( $episode_id );

		if ( $file ) {

			if ( get_option( 'permalink_structure' ) ) {
				$file = $this->get_episode_download_link( $episode_id );
			}

			// Hide audio player in `ss_podcast` shortcode by default
			$show_player = true;
			if ( 'shortcode' === $context ) {
				$show_player = false;
			}

			// Show audio player if requested
			$player_style = get_option( 'ss_podcasting_player_style' );

			$show_player = $this->validate_media_player( $episode_id, $show_player );

			// Allow media player to be dynamically hidden/displayed
			$show_player = apply_filters( 'ssp_show_media_player', $show_player, $context );

			if ( $show_player ) {
				$player .= '<div class="podcast_player">' . $this->load_media_player( $file, $episode_id, $player_style, $context ) . '</div>';
			}
		}

		return apply_filters( 'ssp_episode_meta', $player, $episode_id, $context );
	}

	/**
	 * Get episode enclosure
	 * Wrapper for Episode_Controller get_enclosure method
	 *
	 * @param  integer $episode_id ID of episode
	 * @return string              URL of enclosure
	 */
	public function get_enclosure( $episode_id = 0 ) {
		return $this->episode_controller->get_enclosure( $episode_id );
	}

	/**
	 * Get download link for episode
	 * Wrapper for Episode_Controller get_episode_download_link method
	 *
	 * @param  integer $episode_id ID of episode
	 * @param  string $referrer Referrer
	 * @return string              Episode download link
	 */
	public function get_episode_download_link( $episode_id, $referrer = '' ) {
		return $this->episode_controller->get_episode_download_link( $episode_id, $referrer );
	}

	/**
	 * Return media player for a given file. Used to enable other checks or to prevent the player from loading
	 * @param string $src_file
	 * @param int $episode_id
	 * @param string $player_size
	 * @param string $context
	 *
	 * @return string
	 */
	public function media_player( $src_file = '', $episode_id = 0, $player_size = 'large', $context = 'block' ) {
		$media_player = '';
		$show_player  = $this->validate_media_player( $episode_id );
		if ( $show_player ) {
			$media_player = $this->load_media_player( $src_file, $episode_id, $player_size, $context );
		}
		return $media_player;
	}

	/**
	 * @param string $src_file
	 * @param int $episode_id
	 * @param string $player_size
	 * @param string $context
	 *
	 * @return mixed|void
	 */
	public function load_media_player( $src_file, $episode_id, $player_size, $context = 'block' ) {
		// Get episode type and default to audio
		$type = $this->episode_repository->get_episode_type( $episode_id );
		if ( ! $type ) {
			$type = 'audio';
		}

		// Switch to podcast player URL
		$src_file = str_replace( 'podcast-download', 'podcast-player', $src_file );

		// Set up parameters for media player
		$params = array( 'src' => $src_file, 'preload' => 'none' );


		/**
		 * If the media file is of type video
		 * @todo is this necessary in the case of the HTML5 player?
		 */
		if ( 'video' === $type ) {
			// Use featured image as video poster
			if ( $episode_id && has_post_thumbnail( $episode_id ) ) {
				$poster = wp_get_attachment_url( get_post_thumbnail_id( $episode_id ) );
				if ( $poster ) {
					$params['poster'] = $poster;
				}
			}
			$player = wp_video_shortcode( $params );
			// Allow filtering so that alternative players can be used
			return apply_filters( 'ssp_media_player', $player, $src_file, $episode_id );
		}

		/**
		 * Check if this player is being loaded via the AMP for WordPress plugin and if so, force the standard player
		 * https://wordpress.org/plugins/amp/
		 */
		if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
			$player_size = 'standard';
		}


		if ( 'standard' === $player_size ) {
			$player = $this->players_controller->render_media_player( $episode_id, $context );
		} else {
			$player = $this->players_controller->render_html_player( $episode_id, true, $context );
		}

		// Allow filtering so that alternative players can be used
		return apply_filters( 'ssp_media_player', $player, $src_file, $episode_id );
	}

	/**
	 * Get the type of podcast episode (audio or video)
	 * @param  integer $episode_id ID of episode
	 * @return mixed              [description]
	 * @deprecated Use Episode_Repository::get_episode_type() instead
	 */
	public function get_episode_type( $episode_id = 0 ) {
		return $this->episode_repository->get_episode_type( $episode_id );
	}

	/**
	 * Fetch episode meta details
	 * @param  integer $episode_id ID of episode post
	 * @param  string  $context    Context for display
	 * @return string              Episode meta details
	 */
	public function episode_meta_details( $episode_id = 0, $context = 'content', $return = false ) {
		return $this->players_controller->episode_meta_details( $episode_id, $context, $return );
	}


	/**
	 * Get size of media file
	 * @param  string  $file File name & path
	 * @return boolean       File size on success, boolean false on failure
	 * @deprecated
	 */
	public function get_file_size( $file = '' ) {
		return $this->episode_repository->get_file_size( $file );
	}

	/**
	 * Returns a local file path for the given file URL if it's local. Otherwise
	 * returns the original URL
	 *
	 * @param    string    file
	 * @return   string    file or local file path
	 *
	 * @deprecated
	 */
	function get_local_file_path( $file ) {
		return $this->episode_repository->get_local_file_path( $file );
	}

	/**
	 * Add the meta data to the episode excerpt
	 * @param  string $excerpt Existing excerpt
	 * @return string          Modified excerpt
	 */
	public function get_excerpt_meta_data( $excerpt = '' ) {
		return $this->excerpt_meta_data( $excerpt, 'excerpt' );
	}

	/**
	 * Add episode meta data to the excerpt
	 * @param  string $excerpt Existing excerpt
	 * @return string          Modified excerpt
	 */
	public function excerpt_meta_data( $excerpt = '', $content = 'excerpt' ) {

		global $post;

		if( ! apply_filters( 'ssp_show_excerpt_player', true, $post, $excerpt, $content ) ){
			return $excerpt;
		}

		if ( ! is_a( $post, 'WP_Post' ) ) {
			return $excerpt;
		}

		if( post_password_required( $post->ID ) ) {
			return $excerpt;
		}

		// Don't output episode meta in a REST Request
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return $content;
		}

		$podcast_post_types = ssp_post_types( true );

		$player_visibility = get_option( 'ss_podcasting_player_content_visibility', 'all' );

		switch( $player_visibility ) {
			case 'all': $show_player = true; break;
			case 'membersonly': $show_player = is_user_logged_in(); break;
			default: $show_player = true; break;
		}

		if ( $show_player && in_array( $post->post_type, $podcast_post_types ) && ! is_feed() && ! isset( $_GET['feed'] ) ) {

			$meta = $this->get_episode_player( $post->ID, $content );

			$excerpt = $meta . $excerpt;

		}

		return $excerpt;
	}

	/**
	 * Add the meta data to the embedded episode excerpt
	 * @param  string $excerpt Existing excerpt
	 * @return string          Modified excerpt
	 */
	public function get_embed_meta_data( $excerpt = '' ) {
		return $this->excerpt_meta_data( $excerpt, 'embed' );
	}

	/**
	 * Add podcast to home page query
	 * @param object $query The query object
	 */
	public function add_to_home_query( $query ) {

		if ( is_admin() ) {
			return;
		}

		$include_in_main_query = get_option('ss_podcasting_include_in_main_query');
		if ( $include_in_main_query && $include_in_main_query == 'on' ) {
			if ( $query->is_home() && $query->is_main_query() ) {
				$query->set( 'post_type', array( 'post', SSP_CPT_PODCAST ) );
			}
		}
	}

	public function add_all_post_types ( $query ) {

		if ( is_admin() ) {
			return;
		}

		if ( ! $query->is_main_query() ) {
			return;
		}

		if ( is_post_type_archive( SSP_CPT_PODCAST ) || is_tax( 'series' ) ) {

			$podcast_post_types = ssp_post_types( false );

			if ( empty( $podcast_post_types ) ) {
				$query->set( 'post_type', SSP_CPT_PODCAST );
				return;
			}

			$episode_ids = ssp_episode_ids();
			if ( ! empty( $episode_ids ) ) {

				$query->set( 'post__in', $episode_ids );

				$podcast_post_types = array_merge( array( SSP_CPT_PODCAST ), $podcast_post_types );
				$query->set( 'post_type', $podcast_post_types );
			}
		}

	}

	public function add_all_post_types_for_tag_archive( $query ) {

		if ( is_admin() ) {
			return;
		}

		if ( ! $query->is_main_query() ) {
			return;
		}

		if ( ! is_tag() ) {
			return;
		}

		if ( ! apply_filters( 'ssp_use_post_tags', true ) ) {
			return;
		}

		$post_types             = $query->get( 'post_type' ) ?: array();
		$tag_archive_post_types = apply_filters( 'ssp_tag_archive_post_types', array( 'post', SSP_CPT_PODCAST ) );

		$query->set( 'post_type', array_merge( $post_types, (array) $tag_archive_post_types ) );
	}

	/**
	 * Get duration of audio file
	 * @param  string $file File name & path
	 * @return mixed        File duration on success, boolean false on failure
	 */
	public function get_file_duration( $file ) {
		return $this->episode_repository->get_file_duration( $file );
	}

	/**
	 * Load audio player for given file - wrapper for `media_player` method to maintain backwards compatibility
	 * @param  string  $src 	   Source of audio file
	 * @param  integer $episode_id Episode ID for audio empty string
	 * @return string        	   Audio player HTML on success, false on failure
	 */
	public function audio_player( $src = '', $episode_id = 0 ) {
		$player = $this->media_player( $src, $episode_id );
		return apply_filters( 'ssp_audio_player', $player, $src, $episode_id );
	}

	/**
	 * Get episode image
	 * @param  integer $post_id   ID of episode
	 * @param  string  $size Image size
	 * @return string        Image HTML markup
	 */
	public function get_image( $post_id = 0, $size = 'full' ) {

		$image = '';

		if ( is_int( $size ) || ( 0 < intval( $size ) ) ) {
			$size = array( intval( $size ), intval( $size ) );
		} elseif ( ! is_string( $size ) && ! is_array( $size ) ) {
			$size = array( 200, 9999 );
		}

		$image_id = get_post_meta( $post_id, 'cover_image_id', true );

		if ( $image_id ) {
			$image = wp_get_attachment_image( $image_id, $size );
		}

		if ( empty( $image ) && has_post_thumbnail( $post_id ) ) {
			$image = get_the_post_thumbnail( intval( $post_id ), $size );
		}

		return apply_filters( 'ssp_episode_image', $image, $post_id );
	}

	/**
	 * Get episode image url
	 *
	 * @param integer $post_id ID of episode
	 * @param string $size Image size
	 *
	 * @return string        Image url
	 */
	public function get_episode_image_url( $post_id = 0, $size = 'full' ) {
		$image_url = '';
		$image_id  = get_post_meta( $post_id, 'cover_image_id', true );

		if ( ! $image_id ) {
			$image_id = get_post_thumbnail_id( $post_id );
		}

		if ( $image_id ) {
			$image_att = wp_get_attachment_image_src( $image_id, $size );
			$image_url = isset( $image_att[0] ) ? $image_att[0] : '';
		}

		return apply_filters( 'ssp_episode_image_url', $image_url, $post_id );
	}


	/**
	 * Gets podcast query
	 * @param  mixed $args Arguments to be passed to the query.
	 * @return mixed       Array if true, boolean if false.
	 * Todo: refactoring
	 * @see ss_get_podcast()
	 */
	public function get_podcast( $args = '' ) {
		$defaults = array(
			'title' => '',
			'content' => 'series',
			'series' => ''
		);

		$args = apply_filters( 'ssp_get_podcast_args', wp_parse_args( $args, $defaults ) );

		$query = array();

		if ( 'episodes' == $args['content'] ) {

			// Get selected series
			$podcast_series = '';
			if ( isset( $args['series'] ) && $args['series'] ) {
				$podcast_series = $args['series'];
			}

			// Get query args
			$query_args = apply_filters( 'ssp_get_podcast_query_args', ssp_episodes( -1, $podcast_series, true, '' ) );

			// The Query
			$query = get_posts( $query_args );

			// The Display
			if ( ! is_wp_error( $query ) && is_array( $query ) && count( $query ) > 0 ) {
				foreach ( $query as $k => $v ) {
					// Get the URL
					$query[$k]->url = get_permalink( $v->ID );
				}
			} else {
				$query = false;
			}

		} else {

			$terms = get_terms( 'series' );

			if ( count( $terms ) > 0) {

				foreach ( $terms as $term ) {
					$query[ $term->term_id ] = new stdClass();
					$query[ $term->term_id ]->title = $term->name;
					$query[ $term->term_id ]->url = get_term_link( $term );

					$query_args = apply_filters( 'ssp_get_podcast_series_query_args', ssp_episodes( -1, $term->slug, true, '' ) );

					$posts = get_posts( $query_args );

					$count = count( $posts );
					$query[ $term->term_id ]->count = $count;
				}
			}

		}

		$query['content'] = $args['content'];

		return $query;
	}

	/**
	 * Get episode from audio file
	 * @param  string $file File name & path
	 * @return object       Episode post object
	 */
	public function get_episode_from_file( $file = '' ) {
		global $post;

		$episode = false;

		if ( $file != '' ) {

			$post_types = ssp_post_types( true );

			$args = array(
				'post_type' => $post_types,
				'post_status' => 'publish',
				'posts_per_page' => 1,
				'meta_key' => 'audio_file',
				'meta_value' => $file
			);

			$qry = new WP_Query( $args );

			if ( $qry->have_posts() ) {
				while ( $qry->have_posts() ) { $qry->the_post();
					$episode = $post;
					break;
				}
			}
		}

		return apply_filters( 'ssp_episode_from_file', $episode, $file );

	}

	/**
	 * Public action which is triggered from the Seriously Simple Hosting queue
	 * Imports episodes to Serioulsy Simple Hosting
	 * @deprecated
	 * @todo: investigate and remove. Looks like an obsolete function.
	 */
	public function import_existing_podcast_to_podmotor(){
		// this will soon be deprecated
		$podcast_importer = ( isset( $_GET['podcast_importer'] ) ? filter_var( $_GET['podcast_importer'], FILTER_DEFAULT ) : '' );
		if (empty($podcast_importer)){
			$podcast_importer = ( isset( $_GET['ssp_podcast_importer'] ) ? filter_var( $_GET['ssp_podcast_importer'], FILTER_DEFAULT ) : '' );
		}
		if ( ! empty( $podcast_importer ) && 'true' == $podcast_importer ) {
			$continue = import_existing_podcast();
			if ( $continue ) {
				$reponse = array( 'continue' => 'false', 'response' => 'Podcast data imported' );
			} else {
				$reponse = array( 'continue' => 'true', 'response' => 'An error occurred importing the podcast data' );
			}
			wp_send_json( $reponse );
		}
	}

	/**
	 * Download file from `podcast_episode` query variable
	 * @return void
	 */
	public function download_file() {

		if (  ! ssp_is_podcast_download() ) {
			return;
		}

		global $wp_query;

		// Get requested episode ID
		$episode_id = intval( $wp_query->query_vars['podcast_episode'] );

		if ( isset( $episode_id ) && $episode_id ) {

			// Get episode post object
			$episode = get_post( $episode_id );

			// Make sure we have a valid episode post object
			if ( ! $episode || ! is_object( $episode ) || is_wp_error( $episode ) || ! isset( $episode->ID ) ) {
				return;
			}

			// Do we have newlines?
			$parts = false;
			if( is_string( $episode ) ) {
				$parts = explode( "\n", $episode );
			}

			if ( $parts && is_array( $parts ) && count( $parts ) > 1 ) {
				$file = $parts[0];
			} else {
				// Get audio file for download
				$file = $this->get_enclosure( $episode_id );
			}

			// Ensure that $file is a valid URL
			$is_url = 0 === strpos( $file, 'http' );

			// Exit if no file is found
			if ( ! $is_url ) {
				$this->send_404();
			}

			// Get file referrer
			$referrer = '';
			if( isset( $wp_query->query_vars['podcast_ref'] ) && $wp_query->query_vars['podcast_ref'] ) {
				$referrer = $wp_query->query_vars['podcast_ref'];
			} else {
				if( isset( $_GET['ref'] ) ) {
					$referrer = esc_attr( $_GET['ref'] );
				}
			}

			// Allow other actions - functions hooked on here must not output any data
			do_action( 'ssp_file_download', $file, $episode, $referrer );

			// Set necessary headers
			header( "Pragma: no-cache" );
			header( "Expires: 0" );
			header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
			header( "Robots: none" );

			// Check file referrer
			if( 'download' == $referrer ) {

				// Set size of file
				// Do we have anything in Cache/DB?
				$size = wp_cache_get( $episode_id, 'filesize_raw' );

				// Nothing in the cache, let's see if we can figure it out.
				if ( false === $size ) {

					// Do we have anything in post_meta?
					$size = get_post_meta( $episode_id, 'filesize_raw', true );

					if ( empty( $size ) ) {

						// Let's see if we can figure out the path...
						$attachment_id = $this->get_attachment_id_from_url( $file );

						if ( ! empty( $attachment_id )  ) {
							$size = filesize( get_attached_file( $attachment_id ) );
							update_post_meta( $episode_id, 'filesize_raw', $size );
						}

					}

					// Update the cache
					wp_cache_set( $episode_id, $size, 'filesize_raw' );
				}

				// Send Content-Length header
				if ( ! empty( $size ) ) {
					header( "Content-Length: " . $size );
				}

				// Force file download
				header( "Content-Type: application/force-download" );

				// Set other relevant headers
				header( "Content-Description: File Transfer" );
				header( "Content-Disposition: attachment; filename=\"" . basename( $file ) . "\";" );
				header( "Content-Transfer-Encoding: binary" );

				// Encode spaces in file names until this is fixed in core (https://core.trac.wordpress.org/ticket/36998)
				$file = str_replace( ' ', '%20', $file );

				// Use ssp_readfile_chunked() if allowed on the server or simply access file directly
				@ssp_readfile_chunked( $file ) or header( 'Location: ' . $file );
			} else {

				// Encode spaces in file names until this is fixed in core (https://core.trac.wordpress.org/ticket/36998)
				$file = str_replace( ' ', '%20', $file );

				// For all other referrers redirect to the raw file
				wp_redirect( $file, 302 );
			}

			// Exit to prevent other processes running later on
			exit;

		}
	}

	/**
	 * Show the 404 not found page content.
	 */
	protected function send_404() {
		global $wp_query;
		$wp_query->set_404();
		status_header( 404 );
		get_template_part( 404 );
		exit();
	}

	/**
	 * Get the ID of an attachment from its image URL.
	 *
	 * @param   string      $url    The path to an image.
	 * @return  int|bool            ID of the attachment or 0 on failure.
	 */
	public function get_attachment_id_from_url( $url = '' ) {

		// Let's hash the URL to ensure that we don't get
		// any illegal chars that might break the cache.
		$key = md5( $url );

		// Do we have anything in the cache for this URL?
		$attachment_id = wp_cache_get( $key, 'attachment_id' );

		if ( $attachment_id === false ) {

			// Globalize
			global $wpdb;

			// If there is no url, return.
			if ( '' === $url ) {
				return false;
			}

			// Set the default
			$attachment_id = 0;


			// Function introduced in 4.0, let's try this first.
			if ( function_exists( 'attachment_url_to_postid' ) ) {
				$attachment_id = absint( attachment_url_to_postid( $url ) );
				if ( 0 !== $attachment_id ) {
					wp_cache_set( $key, $attachment_id, 'attachment_id', DAY_IN_SECONDS );
					return $attachment_id;
				}
			}

			// Then this.
			if ( preg_match( '#\.[a-zA-Z0-9]+$#', $url ) ) {
				$sql = $wpdb->prepare(
					"SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND guid = %s",
					esc_url_raw( $url )
				);
				$attachment_id = absint( $wpdb->get_var( $sql ) );
				if ( 0 !== $attachment_id ) {
					wp_cache_set( $key, $attachment_id, 'attachment_id', DAY_IN_SECONDS );
					return $attachment_id;
				}
			}

			// And then try this
			$upload_dir_paths = wp_upload_dir();
			if ( false !== strpos( $url, $upload_dir_paths['baseurl'] ) ) {
				// Ensure that we have file extension that matches iTunes.
				$url = preg_replace( '/(?=\.(m4a|mp3|mov|mp4)$)/i', '', $url );
				// Remove the upload path base directory from the attachment URL
				$url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $url );
				// Finally, run a custom database query to get the attachment ID from the modified attachment URL
				$sql = $wpdb->prepare( "SELECT wposts.ID FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = '_wp_attached_file' AND wpostmeta.meta_value = '%s' AND wposts.post_type = 'attachment'", $url );
				$attachment_id = absint( $wpdb->get_var( $sql ) );
				if ( 0 !== $attachment_id ) {
					wp_cache_set( $key, $attachment_id, 'attachment_id', DAY_IN_SECONDS );
					return $attachment_id;
				}
			}

		}

		return $attachment_id;
	}

	/**
	 * Get MIME type of attachment file
	 *
	 * @param  string $attachment URL of resource
	 *
	 * @return string|false MIME type on success, false on failure
	 */
	public function get_attachment_mimetype( $attachment = '' ) {
		$key = md5( $attachment );
		if ( ! $attachment ) {
			return false;
		}

		$mime = wp_cache_get( $key, 'mime-type' );
		if ( ! $mime ) {
			$filetype = wp_check_filetype( $attachment );
			$mime     = isset( $filetype['type'] ) ? $filetype['type'] : false;
			wp_cache_set( $key, $mime, 'mime-type', DAY_IN_SECONDS );
		}

		return $mime;
	}

	/**
	 * Display plugin name and version in generator meta tag
	 * @return void
	 */
	public function generator_tag( $gen, $type ) {

		// Allow generator tags to be hidden if necessary
		if ( apply_filters( 'ssp_show_generator_tag', true, $type ) ) {

			$generator = 'Seriously Simple Podcasting ' . esc_attr( $this->version );

			switch ( $type ) {
				case 'html':
					$gen .= "\n" . '<meta name="generator" content="' . $generator . '">';
					break;
				case 'xhtml':
					$gen .= "\n" . '<meta name="generator" content="' . $generator . '" />';
					break;
			}

		}

		return $gen;
	}

	/**
	 * Display feed meta tag in site HTML
	 * @return void
	 */
	public function rss_meta_tag() {

		// Get feed slug
		$feed_slug = apply_filters( 'ssp_feed_slug', $this->token );

		if ( get_option( 'permalink_structure' ) ) {
			$feed_url = $this->home_url . 'feed/' . $feed_slug;
		} else {
			$feed_url = $this->home_url . '?feed=' . $feed_slug;
		}

		$custom_feed_url = get_option( 'ss_podcasting_feed_url' );
		if ( $custom_feed_url ) {
			$feed_url = $custom_feed_url;
		}

		$feed_url = apply_filters( 'ssp_feed_url', $feed_url );

		$html = '';

		if( apply_filters( 'ssp_show_global_feed_tag', true ) ) {
			$html = '<link rel="alternate" type="application/rss+xml" title="' . __( 'Podcast RSS feed', 'seriously-simple-podcasting' ) . '" href="' . esc_url( $feed_url ) . '" />';
		}

		// Check if this is a series taxonomy archive and display series-specific RSS feed tag
		$current_obj = get_queried_object();
		if( isset( $current_obj->taxonomy ) && 'series' == $current_obj->taxonomy && isset( $current_obj->slug ) && $current_obj->slug ) {

			if( apply_filters( 'ssp_show_series_feed_tag', true, $current_obj->slug ) ) {

				if ( get_option( 'permalink_structure' ) ) {
					$series_feed_url = $feed_url . '/' . $current_obj->slug;
				} else {
					$series_feed_url = $feed_url . '&podcast_series=' . $current_obj->slug;
				}

				$html .= "\n" . '<link rel="alternate" type="application/rss+xml" title="' . sprintf( __( '%s RSS feed', 'seriously-simple-podcasting' ), $current_obj->name ) . '" href="' . esc_url( $series_feed_url ) . '" />';

			}

		}

		echo "\n" . apply_filters( 'ssp_rss_meta_tag', $html ) . "\n\n";
	}

	/**
	 * Set RSS content type for podcast feed
	 *
	 * @param  string $content_type Current content type
	 * @param  string $type         Type of feed
	 * @return string               Updated content type
	 */
	public function feed_content_type ( $content_type = '', $type = '' ) {

		if( SSP_CPT_PODCAST == $type ) {
			$content_type = 'text/xml';
		}

		return $content_type;
	}

	public function load_localisation () {
		load_plugin_textdomain( 'seriously-simple-podcasting', false, basename( $this->dir ) . '/languages/' );
	}

	/**
	 * Show single podcast episode with specified content items
	 * This is used in the SeriouslySimplePodcasting\Widgets\Single_Episode widget
	 * as well as the SeriouslySimplePodcasting\ShortCodes\Podcast_Episode shortcode
	 *
	 * @param  integer $episode_id    ID of episode post
	 * @param  array   $content_items Ordered array of content items to display
	 * @return string                 HTML of episode with specified content items
	 */
	public function podcast_episode ( $episode_id = 0, $content_items = array( 'title', 'player', 'details' ), $context = '', $style = 'standard' ) {
		global $post, $episode_context;

		if ( ! $episode_id || ! is_array( $content_items ) || empty( $content_items ) ) {
			return '';
		}

		// Get episode object
		$episode = get_post( $episode_id );

		if ( ! $episode || is_wp_error( $episode ) ) {
			return '';
		}

		$html = '<div class="podcast-episode episode-' . esc_attr( $episode_id ) . '">' . "\n";

		// Setup post data for episode post object
		$post = $episode;
		setup_postdata( $post );

		$episode_context = $context;

		foreach ( $content_items as $item ) {

			switch ( $item ) {

				case 'title':
					$html .= '<h3 class="episode-title">' . get_the_title() . '</h3>' . "\n";
					break;

				case 'excerpt':
					$html .= '<p class="episode-excerpt">' . get_the_excerpt() . '</p>' . "\n";
					break;

				case 'content':
					$html .= '<div class="episode-content">' . apply_filters( 'the_content', get_the_content() ) . '</div>' . "\n";
					break;

				case 'player':
					$file = $this->get_enclosure( $episode_id );
					if ( get_option( 'permalink_structure' ) ) {
						$file = $this->get_episode_download_link( $episode_id );
					}

					$html .= '<div id="podcast_player_' . $episode_id . '" class="podcast_player">' . $this->media_player( $file, $episode_id, $style, 'podcast_episode' ) . '</div>' . "\n";
					break;

				case 'details':
				case 'meta':
					$html .= $this->episode_meta_details( $episode_id, $episode_context );
					break;

				case 'image':
					$html .= get_the_post_thumbnail( $episode_id, apply_filters( 'ssp_frontend_context_thumbnail_size', 'thumbnail' ) );
					break;

			}
		}

		// Reset post data after fetching episode details
		wp_reset_postdata();

		$html .= '</div>' . "\n";

		return $html;
	}

	/**
	 * Render the HTML content for the podcast list dynamic block
	 *
	 * @param array $attributes Block attributes.
	 *
	 * @return string
	 */
	public function render_podcast_list_dynamic_block( $attributes ) {
		$player_style             = (string) get_option( 'ss_podcasting_player_style', '' );
		$paged                    = ( filter_input( INPUT_GET, 'podcast_page' ) ) ?: 1;
		$podcast_post_types       = ssp_post_types( true );
		$query_args               = array(
			'post_status'         => 'publish',
			'post_type'           => $podcast_post_types,
			'posts_per_page'      => get_option( 'posts_per_page', 10 ),
			'ignore_sticky_posts' => true,
			'paged'               => $paged,
		);
		$query_args['meta_query'] = array(
			array(
				'key'     => 'audio_file',
				'compare' => '!=',
				'value'   => '',
			),
		);
		$query_args               = apply_filters( 'podcast_list_dynamic_block_query_arguments', $query_args );
		$episodes_query           = new WP_Query( $query_args );

		ob_start();
		if ( $episodes_query->have_posts() ) {
			while ( $episodes_query->have_posts() ) {
				$episodes_query->the_post();
				$episode = get_post();

				$player = '';
				if ( !empty( $attributes['player'] ) ) {
					$file = $this->get_enclosure( $episode->ID );
					if ( get_option( 'permalink_structure' ) ) {
						$file = $this->get_episode_download_link( $episode->ID );
					}
					$player = $this->load_media_player( $file, $episode->ID, $player_style );
				}
				?>
				<article class="podcast-<?php echo $episode->ID ?> podcast type-podcast">
					<h2>
						<a class="entry-title-link" rel="bookmark" href="<?php echo esc_url( get_permalink() ); ?>">
							<?php echo the_title(); ?>
						</a>
					</h2>
					<div class="podcast-content">
						<?php if ( !empty( $attributes['featuredImage'] ) ) { ?>
							<a class="podcast-image-link" href="<?php echo esc_url( get_permalink() ) ?>"
							   aria-hidden="true" tabindex="-1">
								<?php echo ssp_episode_image( $episode->ID, $attributes['featuredImageSize']); ?>
							</a>
						<?php } ?>
						<?php if ( $player && empty( $attributes['playerBelowExcerpt'] ) ) { ?>
							<p><?php echo $player; ?></p>
						<?php } ?>
						<?php if ( !empty( $attributes['excerpt'] ) ) { ?>
							<p><?php echo get_the_excerpt(); ?></p>
						<?php } ?>
						<?php if ( $player && ! empty( $attributes['playerBelowExcerpt'] ) ) { ?>
							<p><?php echo $player; ?></p>
						<?php } ?>
					</div>
				</article>
				<?php
			}
		}
		$episode_items = ob_get_clean();

		// We can't use get_next_posts_link() because it doesn't work on single pages.
		$args = array(
			'format'    => '?podcast_page=%#%',
			'total'     => $episodes_query->max_num_pages,
			'current'   => max( 1, filter_input( INPUT_GET, 'podcast_page' ) ),
			'prev_text' => __( '&laquo; Newer Episodes' ),
			'next_text' => __( 'Older Episodes &raquo;' ),
			'type'      => 'array',
		);

		$args = apply_filters( 'ssp_podcast_list_paginate_args', $args, $episodes_query );

		$all_links = paginate_links( $args );

		$links = array();

		if ( is_array( $all_links ) ) {
			foreach ( $all_links as $item ) {
				if ( strpos( $item, 'class="next' ) || strpos( $item, 'class="prev' ) ) {
					$links[] = $item;
				}
			}
		}

		$links = apply_filters( 'ssp_podcast_list_paginate_links', $links, $all_links, $episodes_query );

		$episode_items .= implode( "\n", $links );

		wp_reset_postdata();

		return apply_filters( 'podcast_list_dynamic_block_html_content', '<div>' . $episode_items . '</div>' );
	}
}
