<?php

if (! defined('ABSPATH')) {
    exit; // Exit if accessed directly.
}


use \Resmush\ShortPixelLogger\ShortPixelLogger as Log;

 /**
   * ReSmushit
   *
   *
   * @package    Resmush.it
   * @subpackage Controller
   * @author     Charles Bourgeaux <contact@resmush.it>
   */
Class reSmushit {

	const MAX_FILESIZE = 5242880;
	const MAX_ATTACHMENTS_REQ = 1000;

	/**
	 *
	 * returns the list of supported extensions by the API
	 *
	 * @return array 	List of extensions
	 */
	public static function authorizedExtensions() {
		return array('jpg', 'jpeg', 'gif', 'png');
	}


	/**
	 *
	 * Optimize an image according to a filepath.
	 *
	 * @param  string $file_path the path to the file on the server
	 * @return bool 	TRUE if the resmush operation worked
	 */
	public static function getPictureQualitySetting() {
		if(get_option( 'resmushit_qlty' )) {
          return (int) apply_filters('resmushit_image_quality', get_option( 'resmushit_qlty'));
		} else {
			if (!defined('RESMUSHIT_QLTY')) {
			  return (int) RESMUSHIT_DEFAULT_QLTY;
			}
			return (int) RESMUSHIT_QLTY;
		}
	}

	/**
	 *
	 * Optimize an image according to a filepath.
	 *
	 * @param  string $file_path the path to the file on the server
	 * @param  boolean The is_original param doesn't make any sense, since it's always true. Also before it was hooked to regenerate attachment metadata, hoping it wouldnt send a second param. This whole thing can probl. go
	 *
	 * @return bool 	TRUE if the resmush operation worked
	 */
	public static function optimize($file_path = NULL, $is_original = TRUE) {
		global $wp_version;
		if(!file_exists($file_path) OR !is_file($file_path)) {
			Log::addError('Error! Picture ' . str_replace(ABSPATH, '/', $file_path) . ' cannot be optimized, file is not found on disk.');
			return false;
		}
		if(filesize($file_path) > self::MAX_FILESIZE){
			Log::addError('Error! Picture ' . str_replace(ABSPATH, '/', $file_path) . ' cannot be optimized, file size is above 5MB ('. reSmushitUI::sizeFormat(filesize($file_path)) .')');
			return false;
		}
		if(! in_array('curl', get_loaded_extensions())){
			return false;
		}

		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, RESMUSHIT_ENDPOINT);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, RESMUSHIT_TIMEOUT);
		curl_setopt($ch, CURLOPT_POST, true);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
		curl_setopt($ch, CURLOPT_USERAGENT, "Wordpress $wp_version/Resmush.it " . RESMUSHIT_VERSION . ' - ' . get_bloginfo('wpurl') );

		if (!class_exists('CURLFile')) {
			$arg = array('files' => '@' . $file_path);
		} else {
			$cfile = new CURLFile($file_path);
			$arg = array(
			  'files' => $cfile,
			);
		}
		if(get_option( 'resmushit_preserve_exif' ) && get_option( 'resmushit_preserve_exif' ) == 1) {
			$arg['exif'] = 'true';
		}

		$arg['qlty'] = self::getPictureQualitySetting();
		curl_setopt($ch, CURLOPT_POSTFIELDS, $arg);

		$data = curl_exec($ch);
		curl_close($ch);

		$json = json_decode($data);
		if($json){
			if (!isset($json->error)) {
				if (ini_get('allow_url_fopen')) {
					$arrContextOptions= array("ssl" => array("verify_peer" => false,"verify_peer_name" => false));
					$data = file_get_contents( $json->dest, false, stream_context_create($arrContextOptions) );
				} else {
					$ch = curl_init();
					curl_setopt($ch, CURLOPT_URL, $json->dest);
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
					curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
					$data = curl_exec($ch);
					curl_close($ch);
				}
				if ($data) {
					if($is_original && get_option('resmushit_remove_unsmushed') == 0){
						$originalFile = pathinfo($file_path);
						$newPath = $originalFile['dirname'] . '/' . $originalFile['filename'] . '-unsmushed.' . $originalFile['extension'];
			 			copy($file_path, $newPath);
			 		}
				  	file_put_contents($file_path, $data);
					Log::addDebug("Optimized file " . str_replace(ABSPATH, '/', $file_path) . " from " . reSmushitUI::sizeFormat($json->src_size) . " to " . reSmushitUI::sizeFormat($json->dest_size));
				  	return $json;
				}
			} else {
				Log::addError("Webservice returned the following error while optimizing $file_path : Code #" . $json->error . " - " . $json->error_long);
			}
		} else {
			Log::addError("Cannot establish connection with reSmush.it webservice while optimizing $file_path (timeout of " . RESMUSHIT_TIMEOUT . "sec.)");
		}
		return false;
	}




	/**
	 *
	 * Revert original file and regenerates attachment thumbnails
	 *
	 * @param  int 		$attachment_id 	ID of the attachment to revert
	 * @return none
	 */
	public static function revert($id, $generateThumbnails = true) {
		global $wp_version;
		global $attachment_id;
		$attachment_id = $id;

		delete_post_meta($attachment_id, 'resmushed_quality');
		delete_post_meta($attachment_id, 'resmushed_cumulated_original_sizes');
		delete_post_meta($attachment_id, 'resmushed_cumulated_optimized_sizes');

    $fs = Resmush()->fs();


		$basepath = dirname(get_attached_file( $attachment_id )) . '/';
		$fileInfo = pathinfo(get_attached_file( $attachment_id ));
		$backupFile = $basepath . $fileInfo['filename'] . '-unsmushed.' . $fileInfo['extension'];


    $backupFileObj = $fs->getFile($backupFile);
    $targetFileObj = $fs->getFile(get_attached_file( $attachment_id ));
		Log::addDebug('Revert original image for : ' . str_replace(ABSPATH, '/', get_attached_file( $attachment_id )));

		if($backupFileObj->exists()) {
      Log::addTemp('moving ' . $backupFileObj->getFullPath() . ' to ' . $targetFileObj->getFullPath() );
			 $res = $backupFileObj->move($targetFileObj);
		}
    else {
      Log::addWarn('BackupFile not existing', $backupFileObj->getFullPath());
    }

		//Regenerate thumbnails
		if($generateThumbnails) {
			wp_generate_attachment_metadata($attachment_id, get_attached_file( $attachment_id ));
		}

		return self::wasSuccessfullyUpdated( $attachment_id );
	}



	/**
	 *
	 * Delete Original file (-unsmushed)
	 *
	 * @param  int 		$attachment_id 	ID of the attachment
	 * @return none
	 */
	public static function deleteOriginalFile($attachment_id) {
		$basepath = dirname(get_attached_file( $attachment_id )) . '/';
		$fileInfo = pathinfo(get_attached_file( $attachment_id ));

		$originalFile = $basepath . $fileInfo['filename'] . '-unsmushed.' . $fileInfo['extension'];
		Log::addDebug('Delete original image for : ' . get_attached_file( $attachment_id ));
		if(file_exists($originalFile))
			unlink($originalFile);
	}

    /**
     * Checking   that we have  backup
     *
     * @param int $attachment_id ID of the attachment
     * @return bool TRUE if there is the original file, FALSE otherwise
     */
    public static function hasBackup($attachment_id) {
        $basepath = dirname(get_attached_file($attachment_id)) . '/';
        $fileInfo = pathinfo(get_attached_file($attachment_id));
        $originalFile = $basepath . $fileInfo['filename'] . '-unsmushed.' . $fileInfo['extension'];
        return file_exists($originalFile);
    }

	/**
      *
      * Detect if optimization process was already launched one time
      *
      * @return boolean
      */
	public static function hasAlreadyRunOnce(){
		global $wpdb;
		$query = $wpdb->prepare(
			"select
				count($wpdb->posts.ID) as count
				from $wpdb->posts
				inner join $wpdb->postmeta on $wpdb->posts.ID = $wpdb->postmeta.post_id and $wpdb->postmeta.meta_key = %s $extraSQL
				limit 1",
				array('resmushed_cumulated_original_sizes')
		);
		return (boolean)$wpdb->get_var($query);
	}
	/**
      *
      * Return optimization statistics
      *
      * @param int 	$attachment_id (optional)
      * @return array of statistics
      */
	public static function getStatistics($attachment_id = null){
		global $wpdb;
		$output = array();
		$extraSQL = null;
		if($attachment_id)
			$extraSQL = "where $wpdb->postmeta.post_id = ". (int)($attachment_id);

		$query = $wpdb->prepare(
			"select
				$wpdb->posts.ID as ID, $wpdb->postmeta.meta_value
				from $wpdb->posts
				inner join $wpdb->postmeta on $wpdb->posts.ID = $wpdb->postmeta.post_id and $wpdb->postmeta.meta_key = %s $extraSQL",
				array('resmushed_cumulated_original_sizes')
		);
		$original_sizes = $wpdb->get_results($query);
		$total_original_size = 0;
		foreach($original_sizes as $s){
			$total_original_size += $s->meta_value;
		}

		$query = $wpdb->prepare(
			"select
				$wpdb->posts.ID as ID, $wpdb->postmeta.meta_value
				from $wpdb->posts
				inner join $wpdb->postmeta on $wpdb->posts.ID = $wpdb->postmeta.post_id and $wpdb->postmeta.meta_key = %s $extraSQL",
				array('resmushed_cumulated_optimized_sizes')
		);
		$optimized_sizes = $wpdb->get_results($query);
		$total_optimized_size = 0;
		foreach($optimized_sizes as $s){
			$total_optimized_size += $s->meta_value;
		}


		$output['total_original_size'] 			= $total_original_size;
		$output['total_optimized_size'] 		= $total_optimized_size;
		$output['total_saved_size'] 			= $total_original_size - $total_optimized_size;

		$output['total_original_size_nice'] 	= reSmushitUI::sizeFormat($total_original_size);
		$output['total_optimized_size_nice'] 	= reSmushitUI::sizeFormat($total_optimized_size);
		$output['total_saved_size_nice'] 		= reSmushitUI::sizeFormat($total_original_size - $total_optimized_size);
		if($total_original_size == 0)
			$output['percent_reduction'] 		= 0;
		else
			$output['percent_reduction'] 		= 100*round(($total_original_size - $total_optimized_size)/$total_original_size,4) . ' %';
		//number of thumbnails + original image
		$output['files_optimized'] 				= sizeof($optimized_sizes);
		$output['files_optimized_with_thumbnails'] = sizeof($optimized_sizes) * (sizeof(get_intermediate_image_sizes()) + 1);

		if(!$attachment_id){
			$output['total_optimizations'] 		= get_option('resmushit_total_optimized');
			$output['total_pictures'] 			= self::getCountAllPictures();
			$output['total_pictures_with_thumbnails'] = self::getCountAllPictures() * (sizeof(get_intermediate_image_sizes()) + 1);
		}
		return $output;
	}



	/**
      *
      * Get the count of all images
      *
      * @param none
      * @return json of unsmushed image attachment IDs
      */
	public static function getCountAllPictures(){
		global $wpdb;

		$queryAllPictures = $wpdb->prepare(
			"select
				Count($wpdb->posts.ID) as count
				from $wpdb->posts
				inner join $wpdb->postmeta on $wpdb->posts.ID = $wpdb->postmeta.post_id and $wpdb->postmeta.meta_key = %s
				where $wpdb->posts.post_type = %s
				and $wpdb->posts.post_mime_type like %s
				and ($wpdb->posts.post_mime_type = 'image/jpeg' OR $wpdb->posts.post_mime_type = 'image/gif' OR $wpdb->posts.post_mime_type = 'image/png')",
				array('_wp_attachment_metadata','attachment', 'image%')
			);
		$data = $wpdb->get_results($queryAllPictures);
		if(isset($data[0]))
			$data = $data[0];

		if(!isset($data->count))
			return 0;
		return $data->count;
	}



	/**
      *
      * Get a list of unoptimized images
      *
      * @param none
      * @return json of unsmushed image attachment IDs
      */
	public static function getNonOptimizedPictures($id_only = FALSE){
		global $wpdb;
		$tmp = array();
		$unsmushed_images = array();
		$files_too_big = array();
		$already_optimized_images_array = array();
		$disabled_images_array = array();
		$files_not_found = array();
		$extra_select = "";
		if($id_only == FALSE) {
			$extra_select = ",POSTS.guid as guid, METAATTACH.meta_value as file_meta";
		}

		$queryUnoptimizedPicture = $wpdb->prepare(
			"SELECT ATTACHMENTS.* FROM (
				select
					POSTS.ID as ID, METAQLTY.meta_value as qlty, METADISABLED.meta_value as disabled
					$extra_select
				from $wpdb->posts as POSTS
				inner join
					$wpdb->postmeta as METAATTACH on POSTS.ID = METAATTACH.post_id
					and METAATTACH.meta_key = %s
				left join
					$wpdb->postmeta as METAQLTY on POSTS.ID = METAQLTY.post_id
					and METAQLTY.meta_key = %s
				left join
					$wpdb->postmeta as METADISABLED on POSTS.ID = METADISABLED.post_id
					and METADISABLED.meta_key = %s
				where
					POSTS.post_type = %s
					and (POSTS.post_mime_type = 'image/jpeg' OR POSTS.post_mime_type = 'image/gif' OR POSTS.post_mime_type = 'image/png')
        ORDER BY POSTS.post_date desc

				) as ATTACHMENTS
				WHERE
					( ATTACHMENTS.qlty IS NULL)
					AND ATTACHMENTS.disabled IS NULL
				LIMIT %d",
    		array('_wp_attachment_metadata','resmushed_quality','resmushed_disabled','attachment', self::MAX_ATTACHMENTS_REQ)
		);
		// Get the images in the attachement table
		//
		Log::addTemp('UnOptimizedPictures Query' . $queryUnoptimizedPicture, debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5));

		$all_images = $wpdb->get_results($queryUnoptimizedPicture);

		foreach($all_images as $image){
			$tmp = array();
			$tmp['ID'] = $image->ID;
			$tmp['attachment_metadata'] = isset($image->file_meta) ? unserialize($image->file_meta) : array();

			if( !file_exists(get_attached_file( $image->ID )) ) {
				$files_not_found[] = $tmp;
				continue;
			}
			//If filesize > 5MB, we do not optimize this image
			if( filesize(get_attached_file( $image->ID )) > self::MAX_FILESIZE ){
				$files_too_big[] = $tmp;
				continue;
			}

			$unsmushed_images[] = $tmp;
		}
		return json_encode(array('nonoptimized' => $unsmushed_images, 'filestoobig' => $files_too_big, 'filesnotfound' => $files_not_found, 'totalresult' => count($all_images) ));
	}


	/**
      *
      * Return the number of unoptimized images
      *
      * @param none
      * @return number of unoptimized images to the current quality factor
      */
	public static function getCountNonOptimizedPictures(){
		$data = json_decode(self::getNonOptimizedPictures());
		$output = array();
		$output['nonoptimized'] = is_array($data->nonoptimized) ? sizeof($data->nonoptimized) : 0;
		$output['filesnotfound'] = is_array($data->filesnotfound) ? sizeof($data->filesnotfound) : 0;
		$output['filestoobig'] = is_array($data->filestoobig) ? sizeof($data->filestoobig) : 0;
    $output['totalresult'] = property_exists($data, 'totalresult') ?  : 0;
		return $output;
	}


	/**
      *
      * Record in DB new status for optimization disabled state
      *
      * @param int 		$id 	ID of postID
      * @param string 	$state 	state of disable
      * @return none
      */
	public static function updateDisabledState($id, $state){
		//if we do not want this attachment to be resmushed.
		if($state == "true"){
			update_post_meta($id, 'resmushed_disabled', 'disabled');
			return 'true';
		} else {
			delete_post_meta($id, 'resmushed_disabled');
			return 'false';
		}
	}



	/**
      *
      * Get Disabled State
      *
      * @param int 		$attachment_id 	Post ID
      * @return boolean true if attachment is disabled
      */
	public static function getDisabledState($attachment_id){
		if(get_post_meta($attachment_id, 'resmushed_disabled'))
			return true;
		return false;
	}

	/* Of course stupid function.  In due time all this doing should be replaced with an Image class, where all this informatie is normally bundled */
	public static function isImageOptimized($attachment_id)
	{
		  $optimized = get_post_meta($attachment_id, 'resmushed_cumulated_optimized_sizes', true);
			if ($optimized !== false && is_numeric($optimized))
			{
				 return true;
			}

			return false;
	}

	/**
	*
	* Detect unsmushed files by browsing the library directory
	*
	* @param none
	* @return none
	*/
	public static function detect_unsmushed_files() {
		$wp_upload_dir=wp_upload_dir();
		return self::glob_recursive($wp_upload_dir['basedir'] . '/*-unsmushed.*');
	}


	/**
	*
	* Find recursively files based on pattern
	*
	* @param string $pattern file search
	* @param boolean $flags
	* @return array
	* @author Mike
	* @link https://www.php.net/manual/en/function.glob.php#106595
	*/
	protected static function glob_recursive($pattern, $flags = 0) {
	    $files = glob($pattern, $flags);

	    foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir)
	    {
	        $files = array_merge($files, self::glob_recursive($dir.'/'.basename($pattern), $flags));
	    }

	    return $files;
	}



	/**
	*
	* retrieve Attachment ID from Path
	* from : https://pippinsplugins.com/retrieve-attachment-id-from-image-url/
	*
	* @param imageURL
	* @return json object
	*/
	public static function resmushit_get_image_id($image_url) {
		global $wpdb;
		$attachment = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE guid='%s';", $image_url ));
Log::addTemp('Attachment', $attachment);
		if (! isset($attachment[0]))
		{

			 return false;
		}
	  return $attachment[0];
	}


	/**
      *
      * Get Last Quality Factor attached to an image
      *
      * @param int 		$attachment_id 	Post ID
      * @return int 	quality setting for this attachment
      */
	public static function getAttachmentQuality($attachment_id){
		$attachmentQuality = get_post_meta($attachment_id, 'resmushed_quality');
		if(isset($attachmentQuality[0]))
			return $attachmentQuality[0];
		return null;
	}



	/**
      *
      * Check if this Attachment was successfully optimized
      *
      * @param int 		$attachment_id 	Post ID
      * @return string	$status
      */
	public static function wasSuccessfullyUpdated($attachment_id){
		if( self::getDisabledState( $attachment_id ))
			return 'disabled';
		if (!file_exists(get_attached_file( $attachment_id ))) {
			Log::addError("Error! File " . get_attached_file( $attachment_id ) . " not found on disk.");
			return 'file_not_found';
		}
		if( filesize(get_attached_file( $attachment_id )) > self::MAX_FILESIZE){
			Log::addDebug('File too big' . $attachment_id);
			return 'file_too_big';
		}

		if( self::getPictureQualitySetting() != self::getAttachmentQuality( $attachment_id ))
		{
			Log::addDebug('Quality setting failed');
			return 'failed';
		}
		return 'success';
	}
}
