<?php

/**
 * The file that defines the core plugin class.
 *
 * @link    https://aiopodcasting.com
 * @since   1.0.0
 *
 * @package AiO_Podcast_Studio
 */
 
// Exit if accessed directly
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * AIOPOD_Init - The main plugin class.
 *
 * @since 1.0.0
 */
class AIOPOD_Init {

	/**
	 * The loader that's responsible for maintaining and registering all hooks that power
	 * the plugin.
	 *
	 * @since  1.0.0
	 * @access protected
	 * @var    AIOPOD_Loader $loader Maintains and registers all hooks for the plugin.
	 */
	protected $loader;

	/**
	 * Get things started.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->load_dependencies();
		$this->define_admin_hooks();
		$this->define_public_hooks();
		$this->define_statistics_hooks();
		$this->blocks_init();
		$this->widgets_init();
		$this->set_meta_caps();
	}

	/**
	 * Load the required dependencies for this plugin.
	 *
	 * Create an instance of the loader which will be used to register the hooks
	 * with WordPress.
	 *
	 * @since  1.0.0
	 * @access private
	 */
	private function load_dependencies() {
		/**
		 * The class responsible for orchestrating the actions and filters of the
		 * core plugin.
		 */
		require_once AIOPOD_PLUGIN_DIR . 'includes/loader.php';

		/**
		 * The class responsibe for defining custom capabilities.
		 */
		 require_once AIOPOD_PLUGIN_DIR . 'includes/roles.php';
		
		/**
		 * The custom walker classes for the 'wp_dropdown_categories' function.
		 */
		require_once AIOPOD_PLUGIN_DIR . 'includes/walker-terms-dropdown.php';
		require_once AIOPOD_PLUGIN_DIR . 'includes/walker-terms-multiselect.php';
		
		/**
		 * The files with the helper functions.
		 */
		require_once AIOPOD_PLUGIN_DIR . 'includes/helpers/functions.php';
		require_once AIOPOD_PLUGIN_DIR . 'includes/helpers/render.php';
		require_once AIOPOD_PLUGIN_DIR . 'includes/helpers/urls.php';

		/**
		 * The class responsible for statistics tracking and the download tracking URL handler.
		 */
		require_once AIOPOD_PLUGIN_DIR . 'includes/statistics.php';

		/**
		 * The classes responsible for defining all actions that occur in the admin area.
		 */
		require_once AIOPOD_PLUGIN_DIR . 'admin/admin.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/podcasts.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/episodes.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/categories.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/tags.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/participants.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/settings.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/shortcode-builder.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/help-tutorials.php';
		require_once AIOPOD_PLUGIN_DIR . 'admin/import.php';

		/**
		 * The classes responsible for defining all actions that occur in the public-facing
		 * side of the site.
		 */
		require_once AIOPOD_PLUGIN_DIR . 'public/public.php';		
		require_once AIOPOD_PLUGIN_DIR . 'public/yoast.php';
		require_once AIOPOD_PLUGIN_DIR . 'public/rank-math.php';			
		require_once AIOPOD_PLUGIN_DIR . 'public/podcasts.php';		
		require_once AIOPOD_PLUGIN_DIR . 'public/podcast.php';
		require_once AIOPOD_PLUGIN_DIR . 'public/episodes.php';		
		require_once AIOPOD_PLUGIN_DIR . 'public/episode.php';
		require_once AIOPOD_PLUGIN_DIR . 'public/categories.php';
		require_once AIOPOD_PLUGIN_DIR . 'public/tags.php';
		require_once AIOPOD_PLUGIN_DIR . 'public/participants.php';
		require_once AIOPOD_PLUGIN_DIR . 'public/feed.php';
		require_once AIOPOD_PLUGIN_DIR . 'public/multilingual.php';
		
		/**
		 * The class responsible for defining actions that occur in the gutenberg blocks.
		 */
		require_once AIOPOD_PLUGIN_DIR. 'blocks/blocks.php';

		/**
		 * The classes responsible for defining all actions that occur in the widgets.
		 */
		require_once AIOPOD_PLUGIN_DIR . 'widgets/podcasts.php';
		require_once AIOPOD_PLUGIN_DIR . 'widgets/episodes.php';
		require_once AIOPOD_PLUGIN_DIR . 'widgets/categories.php';
		require_once AIOPOD_PLUGIN_DIR . 'widgets/tags.php';	
		require_once AIOPOD_PLUGIN_DIR . 'widgets/participants.php';			

		$this->loader = new AIOPOD_Loader();
	}

	/**
	 * Register all of the hooks related to the admin area functionality
	 * of the plugin.
	 *
	 * @since  1.0.0
	 * @access private
	 */
	private function define_admin_hooks() {
		// Hooks common to all admin pages
		$admin = new AIOPOD_Admin();
				
		$this->loader->add_action( 'admin_menu', $admin, 'admin_menu' );
		$this->loader->add_action( 'admin_enqueue_scripts', $admin, 'enqueue_styles' );
		$this->loader->add_action( 'admin_enqueue_scripts', $admin, 'enqueue_scripts' );
		$this->loader->add_action( 'elementor/editor/after_enqueue_styles', $admin, 'enqueue_styles' );
		$this->loader->add_action( 'elementor/editor/after_enqueue_scripts', $admin, 'enqueue_scripts' );
		$this->loader->add_action( 'updated_option', $admin, 'force_rewrite_rules_on_settings_update', 10, 3 );
		$this->loader->add_action( 'save_post_page', $admin, 'force_rewrite_rules_on_page_update', 10, 3 );		

		$this->loader->add_filter( 'display_post_states', $admin, 'add_display_post_states', 10, 2 );
		$this->loader->add_filter( 'plugin_action_links_' . AIOPOD_PLUGIN_FILE_NAME, $admin, 'plugin_action_links' );
		
		// Hooks specific to the podcasts page
		$podcasts = new AIOPOD_Admin_Podcasts();
		
		$this->loader->add_action( 'admin_menu', $podcasts, 'admin_menu' );
		$this->loader->add_action( 'init', $podcasts, 'register_post_type' );
		$this->loader->add_action( 'before_delete_post', $podcasts, 'before_delete_post' );
		
		if ( is_admin() ) {		
			$this->loader->add_action( 'post_submitbox_misc_actions', $podcasts, 'post_submitbox_misc_actions' );
			$this->loader->add_action( 'add_meta_boxes', $podcasts, 'add_meta_boxes' );
			$this->loader->add_action( 'save_post', $podcasts, 'save_meta_data', 10, 2 );
			$this->loader->add_action( 'restrict_manage_posts', $podcasts, 'restrict_manage_posts' );
			$this->loader->add_action( 'manage_aiopod_podcasts_posts_custom_column', $podcasts, 'custom_column_content', 10, 2 );
			
			$this->loader->add_filter( 'parent_file', $podcasts, 'parent_file' );
			$this->loader->add_filter( 'admin_url', $podcasts, 'filter_admin_url', 10, 2 );
			$this->loader->add_filter( 'enter_title_here', $podcasts, 'change_default_title' );
			$this->loader->add_filter( 'parse_query', $podcasts, 'parse_query' );
			$this->loader->add_filter( 'post_row_actions', $podcasts, 'row_actions', 10, 2 );
			$this->loader->add_filter( 'manage_edit-aiopod_podcasts_columns', $podcasts, 'get_columns' );
			$this->loader->add_filter( 'manage_edit-aiopod_podcasts_sortable_columns', $podcasts, 'sortable_columns' );
			$this->loader->add_filter( 'use_block_editor_for_post_type', $podcasts, 'disable_gutenberg', 10, 2 );
			$this->loader->add_filter( 'gutenberg_can_edit_post_type', $podcasts, 'disable_gutenberg', 10, 2 );			
		}

		// Hooks specific to the episodes page
		$episodes = new AIOPOD_Admin_Episodes();
		
		$this->loader->add_action( 'admin_menu', $episodes, 'admin_menu' );
		$this->loader->add_action( 'init', $episodes, 'register_post_type' );
		$this->loader->add_action( 'trashed_post', $episodes, 'trashed_post' );
		$this->loader->add_action( 'before_delete_post', $episodes, 'before_delete_post' );
		
		if ( is_admin() ) {		
			$this->loader->add_action( 'post_submitbox_misc_actions', $episodes, 'post_submitbox_misc_actions' );
			$this->loader->add_action( 'add_meta_boxes', $episodes, 'add_meta_boxes' );
			$this->loader->add_action( 'save_post', $episodes, 'save_meta_data', 10, 2 );
			$this->loader->add_action( 'restrict_manage_posts', $episodes, 'restrict_manage_posts' );
			$this->loader->add_action( 'manage_aiopod_episodes_posts_custom_column', $episodes, 'custom_column_content', 10, 2 );

			$this->loader->add_filter( 'parent_file', $episodes, 'parent_file' );
			$this->loader->add_filter( 'enter_title_here', $episodes, 'change_default_title' );
			$this->loader->add_filter( 'parse_query', $episodes, 'parse_query' );
			$this->loader->add_filter( 'post_row_actions', $episodes, 'row_actions', 10, 2 );
			$this->loader->add_filter( 'manage_edit-aiopod_episodes_columns', $episodes, 'get_columns' );
			$this->loader->add_filter( 'manage_edit-aiopod_episodes_sortable_columns', $episodes, 'sortable_columns' );
			$this->loader->add_filter( 'use_block_editor_for_post_type', $episodes, 'disable_gutenberg', 10, 2 );
			$this->loader->add_filter( 'gutenberg_can_edit_post_type', $episodes, 'disable_gutenberg', 10, 2 );			
		}
		
		// Hooks specific to the categories page
		$categories = new AIOPOD_Admin_Categories();
		
		$this->loader->add_action( 'admin_menu', $categories, 'admin_menu' );
		$this->loader->add_action( 'init', $categories, 'register_taxonomy' );
		$this->loader->add_action( 'admin_post_aiopod_import_categories', $categories, 'import_categories' );
		$this->loader->add_action( 'admin_notices', $categories, 'admin_notices' );
		$this->loader->add_action( 'aiopod_categories_add_form_fields', $categories, 'add_form_fields' );		
		$this->loader->add_action( 'aiopod_categories_edit_form_fields', $categories, 'edit_form_fields' );
		$this->loader->add_action( 'created_aiopod_categories', $categories, 'save_form_fields' );
		$this->loader->add_action( 'edited_aiopod_categories', $categories, 'save_form_fields' );		
		$this->loader->add_action( 'pre_delete_term', $categories, 'pre_delete_term', 10, 2 );
		
		$this->loader->add_filter( 'parent_file', $categories, 'parent_file' );
		$this->loader->add_filter( "manage_edit-aiopod_categories_columns", $categories, 'get_columns' );
		$this->loader->add_filter( "manage_edit-aiopod_categories_sortable_columns", $categories, 'get_columns' );
		$this->loader->add_filter( "manage_aiopod_categories_custom_column", $categories, 'custom_column_content', 10, 3 );		
		
		// Hooks specific to the tags page
		$tags = new AIOPOD_Admin_Tags();
		
		$this->loader->add_action( 'admin_menu', $tags, 'admin_menu' );
		$this->loader->add_action( 'init', $tags, 'register_taxonomy' );
		
		$this->loader->add_filter( 'parent_file', $tags, 'parent_file' );
		$this->loader->add_filter( "manage_edit-aiopod_tags_columns", $tags, 'get_columns' );
		$this->loader->add_filter( "manage_edit-aiopod_tags_sortable_columns", $tags, 'get_columns' );
		$this->loader->add_filter( "manage_aiopod_tags_custom_column", $tags, 'custom_column_content', 10, 3 );

		// Hooks specific to the participants page
		$participants = new AIOPOD_Admin_Participants();
		
		$this->loader->add_action( 'admin_init', $participants, 'admin_init' );
		$this->loader->add_action( 'admin_menu', $participants, 'admin_menu' );
		$this->loader->add_action( 'init', $participants, 'register_taxonomy' );
		$this->loader->add_action( 'aiopod_participants_add_form_fields', $participants, 'add_form_fields' );		
		$this->loader->add_action( 'aiopod_participants_edit_form_fields', $participants, 'edit_form_fields' );
		$this->loader->add_action( 'created_aiopod_participants', $participants, 'save_form_fields' );
		$this->loader->add_action( 'edited_aiopod_participants', $participants, 'save_form_fields' );		
		$this->loader->add_action( 'pre_delete_term', $participants, 'pre_delete_term', 10, 2 );	
		
		$this->loader->add_filter( 'parent_file', $participants, 'parent_file' );
		$this->loader->add_filter( "manage_edit-aiopod_participants_columns", $participants, 'get_columns' );
		$this->loader->add_filter( "manage_edit-aiopod_participants_sortable_columns", $participants, 'get_columns' );
		$this->loader->add_filter( "manage_aiopod_participants_custom_column", $participants, 'custom_column_content', 10, 3 );	

		// Hooks specific to the settings page
		$settings = new AIOPOD_Admin_Settings();
		
		$this->loader->add_action( 'admin_menu', $settings, 'admin_menu' );
		$this->loader->add_action( 'admin_init', $settings, 'admin_init' );

		// Hooks specific to the shortcode builder page
		$shortcode_builder = new AIOPOD_Admin_Shortcode_Builder();
				
		$this->loader->add_action( 'admin_menu', $shortcode_builder, 'admin_menu' );
		
		// Hooks specific to the help & tutorials page
		$help_tutorials = new AIOPOD_Admin_Help_Tutorials();
				
		$this->loader->add_action( 'admin_menu', $help_tutorials, 'admin_menu', 99 );

		// Hooks specific to the podcast import form
		$import = new AIOPOD_Admin_Import();

		$this->loader->add_action( 'init', $import, 'schedule_events' );
		$this->loader->add_action( 'aiopod_schedule_every_five_minutes', $import, 'cron_event' );
		$this->loader->add_action( 'wp_ajax_aiopod_import_podcast', $import, 'ajax_callback_import_podcast' );

		$this->loader->add_filter( 'cron_schedules', $import, 'cron_schedules' );
	}

	/**
	 * Register all of the hooks related to the public-facing functionality
	 * of the plugin.
	 *
	 * @since  1.0.0
	 * @access private
	 */
	private function define_public_hooks() {
		// Hooks common to all public pages
		$public = new AIOPOD_Public();

		$this->loader->add_action( 'template_redirect', $public, 'template_redirect', 0 );
		$this->loader->add_action( 'init', $public, 'init', 11 );
		$this->loader->add_action( 'wp_enqueue_scripts', $public, 'register_styles', 99 );
		$this->loader->add_action( 'wp_enqueue_scripts', $public, 'register_scripts', 99 );
		$this->loader->add_action( 'enqueue_block_assets', $public, 'enqueue_block_assets' );	
		$this->loader->add_action( 'elementor/editor/after_enqueue_scripts', $public, 'enqueue_editor_assets' );
		$this->loader->add_action( 'elementor/preview/enqueue_scripts', $public, 'enqueue_editor_assets' );	
		$this->loader->add_action( 'wp', $public, 'set_mysql_rand_seed_value' );	
		$this->loader->add_action( 'wp_loaded', $public, 'maybe_flush_rules', 11 );				
		$this->loader->add_action( 'wp_head', $public, 'wp_head' );
		
		if ( ! aiopod_is_yoast_or_rank_math_active() ) {
			$this->loader->add_filter( 'wp_title', $public, 'wp_title', 99, 3 );
			$this->loader->add_filter( 'document_title_parts', $public, 'document_title_parts' );
		}

		$this->loader->add_filter( 'query_vars', $public, 'query_vars' );
		$this->loader->add_filter( 'the_title', $public, 'the_title', 99, 2 );
		$this->loader->add_filter( 'single_post_title', $public, 'the_title', 99 );
		$this->loader->add_filter( 'has_post_thumbnail', $public, 'has_post_thumbnail', 10, 3 );
		$this->loader->add_filter( 'post_thumbnail_html', $public, 'post_thumbnail_html', 10, 5 );
		$this->loader->add_filter( 'term_link', $public, 'term_link', 10, 3 );
		
		// Hooks specific to the Yoast SEO plugin
		$yoast = new AIOPOD_Public_Yoast();

		$this->loader->add_filter( 'wpseo_title', $yoast, 'wpseo_title' );
		$this->loader->add_filter( 'wpseo_opengraph_title', $yoast, 'wpseo_title' );
		$this->loader->add_filter( 'wpseo_metadesc', $yoast, 'wpseo_metadesc' );
		$this->loader->add_filter( 'wpseo_opengraph_desc', $yoast, 'wpseo_metadesc' );
		$this->loader->add_filter( 'wpseo_canonical', $yoast, 'wpseo_canonical' );
		$this->loader->add_filter( 'wpseo_opengraph_url', $yoast, 'wpseo_canonical' );
		$this->loader->add_filter( 'wpseo_opengraph_image', $yoast, 'wpseo_opengraph_image' );
		$this->loader->add_filter( 'wpseo_twitter_image', $yoast, 'wpseo_twitter_image' );
		$this->loader->add_filter( 'wpseo_breadcrumb_links', $yoast, 'wpseo_breadcrumb_links' );

		// Hooks specific to the Rank Math SEO plugin
		$rank_math = new AIOPOD_Public_Rank_Math();

		$this->loader->add_filter( 'rank_math/frontend/title', $rank_math, 'meta_title' );
		$this->loader->add_filter( 'rank_math/frontend/description', $rank_math, 'meta_description' );
		$this->loader->add_filter( 'rank_math/frontend/canonical', $rank_math, 'canonical_url' );
		$this->loader->add_filter( 'rank_math/opengraph/facebook/image', $rank_math, 'opengraph_image_url' );
		$this->loader->add_filter( 'rank_math/opengraph/twitter/image', $rank_math, 'opengraph_image_url' );
		$this->loader->add_filter( 'rank_math/frontend/breadcrumb/items', $rank_math, 'breadcrumb_items' );

		// Hooks specific to the podcasts page
		$podcasts = AIOPOD_Public_Podcasts::get_instance();

		$this->loader->add_action( 'wp_ajax_aiopod_load_podcasts', $podcasts, 'ajax_callback_load_podcasts' );
		$this->loader->add_action( 'wp_ajax_nopriv_aiopod_load_podcasts', $podcasts, 'ajax_callback_load_podcasts' );
		
		// Hooks specific to the single podcast page
		$podcast = new AIOPOD_Public_Podcast();
		
		$this->loader->add_filter( 'the_content', $podcast, 'the_content', 11 );
		
		// Hooks specific to the episodes page
		$episodes = AIOPOD_Public_Episodes::get_instance();

		$this->loader->add_action( 'wp_ajax_aiopod_load_episodes', $episodes, 'ajax_callback_load_episodes' );
		$this->loader->add_action( 'wp_ajax_nopriv_aiopod_load_episodes', $episodes, 'ajax_callback_load_episodes' );
		
		// Hooks specific to the single episode page
		$episode = new AIOPOD_Public_Episode();
		
		$this->loader->add_filter( 'the_content', $episode, 'the_content', 11 );
		$this->loader->add_filter( 'comments_open', $episode, 'comments_open', 10, 2 );	

		// Hooks specific to the categories page
		$categories = AIOPOD_Public_Categories::get_instance();

		$this->loader->add_action( 'wp_ajax_aiopod_load_categories', $categories, 'ajax_callback_load_categories' );
		$this->loader->add_action( 'wp_ajax_nopriv_aiopod_load_categories', $categories, 'ajax_callback_load_categories' );

		// Hooks specific to the tags page
		$tags = AIOPOD_Public_Tags::get_instance();
		
		// Hooks specific to the participants page
		$participants = AIOPOD_Public_Participants::get_instance();

		$this->loader->add_action( 'wp_ajax_aiopod_load_participants', $participants, 'ajax_callback_load_participants' );
		$this->loader->add_action( 'wp_ajax_nopriv_aiopod_load_participants', $participants, 'ajax_callback_load_participants' );

		// Hooks that make the plugin multilingual compatible
		if ( ! is_admin() ) {
			$public_multilingual = new AIOPOD_Public_Multilingual();

			$this->loader->add_action( 'init', $public, 'init', 11 );
			$this->loader->add_filter( 'option_aiopod_pages_settings', $public_multilingual, 'filter_pages_settings_for_polylang' );
		}

		// Hooks specific to the RSS feed output
		$feed = new AIOPOD_Public_Feed();

		$this->loader->add_action( 'template_redirect', $feed, 'template_redirect', 0 );
		$this->loader->add_action( 'save_post', $feed, 'bust_feed_cache', 10, 2 );
		$this->loader->add_action( 'before_delete_post', $feed, 'bust_feed_cache', 10, 2 );
	}

	/**
	 * Register all hooks for the statistics tracking layer.
	 *
	 * AIOPOD_Statistics_Helper lives in includes/ and handles concerns that span both
	 * the public (download interception, play tracking) and server (cron,
	 * post-deletion cleanup) layers, so its hooks are registered separately
	 * from define_admin_hooks() and define_public_hooks().
	 *
	 * @since  1.0.0
	 * @access private
	 */
	private function define_statistics_hooks() {
		$statistics = AIOPOD_Statistics_Helper::get_instance();

		$this->loader->add_action( 'template_redirect', $statistics, 'handle_download_request', 0 );
		$this->loader->add_action( 'wp_ajax_aiopod_track_play', $statistics, 'ajax_callback_track_play' );
		$this->loader->add_action( 'wp_ajax_nopriv_aiopod_track_play', $statistics, 'ajax_callback_track_play' );
		$this->loader->add_action( 'before_delete_post', $statistics, 'delete_episode_stats' );
		$this->loader->add_action( 'before_delete_post', $statistics, 'delete_podcast_stats' );
		$this->loader->add_action( 'wp', $statistics, 'maybe_schedule_cleanup' );
		$this->loader->add_action( 'aiopod_cleanup_statistics_logs', $statistics, 'run_cleanup_cron' );
	}

	/**
	 * Initialize Blocks.
	 *
	 * @since  1.0.0
	 * @access private
	 */
	private function blocks_init() {
		if ( is_admin() ) {
			global $pagenow;
			if ( 'widgets.php' === $pagenow ) return;
		}

		global $wp_version;

		$blocks = new AIOPOD_Blocks();

		$this->loader->add_action( 'init', $blocks, 'register_block_types' );
		$this->loader->add_action( 'enqueue_block_editor_assets', $blocks, 'enqueue_block_editor_assets' );

		if ( version_compare( $wp_version, '5.8', '>=' ) ) {
			$this->loader->add_filter( 'block_categories_all', $blocks, 'block_categories' );
		} else {
			$this->loader->add_filter( 'block_categories', $blocks, 'block_categories' );
		}
	}
	
	/**
	 * Add hook to register widgets.
	 *
	 * @since  1.0.0
	 * @access private
	 */
	private function widgets_init() {
		$this->loader->add_action( 'widgets_init', $this, 'register_widgets' );
	}
	
	/**
	 * Register widgets.
	 *
	 * @since 1.0.0
	 */
	public function register_widgets() {		
		register_widget( 'AIOPOD_Widget_Podcasts' );		
		register_widget( 'AIOPOD_Widget_Episodes' );
		register_widget( 'AIOPOD_Widget_Categories' );	
		register_widget( 'AIOPOD_Widget_Tags' );
		register_widget( 'AIOPOD_Widget_Participants' );	
	}
	
	/**
	 * Map meta caps to primitive caps.
	 *
	 * @since  1.0.0
	 * @access private
	 */
	private function set_meta_caps() {
		$roles = new AIOPOD_Roles();
		$this->loader->add_filter( 'map_meta_cap', $roles, 'meta_caps', 10, 4 );
	}

	/**
	 * Run the loader to execute all of the hooks with WordPress.
	 *
	 * @since 1.0.0
	 */
	public function run() {
		$this->loader->run();
	}

}
