<?php

namespace DeliciousBrains\WPMDB\Common\Util;

use DeliciousBrains\WPMDB\Common\Db\MDBWPDB;
use DeliciousBrains\WPMDB\Common\Filesystem\Filesystem;
use DeliciousBrains\WPMDB\Common\Properties\Properties;
use DeliciousBrains\WPMDB\Common\Settings\Settings;
use DeliciousBrains\WPMDB\Container;
use DeliciousBrains\WPMDB\Pro\Transfers\Files\Payload;

/**
 * Class Util
 *
 * Methods in this class should *not* have many external dependencies, if any.
 *
 * @package DeliciousBrains\WPMDB\Common\Util
 */
class Util {

	/**
	 * @var Properties
	 */
	private $props;
	/**
	 * @var Filesystem
	 */
	private $filesystem;
	private $container;
	private $form_data;

	public function __construct(
		Properties $properties,
		Filesystem $filesystem
	) {
		$this->props      = $properties;
		$this->filesystem = $filesystem;
	}

	/**
	 * Has a specific method been called in the stack trace.
	 *
	 * @param string $method
	 *
	 * @return bool
	 */
	public static function has_method_been_called( $method ) {
		$stack = debug_backtrace();
		foreach ( $stack as $caller ) {
			if ( $method === $caller['function'] ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns the wpmdb_bottleneck value in bytes
	 *
	 * @param string $type
	 *
	 * @return int
	 */
	function get_bottleneck( $type = 'regular' ) {
		$suhosin_limit         = false;
		$suhosin_request_limit = false;
		$suhosin_post_limit    = false;

		if ( function_exists( 'ini_get' ) ) {
			$suhosin_request_limit = trim( ini_get( 'suhosin.request.max_value_length' ) );
			$suhosin_post_limit    = trim( ini_get( 'suhosin.post.max_value_length' ) );
		}

		if ( $suhosin_request_limit && $suhosin_post_limit ) {
			$suhosin_limit = min( wp_convert_hr_to_bytes( $suhosin_request_limit ), wp_convert_hr_to_bytes( $suhosin_post_limit ) );
		}

		$post_max_upper_size = apply_filters( 'wpmdb_post_max_upper_size', 26214400 );

		// we have to account for HTTP headers and other bloating, here we minus 1kb for bloat
		$calculated_bottleneck = min( ( $this->get_post_max_size() - 1024 ), $post_max_upper_size );

		if ( 0 >= $calculated_bottleneck ) {
			$calculated_bottleneck = $post_max_upper_size;
		}

		if ( $suhosin_limit ) {
			$calculated_bottleneck = min( $calculated_bottleneck, $suhosin_limit - 1024 );
		}

		if ( $type != 'max' ) {
			$calculated_bottleneck = min( $calculated_bottleneck, Settings::get_setting( 'max_request' ) );
		}

		return apply_filters( 'wpmdb_bottleneck', $calculated_bottleneck );
	}

	// Generates our secret key
	public function generate_key( $length = 40 ) {
		$keyset = 'abcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/';
		$key    = '';

		for ( $i = 0; $i < $length; $i ++ ) {
			$key .= substr( $keyset, wp_rand( 0, strlen( $keyset ) - 1 ), 1 );
		}

		return $key;
	}

	/**
	 * Returns the php ini value for post_max_size in bytes
	 *
	 * @return int
	 */
	public function get_post_max_size() {
		$bytes = max( wp_convert_hr_to_bytes( trim( ini_get( 'post_max_size' ) ) ), wp_convert_hr_to_bytes( trim( ini_get( 'hhvm.server.max_post_size' ) ) ) );

		return $bytes;
	}


	/**
	 * Get estimated usable memory limit.
	 *
	 * @return int
	 */
	public function get_memory_limit() {
		if ( function_exists( 'ini_get' ) ) {
			$memory_limit = ini_get( 'memory_limit' );
		} else {
			$memory_limit = '256M';
		}

		if ( ! $memory_limit || -1 === $memory_limit ) {
			$memory_limit = '1000M';
		}
		return wp_convert_hr_to_bytes( trim( $memory_limit ) ) * 0.8;
	}

	/**
	 * Test to see if executing an AJAX call specific to the WP Migrate DB family of plugins.
	 *
	 * @return bool
	 */
	public static function is_ajax() {
		// must be doing AJAX the WordPress way
		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
			return false;
		}

		// must be one of our actions -- e.g. core plugin (wpmdb_*), media files (wpmdbmf_*)
		if ( ! isset( $_POST['action'] ) || 0 !== strpos( $_POST['action'], 'wpmdb' ) ) {
			return false;
		}

		// must be on blog #1 (first site) if multisite
		if ( is_multisite() && 1 != get_current_site()->id ) {
			return false;
		}

		return true;
	}

	/**
	 * Checks if another version of WPMDB(Pro) is active and deactivates it.
	 * To be hooked on `activated_plugin` so other plugin is deactivated when current plugin is activated.
	 *
	 * @param string $plugin
	 *
	 */
	public static function deactivate_other_instances( $plugin ) {
		if ( ! in_array( basename( $plugin ), array( 'wp-migrate-db-pro.php', 'wp-migrate-db.php' ) ) ) {
			return;
		}

		$plugin_to_deactivate  = 'wp-migrate-db.php';
		$deactivated_notice_id = '1';
		if ( basename( $plugin ) == $plugin_to_deactivate ) {
			$plugin_to_deactivate  = 'wp-migrate-db-pro.php';
			$deactivated_notice_id = '2';
		}

		if ( is_multisite() ) {
			$active_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
			$active_plugins = array_keys( $active_plugins );
		} else {
			$active_plugins = (array) get_option( 'active_plugins', array() );
		}

		foreach ( $active_plugins as $basename ) {
			if ( false !== strpos( $basename, $plugin_to_deactivate ) ) {
				set_transient( 'wp_migrate_db_deactivated_notice_id', $deactivated_notice_id, 1 * HOUR_IN_SECONDS );
				deactivate_plugins( $basename );

				return;
			}
		}
	}

	/**
	 * Return unserialized object or array
	 *
	 * @param string $serialized_string Serialized string.
	 * @param string $method            The name of the caller method.
	 *
	 * @return mixed, false on failure
	 */
	public static function unserialize( $serialized_string, $method = '' ) {
		if ( ! is_serialized( $serialized_string ) ) {
			return false;
		}

		$serialized_string   = trim( $serialized_string );
		$unserialized_string = @unserialize( $serialized_string );

		if ( false === $unserialized_string && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
			$scope = $method ? sprintf( __( 'Scope: %s().', 'wp-migrate-db' ), $method ) : false;
			$error = sprintf( __( 'WPMDB Error: Data cannot be unserialized. %s', 'wp-migrate-db' ), $scope );
			error_log( $error );
		}

		return $unserialized_string;
	}

	/**
	 * Use wp_unslash if available, otherwise fall back to stripslashes_deep
	 *
	 * @param string|array $arg
	 *
	 * @return string|array
	 */
	public static function safe_wp_unslash( $arg ) {
		if ( function_exists( 'wp_unslash' ) ) {
			return wp_unslash( $arg );
		} else {
			return stripslashes_deep( $arg );
		}
	}

	/**
	 * Use gzdecode if available, otherwise fall back to gzinflate
	 *
	 * @param string $data
	 *
	 * @return string|bool
	 */
	public static function gzdecode( $data ) {
		if ( ! function_exists( 'gzdecode' ) ) {
			return @gzinflate( substr( $data, 10, - 8 ) );
		}

		return @gzdecode( $data );
	}

	/**
	 * Require wpmdb-wpdb and create new instance
	 *
	 * @return MDBWPDB
	 */
	public static function make_wpmdb_wpdb_instance() {
		return new MDBWPDB();
	}

	/**
	 * Wrapper for replacing first instance of string
	 *
	 * @return string
	 */
	public static function str_replace_first( $search, $replace, $string ) {
		$pos = strpos( $string, $search );

		if ( false !== $pos ) {
			$string = substr_replace( $string, $replace, $pos, strlen( $search ) );
		}

		return $string;
	}

	/**
	 * Runs WPs create nonce with all filters removed
	 *
	 * @param string|int $action Scalar value to add context to the nonce.
	 *
	 * @return string The Token
	 */
	public static function create_nonce( $action = - 1 ) {
		global $wp_filter;
		$filter_backup = $wp_filter;
		self::filter_nonce_filters();
		$return    = wp_create_nonce( $action );
		$wp_filter = $filter_backup;

		return $return;
	}

	/**
	 * Runs WPs check ajax_referer [sic] with all filters removed
	 *
	 * @param int|string   $action    Action nonce.
	 * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false,
	 *                                `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce'
	 *                                (in that order). Default false.
	 * @param bool         $die       Optional. Whether to die early when the nonce cannot be verified.
	 *                                Default true.
	 *
	 * @return false|int False if the nonce is invalid, 1 if the nonce is valid and generated between
	 *                   0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
	 */
	public static function check_ajax_referer( $action = - 1, $query_arg = false, $die = true ) {
		global $wp_filter;
		$filter_backup = $wp_filter;
		self::filter_nonce_filters();
		$return    = check_ajax_referer( $action, $query_arg, $die );
		$wp_filter = $filter_backup;

		return $return;
	}

	/**
	 * Removes filters from $wp_filter that might interfere with wpmdb nonce generation/checking
	 */
	private static function filter_nonce_filters() {
		global $wp_filter;
		$filtered_filters = apply_filters( 'wpmdb_filtered_filters', array(
			'nonce_life',
		) );
		foreach ( $filtered_filters as $filter ) {
			unset( $wp_filter[ $filter ] );
		}
	}

	/**
	 *
	 * Checks if the current request is a WPMDB request
	 *
	 * @return bool
	 */
	public static function is_wpmdb_ajax_call() {
		if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) && ( isset( $_POST['action'] ) && false !== strpos( $_POST['action'], 'wpmdb' ) ) ) {
			return true;
		}

		return false;
	}

	/**
	 *
	 * Sets 'Expect' header to an empty string which some server/host setups require
	 *
	 * Called from the `http_request_args` filter
	 *
	 * @param $r
	 * @param $url
	 *
	 * @return mixed
	 */
	public static function preempt_expect_header( $r, $url ) {
		if ( self::is_wpmdb_ajax_call() ) {
			$r['headers']['Expect'] = '';
		}

		return $r;
	}

	/*
	 * Patch wp_parse_url if it doesn't exist
	 * for compatibility with WP < 4.4
	 */
	public static function parse_url( $url ) {
		if ( function_exists( 'wp_parse_url' ) ) {
			return wp_parse_url( $url );
		}

		$parts = @parse_url( $url );
		if ( ! $parts ) {
			// < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path
			if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) {
				// Since we know it's a relative path, prefix with a scheme/host placeholder and try again
				if ( ! $parts = @parse_url( 'placeholder://placeholder' . $url ) ) {
					return $parts;
				}
				// Remove the placeholder values
				unset( $parts['scheme'], $parts['host'] );
			} else {
				return $parts;
			}
		}

		// < PHP 5.4.7 compat, doesn't detect schemeless URL's host field
		if ( '//' == substr( $url, 0, 2 ) && ! isset( $parts['host'] ) ) {
			$path_parts    = explode( '/', substr( $parts['path'], 2 ), 2 );
			$parts['host'] = $path_parts[0];
			if ( isset( $path_parts[1] ) ) {
				$parts['path'] = '/' . $path_parts[1];
			} else {
				unset( $parts['path'] );
			}
		}

		return $parts;
	}

	/**
	 * Get the URL to wp-admin/admin-ajax.php for the intended WordPress site.
	 *
	 * The intended WordPress site URL is sent via Ajax, so to get a properly
	 * formatted URL to wp-admin/admin-ajax.php we can't count on the site
	 * URL being sent with a trailing slash.
	 *
	 * @return string URL to wp-admin/admin-ajax.php, e.g. http://example.com/wp-admin/admin-ajax.php
	 */
	function ajax_url() {
		$state_data = Container::getInstance()->get( 'state_data_container' )->state_data;
		static $ajax_url;

		if ( ! empty( $ajax_url ) ) {
			return $ajax_url;
		}

		$ajax_url = trailingslashit( $state_data['url'] ) . 'wp-admin/admin-ajax.php';

		return $ajax_url;
	}

	function open_ssl_enabled() {
		if ( defined( 'OPENSSL_VERSION_TEXT' ) ) {
			return true;
		} else {
			return false;
		}
	}

	function set_time_limit() {
		@set_time_limit( 0 );
	}

	function display_errors() {
		$error_log  = Container::getInstance()->get( 'error_log' );
		$curr_error = $error_log->getError();
		if ( ! empty( $curr_error ) ) {
			echo $error_log->getError();
			$error_log->setError( '' );

			return true;
		}

		return false;
	}

	function diverse_array( $vector ) {
		$result = array();

		foreach ( $vector as $key1 => $value1 ) {
			foreach ( $value1 as $key2 => $value2 ) {
				$result[ $key2 ][ $key1 ] = $value2;
			}
		}

		return $result;
	}

	function set_time_limit_available() {
		if ( ! function_exists( 'set_time_limit' ) || ! function_exists( 'ini_get' ) ) {
			return false;
		}

		$current_max_execution_time  = ini_get( 'max_execution_time' );
		$proposed_max_execution_time = ( $current_max_execution_time == 30 ) ? 31 : 30;
		@set_time_limit( $proposed_max_execution_time );
		$current_max_execution_time = ini_get( 'max_execution_time' );

		return $proposed_max_execution_time == $current_max_execution_time;
	}

	/**
	 * Returns a url string given an associative array as per the output of parse_url.
	 *
	 * @param $parsed_url
	 *
	 * @return string
	 */
	function unparse_url( $parsed_url ) {
		$scheme   = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . '://' : '';
		$host     = isset( $parsed_url['host'] ) ? $parsed_url['host'] : '';
		$port     = isset( $parsed_url['port'] ) ? ':' . $parsed_url['port'] : '';
		$user     = isset( $parsed_url['user'] ) ? $parsed_url['user'] : '';
		$pass     = isset( $parsed_url['pass'] ) ? ':' . $parsed_url['pass'] : '';
		$pass     = ( $user || $pass ) ? "$pass@" : '';
		$path     = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '';
		$query    = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '';
		$fragment = isset( $parsed_url['fragment'] ) ? '#' . $parsed_url['fragment'] : '';

		return "$scheme$user$pass$host$port$path$query$fragment";
	}

	/**
	 * Get a simplified url for use as the referrer.
	 *
	 * @param $referer_url
	 *
	 * @return string
	 *
	 * NOTE: mis-spelling intentional to match usage.
	 */
	function referer_from_url( $referer_url ) {
		$url_parts = self::parse_url( $referer_url );

		if ( false !== $url_parts ) {
			$reduced_url_parts = array_intersect_key( $url_parts, array_flip( array( 'scheme', 'host', 'port', 'path' ) ) );
			if ( ! empty( $reduced_url_parts ) ) {
				$referer_url = $this->unparse_url( $reduced_url_parts );
			}
		}

		return $referer_url;
	}

	/**
	 * Get a simplified base url without scheme.
	 *
	 * @param string $url
	 *
	 * @return string
	 */
	function scheme_less_url( $url ) {
		$url_parts = self::parse_url( $url );

		if ( false !== $url_parts ) {
			$reduced_url_parts = array_intersect_key( $url_parts, array_flip( array( 'host', 'port', 'path', 'user', 'pass' ) ) );
			if ( ! empty( $reduced_url_parts ) ) {
				$url = $this->unparse_url( $reduced_url_parts );
			}
		}

		return $url;
	}

	/**
	 * Converts file paths that include mixed slashes to use the correct type of slash for the current operating system.
	 *
	 * @param $path string
	 *
	 * @return string
	 */
	function slash_one_direction( $path ) {
		return str_replace( array( '/', '\\' ), DIRECTORY_SEPARATOR, $path );
	}

	/**
	 * Returns the absolute path to the root of the website.
	 *
	 * @return string
	 */
	function get_absolute_root_file_path() {
		static $absolute_path;

		if ( ! empty( $absolute_path ) ) {
			return $absolute_path;
		}

		$absolute_path = rtrim( ABSPATH, '\\/' );
		$site_url      = rtrim( site_url( '', 'http' ), '\\/' );
		$home_url      = rtrim( home_url( '', 'http' ), '\\/' );

		if ( $site_url != $home_url ) {
			$difference = str_replace( $home_url, '', $site_url );
			if ( strpos( $absolute_path, $difference ) !== false ) {
				$absolute_path = rtrim( substr( $absolute_path, 0, - strlen( $difference ) ), '\\/' );
			}
		}

		return $absolute_path;
	}

	/**
	 * Returns the function name that called the function using this function.
	 *
	 * @return string
	 */
	public function get_caller_function() {
		list( , , $caller ) = debug_backtrace( false );

		if ( ! empty( $caller['function'] ) ) {
			$caller = $caller['function'];
		} else {
			$caller = '';
		}

		return $caller;
	}

	/**
	 * Returns uploads info for given subsite or primary site.
	 *
	 * @param int $blog_id Optional, defaults to primary.
	 *
	 * @return array
	 *
	 * NOTE: Must be run from primary site.
	 */
	public function uploads_info( $blog_id = 0 ) {
		static $primary_uploads = array();

		if ( ! empty( $blog_id ) && is_multisite() ) {
			switch_to_blog( $blog_id );
		}

		$uploads    = wp_upload_dir();
		$upload_dir = $uploads['basedir'];

		if ( ! empty( $blog_id ) && is_multisite() ) {
			restore_current_blog();

			if ( empty( $primary_uploads ) ) {
				$primary_uploads = $this->uploads_info();
			}

			$main_uploads             = $primary_uploads['basedir'];
			$uploads['short_basedir'] = str_replace( trailingslashit( $main_uploads ), '', trailingslashit( $upload_dir ) );

			if ( defined( 'UPLOADBLOGSDIR' ) && get_site_option( 'ms_files_rewriting' ) ) {

				// Get local upload path info from DB
				switch_to_blog( $blog_id );
				$upload_path = get_option( 'upload_path' );
				if ( ! empty( $upload_path ) ) {
					$uploads['short_basedir'] = str_replace( trailingslashit( UPLOADBLOGSDIR ), '', trailingslashit( $upload_path ) );
				}
				restore_current_blog();
			}
		}

		return $uploads;
	}

	/**
	 * Returns the profile value for a given key.
	 *
	 * @param string $key
	 *
	 * @param array  $form_data
	 *
	 * @return mixed
	 */
	function profile_value( $key, $form_data = [] ) {
		if ( empty( $form_data ) ) {
			$form_data = Container::getInstance()->get( 'form_data' )->getFormData();
		}

		if ( ! empty( $key ) && ! empty( $form_data ) && isset( $form_data[ $key ] ) ) {
			return $form_data[ $key ];
		}

		return null;
	}

	/**
	 * Returns a simplified site url (good for identifying subsites).
	 *
	 * @param string $site_url
	 *
	 * @return string
	 */
	public function simple_site_url( $site_url ) {
		$site_url = untrailingslashit( $this->scheme_less_url( $site_url ) );

		return $site_url;
	}

	/**
	 * Returns an associative array of html escaped useful information about the site.
	 *
	 * @return array
	 */
	public function site_details() {
		global $wpdb;
		$table_prefix = $wpdb->base_prefix;
		$uploads      = wp_upload_dir();

		$site_details = array(
			'is_multisite'         => esc_html( is_multisite() ? 'true' : 'false' ),
			'site_url'             => esc_html( addslashes( site_url() ) ),
			'home_url'             => esc_html( addslashes( home_url() ) ),
			'prefix'               => esc_html( $table_prefix ),
			'uploads_baseurl'      => esc_html( addslashes( trailingslashit( $uploads['baseurl'] ) ) ),
			'uploads'              => $this->uploads_info(),
			'uploads_dir'          => esc_html( addslashes( $this->get_short_uploads_dir() ) ),
			'subsites'             => $this->subsites_list(),
			'subsites_info'        => $this->subsites_info(),
			'is_subdomain_install' => esc_html( ( is_multisite() && is_subdomain_install() ) ? 'true' : 'false' ),
		);

		$site_details = apply_filters( 'wpmdb_site_details', $site_details );

		return $site_details;
	}

	/**
	 * Returns an uploads dir without leading path to site.
	 *
	 * @return string
	 */
	public function get_short_uploads_dir() {
		$short_path = str_replace( $this->get_absolute_root_file_path(), '', $this->filesystem->get_upload_info( 'path' ) );

		return trailingslashit( substr( str_replace( '\\', '/', $short_path ), 1 ) );
	}

	/**
	 * Returns max upload size in bytes, defaults to 25M if no limits set.
	 *
	 * @return int
	 */
	public function get_max_upload_size() {
		$bytes = wp_max_upload_size();

		if ( 1 > (int) $bytes ) {
			$p_bytes = wp_convert_hr_to_bytes( ini_get( 'post_max_size' ) );
			$u_bytes = wp_convert_hr_to_bytes( ini_get( 'upload_max_filesize' ) );

			// If HHVM bug not returning either value, try its own settings.
			// If HHVM not involved, will drop through to default value.
			if ( empty( $p_bytes ) && empty( $u_bytes ) ) {
				$p_bytes = wp_convert_hr_to_bytes( ini_get( 'hhvm.server.max_post_size' ) );
				$u_bytes = wp_convert_hr_to_bytes( ini_get( 'hhvm.server.upload.upload_max_file_size' ) );

				$bytes = min( $p_bytes, $u_bytes );

				if ( 0 < (int) $bytes ) {
					return $bytes;
				}
			}

			if ( 0 < (int) $p_bytes ) {
				$bytes = $p_bytes;
			} elseif ( 0 < (int) $u_bytes ) {
				$bytes = $u_bytes;
			} else {
				$bytes = wp_convert_hr_to_bytes( '25M' );
			}
		}

		return $bytes;
	}


	/**
	 * Extend Cache-Control header to include "no-store" so that Firefox doesn't override input selection after refresh.
	 *
	 * @param array $headers
	 *
	 * @return array
	 */
	public function nocache_headers( $headers ) {
		if ( is_array( $headers ) &&
		     key_exists( 'Cache-Control', $headers ) &&
		     false === strpos( $headers['Cache-Control'], 'no-store' )
		) {
			$headers['Cache-Control'] .= ', no-store';
		}

		return $headers;
	}

	static function is_json( $string, $strict = false ) {
		$json = @json_decode( $string, true );
		if ( $strict == true && ! is_array( $json ) ) {
			return false;
		}

		return ! ( $json == null || $json == false );
	}

	/**
	 * Checks if the compatibility mu-plugin is installed
	 *
	 * @return bool $installed
	 */
	public function is_muplugin_installed() {
		$plugins           = wp_get_mu_plugins();
		$muplugin_filename = basename( $this->props->mu_plugin_dest );
		$installed         = false;

		foreach ( $plugins as $plugin ) {
			if ( false !== strpos( $plugin, $muplugin_filename ) ) {
				$installed = true;
			}
		}

		return $installed;
	}


	/**
	 *
	 * Utility function to check if the mu-plugin directory and compatibility plugin are both writable
	 *
	 *
	 * @return bool
	 */
	public function is_muplugin_writable() {
		//Assumes by default we can create the mu-plugins folder and compatibility plugin if they don't exist
		$mu_folder_writable = true;
		$mu_plugin_writable = true;

		//If the mu-plugins folder exists, make sure it's writable.
		if ( true === $this->filesystem->is_dir( $this->props->mu_plugin_dir ) ) {
			$mu_folder_writable = $this->filesystem->is_writable( $this->props->mu_plugin_dir );
		}

		//If the mu-plugins/wp-migrate-db-pro-compatibility.php file exists, make sure it's writable.
		if ( true === $this->filesystem->file_exists( $this->props->mu_plugin_dest ) ) {
			$mu_plugin_writable = $this->filesystem->is_writable( $this->props->mu_plugin_dest );
		}

		if ( false === $mu_folder_writable || false === $mu_plugin_writable ) {
			return false;
		}

		return true;
	}

	function get_plugin_details( $plugin_path, $prefix = '' ) {
		$plugin_data = get_plugin_data( $plugin_path );
		$plugin_name = strlen( $plugin_data['Name'] ) ? $plugin_data['Name'] : basename( $plugin_path );

		if ( empty( $plugin_name ) ) {
			return;
		}

		$version = '';
		if ( $plugin_data['Version'] ) {
			$version = sprintf( " (v%s)", $plugin_data['Version'] );
		}

		$author = '';
		if ( $plugin_data['AuthorName'] ) {
			$author = sprintf( " by %s", $plugin_data['AuthorName'] );
		}

		return sprintf( "%s %s%s%s", $prefix, $plugin_name, $version, $author );
	}

	function print_plugin_details( $plugin_path, $prefix = '' ) {
		echo $this->get_plugin_details( $plugin_path, $prefix ) . "\r\n";
	}

	function remove_wp_plugin_dir( $name ) {
		$plugin = str_replace( WP_PLUGIN_DIR, '', $name );

		return substr( $plugin, 1 );
	}

	public static function gzip() {
		return function_exists( 'gzopen' );
	}

	function get_path_from_url( $url ) {
		$parts = self::parse_url( $url );

		return ( ! empty( $parts['path'] ) ) ? trailingslashit( $parts['path'] ) : '/';
	}

	function get_path_current_site() {
		if ( ! is_multisite() ) {
			return '';
		}

		$current_site = get_current_site();

		return $current_site->path;
	}

	function get_short_home_address_from_url( $url ) {
		return untrailingslashit( str_replace( array( 'https://', 'http://', '//' ), '', $url ) );
	}


	/**
	 * Get a plugin folder from the slug
	 *
	 * @param string $slug
	 *
	 * @return mixed
	 */
	public function get_plugin_folder( $slug ) {
		if ( isset( $GLOBALS['wpmdb_meta'][ $slug ]['folder'] ) ) {
			return $GLOBALS['wpmdb_meta'][ $slug ]['folder'];
		}

		// If pre-1.1.2 version of Media Files addon, use the slug as folder name
		return $slug;
	}

	/**
	 * Get array of subsite simple urls keyed by their ID.
	 *
	 * @return array
	 */
	public function subsites_list() {
		$subsites = array();

		if ( ! is_multisite() ) {
			return $subsites;
		}


		if ( version_compare( $GLOBALS['wp_version'], '4.6', '>=' ) ) {
			$sites = get_sites( array( 'number' => false ) );
		} else {
			$sites = wp_get_sites( array( 'limit' => 0 ) );
		}

		if ( ! empty( $sites ) ) {
			foreach ( (array) $sites as $subsite ) {
				$subsite                         = (array) $subsite;
				$subsites[ $subsite['blog_id'] ] = $this->simple_site_url( get_blogaddress_by_id( $subsite['blog_id'] ) );
			}
		}

		return $subsites;
	}

	/**
	 * Get array of subsite info keyed by their ID.
	 *
	 * @return array
	 */
	public function subsites_info() {
		$subsites = array();

		if ( ! is_multisite() ) {
			return $subsites;
		}

		if ( version_compare( $GLOBALS['wp_version'], '4.6', '>=' ) ) {
			$sites = get_sites( array( 'number' => false ) );
		} else {
			$sites = wp_get_sites( array( 'limit' => 0 ) );
		}

		if ( ! empty( $sites ) ) {
			// We to fix up the urls in uploads as they all use primary site's base!
			$primary_url = site_url();

			foreach ( $sites as $subsite ) {
				$subsite                                     = (array) $subsite;
				$subsites[ $subsite['blog_id'] ]['site_url'] = get_site_url( $subsite['blog_id'] );
				$subsites[ $subsite['blog_id'] ]['home_url'] = get_home_url( $subsite['blog_id'] );
				$subsites[ $subsite['blog_id'] ]['uploads']  = $this->uploads_info( $subsite['blog_id'] );

				$subsites[ $subsite['blog_id'] ]['uploads']['url']     = substr_replace( $subsites[ $subsite['blog_id'] ]['uploads']['url'], $subsites[ $subsite['blog_id'] ]['site_url'], 0, strlen( $primary_url ) );
				$subsites[ $subsite['blog_id'] ]['uploads']['baseurl'] = substr_replace( $subsites[ $subsite['blog_id'] ]['uploads']['baseurl'], $subsites[ $subsite['blog_id'] ]['site_url'], 0, strlen( $primary_url ) );
			}
		}

		return $subsites;
	}

	// Ripped from WP Core to be used in `plugins_loaded` hook
	public static function is_plugin_active( $plugin ) {
		return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) || self::is_plugin_active_for_network( $plugin );
	}

	// Ripped from WP Core to be used in `plugins_loaded` hook
	public static function is_plugin_active_for_network( $plugin ) {
		if ( ! is_multisite() ) {
			return false;
		}

		$plugins = get_site_option( 'active_sitewide_plugins' );
		if ( isset( $plugins[ $plugin ] ) ) {
			return true;
		}

		return false;
	}

	public static function get_state_data() {
		return Container::getInstance()->get( 'state_data_container' )->state_data;
	}

	public static function json_encode_trim( $item ) {
		return trim( json_encode( $item ), '"' );
	}
}

