<?php
defined( 'ABSPATH' ) || exit;

/**
 * Count number of attachments.
 *
 * @since  1.0
 * @author Jonathan Buttigieg
 *
 * @return int The number of attachments.
 */
function imagify_count_attachments() {
	global $wpdb;
	static $count;

	/**
	 * Filter the number of attachments.
	 * 3rd party will be able to override the result.
	 *
	 * @since 1.5
	 *
	 * @param int|bool $pre_count Default is false. Provide an integer.
	 */
	$pre_count = apply_filters( 'imagify_count_attachments', false );

	if ( false !== $pre_count ) {
		return (int) $pre_count;
	}

	if ( isset( $count ) ) {
		return $count;
	}

	$mime_types   = Imagify_DB::get_mime_types();
	$statuses     = Imagify_DB::get_post_statuses();
	$nodata_join  = '';
	$nodata_where = '';

	if ( ! imagify_has_attachments_without_required_metadata() ) {
		$nodata_join  = Imagify_DB::get_required_wp_metadata_join_clause(
			'p.ID',
			true,
			true,
			"AND p.post_mime_type IN ( $mime_types )
			AND p.post_type = 'attachment'
			AND p.post_status IN ( $statuses )"
		);
		$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
	}

	$count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
		"
		SELECT COUNT( p.ID )
		FROM $wpdb->posts AS p
			$nodata_join
		WHERE p.post_mime_type IN ( $mime_types )
			AND p.post_type = 'attachment'
			AND p.post_status IN ( $statuses )
			$nodata_where"
	);

	if ( $count > imagify_get_unoptimized_attachment_limit() ) {
		set_transient( 'imagify_large_library', 1 );
	} elseif ( get_transient( 'imagify_large_library' ) ) {
		// In case the number is decreasing under our limit.
		delete_transient( 'imagify_large_library' );
	}

	return $count;
}

/**
 * Count number of optimized attachments with an error.
 *
 * @since  1.0
 * @author Jonathan Buttigieg
 *
 * @return int The number of attachments.
 */
function imagify_count_error_attachments() {
	global $wpdb;
	static $count;

	/**
	 * Filter the number of optimized attachments with an error.
	 * 3rd party will be able to override the result.
	 *
	 * @since 1.5
	 *
	 * @param int|bool $pre_count Default is false. Provide an integer.
	 */
	$pre_count = apply_filters( 'imagify_count_error_attachments', false );

	if ( false !== $pre_count ) {
		return (int) $pre_count;
	}

	if ( isset( $count ) ) {
		return $count;
	}

	$mime_types   = Imagify_DB::get_mime_types();
	$statuses     = Imagify_DB::get_post_statuses();
	$nodata_join  = '';
	$nodata_where = '';

	if ( ! imagify_has_attachments_without_required_metadata() ) {
		$nodata_join  = Imagify_DB::get_required_wp_metadata_join_clause();
		$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
	}

	$count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
		"
		SELECT COUNT(*)
		FROM (
			SELECT p.ID
			FROM $wpdb->posts AS p
				$nodata_join
			INNER JOIN $wpdb->postmeta AS mt1
				ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
			WHERE p.post_mime_type IN ( $mime_types )
				AND p.post_type = 'attachment'
				AND p.post_status IN ( $statuses )
				AND mt1.meta_value = 'error'
				$nodata_where GROUP BY p.ID
		) AS imagify_count_error"
	);

	return $count;
}

/**
 * Count number of optimized attachments (by Imagify or an other tool before).
 *
 * @since  1.0
 * @author Jonathan Buttigieg
 *
 * @return int The number of attachments.
 */
function imagify_count_optimized_attachments() {
	global $wpdb;
	static $count;

	/**
	 * Filter the number of optimized attachments.
	 * 3rd party will be able to override the result.
	 *
	 * @since 1.5
	 *
	 * @param int|bool $pre_count Default is false. Provide an integer.
	 */
	$pre_count = apply_filters( 'imagify_count_optimized_attachments', false );

	if ( false !== $pre_count ) {
		return (int) $pre_count;
	}

	if ( isset( $count ) ) {
		return $count;
	}

	$mime_types   = Imagify_DB::get_mime_types();
	$statuses     = Imagify_DB::get_post_statuses();
	$nodata_join  = '';
	$nodata_where = '';

	if ( ! imagify_has_attachments_without_required_metadata() ) {
		$nodata_join  = Imagify_DB::get_required_wp_metadata_join_clause();
		$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
	}

	$count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
		"
		SELECT COUNT( DISTINCT p.ID )
		FROM $wpdb->posts AS p
		$nodata_join
		INNER JOIN $wpdb->postmeta AS mt1
			ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
		WHERE p.post_mime_type IN ( $mime_types )
			AND p.post_type = 'attachment'
			AND p.post_status IN ( $statuses )
			AND mt1.meta_value IN ( 'success', 'already_optimized' )
			$nodata_where"
	);

	return $count;
}

/**
 * Count number of unoptimized attachments.
 *
 * @since  1.0
 * @author Jonathan Buttigieg
 *
 * @return int The number of attachments.
 */
function imagify_count_unoptimized_attachments() {
	/**
	 * Filter the number of unoptimized attachments.
	 * 3rd party will be able to override the result.
	 *
	 * @since 1.5
	 *
	 * @param int|bool $pre_count Default is false. Provide an integer.
	 */
	$pre_count = apply_filters( 'imagify_count_unoptimized_attachments', false );

	if ( false !== $pre_count ) {
		return (int) $pre_count;
	}

	return imagify_count_attachments() - imagify_count_optimized_attachments() - imagify_count_error_attachments();
}

/**
 * Count percent of optimized attachments.
 *
 * @since  1.0
 * @author Jonathan Buttigieg
 *
 * @return int The percent of optimized attachments.
 */
function imagify_percent_optimized_attachments() {
	/**
	 * Filter the percent of optimized attachments.
	 * 3rd party will be able to override the result.
	 *
	 * @since 1.5
	 *
	 * @param int|bool $percent Default is false. Provide an integer.
	 */
	$percent = apply_filters( 'imagify_percent_optimized_attachments', false );

	if ( false !== $percent ) {
		return (int) $percent;
	}

	$total_attachments           = imagify_count_attachments();
	$total_optimized_attachments = imagify_count_optimized_attachments();

	if ( ! $total_attachments || ! $total_optimized_attachments ) {
		return 0;
	}

	return min( round( 100 * $total_optimized_attachments / $total_attachments ), 100 );
}

/**
 * Count percent, original & optimized size of all images optimized by Imagify.
 *
 * @since  1.0
 * @since  1.6.7 Revamped to handle huge libraries.
 * @author Jonathan Buttigieg
 *
 * @param  string $key What data to return. Choices are between 'count', 'original_size', 'optimized_size', and 'percent'. If left empty, the whole array is returned.
 * @return array|int   An array containing the optimization data. A single data if $key is provided.
 */
function imagify_count_saving_data( $key = '' ) {
	global $wpdb;

	/**
	 * Filter the query to get all optimized attachments.
	 * 3rd party will be able to override the result.
	 *
	 * @since 1.5
	 * @since 1.6.7 This filter should return an array containing the following keys: 'count', 'original_size', and 'optimized_size'.
	 *
	 * @param bool|array $attachments An array containing the keys ('count', 'original_size', and 'optimized_size'), or an array of attachments (back compat', deprecated), or false.
	 */
	$attachments = apply_filters( 'imagify_count_saving_data', false );

	$original_size  = 0;
	$optimized_size = 0;
	$count          = 0;

	if ( is_array( $attachments ) ) {
		/**
		 * Bypass.
		 */
		if ( isset( $attachments['count'], $attachments['original_size'], $attachments['optimized_size'] ) ) {
			/**
			 * We have the results we need.
			 */
			$attachments['percent'] = $attachments['optimized_size'] && $attachments['original_size'] ? ceil( ( ( $attachments['original_size'] - $attachments['optimized_size'] ) / $attachments['original_size'] ) * 100 ) : 0;

			return $attachments;
		}

		/**
		 * Back compat'.
		 * The following shouldn't be used. Sites with a huge library won't like it.
		 */
		$attachments = array_map( 'maybe_unserialize', (array) $attachments );

		if ( $attachments ) {
			foreach ( $attachments as $attachment_data ) {
				if ( ! $attachment_data ) {
					continue;
				}

				++$count;
				$original_data = $attachment_data['sizes']['full'];

				// Increment the original sizes.
				$original_size  += $original_data['original_size'] ? $original_data['original_size'] : 0;
				$optimized_size += $original_data['optimized_size'] ? $original_data['optimized_size'] : 0;

				unset( $attachment_data['sizes']['full'] );

				// Increment the thumbnails sizes.
				if ( $attachment_data['sizes'] ) {
					foreach ( $attachment_data['sizes'] as $size_data ) {
						if ( ! empty( $size_data['success'] ) ) {
							$original_size  += $size_data['original_size'] ? $size_data['original_size'] : 0;
							$optimized_size += $size_data['optimized_size'] ? $size_data['optimized_size'] : 0;
						}
					}
				}
			}
		}
	} else {
		/**
		 * Filter the chunk size of the requests fetching the data.
		 * 15,000 seems to be a good balance between memory used, speed, and number of DB hits.
		 *
		 * @param int $limit The maximum number of elements per chunk.
		 */
		$limit = apply_filters( 'imagify_count_saving_data_limit', 15000 );
		$limit = absint( $limit );

		$mime_types   = Imagify_DB::get_mime_types();
		$statuses     = Imagify_DB::get_post_statuses();
		$nodata_join  = '';
		$nodata_where = '';

		if ( ! imagify_has_attachments_without_required_metadata() ) {
			$nodata_join  = Imagify_DB::get_required_wp_metadata_join_clause();
			$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
		}

		$attachment_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
			"
			SELECT p.ID
			FROM $wpdb->posts AS p
				$nodata_join
			INNER JOIN $wpdb->postmeta AS mt1
				ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
			WHERE p.post_mime_type IN ( $mime_types )
				AND p.post_type = 'attachment'
				AND p.post_status IN ( $statuses )
				AND mt1.meta_value = 'success'
				$nodata_where
			ORDER BY CAST( p.ID AS UNSIGNED )"
		);
		$wpdb->flush();

		$attachment_ids = array_map( 'absint', array_unique( $attachment_ids ) );
		$attachment_ids = array_chunk( $attachment_ids, $limit );

		while ( $attachment_ids ) {
			$limit_ids = array_shift( $attachment_ids );
			$limit_ids = implode( ',', $limit_ids );

			$attachments = $wpdb->get_col( // WPCS: unprepared SQL ok.
				"
				SELECT meta_value
				FROM $wpdb->postmeta
				WHERE post_id IN ( $limit_ids )
					AND meta_key = '_imagify_data'"
			);
			$wpdb->flush();

			unset( $limit_ids );

			if ( ! $attachments ) {
				continue;
			}

			$attachments = array_map( 'maybe_unserialize', $attachments );

			foreach ( $attachments as $attachment_data ) {
				if ( ! $attachment_data ) {
					continue;
				}

				if ( empty( $attachment_data['sizes']['full']['success'] ) ) {
					/**
					 * - Case where this attachment has multiple '_imagify_status' metas, and is fetched (in the above query) as a "success" while the '_imagify_data' says otherwise.
					 * - Case where this meta has no "full" entry.
					 * Don't ask how it's possible, I don't know ¯\(°_o)/¯
					 */
					continue;
				}

				$original_data = $attachment_data['sizes']['full'];

				++$count;

				// Increment the original sizes.
				$original_size  += ! empty( $original_data['original_size'] ) ? $original_data['original_size'] : 0;
				$optimized_size += ! empty( $original_data['optimized_size'] ) ? $original_data['optimized_size'] : 0;

				unset( $attachment_data['sizes']['full'], $original_data );

				// Increment the thumbnails sizes.
				if ( $attachment_data['sizes'] ) {
					foreach ( $attachment_data['sizes'] as $size_data ) {
						if ( ! empty( $size_data['success'] ) ) {
							$original_size  += ! empty( $size_data['original_size'] ) ? $size_data['original_size'] : 0;
							$optimized_size += ! empty( $size_data['optimized_size'] ) ? $size_data['optimized_size'] : 0;
						}
					}
				}

				unset( $size_data );
			}

			unset( $attachments, $attachment_data );
		}
	}

	$data = array(
		'count'          => $count,
		'original_size'  => $original_size,
		'optimized_size' => $optimized_size,
		'percent'        => $original_size && $optimized_size ? ceil( ( ( $original_size - $optimized_size ) / $original_size ) * 100 ) : 0,
	);

	if ( ! empty( $key ) ) {
		return isset( $data[ $key ] ) ? $data[ $key ] : 0;
	}

	return $data;
}

/**
 * Returns the estimated total size of the images not optimized.
 *
 * We estimate the total size of the images in the library by getting the latest 250 images and their thumbnails
 * add up their filesizes, and doing some maths to get the total size.
 *
 * @since  1.6
 * @author Remy Perona
 *
 * @return int The current estimated total size of images not optimized.
 */
function imagify_calculate_total_size_images_library() {
	global $wpdb;

	$mime_types   = Imagify_DB::get_mime_types();
	$statuses     = Imagify_DB::get_post_statuses();
	$nodata_join  = Imagify_DB::get_required_wp_metadata_join_clause();
	$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
	$image_ids    = $wpdb->get_col( // WPCS: unprepared SQL ok.
		"
		SELECT p.ID
		FROM $wpdb->posts AS p
			$nodata_join
		WHERE p.post_mime_type IN ( $mime_types )
			AND p.post_type = 'attachment'
			AND p.post_status IN ( $statuses )
			$nodata_where
		LIMIT 250
	"
	);

	if ( ! $image_ids ) {
		return 0;
	}

	$count_latest_images = count( $image_ids );
	$count_total_images  = imagify_count_attachments();

	return imagify_calculate_total_image_size( $image_ids, $count_latest_images, $count_total_images );
}

/**
 * Returns the estimated average size of the images uploaded per month.
 *
 * We estimate the average size of the images uploaded in the library per month by getting the latest 250 images and their thumbnails
 * for the 3 latest months, add up their filesizes, and doing some maths to get the total average size.
 *
 * @since  1.6
 * @since  1.7 Use wpdb instead of WP_Query.
 * @author Remy Perona
 *
 * @return int The current estimated average size of images uploaded per month.
 */
function imagify_calculate_average_size_images_per_month() {
	global $wpdb;

	$mime_types   = Imagify_DB::get_mime_types();
	$statuses     = Imagify_DB::get_post_statuses();
	$nodata_join  = Imagify_DB::get_required_wp_metadata_join_clause( "$wpdb->posts.ID" );
	$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
	$limit        = ' LIMIT 0, 250';
	$query        = "
		SELECT $wpdb->posts.ID
		FROM $wpdb->posts
			$nodata_join
		WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
			AND $wpdb->posts.post_type = 'attachment'
			AND $wpdb->posts.post_status IN ( $statuses )
			$nodata_where
			%date_query%";

	// Queries per month.
	$date_query = new WP_Date_Query(
		array(
			array(
				'before' => 'now',
				'after'  => '1 month ago',
			),
		)
	);

	$partial_images_uploaded_last_month = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.

	$date_query = new WP_Date_Query(
		array(
			array(
				'before' => '1 month ago',
				'after'  => '2 months ago',
			),
		)
	);

	$partial_images_uploaded_two_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.

	$date_query = new WP_Date_Query(
		array(
			array(
				'before' => '2 month ago',
				'after'  => '3 months ago',
			),
		)
	);

	$partial_images_uploaded_three_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.

	// Total for the 3 months.
	$partial_images_uploaded_id = array_merge( $partial_images_uploaded_last_month, $partial_images_uploaded_two_months_ago, $partial_images_uploaded_three_months_ago );

	if ( ! $partial_images_uploaded_id ) {
		return 0;
	}

	// Total for the 3 months, without the "250" limit.
	$date_query = new WP_Date_Query(
		array(
			array(
				'before' => 'now',
				'after'  => '3 month ago',
			),
		)
	);

	$images_uploaded_id = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query ) ); // WPCS: unprepared SQL ok.

	if ( ! $images_uploaded_id ) {
		return 0;
	}

	// Number of image attachments uploaded for the 3 latest months, limited to 250 per month.
	$partial_total_images_uploaded = count( $partial_images_uploaded_id );
	// Total number of image attachments uploaded for the 3 latest months.
	$total_images_uploaded = count( $images_uploaded_id );

	return imagify_calculate_total_image_size( $partial_images_uploaded_id, $partial_total_images_uploaded, $total_images_uploaded ) / 3;
}

/**
 * Returns the estimated total size of images.
 *
 * @since  1.6
 * @author Remy Perona
 *
 * @param  array $image_ids            Array of image IDs.
 * @param  int   $partial_total_images The number of image attachments we're doing the calculation with.
 * @param  int   $total_images         The total number of image attachments.
 * @return int                         The estimated total size of images.
 */
function imagify_calculate_total_image_size( $image_ids, $partial_total_images, $total_images ) {
	global $wpdb;

	$image_ids = array_filter( array_map( 'absint', $image_ids ) );

	if ( ! $image_ids ) {
		return 0;
	}

	$results = Imagify_DB::get_metas(
		array(
			// Get attachments filename.
			'filenames'    => '_wp_attached_file',
			// Get attachments data.
			'data'         => '_wp_attachment_metadata',
			// Get Imagify data.
			'imagify_data' => '_imagify_data',
			// Get attachments status.
			'statuses'     => '_imagify_status',
		),
		$image_ids
	);

	// Number of image attachments we're doing the calculation with. In case array_filter() removed results.
	$partial_total_images = count( $image_ids );
	// Total size of unoptimized size.
	$partial_size_images = 0;
	// Total number of thumbnails.
	$partial_total_intermediate_images = 0;

	$filesystem            = imagify_get_filesystem();
	$is_active_for_network = imagify_is_active_for_network();
	$disallowed_sizes      = get_imagify_option( 'disallowed-sizes' );

	foreach ( $image_ids as $i => $image_id ) {
		$attachment_status = isset( $results['statuses'][ $image_id ] ) ? $results['statuses'][ $image_id ] : false;

		if ( 'success' === $attachment_status ) {
			/**
			 * The image files have been optimized.
			 */
			// Original size.
			$partial_size_images += isset( $results['imagify_data'][ $image_id ]['stats']['original_size'] ) ? $results['imagify_data'][ $image_id ]['stats']['original_size'] : 0;
			// Number of thumbnails.
			$partial_total_intermediate_images += count( $results['imagify_data'][ $image_id ]['sizes'] );
			unset(
				$image_ids[ $i ],
				$results['filenames'][ $image_id ],
				$results['data'][ $image_id ],
				$results['imagify_data'][ $image_id ],
				$results['statuses'][ $image_id ]
			);
			continue;
		}

		/**
		 * The image files are not optimized.
		 */
		// Create an array containing all this attachment files.
		$files = array(
			'full' => get_imagify_attached_file( $results['filenames'][ $image_id ] ),
		);

		$sizes = isset( $results['data'][ $image_id ]['sizes'] ) ? $results['data'][ $image_id ]['sizes'] : array();

		if ( $sizes && is_array( $sizes ) ) {
			if ( ! $is_active_for_network ) {
				$sizes = array_diff_key( $sizes, $disallowed_sizes );
			}

			if ( $sizes ) {
				$full_dirname = $filesystem->dir_path( $files['full'] );

				foreach ( $sizes as $size_key => $size_data ) {
					$files[ $size_key ] = $full_dirname . '/' . $size_data['file'];
				}
			}
		}

		/**
		 * Allow to provide all files size and the number of thumbnails.
		 *
		 * @since  1.6.7
		 * @author Grégory Viguier
		 *
		 * @param  bool  $size_and_count False by default.
		 * @param  int   $image_id       The attachment ID.
		 * @param  array $files          An array of file paths with thumbnail sizes as keys.
		 * @param  array $image_ids      An array of all attachment IDs.
		 * @return bool|array            False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails).
		 */
		$size_and_count = apply_filters( 'imagify_total_attachment_filesize', false, $image_id, $files, $image_ids );

		if ( is_array( $size_and_count ) ) {
			$partial_size_images               += $size_and_count['filesize'];
			$partial_total_intermediate_images += $size_and_count['thumbnails'];
		} else {
			foreach ( $files as $file ) {
				if ( $filesystem->exists( $file ) ) {
					$partial_size_images += $filesystem->size( $file );
				}
			}

			unset( $files['full'] );
			$partial_total_intermediate_images += count( $files );
		}

		unset(
			$image_ids[ $i ],
			$results['filenames'][ $image_id ],
			$results['data'][ $image_id ],
			$results['imagify_data'][ $image_id ],
			$results['statuses'][ $image_id ]
		);
	}

	// Number of thumbnails per attachment = Number of thumbnails / Number of attachments.
	$intermediate_images_per_image = $partial_total_intermediate_images / $partial_total_images;
	/**
	 * Note: Number of attachments ($partial_total_images) === Number of full sizes.
	 * Average image size = Size of the images / ( Number of full sizes + Number of thumbnails ).
	 * Average image size = Size of the images / Number of images.
	 */
	$average_size_images = $partial_size_images / ( $partial_total_images + $partial_total_intermediate_images );
	/**
	 * Note: Total number of attachments ($total_images) === Total number of full sizes.
	 * Total images size = Average image size * ( Total number of full sizes + ( Number of thumbnails per attachment * Total number of attachments ) ).
	 * Total images size = Average image size * ( Total number of full sizes + Total number of thumbnails ).
	 */
	$total_size_images = $average_size_images * ( $total_images + ( $intermediate_images_per_image * $total_images ) );

	return $total_size_images;
}

/**
 * Get all generic stats to be used in the bulk optimization page.
 *
 * @since  1.7.1
 * @author Grégory Viguier
 *
 * @param  array $types The folder types. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
 * @param  array $args  {
 *     Optional. An array of arguments.
 *
 *     @type bool $fullset True to return the full set of data. False to return only the main data.
 *     @type bool $formatting Some of the data is returned formatted.
 * }
 * @return array
 */
function imagify_get_bulk_stats( $types, $args = array() ) {
	$types = $types && is_array( $types ) ? $types : array();
	$args  = array_merge(
		array(
			'fullset'    => false,
			'formatting' => true,
		),
		(array) $args
	);

	$data = array(
		// Global chart.
		'total_attachments'             => 0,
		'unoptimized_attachments'       => 0,
		'optimized_attachments'         => 0,
		'errors_attachments'            => 0,
		// Stats block.
		'already_optimized_attachments' => 0,
		'original_human'                => 0,
		'optimized_human'               => 0,
	);

	if ( isset( $types['library|wp'] ) ) {
		/**
		 * Library.
		 */
		$saving_data = imagify_count_saving_data();

		// Global chart.
		$data['total_attachments']       += imagify_count_attachments();
		$data['unoptimized_attachments'] += imagify_count_unoptimized_attachments();
		$data['optimized_attachments']   += imagify_count_optimized_attachments();
		$data['errors_attachments']      += imagify_count_error_attachments();
		// Stats block.
		$data['already_optimized_attachments'] += $saving_data['count'];
		$data['original_human']                += $saving_data['original_size'];
		$data['optimized_human']               += $saving_data['optimized_size'];
	}

	if ( isset( $types['custom-folders|custom-folders'] ) ) {
		/**
		 * Custom folders.
		 */
		// Global chart.
		$data['total_attachments']       += Imagify_Files_Stats::count_all_files();
		$data['unoptimized_attachments'] += Imagify_Files_Stats::count_no_status_files();
		$data['optimized_attachments']   += Imagify_Files_Stats::count_optimized_files();
		$data['errors_attachments']      += Imagify_Files_Stats::count_error_files();
		// Stats block.
		$data['already_optimized_attachments'] += Imagify_Files_Stats::count_success_files();
		$data['original_human']                += Imagify_Files_Stats::get_original_size();
		$data['optimized_human']               += Imagify_Files_Stats::get_optimized_size();
	}

	/**
	 * Full set of data.
	 */
	if ( $args['fullset'] ) {
		// User account.
		$views = Imagify_Views::get_instance();

		$data['unconsumed_quota'] = $views->get_quota_percent();
		$data['quota_class']      = $views->get_quota_class();
		$data['quota_icon']       = $views->get_quota_icon();
	}

	/**
	 * Filter the generic stats used in the bulk optimization page.
	 *
	 * @since  1.7.1
	 * @author Grégory Viguier
	 *
	 * @param array $data  The data.
	 * @param array $types The folder types. They are passed as array keys.
	 * @param array $args  {
	 *     Optional. An array of arguments.
	 *
	 *     @type bool $fullset True to return the full set of data. False to return only the main data.
	 *     @type bool $formatting Some of the data is returned formatted.
	 * }
	 */
	$data = apply_filters( 'imagify_bulk_stats', $data, $types, $args );

	/**
	 * Percentages.
	 */
	if ( $data['total_attachments'] && $data['optimized_attachments'] ) {
		$data['optimized_attachments_percent'] = round( 100 * $data['optimized_attachments'] / $data['total_attachments'] );
	} else {
		$data['optimized_attachments_percent'] = 0;
	}

	if ( $data['original_human'] && $data['optimized_human'] ) {
		$data['optimized_percent'] = ceil( 100 - ( 100 * $data['optimized_human'] / $data['original_human'] ) );
	} else {
		$data['optimized_percent'] = 0;
	}

	/**
	 * Formating.
	 */
	if ( $args['formatting'] ) {
		$data['already_optimized_attachments'] = number_format_i18n( $data['already_optimized_attachments'] );
		$data['original_human']                = imagify_size_format( $data['original_human'], 1 );
		$data['optimized_human']               = imagify_size_format( $data['optimized_human'], 1 );
	}

	return $data;
}
