<?php
/*
* SPEEDYCACHE
* https://speedycache.com/
* (c) SpeedyCache Team
*/

namespace SpeedyCache;

class Image{

	static function init(){
		global $speedycache, $speedycache_optm_method;
		
		$speedycache->image = array();
		$speedycache->image['upload_dir'] = wp_upload_dir();
		$speedycache->image['id'] = false;
		$speedycache->image['metadata'] = array();
		$speedycache->image['name'] = '';
		$speedycache->image['path'] = '';
		$speedycache->image['url'] = '';
		$speedycache->image['images'] = array();
		$speedycache->image['images_clone'] = array();
		$speedycache->image['disabled_method'] = 0;
		
		// Default settings for img optimization
		$speedycache->image['settings'] = array(
			'on_upload' => false,
			'automatic_optm' => false,
			'url_rewrite' => false,
			'compression_method' => 'gd',
			'compression_quality' => '70'
		);
		
		$speedycache_optm_method = array(
			'gd' => array(
				'title' => 'GD',
				'desc' => 'Compress using PHP\'s native Extension for compression and conversion.'
			),
			'imagick' => array(
				'title' => 'Imagick',
				'desc' => 'Imagick outputs better image quality after compression at the cost of a little bigger file size compared to GD.'
			),
			'cwebp' => array(
				'title' => 'cWebP',
				'desc' => 'cwebp is a utility which can be downloaded on your server and its fast and light on you server.'
			)
		);
		
		// Binaries for cwebp
		$speedycache->image['binaries'] = array(
			'WINNT' => ['cwebp-122-windows-x64.exe', 'gif2webp-122-windows-x64.exe'],
			'Linux' => ['cwebp-122-linux-x86-64', 'gif2webp-122-linux-x86-64']
		);
		
		if(array_key_exists(PHP_OS, $speedycache->image['binaries'])){
			$speedycache->image['cwebp_binary'] = $speedycache->image['binaries'][PHP_OS][0];
			$speedycache->image['cwebp_gif'] = $speedycache->image['binaries'][PHP_OS][1];
		}

		if($img_settings = get_option('speedycache_img')){
			$speedycache->image['settings'] = array_merge($speedycache->image['settings'], $img_settings);
			self::compression_method_checks();

			return;
		}
		
		self::compression_method_checks();
		update_option('speedycache_img', $speedycache->image['settings']);

	}

	static function total_reduction(){
		global $wpdb;
		
		$query = "SELECT sum(`meta_value`) as total FROM `".$wpdb->prefix."postmeta` WHERE `meta_key`= 'speedycache_optimisation_reduction'";
		$result = $wpdb->get_row( $query );
		
		if(!empty($result->total)){
			$reduced = ($result->total && $result->total > 0) ? $result->total : 0;
			
			return $reduced > 10000 ? $reduced/1000 : $reduced;	
		}
		
		return 0;
	}

	static function optimized_file_count(){
		global $wpdb;
		
		$img_stats = get_option('speedycache_optm_img_stats');
		
		if($img_stats !== FALSE){
			return $img_stats;
		}

		$query = "SELECT count(`meta_value`) as optimized FROM `".$wpdb->prefix."postmeta` WHERE `meta_key`= 'speedycache_optimisation'";
		$result = $wpdb->get_row($query);
		
		if($result->optimized && $result->optimized > 0){
			update_option('speedycache_optm_img_stats', $result->optimized, false);
			
			return $result->optimized;
		}
		
		return 0;
	}

	static function optm_img_count(){

		return self::count_query(array(
			'post_type' => 'attachment',
			'post_mime_type' => array('image/jpeg', 'image/png', 'image/gif'),
			'post_status' => 'inherit',
			'posts_per_page' => -1,
			'meta_query' => array(
				array(
					'key' => 'speedycache_optimisation',
					'compare' => 'EXISTS'
				),
				array(
					'key' => 'speedycache_optimisation',
					'value' => base64_encode('"destination_path"'),
					'compare' => 'LIKE'
				),
				array(
					'key' => '_wp_attached_file',
					'value' => '.webp',
					'compare' => 'NOT LIKE'
				)
			)
		));
	}

	static function error_count(){

		return self::count_query(array(
			'post_type' => 'attachment',
			'post_mime_type' => array('image/jpeg', 'image/png', 'image/gif'),
			'post_status' => 'inherit',
			'posts_per_page' => -1,
			'meta_query' => array(
				array(
					'key' => 'speedycache_optimisation',
					'compare' => 'EXISTS'
				),
				array(
					'key' => 'speedycache_optimisation',
					'value' => base64_encode('"error_code"'),
					'compare' => 'LIKE'
				),
				array(
					'key' => '_wp_attached_file',
					'value' => '.webp',
					'compare' => 'NOT LIKE'
				)
			)
		));
	}

	static function null_posts_groupby(){
		return '';
	}

	static function count_posts_fields(){
		return 'COUNT(*) as post_count_speedycache';
	}

	static function count_query($query_images_args){
		add_filter('posts_fields', '\SpeedyCache\Image::count_posts_fields');
		add_filter('posts_groupby', '\SpeedyCache\Image::null_posts_groupby');

		unset($query_images_args['offset']);
		unset($query_images_args['order']);
		unset($query_images_args['orderby']);

		$query_images_args['posts_per_page'] = -1;

		$query_image = new \WP_Query( $query_images_args );

		return $query_image->posts[0]->post_count_speedycache;
	}

	static function image_count(){

		return self::count_query(array(
			'post_type' => 'attachment',
			'post_mime_type' => array('image/jpeg', 'image/png', 'image/gif'),
			'post_status' => 'inherit',
			'posts_per_page' => -1,
			'meta_query' => array(
				array(
					'key' => '_wp_attachment_metadata',
					'compare' => 'EXISTS'
				),
				array(
					'key' => '_wp_attached_file',
					'value' => '.webp',
					'compare' => 'NOT LIKE'
				)
			)
		));
	}

	static function uncompressed_count(){

		return self::count_query(array(
			'post_type' => 'attachment',
			'post_mime_type' => array('image/jpeg', 'image/png', 'image/gif'),
			'post_status' => 'inherit',
			'posts_per_page' => -1,
			'meta_query' => array(
				array(
					'key' => 'speedycache_optimisation',
					'compare' => 'NOT EXISTS'
				),
				array(
					'key' => '_wp_attached_file',
					'value' => '.webp',
					'compare' => 'NOT LIKE'
				)
			)
		));
	}

	static function unoptimized(){
		global $speedycache;
		
		$tmp_image = array();

		$optimization_data = get_post_meta($speedycache->image['id'], 'speedycache_optimisation', true);
		$optm_json = base64_decode($optimization_data);
		
		if(empty($optm_json)){
			$percentage = 100 / count($speedycache->image['images']);
			
			return array('meta_optimized' => array(), 'images' => $speedycache->image['images'], 'total_reduction' => 0, 'percentage' => $percentage);
		}

		$optm_json = json_decode($optm_json, true);
		$meta_optimized = self::object_to_array($optm_json);
		$percentage = count($meta_optimized) * 100/count($speedycache->image['images']);
		
		foreach($speedycache->image['images'] as $key => $value){
			$exist = false;

			foreach($meta_optimized as $meta_key => $meta_value){
				if($value['file'] == $meta_value['file']){
					$exist = true;
					//break;
				}
			}

			if(empty($exist)){
				array_push($tmp_image, $value);
			}
		}

		//START: total reduction
		$total_reduction = 0;
		
		foreach($meta_optimized as $m_key => $m_value){
			$m_value['reduction'] = isset($m_value['reduction']) ? $m_value['reduction'] : 0;
			
			if($m_key == 0){
				$reduction = $m_value['reduction'];
			}
			
			$total_reduction += $m_value['reduction'];
		}
		//END: total reduction

		if(count($tmp_image) <= 0){
			return array('meta_optimized' => array(), 'images' => array(), 'total_reduction' => 0);
		}

		$last = speedycache_optget('last');
		if(!empty($last)){
			if(preg_match('/last-(\d+)/', $last, $last_number)){
				if(count($tmp_image) > 5){
					$tmp_image = array_slice($tmp_image, $last_number[1]*-1, 1);
				}
			}
		}

		return array('meta_optimized' => $meta_optimized, 'images' => array_slice($tmp_image, 0, 1), 'total_reduction' => $total_reduction, 'percentage' => $percentage);
	}

	static function object_to_array($obj){
		if(is_object($obj)){
			$obj = (array) $obj;
		}
		
		if(!is_array($obj)){
			$new = $obj;
			return $new;
		}
		
		$new = array();
		foreach($obj as $key => $val){
			$new[$key] = self::object_to_array($val);
		}
		return $new;
	}

	static function reorder_by_dimensions(){
		global $speedycache;
		
		$tmp = $speedycache->image['images'];

		foreach($tmp as $key => $value){
			$width_list[$key] = $value['width'];
		}

		array_multisort($width_list, SORT_DESC, $tmp);
		
		return $tmp;
	}

	static function optimize_single($id = null){
		global $speedycache;

		if(!empty($id)){
			self::init();
		}

		self::set_id($id);
		self::set_meta_data();

		if(wp_next_scheduled('speedycache_auto_optm', array($speedycache->image['id']))){
			wp_clear_scheduled_hook('speedycache_auto_optm' , array($speedycache->image['id']));
		}

		if(empty($speedycache->image['id'])){
			return array('finish', 'success'); 
		}

		if(!isset($speedycache->image['metadata']['file']) && !empty($speedycache->image['id'])){
			$meta_optimized = array();
			$meta_optimized[0]['time'] = time();
			$meta_optimized[0]['id'] = $speedycache->image['id'];
			$meta_optimized[0]['error_code'] = 17;

			update_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction', 0);
			update_post_meta($speedycache->image['id'], 'speedycache_optimisation', base64_encode(json_encode($meta_optimized)));

			return array('Image has been optimized', 'success', $speedycache->image['id'], 100);
		}

		self::set_name();
		self::set_path();
		self::set_url();
		self::set_images();

		if(!empty($speedycache->image['id']) && count($speedycache->image['images']) == 0){
			$meta_optimized = array();
			$meta_optimized[0]['time'] = time();
			$meta_optimized[0]['id'] = $speedycache->image['id'];
			$meta_optimized[0]['error_code'] = 18;

			update_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction', 0);
			update_post_meta($speedycache->image['id'], 'speedycache_optimisation', base64_encode(json_encode($meta_optimized)));

			return array('Image has been optimized', 'success', $speedycache->image['id'], 100);
		}

		$error_exist = false;
		$meta_optimized = array();
		$total_reduction = 0;
		
		$speedycache->image['images'] = self::unique_array($speedycache->image['images']);
		$speedycache->image['images'] = self::reorder_by_dimensions();

		$speedycache->image['images_clone'] = $speedycache->image['images'];

		$unoptimized = self::unoptimized();
		$speedycache->image['images'] = $unoptimized['images'];
		$meta_optimized = $unoptimized['meta_optimized'];
		$total_reduction = $unoptimized['total_reduction'];
		$percentage = isset($unoptimized['percentage']) && $unoptimized['percentage'] ? $unoptimized['percentage'] : 0;

		if(count($speedycache->image['images']) == 0){
			return array('Image has been optimized', 'success', '', 100);
		}

		foreach($speedycache->image['images'] as $key => $value){
		
			$res = self::compress($value);
			
			if(!empty($res['success'])){
				$value['destination_path'] = $res['destination_path'];
				$value['reduction'] = $res['reduction'];

				$total_reduction += $value['reduction'];

				$value['time'] = time();
				$value['id'] = $speedycache->image['id'];
				
				array_push($meta_optimized, $value);
			}else{
				if(!isset($res['error_code']) && isset($res['error_message'])){
					return array($res['error_message'], 'error');
					break;
				}

				if(in_array($res['error_code'] , array(2, 6, 7, 11, 19, 20, 22, 23))){
					return array(self::get_error_text($res['error_code']), 'error');
					break;
				}

				$value['error_code'] = $res['error_code'];
				$error_exist = true;
			}

			$value['time'] = time();
			$value['id'] = $speedycache->image['id'];

			if(!empty($value['error_code'])){
				if($value['error_code'] != 8 || ($value['error_code'] == 8 && $key === 0)){
					array_push($meta_optimized, $value);
				}
			}
		}
		
		$percentage = self::update_meta($total_reduction, $meta_optimized);
		
		delete_option('speedycache_optm_img_stats'); // Deleting the cached stat

		return array('Image has been optimized', 'success', $speedycache->image['id'], $percentage, $total_reduction);
	}

	static function update_meta($total_reduction, $meta_optimized){
		global $speedycache;
		
		if(isset($meta_optimized[0]) && isset($meta_optimized[0]['error_code']) && $meta_optimized[0]['error_code']){

			update_post_meta($speedycache->image['id'], 'speedycache_optimisation', base64_encode(json_encode($meta_optimized)));
			update_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction', 0);

			return 100;
		}

		$percentage = 0;
		$meta_temp = array();
		
		foreach($speedycache->image['images_clone'] as $key => $value){
			$backup_file = $value['file'];
			
			$value['file'] = preg_replace('/.(jpg|jpeg|png|gif)$/', '.webp', $value['file']);
			
			if(!file_exists($value['file']) || !file_exists($backup_file)){
				continue;
			}

			$diff = filesize($backup_file) - filesize($value['file']);
			$diff = $diff > 0 ? $diff : 0;

			$value['destination_path'] = $backup_file;
			$value['reduction'] = $diff;
			$value['time'] = time();
			$value['id'] = $speedycache->image['id'];

			array_push($meta_temp, $value);
		}

		foreach($meta_optimized as $m_key => $m_value){
			if(empty($m_value['error_code'])){
				continue;
			}

			$exist = false;

			for($i=0; $i < count($meta_temp); $i++){
				if($meta_temp['file'] == $m_value['file']){
					$exist = true;
				}
			}

			if(empty($exist)){
				$m_value['destination_path'] = '';
				$m_value['reduction'] = 0;
				$m_value['time'] = time();
				$m_value['id'] = $speedycache->image['id'];

				array_push($meta_temp, $m_value);
			}
		}
		
		if(count($meta_temp) > 0){
			$percentage = count($meta_temp)*100/count($speedycache->image['images_clone']);
		}else{
			$percentage = 0;
		}

		update_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction', $total_reduction);
		update_post_meta($speedycache->image['id'], 'speedycache_optimisation', base64_encode(json_encode($meta_temp)));

		return $percentage;
	}

	static function compress($source_image){
		global $speedycache;

		/*
			Error Codes
			2 = in backup folder parent folder not writable
			3 = no need to optimize
			4 = source is not writable
			5 = destination is not writable
			6 = ImageMagick library is not avaliable
			7 = Error via api
			8 = Source file does not exist
			9 = Image size exceed 5mb limit while processing
		   11 = Empty Name
		   12 = Forbidden
		   13 = CloudFlare to restrict access
		   14 = No Extension
		   15 = Image size is 0Kb
		   16 = Corrupted Image
		   17 = Empty Metadata
		   18 = No Image
		   19 = webp is not saved
		   20 = file size of destination_move_source_path is zero
		   21 = Unacceptable file type
		   22 = Unable to Convert Image using cwebp.
		   23 = The URL of the image has webp extension.
		*/

		// if the url starts with /wp-content
		if(preg_match('/^\/' . SPEEDYCACHE_WP_CONTENT_DIR . '/i', $source_image['url'])){
			$source_image['url'] = home_url().$source_image['url'];
		}

		$source_path = $source_image['file'];
		$res_backup = array('success' => true, 'error_message' => '');
		$webp_path = preg_replace('/.(jpe?g|png|gif)$/', '.webp', $source_path);

		if(strlen($speedycache->image['name']) === 0){
			return array('success' => false, 'error_code' => 11);
		}

		if(!file_exists($source_path)){
			return array('success' => false, 'error_code' => 8);
		}

		if(!pathinfo($source_image['url'], PATHINFO_EXTENSION)){
			return array('success' => false, 'error_code' => 14);
		}

		// If the URL of the image is a webp
		if(pathinfo($source_image['url'], PATHINFO_EXTENSION) == 'webp' && file_exists($webp_path)){
			return array('success' => false, 'error_code' => 23);
		}

		if(@filesize($source_path) > 5000000){
			return array("success" => false, 'error_code' => 9);
		}

		if(!self::allowed_mime($source_path)){
			return array('success' => false, 'error_code' => 21);
		}

		if(!self::path_is_image($source_path)){
			return array('success' => false, 'error_code' => 16);
		}

		if(filesize($source_path) == 0){
			return array('success' => false, 'error_code' => 15);
		}

		if(@rename($source_path, $source_path.'_writabletest')){
			rename($source_path.'_writabletest', $source_path);
		}else{
			return array('success' => false, 'error_message' => $source_path . ' is not writable', 'error_code' => 4);
		}

		$optm_result = self::start_optimization($source_path); // here we need to plugin compression static function
		
		if(empty($optm_result['success'])){
			//NOTE:Place some error Message here.
			return $optm_result;
		}

		if(!file_exists($webp_path)){
			return array('success' => false, 'error_code' => 19, 'error_message' => $webp_path . ' destination_path is not saved');
		}
		
		if(filesize($webp_path) <= 0){
			return array('success' => false, 'error_code' => 20, 'error_message' => $webp_path . ' file size of destination_path is zero');
		}

		$diff = self::compare_sizes($source_path, $webp_path);
		return array('success' => true, 'destination_path' => $webp_path, 'reduction' => $diff);
	}


	static function start_optimization($img){
		global $speedycache;

		if(empty($speedycache->image['settings']['compression_method'])){
			return array('success' => false,  'error_message' => __('The provided conversion method is not valid', 'speedycache-pro'));
		}

		switch($speedycache->image['settings']['compression_method']){
			case 'gd': 
				return self::gd_webp($img);
				
			case 'imagick':
				return self::imagick_webp($img);
				
			case 'cwebp':
				if(defined('SPEEDYCACHE_PRO')){					
					return \SpeedyCache\Image::cwebp_convert($img);
				}
				
				return array('success' => false, 'error_message' => 'SpeedyCache cwebp is a Pro feature');
				
			default:
				return array('success' => false,  'error_message' => 'The provided conversion method is not valid');
		}
	}

	static function path_is_image($source_path){
		$size = getimagesize($source_path);

		if(empty($size)){
			return false;
		}

		return true;
	}

	static function get_quality($img){
		$dimensions = $img->getImageGeometry();
		
		if($dimensions['width'] < 200 && $dimensions['height'] < 200){
			return 85;
		}

		return 90;
	}

	static function compare_sizes($source_path, $destination_path){
		$diff = filesize($source_path) - filesize($destination_path);

		return ($diff > 0) ? $diff : 1;
	}


	//TODO:: Will need it when we will add non webp compression
	static function create_backup_folder($destination_path){
		global $speedycache;
		
		$destination_path = str_replace($speedycache->image['upload_dir']['basedir'], '', $destination_path);
		$path_arr = explode('/', $destination_path);

		$path = $speedycache->image['upload_dir']['basedir'];

		for ($i=1; $i < count($path_arr) - 1; $i++){
			$parent_path = $path;
			$path = $path.'/'.$path_arr[$i];

			if(is_dir($path)){
				continue;
			}
			
			if(@mkdir($path, 0755, true)){
				//
			}else{
				//warning
				if($path_arr[$i] == 'speedycache-backup'){
					//toDO: to stop cron job and warn the user
				}

				return array('success' => false, 'error_message' => $parent_path.' is not writable', 'error_code' => 2);
			}
		}

		return array('success' => true, 'error_message' => '');
	}

	static function set_id($id = null){
		global $speedycache;
		
		$get_id = speedycache_optget('id');
		
		if(!empty($get_id)){
			$speedycache->image['id'] = intval($get_id);
		}elseif(!empty($id)){
			$speedycache->image['id'] = intval($id);
		}else{
			$speedycache->image['id'] = self::get_first_id();
		}
	}

	static function set_images(){
		global $speedycache;
		
		if(empty($speedycache->image['metadata']['file'])){
			return;
		}

		$arr = array(
			'file' => $speedycache->image['upload_dir']['basedir'].'/'.$speedycache->image['metadata']['file'],
			'url' => $speedycache->image['upload_dir']['baseurl'].'/'.$speedycache->image['metadata']['file'],
			'width' => $speedycache->image['metadata']['width'],
			'height' => $speedycache->image['metadata']['height'],
			'mime_type' => ''
		);
		
		array_push($speedycache->image['images'], $arr);

		$i = 0;
		$image_error = false;

		if(!is_array($speedycache->image['metadata']['sizes'])){
			if(empty($image_error)){
				self::not_in_metadata();
			}
		}

		foreach((array)$speedycache->image['metadata']['sizes'] as $key => $value){
			$value['url'] = $speedycache->image['url'].$value['file'];
			$value['file'] = $speedycache->image['path'].$value['file'];
			$value['mime_type'] = isset($value['mime-type']) ? $value['mime-type'] : '';

			unset($value['mime-type']);

			if($i == 0){
				if(self::not_found(self::get_correct_url($speedycache->image['upload_dir']['baseurl'].'/'.$speedycache->image['metadata']['file']))){
					$image_error = true;
					break;
				}
			}

			if(!self::not_found(self::get_correct_url($value['url'])) && self::allowed_mime($value['file'])){
				array_push($speedycache->image['images'], $value);
			}

			$i++;
		}

		if(empty($image_error)){
			self::not_in_metadata();
		}
	}

	static function get_correct_url($path){
		if(preg_match('/^\/'.SPEEDYCACHE_WP_CONTENT_DIR.'/i', $path)){
			
			// content_url() must return HTTP but it return /wp-content so we need to check
			if(content_url() == '/'.SPEEDYCACHE_WP_CONTENT_DIR && home_url() == site_url()){
				$path = home_url().$path;
			}
		}

		return $path;
	}

	static function not_in_metadata(){
		global $speedycache;
		
		$paths = array();

		foreach($speedycache->image['images'] as $key => $value){
			array_push($paths, $value['file']);
		}
		
		$files = glob($speedycache->image['path'].$speedycache->image['name'].'-'.'*');

		foreach((array)$files as $file){
			if(@filesize($file) > 1000000){
				continue;
			}

			if(!preg_match('/\.(jpg|jpeg|jpe|png|gif)$/i', $file)){
				continue;
			}

			$exp_dos = explode('/',$file);
			$basename = end($exp_dos);

			if(in_array($file, $paths)){
				continue;
			}
			
			if(!preg_match('/'.preg_quote($speedycache->image['name'], '/').'-(\d+)x(\d+)\..+/', $basename, $dimensions)){
				continue;
			}
			
			$value = array(
				'url' => $speedycache->image['url'].$basename,
				'file' => $file,
				'width' => $dimensions[1],
				'height' => $dimensions[2],
			);

			if(!self::not_found($value['url'])){
				array_push($speedycache->image['images'], $value);
			}
		}
	}

	static function set_path(){
		global $speedycache;
		
		$speedycache->image['path'] = $speedycache->image['upload_dir']['basedir'].'/'.preg_replace('/'.preg_quote($speedycache->image['name'], '/').'.+/', '', $speedycache->image['metadata']['file']);
	}

	static function set_url(){
		global $speedycache;
		
		$speedycache->image['url'] = $speedycache->image['upload_dir']['baseurl'].'/'.preg_replace('/'.preg_quote($speedycache->image['name'], '/').'.+/', '', $speedycache->image['metadata']['file']);
	}

	static function set_name(){
		global $speedycache;
		
		if(empty($speedycache->image['metadata'])){
			return;
		}

		if(isset($speedycache->image['metadata']['sizes']) && count($speedycache->image['metadata']['sizes']) > 0){
			$array_values = array_values($speedycache->image['metadata']['sizes']);
			$speedycache->image['name'] = preg_replace('/-'.$array_values[0]['width'].'x'.$array_values[0]['height'].'.+/', '', $array_values[0]['file']);

			if(!$speedycache->image['name']){
				$speedycache->image['name'] = substr($speedycache->image['metadata']['file'], strrpos($speedycache->image['metadata']['file'], '/') + 1);
			}
			
			return;
		}

		$info = pathinfo($speedycache->image['metadata']['file']);
		$speedycache->image['name'] =  basename($speedycache->image['metadata']['file'],'.'.$info['extension']);

		//$this->name = substr($this->metadata['file'], strrpos($this->metadata['file'], '/') + 1);
	}

	static function set_meta_data(){
		global $speedycache;
		
		$speedycache->image['metadata'] = wp_get_attachment_metadata($speedycache->image['id']);
	}

	//to get last image which is not optimized
	static function get_first_id(){

		$query_image = new \WP_Query(array(
			'order' => 'DESC',
			'orderby' => 'ID',
			'post_type' => 'attachment', 
			'post_mime_type' => 'image/jpeg, image/png, image/gif', 
			'post_status' => 'inherit',
			'posts_per_page' => 1,
			'meta_query' => array(
				array(
					'key' => 'speedycache_optimisation',
					'compare' => 'NOT EXISTS'
				),
				array(
					'key' => '_wp_attachment_metadata',
					'compare' => 'EXISTS'
				),array(
					'key' => '_wp_attached_file',
					'value' => '.webp',
					'compare' => 'NOT LIKE'
				)
			)
		));

		return count($query_image->posts) == 1 ? $query_image->posts[0]->ID : false;
	}

	static function statics_data(){
		$res = array(
			'total_image_number' => self::image_count(),
			'error' => self::error_count(),
			'optimized' => self::optimized_file_count(),
			'uncompressed' => self::uncompressed_count(),
			'reduction' => self::total_reduction(),
			'percent' => 0,
		);

		if($res['optimized'] > 0){
			$res['percent'] = ($res['optimized'] - $res['error']) * 100/$res['optimized'];
		}else{
			$res['percent'] = 0;
		}
		
		$res['percent'] = number_format($res['percent'], 2);
		$res['reduction'] = $res['reduction'];
		
		return $res;
	}

	static function revert_all(){
		global $speedycache;
		
		if(!current_user_can('manage_options')){
			wp_die('Must Be admin');
		}

		$images = new \WP_Query(array(
			'order' => 'DESC',
			'orderby' => 'ID',
			'post_type' => 'attachment',
			'post_mime_type' => array('image/jpeg', 'image/png', 'image/gif'),
			'post_status' => 'inherit',
			'posts_per_page' => -1,
			'meta_query' => array(
				array(
					'key' => 'speedycache_optimisation',
					'compare' => 'EXISTS'
				)
			)
		));

		$failed = true;
		
		if(count($images->posts) <= 100){
			
			if($images->posts && count($images->posts) > 0){
				foreach($images->posts as $key => $post){
					$speedycache->image['id'] = $post->ID;
					$result = self::revert();
					
					if($result['success'] === true){
						$failed = false;
					}
				}
			}
			
			if(!empty($failed)){
				wp_send_json(array('success' => false, 'message' => __('Files can\'t be reverted', 'speedycache'))); 
			}
			
			wp_send_json(array('success' => true));
		}

		$schedule_posts = [];
		foreach($images->posts as $key => $post){
			
			if(100 === count($schedule_posts) || !empty($erase)){
				$schedule_posts = [];
			}

			$erase = false; // Flag to unset the $scheduled_posts
			
			$schedule_posts[] = $post->ID;

			// Check if we have reached 99 and make sure its not the last index
			if(count($schedule_posts) <= 99 && $key !== (count($images->posts) - 1)){
				continue;
			}
			
			// Skip if alredy on schedule list.
			if(wp_next_scheduled('speedycache_img_delete', array($schedule_posts))){
				$erase = true;
				continue;
			}
		
			$scheduled = self::get_optimization_schedule(array('speedycache_img_delete'));
			$time = time();
		
			if(!empty($scheduled) && isset(end($scheduled)['time'])){
				// getting the last index to get the last scheduled event
				$time = end($scheduled)['time'];
			}
			
			$final_schd_time = $time + 10;
		
			if(!wp_next_scheduled('speedycache_img_delete', array($schedule_posts))){
				wp_schedule_single_event($final_schd_time, 'speedycache_img_delete', array($schedule_posts));
				
				continue;
			}
		}

		wp_send_json(array('success' => true));
	}

	// Schedule deletion of image to reduce load on the server at a single time.
	static function scheduled_delete($img_id){
		global $speedycache;
		
		if(empty($img_id)){
			return;
		}

		if(is_scalar($img_id)){
			$speedycache->image['id'] = $img_id;
			self::revert();
			return;
		}
		
		foreach($img_id as $id){
			$speedycache->image['id'] = $id;
			self::revert();
		}
	}

	// Reverts a single image
	static function revert(){
		global $speedycache;
		
		if(empty($speedycache->image['id'])){
			return array('success' => false);
		}
		
		$optimization_data = get_post_meta($speedycache->image['id'], 'speedycache_optimisation', true);
		
		if(empty($optimization_data)){
			return array('success' => false);
		}

		// optm = optimization
		$optm_json = base64_decode($optimization_data);
		$optm_json = json_decode($optm_json, true);
		
		if(empty($optm_json)){
			return array('success' => false);
		}
		
		delete_option('speedycache_optm_img_stats'); // Deleting the cached stat

		if(!empty($optm_json) && is_array($optm_json) && count($optm_json) == 1 && !empty($optm_json[0])){
			if(!empty($optm_json[0]['error_code']) && $optm_json[0]['error_code'] == 18){
				delete_post_meta($speedycache->image['id'], 'speedycache_optimisation');
				delete_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction');

				return array('success' => true);
			}
		}

		$result = false;

		$optm_json = array_reverse($optm_json);
		$error_numbers = 0;
		
		foreach($optm_json as $key => $image){

			if(@!is_writable($image['file'])){
				if(isset($speedycache->image['metadata']['file']) && preg_match('/'.preg_quote($speedycache->image['metadata']['file'], '/').'/', $image['url'])){
					if(file_exists($image['file'])){
						$result = array('success' => true, 'message' => $image['file'].' is not writable');
						break;
					}
					
					delete_post_meta($speedycache->image['id'], 'speedycache_optimisation');
					delete_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction');
				}
			}

			if(
				!empty($image['url']) && 
				!preg_match('/\.webp$/', $image['url']) && 
				isset($image['destination_path']) && 
				file_exists($image['destination_path']) &&
				(pathinfo($image['destination_path'], PATHINFO_EXTENSION) !== 'webp') &&
				(pathinfo($image['file'], PATHINFO_EXTENSION) == 'webp')
			){
				@unlink($image['file']);

				delete_post_meta($speedycache->image['id'], 'speedycache_optimisation');
				delete_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction');
				$result = array('success' => true);
				
				continue;
			}

			if(!empty($image['error_code'])){
				if(isset($speedycache->image['metadata']['file']) && preg_match('/'.preg_quote($speedycache->image['metadata']['file'], '/').'/', $image['url'])){
					delete_post_meta($speedycache->image['id'], 'speedycache_optimisation');
					delete_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction');
					$result = array('success' => true);
				}else{
					$error_numbers++;

					if($error_numbers == count($optm_json)){
						delete_post_meta($speedycache->image['id'], 'speedycache_optimisation');
						delete_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction');
						$result = array('success' => true);
					}
				}
				
				continue;
			}

			if(preg_match('/'.preg_quote($speedycache->image['metadata']['file'], '/').'/', $image['url'])){
				delete_post_meta($speedycache->image['id'], 'speedycache_optimisation');
				delete_post_meta($speedycache->image['id'], 'speedycache_optimisation_reduction');
				$result = array('success' => true);
			}

		}
		
		return $result;
	}

	static function revert_on_delete($id){
		global $speedycache;
		
		if(!wp_attachment_is_image($id)){
			return;
		}
		
		$speedycache->image['id'] = $id;
		
		self::revert();
	}

	static function get_error_text($id){
		
		//Error Codes
		$errors = array(
			2 => __('In backup folder parent folder not writable', 'speedycache'),
			3 => __('No need to optimize', 'speedycache'),
			4 => __('Source is not writable', 'speedycache'),
			5 => __('Destination is not writable', 'speedycache'),
			6 => __('ImageMagick library is not avaliable', 'speedycache'),
			7 => __('Error via api', 'speedycache'),
			8 => __('Source file does not exist', 'speedycache'),
			9 => __('Image size exceed 20mb limit while processing', 'speedycache'),
		   11 => __('Empty Name', 'speedycache'),
		   12 => __('Forbidden', 'speedycache'),
		   13 => __('CloudFlare to restrict access', 'speedycache'),
		   14 => __('No Extension', 'speedycache'),
		   15 => __('Image size is 0Kb', 'speedycache'),
		   16 => __('Corrupted Image', 'speedycache'),
		   17 => __('Empty metadata', 'speedycache'),
		   18 => __('No Image', 'speedycache'),
		   19 => __('destination_move_source_path is not saved', 'speedycache'),
		   20 => __('file size of destination_move_source_path is zero', 'speedycache'),
		   21 => __('Unacceptable file type', 'speedycache'),
		   23 => __('WEBP create by other tool', 'speedycache'),
		);

		return isset($errors[$id]) ? $errors[$id] : 'Unkown error code';
	}

	// TODO:: Will use it when we will do non-webp compression
	static function backup_folder_exist(){
		global $speedycache;
		
		$backup_folder_path = $speedycache->image['upload_dir']['basedir'].'/speedycache-backup';

		if(is_dir($backup_folder_path)){
			return true;
		}

		if(@mkdir($backup_folder_path, 0755, true)){
			return true;
		}
		return false;

	}

	static function push_main_image($arr){
		if(!isset($arr[0]) || isset($arr[0]->error_code)){
			return $arr;
		}

		$main = clone $arr[0];
		$total_reduction = 0;

		foreach($arr as $std_key => $std_value){
			if(!isset($std_value->error_code)){
				if(isset($std_value->reduction) && $std_value->reduction){
					$total_reduction = $total_reduction + $std_value->reduction;
				}
			}
		}

		$main->reduction = $total_reduction;

		array_unshift($arr, $main);

		return $arr;
	}

	static function list_content($query_images_args = array()){
		global $speedycache;
		
		remove_filter('posts_fields', '\SpeedyCache\Image::count_posts_fields'); //was causing bug
		
		$query_image = new \WP_Query( $query_images_args );
		$return_output = '';
		
		if(empty($query_image->posts) || count($query_image->posts) <= 0){
			return self::get_empty_row();
		}
		
		$count = 0;
		foreach($query_image->posts as $key => $post){
			$count++;
			$value_json = get_post_meta($post->ID, 'speedycache_optimisation', true);
			$tmpvalue_json = base64_decode($value_json);
			
			$std = json_decode($tmpvalue_json);

			$revert = true;

			$std = self::push_main_image($std);
			
			foreach($std as $std_key => $std_value){
				
				$content = ($std_key === 0) ? self::get_row() : self::get_child_row();

				if(empty($content)){
					continue;
				}

				$std_value->destination_path = isset($std_value->destination_path) ? $std_value->destination_path : '';
				$std_value->reduction = isset($std_value->reduction) ? $std_value->reduction : 0;
				
				if($std_key === 0 && $revert){
					$revert_button = '';
				}else{
					$revert_button = 'display:none;';
				}

				if(isset($std_value->error_code) && $std_value->error_code == 8){
					$revert_button = 'display:none;';
				}

				if(file_exists($std_value->destination_path)){
					$backup_url = $std_value->url.'?v='.time();
					$backup_title = 'Original Image';
					$backup_error_style = '';
				}else{
					if(isset($std_value->error_code) && $std_value->error_code){
						$backup_url = get_edit_post_link($std_value->id);
						$backup_title = self::get_error_text($std_value->error_code);
						$backup_error_style = 'color: #FF0000;cursor:pointer;font-weight:bold;';
					}else{
						$backup_url = '#';
						$backup_title = '';
						$backup_error_style = '';
					}
				}
				
				$std_value->file = preg_replace('/.(jpg|jpeg|png|gif)$/','.webp', $std_value->file);
				
				if(file_exists($std_value->file)){
					$std_value->url = preg_replace('/.(jpg|jpeg|png|gif)$/','.webp', $std_value->url).'?v='.time();
				}else{
					$std_value->url = SPEEDYCACHE_PRO_URL.'/assets/images/no-image.png';
				}

				$short_code = array(
					'{{post_id}}',
					'{{attachment}}',
					'{{post_title}}',
					'{{url}}',
					'{{width}}',
					'{{height}}',
					'{{reduction}}',
					'{{date}}',
					'{{revert_button}}',
					'{{backup_url}}',
					'{{backup_title}}',
					'{{backup_error_style}}'
				);
				
				$datas = array(
					$std_value->id,
					$std_value->url,
					$post->post_title,
					$std_value->url,
					$std_value->width,
					$std_value->height,
					$std_value->reduction/1000,
					date('d-m-Y <br> H:i:s', $std_value->time),
					$revert_button,
					$backup_url,
					$backup_title,
					$backup_error_style
				);
			
				$return_output .= str_replace($short_code, $datas, $content);
			}
		}
		return $return_output;
	}

	static function allowed_mime($filename){
		global $speedycache;
		
		$mimetype = false;

		if(!file_exists($filename)){
			return false;
		}

		if(function_exists('finfo_open')){
		   $finfo = finfo_open(FILEINFO_MIME_TYPE);
		   $mimetype = finfo_file($finfo, $filename);
		   finfo_close($finfo);
		}else if(function_exists('getimagesize')){
		   $img = getimagesize($filename);
		   $mimetype = $img['mime'];
		}else{
			echo 'not found mime_content_type';
			exit;
		}
		
		if(!empty($speedycache->image['settings']['compression_method']) && $speedycache->image['settings']['compression_method'] === 'cwebp' && self::gif2webp_exists()){
			if(preg_match('/(jpg|jpeg|jpe|png|gif)/i', $mimetype)){
				return true;
			}
			
			return false;
		}

		if(preg_match('/(jpg|jpeg|jpe|png)/i', $mimetype)){
			return true;
		}
		
		return false;
	}

	// Check if the image is available, the response code should be 200
	static function not_found($url){
		return false;
		
		$res = wp_remote_head($url, array('timeout' => 3 ));

		if(is_wp_error($res)){
			$url_header = @get_headers($url);

			if(preg_match('/200\s+OK/i', $url_header[0])){
				return false;
			}
			
			return true;
		}
		
		if($res['response']['code'] == 200){
			return false;
		}

		return true;
	}

	//Checks if the image is Unique
	static function unique_array($images){
		if(count($images) <= 1){
			return $images;
		}

		$arr = array();
		$images_tmp = array();

		foreach($images as $key => $value){
			if(!in_array($value['file'], $arr)){
				array_push($arr, $value['file']);
				array_push($images_tmp, $value);
			}
		}
		
		return $images_tmp;
	}
	//Template static functions Starts Here

	//Table row for the converted Image
	static function get_row(){
		return '<tr class="alternate author-self status-inherit" post-id="{{post_id}}">
			<td class="column-icon media-icon image-icon">
				<img width="39" height="60" src="{{attachment}}" class="attachment-80x60">
			</td>
			<td class="title column-title">
				<p style="margin-bottom: 0;"><a href="{{url}}" target="_blank">{{post_title}}</a></p>
				<p style="margin-bottom: 0;">
					<a style="{{backup_error_style}}" href="{{backup_url}}" target="_blank"><strong>{{backup_title}}</strong></a>
				</p>
			</td>
			<td class="author column-author speedycache-open-image-details" style="text-align: center;cursor: pointer;">{{reduction}}KB<span class="dashicons dashicons-arrow-down-alt2"></span></td>
			<td class="date column-date" style="text-align: center;">{{date}}</td>
			<td class="date column-date" style="text-align: center;">
			<div class="speedycache-revert" style="{{revert_button}}">
				<input type="hidden" value="{{post_id}}">
			</div>
			</td>
		</tr>';
	}

	//table row for different sizes of image.
	static function get_child_row(){
		
		return '<tr class="alternate author-self status-inherit" post-id="{{post_id}}" post-type="detail" style="display: none; padding-left: 20px;">
			<td class="column-icon media-icon" style="font-size:3.5em; color:#ccc;"><span class="dashicons dashicons-format-image"></span></td>
			<td class="title column-title">
				<p style="margin-bottom: 0;"><a href="{{url}}" target="_blank">{{post_title}}</a></p>
				<p style="margin-bottom: 0;">
					<a style="{{backup_error_style}}" href="{{backup_url}}" target="_blank"><strong>{{backup_title}}</strong></a>
				</p>
				<p style="margin-bottom: 0;">{{width}}x{{height}}</p>
			</td>
			<td class="author column-author" style="text-align: center;">{{reduction}}KB</td>
			<td class="date column-date" style="text-align: center;">{{date}}</td>
			<td class="date column-date" style="text-align: center;">
			</td>
		</tr>';
	}

	//No Image optimized
	static function get_empty_row(){
		return '<tr class="author-self status-inherit">
			<td colspan="4">No image has been optimized yet</td>
		</tr>';

	}

	//Paging for the converted Images Table
	static function paging(){ ?>
		<div class="tablenav bottom" style="padding:5px 16px 5px 19px">
			<div style="float:left;">
				<span class="deleteicon">
					<input type="text" style="height:28px;" placeholder="Search" id="speedycache-image-search-input" class="deletable" value="">
					<span class="cleared"></span>
				</span>
				<input type="submit" class="button action" value="Search" id="speedycache-image-search-button" name="filter_action">
			</div>
			<div style="float:left;padding-left:5px;">
				<select id="speedycache-image-list-filter" class="bulk-action-selector-top">
					<option value="" selected="selected">All</option>
					<option value='error_code'>Errors</option>
				</select>
			</div>
			<div style="float:left;padding-left:5px;">
				<select id="speedycache-image-per-page" class="bulk-action-selector-top">
					<option value="5" selected="selected">5</option>
					<option value="10">10</option>
					<option value="25">25</option>
				</select>
			</div>
			<div class="tablenav-pages">
				<span class="pagination-links">
					<a class="tablenav-pages-navspan button first-page disabled speedycache-image-list-first-page" aria-hidden="true" data-page-action="first-page">«</a>
					<a class="tablenav-pages-navspan button prev-page speedycache-image-list-prev-page" aria-hidden="true" data-page-action="prev-page">‹</a>
					<a class="paging-input"><label class="speedycache-current-page">1</label> / <span class="total-pages speedycache-total-pages">1</span></a>
					<a class="next-page button speedycache-image-list-next-page" data-page-action="next-page">›</a>
					<a class="last-page button speedycache-image-list-last-page" data-page-action="last-page">»</a>
				</span>
			</div>
			<br class="clear">
		</div>
	<?php
	}

	//Settings for Image Optimization
	static function settings(){
		global $speedycache, $speedycache_optm_method; 

		?>
		<div class="speedycache-block">
			<div class="speedycache-block-title"><h2><?php _e('Settings', 'speedycache'); ?></h2></div>
			<form class="speedycache-img-opt-settings">
				<div class="speedycache-option-wrap">
					<label for="speedycache_img_automatic_optm" class="speedycache-custom-checkbox">
						<input type="checkbox" id="speedycache_img_automatic_optm" name="img_automatic_optm" <?php echo (isset($speedycache->image['settings']['automatic_optm']) && $speedycache->image['settings']['automatic_optm']) ? ' checked="true"' : '';?>/>
						<div class="speedycache-input-slider"></div>
					</label>
					<div class="speedycache-option-info">
						<span class="speedycache-option-name"><?php esc_html_e('Automatic optimization', 'speedycache'); ?></span>
						<span class="speedycache-option-desc"><?php esc_html_e('Whenever user visits the website and there are images that haven\'t been converted to webp then that image(s) gets converted.', 'speedycache'); ?></span>
					</div>
				</div>
				<div class="speedycache-option-wrap">
					<label for="speedycache_img_on_upload" class="speedycache-custom-checkbox">
						<input type="checkbox" id="speedycache_img_on_upload" name="img_on_upload" <?php echo (isset($speedycache->image['settings']['on_upload']) && $speedycache->image['settings']['on_upload']) ? ' checked="true"' : '';?>/>
						<div class="speedycache-input-slider"></div>
					</label>
					<div class="speedycache-option-info">
						<span class="speedycache-option-name"><?php esc_html_e('Optimize on Image Upload', 'speedycache'); ?></span>
						<span class="speedycache-option-desc"><?php esc_html_e('Images will be added to a queue and will optimized after a certain interval to reduce load on the server.', 'speedycache'); ?></span>
					</div>
				</div>
				
				<div class="speedycache-option-wrap">
					<label for="speedycache_img_url_rewrite" class="speedycache-custom-checkbox">
						<input type="checkbox" id="speedycache_img_url_rewrite" name="img_url_rewrite" <?php echo (isset($speedycache->image['settings']['url_rewrite']) && $speedycache->image['settings']['url_rewrite']) ? ' checked="true"' : '';?>/>
						<div class="speedycache-input-slider"></div>
					</label>
					<div class="speedycache-option-info">
						<span class="speedycache-option-name"><?php esc_html_e('Rewrite Image URL\'s', 'speedycache'); ?></span>
						<span class="speedycache-option-desc"><?php esc_html_e('Rewrites the url of the image on the page to the webp version if webp version is avaliable.', 'speedycache'); ?></span>
					</div>
				</div>

				<div class="speedycache-option-wrap">
					<label for="speedycache_img_quality">
						<input type="number" id="speedycache_img_quality" name="img_compression_quality" value="<?php echo esc_attr($speedycache->image['settings']['compression_quality']);?>" min="50" step="5" max="90"/>
					</label>
					<div class="speedycache-option-info">
						<span class="speedycache-option-name"><?php esc_html_e('Compression Quality', 'speedycache'); ?></span>
						<span class="speedycache-option-desc"><?php esc_html_e('Higher the number higher will be the converted image quality in webp', 'speedycache'); ?></span>
					</div>
				</div>
				
				<div class="speedycache-block-title" style="flex-direction:column">
					<h4><?php esc_html_e('Conversion Methods', 'speedycache'); ?></h4>
					<p style="display:block; font-family: monospace; background-color: #f2f2f2; border-radius:6px; padding:10px;">
						<span style="color:var(--speedycache-red); font-weight:bold;"><span class="dashicons dashicons-info-outline"></span> Note: </span>
						<?php 
							esc_html_e('Only cwebp supports gif conversions to webp. But when you upload an image to ' . (defined('SITEPAD') ? 'Sitepad' : 'WordPress') . ' whether it be a jpg, png, or gif format, ' . (defined('SITEPAD') ? 'Sitepad' : 'WordPress') . ' creates different sizes of that image on upload so, in the process of resizing some resized version of gifs may lose their animation.', 'speedycache-pro');
						?>
					</p>
				</div>
				<?php foreach($speedycache_optm_method as $m_key => $method){ ?>
					<div class="speedycache-option-wrap <?php echo !$method['status'] ? ' speedycache-disabled-methods' : '' ?>">
						<label for="speedycache-img-<?php echo esc_attr($m_key);?>">
							<input type="radio" value="<?php echo esc_attr($m_key);?>" name="img_compression_method" id="speedycache-img-<?php echo esc_attr($m_key);?>" <?php echo (isset($speedycache->image['settings']['compression_method']) && $speedycache->image['settings']['compression_method'] == $m_key ? ' checked="true"' : '');?> <?php echo !$method['status'] ? ' disabled' : '' ?>/>
						</label>
						<div class="speedycache-option-info">
							<span class="speedycache-option-name"><?php echo esc_html($method['title']); ?></span>
							<span class="speedycache-option-desc"><?php echo esc_html($method['desc']); ?></span>
						</div>
						<?php 
						// TODO:: Need to fix this _e as it is a dynamic value we will need to use sprintf
						if(!$speedycache_optm_method[$m_key]['status']){ ?>
							<div class="speedycache-more-info" data-info="<?php echo esc_html($speedycache_optm_method[$m_key]['message']);?>" >
								<span class="dashicons dashicons-editor-help"></span>
							</div>
						<?php } ?>
							<div class="speedycache-img-method-actions">
						<?php 
						
						if($m_key == 'cwebp'){
							if(defined('SPEEDYCACHE_PRO') && !file_exists(wp_upload_dir()['basedir']. '/speedycache-binary/'.$speedycache->image['cwebp_binary'])){
								echo '<button style="margin-right:5px;" class="speedycache-button speedycache-btn-black speedycache-webp-download">Download Binary</button>';
							}
							
							if(defined('SPEEDYCACHE_PRO') && !self::gif2webp_exists()){
								echo '<button class="speedycache-button speedycache-btn-black speedycache-webp-download" style="margin-top:2px;" data-type="gif">Download Binary for GIF</button>';
							}
						}
						?>
						</div>
					</div>	
				<?php } ?>
			</form>
		</div>
	<?php 
	}


	//Image Optimization Stats
	static function statics(){
		$res = ['total_image_number' => 0, 'error' => 0, 'optimized' => 0, 'uncompressed' => 0, 'reduction' => 0, 'percent' => 0];
		$ring_success = 100 - (int)$res['percent'];
		
		$scheduled = self::get_optimization_schedule(array('speedycache_auto_optm', 'speedycache_img_delete'));
		$scheduled_count = count($scheduled);
		
		//the css below is written in one line to make regex replace easy in js update.
	?>
		<style>
			@keyframes donut1 {
			0%{stroke-dasharray: 0, 100;}
			100%{stroke-dasharray: <?php echo esc_attr($res['percent'] .', ' . $ring_success);?>;}
		}
		</style>
		<div class="speedycache-block">
			<div class="speedycache-img-stats speedycache-admin-row">
				<div class="speedycache-is-block speedycache-img-graph">
					<div class="speedycache-card-body">
						<div class="speedycache-img-start-optm">
							<div class="speedycache-img-stat-info">
								<div class="speedycache-img-main-stat">
									<?php esc_html_e('Success', 'speedycache'); ?>
									<span class="speedycache-img-success-per"><?php echo esc_html($res['percent']);?>%</span>
								</div>
								<div class="speedycache-img-main-stat">
									<?php esc_html_e('Error', 'speedycache');?>
									<span class="speedycache-img-error-count" <?php echo $res['error'] > 0 ? 'style="color:#dc3545"' : '' ?>><?php echo esc_html($res['error']);?></span>
								</div>
								<div class="speedycache-img-main-stat">
									<?php esc_html_e('Scheduled', 'speedycache');?>
									<span class="speedycache-scheduled-count <?php echo $scheduled_count > 0 ? 'speedycache-scheduled-count-indicator' : '';?>" setting-id="speedycache-img-scheduled-modal"><?php echo esc_html($scheduled_count);?></span>
								</div>
							</div>
							<div class="speedycache-img-remain-optm">
								<span class="speedycache_img_optm_status" style="background-color:<?php echo $res['uncompressed'] > 0 ? '#EED202' : '#90ee90'?>"></span>
								<span>
								<?php 
									if($res['uncompressed'] > 0){
										echo esc_html($res['uncompressed']) . ' ';
										esc_html_e('Files needed to be optimized', 'speedycache');
									} else {
										esc_html_e('All images are optimized', 'speedycache');
									}	
								?></span>
									
							</div>
							<div class="speedycache-img-optimize-all" style="margin-left:auto;">
								<button class="speedycache-button speedycache-btn-black speedycache-img-optm-btn" setting-id="speedycache-modal-optimize-all"><?php esc_html_e('Optimize All', 'speedycache'); ?></button>
								<button class="speedycache-img-delete-all-conv speedycache-button speedycache-btn-transparent"><?php esc_html_e('Delete all conversions', 'speedycache'); ?></button>
							</div>
						</div>
					</div>
				</div>
		
				<div class="speedycache-is-block speedycache-img-count" style="position:relative;">
					<div>
						<div class="speedycache-img-opt-stat">
							<div class="speedycache-img-optm-count"><?php echo esc_html($res['optimized'] . '/' . $res['total_image_number']); ?></div>
							<div class="speedycache-img-optm-count-text"><?php echo esc_html_e('Image(s) optimized', 'speedycache'); ?>
							<?php echo esc_html_e('with total reduction of', 'speedycache'); ?> <span class="speedycache-img-reduced-size"><?php echo esc_html($res['reduction'] > 10000 ? round($res['reduction']/1000, 2).'MB' : $res['reduction'].'KB');?></span></div>
						</div>
					</div>
					<div class="speedycache-img-stat-update-status">Updating Stats <span class="dashicons dashicons-image-rotate"></span></div>
				</div>
			</div>
		</div>
		
		<div modal-id="speedycache-img-scheduled-modal" class="speedycache-modal">
			<div class="speedycache-modal-wrap">
				<div class="speedycache-modal-header">
					<div><?php esc_html_e('Scheduled Tasks', 'speedycache'); ?></div>
					<div title="Close Modal" class="speedycache-close-modal">
						<span class="dashicons dashicons-no"></span>
					</div>
				</div>
				<div class="speedycache-modal-content speedycache-info-modal">
					<?php if($scheduled_count < 1){ ?>
						<span><?php esc_html_e('No Image Scheduled', 'speedycache'); ?></span>
					<?php } else { ?>
					<!--<p style="text-align:center;"><?php //_e('Time Interval of the Schedule is 1 minute', 'speedycache');?></p>-->
					<table style="margin:auto; width: 100%;">
						<thead>
							<tr>
								<th class="speedycache-table-hitem" scope="col"><?php esc_html_e('Image', 'speedycache'); ?></th>
								<th class="speedycache-table-hitem" scope="col"><?php esc_html_e('Time', 'speedycache'); ?></th>
							</tr>
						</thead>
						<tbody>
							<?php
							foreach($scheduled as $key => $schedule){

								$time = $schedule['time'] - time();

								$time_str = 'in '.$time. ' seconds';
								
								if($time > 59){
									$time_str = 'in '. round($time/60, 1) . ' minute(s)';
								} else if($time <= 0){
									$time_str = 'now';
								}
								?>
							<tr>
								<td class="speedycache-table-item"><?php echo esc_html('Batch ' . ($key + 1)); ?></td>
								<td class="speedycache-table-item" style="text-align:right;"><?php echo esc_html($time_str);?> <span class="dashicons dashicons-clock"></span></td>
							</tr>
							<?php } ?>
							
						</tbody>
					</table>
					<?php } ?>
				</div>
			</div>
		</div>
		
		<div modal-id="speedycache-modal-all-img-revert" class="speedycache-modal">
			<div class="speedycache-modal-wrap">
				<div class="speedycache-modal-content">
					<span class="dashicons dashicons-info"></span>
					<h1><?php esc_html_e('Revert All Images!', 'speedycache'); ?></h1>
					<p><?php esc_html_e('Once deleted the changes won\'t be reversible.', 'speedycache'); ?></p>
					<div class="speedycache-modal-db-actions">
						<button class="speedycache-button speedycache-btn-black speedycache-db-confirm-yes"><?php esc_html_e('Yes', 'speedycache'); ?></button>
						<button class="speedycache-button speedycache-db-confirm-no"><?php esc_html_e('No', 'speedycache'); ?></button>
					</div>
				</div>
			</div>
		</div>
	<?php
	}
	//Templates Ends here

	//Need to create logs for Automatic Optimization
	static function log(){
		
	}

	static function compression_method_checks(){
		global $speedycache, $speedycache_optm_method;
		
		$gd = self::gd_check();
		$speedycache_optm_method['gd'] = array_merge($speedycache_optm_method['gd'], $gd);
		
		if(!$speedycache_optm_method['gd']['status']){
			$speedycache->image['disabled_method']++;
			self::change_method_if_not_available('gd');
		}
		
		$imagick = self::imagick_check();
		$speedycache_optm_method['imagick'] = array_merge($speedycache_optm_method['imagick'], $imagick);
		
		if(!$speedycache_optm_method['imagick']['status']){
			$speedycache->image['disabled_method']++;
			self::change_method_if_not_available('imagick');
		}
		
		$cwebp = self::cwebp_check();
		$speedycache_optm_method['cwebp'] = array_merge($speedycache_optm_method['cwebp'], $cwebp);
		
		if(!$speedycache_optm_method['cwebp']['status']){
			$speedycache->image['disabled_method']++;
			self::change_method_if_not_available('cwebp');
		}
	}

	// GD static function Starts here
	static function gd_check(){
		if(!extension_loaded('gd')){
			return array('status' => false, 'message' => 'You dont have GD PHP extension');
		}

		if(isset(gd_info()['WebP Support']) && !gd_info()['WebP Support']){
			return array('status' => false, 'message' => 'GD extension on your server dosen\'t support WEBP');
		}
		
		if(isset(gd_info()['WebP Support']) && gd_info()['WebP Support']){
			return array('status' => true, 'message' => 'Operational');
		}
	}

	static function gd_webp($file){
		global $speedycache, $speedycache_optm_method;

		if(isset($speedycache_optm_method['gd']['status']) && !$speedycache_optm_method['gd']['status']){
			return array('success' => false, 'error_message' => $speedycache_optm_method['gd']['message']);
		}
		
		$image_size = getimagesize($file);
		$width = $image_size[0];
		$height = $image_size[1];
		
		switch($image_size['mime']){
			case 'image/jpg':
			case 'image/jpeg':
				$source = imagecreatefromjpeg($file);
				break;
			
			case 'image/png':
				$source = imagecreatefrompng($file);
				break;
				
			case 'image/webp': 
				return array('success' => false, 'error_message' => 'File is alredy a WEBP file');
				
			default:
				return array('success' => false, 'error_message' => 'File has unsupported mime'.$image_size['mime']);
		}
		
		if(empty($source)){
			return false;
		}
		
		$dst = imagecreatetruecolor($width, $height);
		
		if(imagealphablending($dst, false) !== false){
			//change the RGB values if you need, but leave alpha at 127
			$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);

			if($transparent !== false){
				//simpler than flood fill
				if(imagefilledrectangle($dst, 0, 0, $width, $height, $transparent) !== false){
					//restore default blending
					if(imagealphablending($dst, true) !== false){
						if(imagecopy($dst, $source, 0, 0, 0, 0, $width, $height) !== false){
							$success = true;
						}
					};
				}
			}
		}
		
		if(!$success){
			return array('success' => false, 'error_message' => 'Couldn\'t convert the given image.');
		}
		
		$webp_path = preg_replace('/.(jpe?g|png)$/', '.webp', $file);
		
		imagedestroy($source);
		imagewebp($dst, $webp_path, $speedycache->image['settings']['compression_quality']);
		imagedestroy($dst);
		
		if(!file_exists($webp_path)){
			return array('url' => $webp_path, 'success' => true, 'error_message' => 19);
		}

		return array(
			'url' => $webp_path,
			'success' => true,
			'error_message' => ''
		);
	}
	//GD Ends Here

	//Imagick static functions
	static function imagick_check(){
		
		if(!extension_loaded('imagick')){
			return array('status' => false, 'message' => 'Imagick extension isn\'t installed');
		}
		
		$image = new \Imagick();
		
		if(!in_array('WEBP', $image->queryFormats('WEBP'))){
			$image->clear();
			$image->destroy();
		
			return array('status' => false, 'message' => 'Imagick installed on your system is without WEBP support');
		}
		
		return array('status' => true, 'message' => 'Operational');
	}

	static function change_method_if_not_available($m_name){
		global $speedycache, $speedycache_optm_method;
		
		if($speedycache->image['settings']['compression_method'] != $m_name){
			return;
		}
		
		$valid_method = '';
		
		foreach($speedycache_optm_method as $key => $method){
			if($key == $m_name){
				continue;
			}
			
			if(!empty($method['status'])){
				$speedycache->image['settings']['compression_method'] = $key;
				return;
			}
		}

		$speedycache->image['settings']['compression_method'] = $valid_method;
	}

	//Conversion of image to webp
	static function imagick_webp($file){
		global $speedycache, $speedycache_optm_method;
		
		if(isset($speedycache_optm_method['imagick']['status']) && !$speedycache_optm_method['imagick']['status']){
			return array('success' => false, 'error_message' => $speedycache_optm_method['imagick']['message']);
		}
		
		$dest = preg_replace('/.(png|jpe?g)$/', '.webp', $file);
		
		$image = new \Imagick();
		$image->readImage($file);
		$image->setImageCompressionQuality($speedycache->image['settings']['compression_quality']);
		$image->setImageFormat('WEBP');
		
		if($image->writeImage($dest)){
			$image->clear();
			$image->destroy();
			
			return array('success' => true, 'error_message' => '');
		}
		
		$image->clear();
		$image->destroy();
		
		return array('success' => false, 'error_message' => 'File didn\'t got saved');
	}

	//cwebp static functions
	static function cwebp_check(){
		global $speedycache;
		
		if(!defined('SPEEDYCACHE_PRO')){
			return array('status' => false, 'message' => 'cwebp is a SPEEDYCACHE PRO feature.');
		}
		
		if(!function_exists('exec')){
			return array('status' => false, 'message' => 'You dont have access to exec static function hence you cant use cWebP for Image optimizations');
		}
		
		if(isset($speedycache->image['cwebp_binary'])){
			
			if(!file_exists(wp_upload_dir()['basedir'].'/speedycache-binary/'.$speedycache->image['cwebp_binary'])){
				return array('status' => false, 'message' => 'You don\'t have the cwebp Binary on your server.', 'downloaded' => false);
			} 
		}
		
		return array('status' => true, 'message' => 'Operational');
	}

	static function rewrite_url_to_webp($content){
		if(strpos(speedycache_optserver('HTTP_ACCEPT'), 'image/webp') < 0){
			return $content;
		}
		
		if( ! preg_match_all( '/<img\s[^>]+>/', $content, $matches, PREG_SET_ORDER ) ){
			return $content;
		}
		
		foreach($matches as $match){
			///(?i)(https?:\/\/|www.|\w+\.(png|jpe?g)$)[^\s]+/
			preg_match_all('/https?:\/(\/[^\/]+)+\.(?:jpe?g|png|gif)/', $match[0], $urls, PREG_SET_ORDER); 
			
			foreach($urls as $url){
				$file_url = preg_replace('/.(jpe?g|png|gif)$/', '.webp', $url[0]);
				
				$file_path = explode(SPEEDYCACHE_WP_CONTENT_DIR.'/uploads', $file_url);

				if(!isset($file_path[1]) || !file_exists(wp_upload_dir()['basedir'].$file_path[1])){
					continue;
				}
				
				$content = str_replace($url[0], $file_url, $content);
			}
		
		}
		
		return $content;
	}

	//Adding wp event for automatic optm on image upload
	static function convert_on_upload($id){
		global $speedycache;

		if(empty($speedycache->image['settings']['on_upload'])){
			return;
		}
		
		if(!wp_attachment_is_image($id)){
			return;
		}
		
		$scheduled = self::get_optimization_schedule(array('speedycache_auto_optm'));
		$time = time();
		
		if(!empty($scheduled) && isset(end($scheduled)['time'])){
			$time = end($scheduled)['time'];
		}
		
		// Convert after 5 minutes of upload
		if(!wp_next_scheduled('speedycache_auto_optm', array($id))){
			wp_schedule_single_event($time + 300, 'speedycache_auto_optm', array($id));
			return;
		}
	}

	//Optimizing image on Upload
	static function auto_optimize($img_id){
		self::optimize_single($img_id);
	}

	//Whenever user visits a page and there are images that dont have a webp versions then those images are set to be converted.
	static function optimize_on_fly($content){
		global $wpdb, $speedycache;

		//Sitepad dosen't have this static function.
		if(function_exists('wp_filter_content_tags')){
			$content = wp_filter_content_tags($content);
		}

		if(!preg_match_all('/<img\s[^>]+>|background:\s*url\(["\']?(.*?)["\']?\)/i', $content, $matches, PREG_SET_ORDER)){
			return $content;
		}
		
		$queued_images = 0;
		foreach($matches as $match){
			// If the page has a lot of images, then the process could get slower.
			if($queued_images >= 10){
				break;
			}

			// pl-image{post-id} will work only after Pagelayer 1.9.0
			if(!preg_match('/(?:wp|pl)-image-([\d]+)/i', $match[0], $post_id)){
				/*
				* This is for compatibility with other editors and for inline css background url
				* what we do here is look for either img src url or background:url()
				* Then we attempt to get id from the URL.
				*/
				//<img(?:[^>]|[^\/>]).*src[\s*]?=[\s*]?"(.+?)" just in case the one used causes bugs
				if(strpos($match[0], 'background:url') === FALSE && !preg_match('/<img.*?src="(.*?)"/', $match[0], $url)){
					continue;
				}
				
				if(strpos($match[0], 'background:url') !== FALSE){
					$image_url = sanitize_url($match[1]);
				} else {
					$image_url = sanitize_url($url[1]);
				}

				$post_id = (int) attachment_url_to_postid($image_url);
				
				if(empty($post_id)){
					$file = basename($image_url);
					$like = '%'.$wpdb->esc_like($file) .'%';
					$query = $wpdb->prepare("SELECT `post_id` FROM `".$wpdb->prefix."postmeta` WHERE `meta_value` LIKE %s", $like);
					$post_id = (int) $wpdb->get_var($query);
				}

				if(empty($post_id)){
					continue;
				}
			}
		
			if(empty($post_id)){
				continue;
			}
			
			// If we captured post id through wp-image|pl-image
			if(is_array($post_id) && isset($post_id[1])){
				$post_id = intval($post_id[1]);
			}

			// If the image is already scheduled for optimization then just skip it.
			if(wp_next_scheduled('speedycache_auto_optm', array($post_id))){
				continue;
			}
			
			if(!get_post($post_id)){
				continue;
			}

			$optimized = (int) $wpdb->get_var($wpdb->prepare("SELECT 1 as optimized FROM `".$wpdb->prefix."postmeta` WHERE `meta_key`= 'speedycache_optimisation' AND `post_id`=%d LIMIT 1", $post_id));
			
			if($optimized > 0){
				continue;
			}
			
			$scheduled = self::get_optimization_schedule(array('speedycache_auto_optm'));
			$time = time();
		
			if(!empty($scheduled) && isset(end($scheduled)['time'])){
				// getting the last index to get the last scheduled event
				$time = end($scheduled)['time'];
				$final_schd_time = $time + 30;
			} else {
				$final_schd_time = $time;
			}

			if(!wp_next_scheduled('speedycache_auto_optm', array($post_id))){
				wp_schedule_single_event($final_schd_time, 'speedycache_auto_optm', array($post_id));
				$queued_images++;
				
				continue;
			}
		}

		return $content;
	}

	// Returns an array of cron event "speedycache_auto_optm"
	static function get_optimization_schedule($event_name){
		$cron = _get_cron_array();
		
		if(empty($cron)){
			return false;
		}
		
		$scheduled = array();
		
		foreach($cron as $key => $crn){
			foreach($crn as $e_key => $event){
				if(!in_array($e_key, $event_name)){
					continue;
				}

				foreach($event as $evt){
					$args = $evt['args'][0];
				}
				
				array_push($scheduled, array('name' => get_the_title($args), 'time' => $key));
			}
		}
		
		return $scheduled;
	}

	static function gif2webp_exists(){
		global $speedycache;
		
		if(file_exists(wp_upload_dir()['basedir']. '/speedycache-binary/'.$speedycache->image['cwebp_gif'])){
			return true;
		}
		
		return false;
	}
	
	static function download_cwebp(){
		global $speedycache;

		check_ajax_referer('speedycache_ajax_nonce', 'security');
		
		$binary_name = $speedycache->image['cwebp_binary'];
		
		$type = speedycache_optget('type');
		
		switch($type){
			case 'gif':
				$binary_name = $speedycache->image['cwebp_gif'];
				break;
				
			case 'cwebp':
				$binary_name = $speedycache->image['cwebp_binary'];
				break;
				
			default:
				return;
		}
		
		$binary_path = wp_upload_dir()['basedir'].'/speedycache-binary';
		
		if(!file_exists($binary_path)){
			@mkdir($binary_path);
		}
		
		//If The binary alredy exists on the server
		if(file_exists($binary_path.'/'.$binary_name)){
			wp_send_json(array('success' => false, 'error_message' => 'Binary Exists already.'));
		}
		
		$resp = wp_remote_get(SPEEDYCACHE_API.'/files/cwebp/'.$binary_name);
		$json = wp_remote_get(SPEEDYCACHE_API.'/files/hash.json');
		
		$hash = json_decode($json['body'], true);
		$file = $resp['body'];
		
		if(!$hash){
			wp_send_json(array('success' => false, 'error_message' => 'Unable to verify the downloaded binary.'));
		}
		
		if(!$file){
			wp_send_json(array('success' => false, 'error_message' => 'Could not download the file please try again later.'));
		}
		
		$binary_file = $binary_path.'/'.$binary_name;
		
		file_put_contents($binary_file, $file);
		
		if(!file_exists($binary_file)){
			wp_send_json(array('success' => false, 'error_message' => 'There was issue downloading the binary.'));
		}
		
		if(hash_file('md5', $binary_file) != $hash[$binary_name]){
			@unlink($binary_path.'/'.$binary_name);
			wp_send_json(array('success' => false, 'error_message' => 'Hash of the file downloaded didn\'t match'));
		}

		@chmod($binary_file, 0755);
		
		$output = null;
		$res = null;

		if(!function_exists('exec')){
			wp_send_json(array('success' => false, 'error_message' => 'Your server dosen\'t supports exec'));
		}

		$exec_name = $type === 'gif' ? 'gf2webp' : 'cwebp';

		/*
		* To check if the binary is supported by the server.
		*/
		exec('"'.$binary_file.'"  '.$exec_name.' -version 2>&1', $output, $res);
		
		if($res != 0){
			wp_send_json(array('success' => false, 'error_messsage' => 'The binary for cwebp isn\'t supported by your server'));
		}
		
		if($type = 'gif'){
			$speedycache->image['cwebp_gif'] = $binary_name;
		} else {
			$speedycache->image['cwebp_binary'] = $binary_name;
		}
		
		wp_send_json(array('success' => true));
	}

	static function cwebp_convert($file){
		global $speedycache, $speedycache_optm_method;
		
		if(isset($speedycache_optm_method['cwebp']['status']) && !$speedycache_optm_method['cwebp']['status']){
			return array('success' => false, 'error_message' => $speedycache_optm_method['cwebp']['message']);
		}
			
		$mime = mime_content_type($file);
		
		if($mime == 'image/gif'){
			$binary_path = wp_upload_dir()['basedir']. '/speedycache-binary/'.$speedycache->image['cwebp_gif'];
			$binary_name = 'gif2webp';
		} else {
			$binary_path = wp_upload_dir()['basedir']. '/speedycache-binary/'.$speedycache->image['cwebp_binary'];
			$binary_name = 'cwebp';
		}
		
		$output_file = preg_replace('/.(jpe?g|png|gif)$/', '.webp', $file);
		

		$res = null;
		$output = null;
		
		if(!file_exists($binary_path)){
			return array('success' => false, 'error_message' => 'The binary to use cwebp not found');
		}
		
		/*
		*	-- Use "" around the paths in exec, as without "" can cause a bug on some servers installs. 
		*	-- The Structure of the command below is :- 
		*	"path_of_binary" cwebp -q [quality_in_int] "original_file_path" -o "output_file.webp"
		*	-- 2>&1 is used to redirect stderr to same place as stdout
		*	https://www.brianstorti.com/understanding-shell-script-idiom-redirect/
		*/
		exec('"'.$binary_path.'" '.$binary_name.' -q '.$speedycache->image['settings']['compression_quality'].' "'.$file. '" -o "'. $output_file. '" 2>&1', $output, $res);
		
		if($res != 0){
			return array('success' => false, 'error_message' => 'Couldn\'t convert the given image using cwebp.', 'error_code' => 22);
		}
		
		return array('success' => true, 'error_message' => '', 'dest' => $output_file);
	}
	
	static function list_image_html(){
	?>
	<div id="speedycache-image-list">
		<?php \SpeedyCache\Image::paging(); ?>
		<div style="width:100%; overflow-x:auto;">
			<table class="wp-list-table widefat fixed media" style="width: 95%; margin-left: 20px;">
				<thead>
					<tr style="height: 35px;">
						<th scope="col" id="icon" class="manage-column column-icon" style=""></th>
						<th scope="col" id="title" class="manage-column column-title sortable desc" style="width: 323px;">
							<span style="padding-left: 8px;"><?php esc_html_e('File Name', 'speedycache'); ?></span>
						</th>
						<th scope="col" id="author" class="manage-column column-author sortable desc" style="width: 120px;text-align: center;">
							<span><?php esc_html_e('Reduction', 'speedycache'); ?></span>
						</th>
						<th scope="col" id="date" class="manage-column column-date sortable asc" style="width: 91px;text-align: center;">
							<span><?php esc_html_e('Date', 'speedycache'); ?></span>
						</th>
						<th scope="col" id="date" class="manage-column column-date sortable asc" style="width: 60px;text-align: center;">
							<span><?php esc_html_e('Revert', 'speedycache'); ?></span>
						</th>	
					</tr>
				</thead>
				<tbody id="the-list">
					<?php 
						$query_images_args = array();

						$query_images_args['order'] = 'DESC';
						$query_images_args['orderby'] = 'ID';
						$query_images_args['post_type'] = 'attachment';
						$query_images_args['post_mime_type'] = array('image/jpeg', 'image/png', 'image/gif');
						$query_images_args['post_status'] = 'inherit';
						$query_images_args['posts_per_page'] = 5;
						$query_images_args['meta_query'] = array(
							array(
								'key' => 'speedycache_optimisation',
								'compare' => 'EXISTS'
							)
						);

						echo \SpeedyCache\Image::list_content($query_images_args);
					?>
				</tbody>
			</table>
		</div>
	</div>
	<?php 
	}

}

