<?php
	/**
	 * @package     Freemius
	 * @copyright   Copyright (c) 2015, Freemius, Inc.
	 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
	 * @since       2.0.0
	 */

	if ( ! defined( 'ABSPATH' ) ) {
		exit;
	}

	/**
	 * WP Admin notices manager both for site level and network level.
	 *
	 * Class FS_Admin_Notices
	 */
	class FS_Admin_Notices {
		/**
		 * @since 1.2.2
		 *
		 * @var string
		 */
		protected $_module_unique_affix;
		/**
		 * @var string
		 */
		protected $_id;
		/**
		 * @var string
		 */
		protected $_title;
		/**
		 * @var FS_Admin_Notice_Manager
		 */
		protected $_notices;
		/**
		 * @var FS_Admin_Notice_Manager
		 */
		protected $_network_notices;
		/**
		 * @var int The ID of the blog that is associated with the current site level options.
		 */
		private $_blog_id = 0;
		/**
		 * @var bool
		 */
		private $_is_multisite;
		/**
		 * @var FS_Admin_Notices[]
		 */
		private static $_instances = array();

		/**
		 * @param string $id
		 * @param string $title
		 * @param string $module_unique_affix
		 * @param bool   $is_network_and_blog_admins Whether or not the message should be shown both on network and
		 *                                           blog admin pages.
		 *
		 * @return FS_Admin_Notices
		 */
		static function instance( $id, $title = '', $module_unique_affix = '', $is_network_and_blog_admins = false ) {
			if ( ! isset( self::$_instances[ $id ] ) ) {
				self::$_instances[ $id ] = new FS_Admin_Notices( $id, $title, $module_unique_affix, $is_network_and_blog_admins );
			}

			return self::$_instances[ $id ];
		}

		/**
		 * @param string $id
		 * @param string $title
		 * @param string $module_unique_affix
		 * @param bool   $is_network_and_blog_admins Whether or not the message should be shown both on network and
		 *                                           blog admin pages.
		 */
		protected function __construct( $id, $title = '', $module_unique_affix = '', $is_network_and_blog_admins = false ) {
			$this->_id                  = $id;
			$this->_title               = $title;
			$this->_module_unique_affix = $module_unique_affix;
			$this->_is_multisite        = is_multisite();

			if ( $this->_is_multisite ) {
				$this->_blog_id = get_current_blog_id();

				$this->_network_notices = FS_Admin_Notice_Manager::instance(
					$id,
					$title,
					$module_unique_affix,
					$is_network_and_blog_admins,
					true
				);
			}

			$this->_notices = FS_Admin_Notice_Manager::instance(
				$id,
				$title,
				$module_unique_affix,
				false,
				$this->_blog_id
			);
		}

		/**
		 * Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked.
		 *
		 * @author Vova Feldman (@svovaf)
		 * @since  1.0.4
		 *
		 * @param string   $message
		 * @param string   $title
		 * @param string   $type
		 * @param bool     $is_sticky
		 * @param string   $id Message ID
		 * @param bool     $store_if_sticky
		 * @param int|null $network_level_or_blog_id
		 *
		 * @uses   add_action()
		 */
		function add(
			$message,
			$title = '',
			$type = 'success',
			$is_sticky = false,
			$id = '',
			$store_if_sticky = true,
			$network_level_or_blog_id = null,
			$is_dimissible = null
		) {
			$notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );

			$notices->add(
				$message,
				$title,
				$type,
				$is_sticky,
				$id,
				$store_if_sticky,
				null,
				null,
				false,
				$is_dimissible
			);
		}

		/**
		 * @author Vova Feldman (@svovaf)
		 * @since  1.0.7
		 *
		 * @param string|string[] $ids
		 * @param int|null        $network_level_or_blog_id
		 * @param bool            $store
		 */
		function remove_sticky( $ids, $network_level_or_blog_id = null, $store = true ) {
			if ( ! is_array( $ids ) ) {
				$ids = array( $ids );
			}

			if ( $this->should_use_network_notices( $ids[0], $network_level_or_blog_id ) ) {
				$notices = $this->_network_notices;
			} else {
				$notices = $this->get_site_notices( $network_level_or_blog_id );
			}

			return $notices->remove_sticky( $ids, $store );
		}

		/**
		 * Check if sticky message exists by id.
		 *
		 * @author Vova Feldman (@svovaf)
		 * @since  1.0.9
		 *
		 * @param string   $id
		 * @param int|null $network_level_or_blog_id
		 *
		 * @return bool
		 */
		function has_sticky( $id, $network_level_or_blog_id = null ) {
			$notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );

			return $notices->has_sticky( $id );
		}

		/**
		 * Adds sticky admin notification.
		 *
		 * @author Vova Feldman (@svovaf)
		 * @since  1.0.7
		 *
		 * @param string      $message
		 * @param string      $id Message ID
		 * @param string      $title
		 * @param string      $type
		 * @param int|null    $network_level_or_blog_id
		 * @param number|null $wp_user_id
		 * @param string|null $plugin_title
		 * @param bool        $is_network_and_blog_admins Whether or not the message should be shown both on network and
		 *                                                blog admin pages.
		 * @param bool        $is_dismissible
		 */
		function add_sticky(
			$message,
			$id,
			$title = '',
			$type = 'success',
			$network_level_or_blog_id = null,
			$wp_user_id = null,
			$plugin_title = null,
			$is_network_and_blog_admins = false,
			$is_dismissible = true,
			$data = array()
		) {
			$notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );

			$notices->add_sticky( $message, $id, $title, $type, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dismissible, $data );
		}

		/**
		 * Retrieves the data of a sticky notice.
		 *
		 * @author Leo Fajardo (@leorw)
		 * @since 2.4.3
		 *
		 * @param string   $id
		 * @param int|null $network_level_or_blog_id
		 *
		 * @return array|null
		 */
		function get_sticky( $id, $network_level_or_blog_id ) {
			$notices = $this->get_site_or_network_notices( $id, $network_level_or_blog_id );

			return $notices->get_sticky( $id );
		}

		/**
		 * Clear all sticky messages.
		 *
		 * @author Vova Feldman (@svovaf)
		 * @since  2.0.0
		 *
		 * @param int|null $network_level_or_blog_id
		 * @param bool     $is_temporary
		 */
		function clear_all_sticky( $network_level_or_blog_id = null, $is_temporary = false ) {
			if ( ! $this->_is_multisite ||
				 false === $network_level_or_blog_id ||
				 0 == $network_level_or_blog_id ||
				 is_null( $network_level_or_blog_id )
			) {
				$notices = $this->get_site_notices( $network_level_or_blog_id );
				$notices->clear_all_sticky( $is_temporary );
			}

			if ( $this->_is_multisite &&
				 ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
			) {
				$this->_network_notices->clear_all_sticky( $is_temporary );
			}
		}

		/**
		 * Add admin message to all admin messages queue, and hook to all_admin_notices if not yet hooked.
		 *
		 * @author Vova Feldman (@svovaf)
		 * @since  1.0.4
		 *
		 * @param string $message
		 * @param string $title
		 * @param string $type
		 * @param bool   $is_sticky
		 * @param string $id Message ID
		 */
		function add_all( $message, $title = '', $type = 'success', $is_sticky = false, $id = '' ) {
			$this->add( $message, $title, $type, $is_sticky, true, $id );
		}

		#--------------------------------------------------------------------------------
		#region Helper Methods
		#--------------------------------------------------------------------------------

		/**
		 * @author Vova Feldman (@svovaf)
		 * @since  2.0.0
		 *
		 * @param int $blog_id
		 *
		 * @return FS_Admin_Notice_Manager
		 */
		private function get_site_notices( $blog_id = 0 ) {
			if ( 0 == $blog_id || $blog_id == $this->_blog_id ) {
				return $this->_notices;
			}

			return FS_Admin_Notice_Manager::instance(
				$this->_id,
				$this->_title,
				$this->_module_unique_affix,
				false,
				$blog_id
			);
		}

		/**
		 * Check if the network notices should be used.
		 *
		 * @author Vova Feldman (@svovaf)
		 * @since  2.0.0
		 *
		 * @param string        $id
		 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite notices (if there's a network). When `false`, use the current context blog notices. When `null`, the decision which notices manager to use (MS vs. Current S) will be handled internally and determined based on the $id and the context admin (blog admin vs. network level admin).
		 *
		 * @return bool
		 */
		private function should_use_network_notices( $id = '', $network_level_or_blog_id = null ) {
			if ( ! $this->_is_multisite ) {
				// Not a multisite environment.
				return false;
			}

			if ( is_numeric( $network_level_or_blog_id ) ) {
				// Explicitly asked to use a specified blog storage.
				return false;
			}

			if ( is_bool( $network_level_or_blog_id ) ) {
				// Explicitly specified whether should use the network or blog level storage.
				return $network_level_or_blog_id;
			}

			return fs_is_network_admin();
		}

		/**
		 * Retrieves an instance of FS_Admin_Notice_Manager.
		 *
		 * @author Leo Fajardo (@leorw)
		 * @since 2.5.0
		 *
		 * @param string   $id
		 * @param int|null $network_level_or_blog_id
		 *
		 * @return FS_Admin_Notice_Manager
		 */
		private function get_site_or_network_notices( $id, $network_level_or_blog_id ) {
			return $this->should_use_network_notices( $id, $network_level_or_blog_id ) ?
				$this->_network_notices :
				$this->get_site_notices( $network_level_or_blog_id );
		}

		#endregion
	}