<?php
namespace SPAATG\Model\Image;


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

use SPAATG\ShortPixelLogger\ShortPixelLogger as Log;

use SPAATG\Controller\ResponseController as ResponseController;
use SPAATG\Controller\Api\ApiController as ApiController;

use SPAATG\Model\File\FileModel as FileModel;
use SPAATG\Model\AccessModel as AccessModel;
use SPAATG\Helper\UtilHelper as UtilHelper;


use SPAATG\Model\Converter\Converter as Converter;

/* ImageModel class.
*
*
* - Represents a -single- image entity *not file*.
* - Can be either MediaLibrary, or Custom .
* - Not a replacement of Meta, but might be.
* - Goal: Structural ONE method calls of image related information, and combining information. Same task is now done on many places.
* -- ShortPixel Class should be able to blindly call model for information, correct metadata and such.
*/

abstract class ImageModel extends \SPAATG\Model\File\FileModel
{
    // File Status Constants
    const FILE_STATUS_ERROR = -1;
    const FILE_STATUS_UNPROCESSED = 0;
    const FILE_STATUS_PENDING = 1;
    const FILE_STATUS_SUCCESS = 2;
    const FILE_STATUS_RESTORED = 3;
    const FILE_STATUS_TORESTORE = 4; // Used for Bulk Restore

    const FILE_STATUS_PREVENT = -10;
    const FILE_STATUS_MARKED_DONE = -11;
    const FILE_STATUS_BAD_METADATA = -12;

    // Compression Option Consts - must be replicated in screen-base.js
    const COMPRESSION_LOSSLESS = 0;
    const COMPRESSION_LOSSY = 1;
    const COMPRESSION_GLOSSY = 2;

		const ACTION_SMARTCROP = 100;
		const ACTION_SMARTCROPLESS = 101;

    // Extension that we process . Minus the one that one MediaLibraryModel should handle, so it doesn't touch the thumbns.
    const PROCESSABLE_EXTENSIONS = array('jpg', 'jpeg', 'gif', 'png', 'pdf', 'webp');

    //
    const P_PROCESSABLE = 0;
    const P_FILE_NOT_EXIST  = 1;
    const P_EXCLUDE_EXTENSION = 2;
    const P_EXCLUDE_SIZE  = 3;
	    const P_EXCLUDE_PATH  = 4;
	    const P_IS_OPTIMIZED = 5;
	    const P_FILE_NOTWRITABLE = 6;
			const P_OPTIMIZE_PREVENTED = 9;
			const P_DIRECTORY_NOTWRITABLE = 10;
    const P_EXCLUDE_EXTENSION_PDF = 11;
    const P_IMAGE_ZERO_SIZE = 12;
    const P_EXCLUDE_DATE = 13;
    const P_EXCLUDE_FILESIZE = 14;

			// For restorable status
			const P_RESTORABLE = 109;
			const P_NOT_OPTIMIZED = 111;

		const IMAGE_TYPE_MAIN = 0;
		const IMAGE_TYPE_THUMB = 1;
		const IMAGE_TYPE_ORIGINAL = 2;
		const IMAGE_TYPE_RETINA = 3;
		const IMAGE_TYPE_DUPLICATE = 4;

		const FILETYPE_BIGGER = -10;

    protected $image_meta; // metadata Object of the image.
		protected $recordChanged = false;

    // ImageModel properties are not stored but is generated data.  Only storage should happen to the values in Meta.
		/** @var string */
    protected $width;

		/** @var string */
    protected $height;

		/** @var string */
    protected $mime;
   // protected $url; // possibly not in use.

	  /** @var string */
    protected $error_message;

		/** @var int */
    protected $id;

		/** @var string */
		protected $imageType;

		/** @var int */
    protected $processable_status = null;

		/** @var int */
		protected $restorable_status = null;

    /** @var string */
	protected $optimizePreventedReason;

		// Public var that can be set by QueueController to prevent double queries.
		/** @var boolean */
		public $is_in_queue;

    abstract public function getOptimizeUrls();
    abstract protected function saveMeta();
    abstract protected function loadMeta();

    abstract protected function getImprovements();
    abstract protected function getExcludePatterns(); // get the Exclude Pattern(s) for -this- image to compare.

   // abstract protected function getOptimizeFileType();

    // Function to prevent image from doing anything automatically - after fatal error.
    abstract protected function preventNextTry($reason = '');
    abstract public function isOptimizePrevented();
    abstract public function resetPrevent(); // to get going.
    abstract public function getParent();

    // Construct
    public function __construct($path)
    {
      parent::__construct($path);
    }

    /* Function to run on load-time ( loadMeta ) to check certain values and make sure all data is loaded properly and in case of missing data, to supplant that */
    protected function verifyImage()
    {

      // Only get data from Image if not yet set in metadata.
      if (is_null($this->getMeta('originalWidth')))
        $this->setMeta('originalWidth', $this->get('width'));

      if (is_null($this->getMeta('originalHeight')))
        $this->setMeta('originalHeight', $this->get('height'));

      if (is_null($this->getMeta('tsAdded')))
        $this->setMeta('tsAdded', time());

      $this->setWebp();
      $this->setAvif();

    }

    protected function setImageSize()
    {
      // to prevent is_null check on get to loop if something is off.
      if (is_null($this->width))
      {
        $this->width = false;
      }
      if (is_null($this->height))
      {
        $this->height = false;
      }

      if (! $this->isExtensionExcluded() && $this->isImage() && $this->is_readable() && ! $this->is_virtual() )
      {
         list($width, $height) = @getimagesize($this->getFullPath());
         if ($width)
         {
          $this->width = $width;
         }
         if ($height)
         {
          $this->height = $height;
         }
      }


    }
    /* Check if an image in theory could be processed. Check only exclusions, don't check status etc */
    public function isProcessable()
    {
        // isprocessable runs zillion times, so take the edge off a little.
        if (! is_null($this->processable_status))
        {
            if (self::P_PROCESSABLE === $this->processable_status)
            {
               return true;
            }
            else {
                return false;
            }
        }

        if ( $this->isOptimized() || ! $this->exists()  || (! $this->is_virtual() && ! $this->is_writable()) ||
        (! $this->is_virtual() && ! $this->is_directory_writable() ||
        $this->isPathExcluded() ||
        $this->isExtensionExcluded() ||
        $this->isSizeExcluded() ||
        $this->isFileSizeExcluded()
        )
				|| $this->isOptimizePrevented() !== false
        || ! $this->isFileSizeOK() )
        {
          if(! $this->is_writable() && $this->processable_status == 0)
					{
            $this->processable_status = self::P_FILE_NOTWRITABLE;
					}
					elseif(! $this->is_directory_writable() && $this->processable_status == 0)
					{
            $this->processable_status = self::P_DIRECTORY_NOTWRITABLE;
					}

          return false;
        }
        else
				{
					$this->processable_status = self::P_PROCESSABLE;
          return true;
				}

    }

    public function isProcessableFileType($type = '')
    {
        return false;
    }

		public function isProcessableAnyFileType()
		{
			return false;
		}

    // Function to check if the reason it won't process is because user did some setting
    public function isUserExcluded()
    {
      if (is_null($this->processable_status))
      {
         $this->isProcessable();
      }

        $reasons = array(
            self::P_EXCLUDE_PATH,
            self::P_EXCLUDE_SIZE,
            self::P_EXCLUDE_FILESIZE,
        );

        if (in_array($this->processable_status, $reasons))
        {
           return true;
        }
        return false;
    }

    public function cancelUserExclusions()
    {
       if ($this->isUserExcluded())
       {
          $this->processable_status = 0;
       }
    }

    public function exists($forceCheck = false)
    {
       $result = parent::exists($forceCheck);
       if ($result === false)
       {
          $this->processable_status = self::P_FILE_NOT_EXIST;
       }
       return $result;
    }

		/** In time this should replace the other. This one added for semantic reasons. */
		public function getReason($name = 'processable')
		{
				$status = null;

			 if ($name == 'processable')
				$status = $this->processable_status;
			 elseif($name == 'restorable')
				$status = $this->restorable_status;

			 return $this->getProcessableReason($status);
		}

    public function getProcessableReason($status = null)
    {
      $message = false;
			$status = (! is_null($status)) ? $status : $this->processable_status;

      switch($status)
      {
         case self::P_PROCESSABLE:
            $message = __('Image Processable', 'shortpixel-ai-alt-text-generator');
         break;
         case self::P_FILE_NOT_EXIST:
            $message = __('File does not exist', 'shortpixel-ai-alt-text-generator');
         break;
         case self::P_EXCLUDE_EXTENSION:
            $message = __('Image Extension not processable', 'shortpixel-ai-alt-text-generator');
         break;
         case self::P_EXCLUDE_EXTENSION_PDF:
            /* translators: 1, 2: settings link tags. */
            $message = sprintf(__('PDF processing is not enabled in the %1$ssettings%2$s', 'shortpixel-ai-alt-text-generator'), '<a href="' .  esc_url(admin_url('options-general.php?page=spaatg-settings&part=optimisation')) . '">', '</a>');
         break;
         case self::P_EXCLUDE_SIZE:
            $message = __('Image Size Excluded', 'shortpixel-ai-alt-text-generator');
         break;
         case self::P_EXCLUDE_FILESIZE:
            $message = __('Image Filesize excluded', 'shortpixel-ai-alt-text-generator');
          break;
         case self::P_EXCLUDE_PATH:
            $message = __('Image Excluded', 'shortpixel-ai-alt-text-generator');
         break;
         case self::P_IS_OPTIMIZED:
            $message = __('Image is already optimized', 'shortpixel-ai-alt-text-generator');
         break;
         case self::P_FILE_NOTWRITABLE:
            /* translators: 1: image file name, 2: image file directory. */
            $message = sprintf(__('Image %1$s (or related thumbnails) is not writable in %2$s', 'shortpixel-ai-alt-text-generator'), $this->getFileName(), (string) $this->getFileDir());
         break;
				 case self::P_DIRECTORY_NOTWRITABLE:
						/* translators: %s: image directory. */
						$message = sprintf(__('Image directory %s is not writable', 'shortpixel-ai-alt-text-generator'), (string) $this->getFileDir());
				 break;
					 case self::P_OPTIMIZE_PREVENTED:
						$message = __('Fatal error preventing processing', 'shortpixel-ai-alt-text-generator');
						if (property_exists($this, 'optimizePreventedReason'))
						$message = $this->get('optimizePreventedReason');
				 break;
				 // Restorable Reasons
				 case self::P_RESTORABLE:
						$message = __('Image restorable', 'shortpixel-ai-alt-text-generator');
				 break;
					 case self::P_NOT_OPTIMIZED:
						$message = __('Image is not optimized', 'shortpixel-ai-alt-text-generator');
				 break;
         case self::P_IMAGE_ZERO_SIZE:
            $message = __('File seems empty, or failure on image size', 'shortpixel-ai-alt-text-generator');
         break;
         case self::P_EXCLUDE_DATE:
             $message = __('Date is excluded', 'shortpixel-ai-alt-text-generator');
          break;
         default:
            /* translators: %s: processable status code. */
            $message = sprintf(__('Unknown Issue, Code %s', 'shortpixel-ai-alt-text-generator'), $this->processable_status);
         break;
      }

      return $message;
    }



    public function isImage()
    {
        if (! $this->exists())
        {
          return false;
        }
        if ($this->is_virtual()) // if virtual, don't filecheck on image.
        {
            if (! $this->isExtensionExcluded() )
              return true;
            else
              return false;
        }


				if (is_null($this->mime) && \wpSPAATG()->env()->is_function_usable('finfo_open')) // Faster function for getting mime types
					 {
						 $fileinfo = finfo_open(FILEINFO_MIME_TYPE);
						 $this->mime = finfo_file($fileinfo, $this->getFullPath());
             // Deprecated from version 8.5
             if (false === \wpSPAATG()->env()->checkPHPversion('8.5') )
             {
						  finfo_close($fileinfo);
             }
						 //FILEINFO_MIME_TYPE
					}
					elseif(is_null($this->mime) && \wpSPAATG()->env()->is_function_usable('mime_content_type')) {
						$this->mime = mime_content_type($this->getFullPath());
					}
					elseif(is_null($this->mime)) {
						return true; // assume without check, that extension says what it is.
						// @todo This should probably trigger a notice in adminNoticesController.
					}

	        if (strpos($this->mime, 'image') !== false)
	           return true;
	        else
	          return false;

    }

    public function get($name)
    {
       if (property_exists($this, $name))
       {
          if ( ($name == 'width' || $name == 'height') && is_null($this->$name))  // dynamically load this.
          {
            $this->setImageSize();
          }

        return $this->$name;
       }

       return null;
    }


    public function __get($name)
    {
        return $this->get($name);
    }

    public function getMeta($name = false)
    {
      if ($name === false)
      {
        return $this->image_meta;
      }

      if (! property_exists($this->image_meta, $name))
      {
          Log::addWarn('GetMeta on Undefined Property : ' . $name);
          return null;
      }

      return $this->image_meta->$name;
    }

		/* Get counts of what needs to be optimized still
		* @param String What to count: thumbnails, webp, avif.
		*/
		public function getCountOptimizeData($param = 'thumbnails')
		{
				$optimizeData = $this->getOptimizeData();

				if (! isset($optimizeData['params']) || ! isset($optimizeData['urls']))
				{
					array([], 0);
				}

				$count = 0;
				$urls = [];

				$params = $optimizeData['params'];

				if ($param == 'thumbnails')
					$param = 'image';

				// Take the optimizeData and take key - param column, then check if the param (image/webp/avif) is true (filter) .
				$combinedArray = array_filter(array_combine(array_keys($params), array_column($params, $param)));

				$count = count($combinedArray);
				foreach($combinedArray as $sizeName => $unneeded)
				{
					 $urls[] = $optimizeData['paths'][$sizeName];
				}
				return array($urls, $count);

		}

	  protected function getImageType($type = 'webp')
	  {
	    $fs = \wpSPAATG()->filesystem();
			if ($this->getMeta($type) === self::FILETYPE_BIGGER)
				return false;

	    if (! is_null($this->getMeta($type)))
	    {
				// Filter to disable assumption(s) on the file basis of imageType.  Active when something has manually been deleted.
				$metaCheck = apply_filters('spaatg/image/filecheck', false);
	      $filepath = $this->getFileDir() . $this->getMeta($type);
	      $file = $fs->getFile($filepath);

				if ($metaCheck === false)
				{
					 return $file;
				}
	    }

			if ($type == 'webp')
			{
		$is_double = \wpSPAATG()->env()->useDoubleWebpExtension();
			}
			if ($type == 'avif')
			{
				$is_double = \wpSPAATG()->env()->useDoubleAvifExtension();
			}

			$double_filepath = $this->getFileDir() .  $this->getFileName() . '.' . $type;
		  $filepath = $this->getFileDir() . $this->getFileBase() . '.' . $type;

			if ($is_double)
				$file = $fs->getFile($double_filepath);
			else
		$file = $fs->getFile($filepath);

			// If double extension is enabled, but no file, check the alternative.
			if (! $file->is_virtual() && ! $file->exists())
			{
				 if ($is_double)
						$file = $fs->getFile($filepath);
				 else
						$file = $fs->getFile($double_filepath);
			}

	    if (! $file->is_virtual() && $file->exists())
	      return $file;

      // If we are in filtered special mode and indeed file doesn't not exist anymore, save it. . Metacheck implies that the imagetype was set before the check
      if ( isset($metaCheck) && true === $metaCheck && false === $file->exists())
      {
          $this->setMeta($type, null);
      }
	    return false;
	  }

    // @todo Deprecate this in favor of getImageType
		public function getWebp()
		{
				return $this->getImageType('webp');
		}

    // @todo Deprecate this in favor of getImageType
	  public function getAvif()
	  {
		return $this->getImageType('avif');
	  }

	  protected function setWebp()
	  {
	      $webp = $this->getImageType('webp');
	      if ($webp !== false && $webp->exists())
        {
	        $this->setMeta('webp', $webp->getFileName() );
        }
	  }

	  protected function setAvif()
	  {
	      $avif = $this->getImageType('avif');
	      if ($avif !== false && $avif->exists())
        {
	        $this->setMeta('avif', $avif->getFileName() );
        }
	  }

    public function setMeta($name, $value)
    {
      if (! $this->hasMeta($name))
      {
          Log::addDebug('Writing meta non existing' . $name);
          return false;
      }
      else
			{
				if ($this->image_meta->$name !== $value)
				{
					 $this->recordChanged(true, $this->image_meta->$name, $value);
				}
        $this->image_meta->$name = $value;

			}
    }

		// Indicates this image has changed data.  Parameters optional for future use.
		protected function recordChanged($bool = true, $old_value = null, $new_value = null)
		{
			 $this->recordChanged = $bool; // Updated record for this image.
		}

    protected function didRecordChange()
    {
       return $this->recordChanged;
    }

    public function hasMeta($name)
    {
        return (property_exists($this->image_meta, $name));
    }

    public function isOptimized()
    {
      if ($this->getMeta('status') == self::FILE_STATUS_SUCCESS)
      {
          $this->processable_status = self::P_IS_OPTIMIZED;
          return true;
      }

      return false;
    }

    /* Returns the improvement of Image by optimizing
    * @param boolean $int When true, returns only integer, otherwise a formatted number for display
    */
    public function getImprovement($int = false)
    {
        if ($this->isOptimized())
        {
            $original = $this->getMeta('originalSize');
            $optimized = $this->getMeta('compressedSize');

            //$diff = $original - $optimized;
            if ($original <= 0 || $optimized <= 0)
              return null;

            if (! $int)
            {
              $number = round(100.0 * (1.0 - $optimized / $original), 2);
            }
            else
            {
              $number =  $original - $optimized;
            }

            if ($number < 0)
            {
               return 0;
            }
            return $number;
        }
        else
          return false;
    }


    /** Handles an Optimized Image in a general way
    *
    * - This function doesn't handle any specifics like custom / thumbnails or anything else, just for a general image
    * - This function doesn't save metadata, that's job of subclass
    *
    * @param Array Result Array. One image result array. ie.
		*
    */
		/*
						[image] => Array
                (
                    [url] =>
                    [originalSize] => 46188
                    [optimizedSize] => 21200
                    [status] => 2
                )

            [webp] => Array
                (
                    [url] =>
                    [size] => 14280
                    [status] => 2
                )

            [avif] => Array
                (
                    [url] =>
                    [size] => 14094
                    [status] => 2
                )
		*/
    public function handleOptimized($results, $args = array())
    {
        return false;
    }

    public function handleOptimizedFileType($downloadResult)
    {
        return false;
    }

    public function isRestorable()
    {
        $this->restorable_status = self::P_NOT_OPTIMIZED;
        return false;
    }

    public function restore()
    {
        return false;
    }

    /** When an image is deleted
    *
    *  Handle an image delete i.e. by WordPress or elsehow.
    */
    public function onDelete()
    {
        return true;
    }

    protected function handleWebp(FileModel $tempFile)
    {
         $fs = \wpSPAATG()->filesystem();
				 if ($this->is_virtual())
				 {
						$fullpath = apply_filters('spaatg/file/virtual/translate', $this->getFullPath(), $this);
						$fileObj = $fs->getFile($fullpath);
						$fileDir = $fileObj->getFileDir();
				 }
				 else {
						$fileDir = $this->getFileDir();
				 }

         $target = $fs->getFile( (string) $fileDir . $this->getFileBase() . '.webp');

            // only copy when this constant is set.
            if( true === \wpSPAATG()->env()->useDoubleWebpExtension() ) {
                 $target = $fs->getFile((string) $fileDir . $this->getFileName() . '.webp'); // double extension, if exists.
            }

            $result = false;

            if (false === $target->exists()) // don't copy if exists.
            {
							$result = $tempFile->move($target);
						}
            else
						{
              $result = true; // if already exists, all fine by us.
						}

            if (false === $result)
						{
              Log::addWarn('Could not copy Webp to destination ' . $target->getFullPath() );
							return false;
						}
            return $target;

         return false;
    }

    protected function handleAvif(FileModel $tempFile)
    {
         $fs = \wpSPAATG()->filesystem();
				 if ($this->is_virtual())
				 {
						$fullpath = apply_filters('spaatg/file/virtual/translate', $this->getFullPath(), $this);
						$fileObj = $fs->getFile($fullpath);
						$fileDir = $fileObj->getFileDir();
				 }
				 else {
						$fileDir = $this->getFileDir();
				 }

            $target = $fs->getFile( (string) $fileDir . $this->getFileBase() . '.avif');

						// only copy when this constant is set.
            if( true === \wpSPAATG()->env()->useDoubleAvifExtension() ) {
                 $target = $fs->getFile((string) $fileDir . $this->getFileName() . '.avif'); // double extension, if exists.
            }

            $result = $tempFile->move($target);
            if (! $result)
            {
              Log::addWarn('Could not copy Avif to destination ' . $target->getFullPath() );
            }
            return $target;

         return false;
    }



    protected function isPathExcluded()
    {
       $excludePatterns = $this->getExcludePatterns();

        if(!$excludePatterns || !is_array($excludePatterns)) { return false; }

        foreach($excludePatterns as $item) {
            $type = (isset($item['type'])) ? trim($item["type"]) : '';
            if(in_array($type, array("name", "path", 'regex-name','regex-path'))) {
                $pattern = trim($item["value"]);
                $target = ($type == "name") ? $this->getFileName() : $this->getFullPath();


                if ($type == 'regex-name' || $type == 'regex-path')
                {
                    $result = $this->matchExludeRegexPattern($target, $pattern);
                }
                else {
                    $result =  $this->matchExcludePattern($target, $pattern);
                }
                if($result === true) { //search as a substring if not
                    $this->processable_status = self::P_EXCLUDE_PATH;
                    return true;
                }
            }
        }
        return false;
    }

		// Checks Processable extensions. The other way of approval is having the file be convertable.
    protected function isExtensionExcluded()
    {

       if ('pdf' === $this->getExtension())
       {
         $settings = \wpSPAATG()->settings();
         if (! $settings->optimizePdfs )
         {
           $this->processable_status = self::P_EXCLUDE_EXTENSION_PDF;

            return true;
         }
       }

        if (! is_null($this->getExtension()) && in_array( strtolower($this->getExtension()) , self::PROCESSABLE_EXTENSIONS))
        {
            return false;
        }


				// If extension not in allowed list, check converters.
				// @todo Most likely move this higher up the chain.
				if (true === $this->is_main_file)
				{
					$converter = Converter::getConverter($this, true);
					if (is_object($converter))
					{
							// Yes can convert, so do not exclude.
							if (true === $converter->isConvertable())
							{
								 return false;
							}
					}
				}


        $this->processable_status = self::P_EXCLUDE_EXTENSION;
        return true;
    }

    protected function matchExcludePattern($target, $pattern) {
        if(strlen($pattern) == 0)  // can happen on faulty input in settings.
          return false;

        if (strpos($target, $pattern) !== false)
        {
          return true;
        }

        return false;
    }

    protected function matchExludeRegexPattern($target, $pattern)
    {
      if(strlen($pattern) == 0)  // can happen on faulty input in settings.
        return false;

			$matches = array();
      $m = preg_match($pattern,  $target, $matches);

      if ($m !== false && $m > 0) // valid regex, more hits than zero
      {
        return true;
      }

      return false;
    }

		protected function isSizeExcluded()
		{
			$excludePatterns = $this->getExcludePatterns();
			if (! $excludePatterns || ! is_array($excludePatterns) ) // no patterns, nothing excluded
				return false;

			$bool = false;

			foreach($excludePatterns as $item) {
					$type = (isset($item['type'])) ? trim($item["type"]) : '';
					if($type == "size") {

							$width = $this->get('width');
							$height = $this->get('height');

							if( $width && $height
									 && $this->isProcessableSize($width, $height, $item["value"]) === false){
										 $this->processable_status = self::P_EXCLUDE_SIZE;
										return true; // exit directly because we have our exclusion
								}
							else
									$bool = false; // continue and check all patterns, there might be multiple.
						}
			 }

			 return $bool;
		}

    private function isFileSizeExcluded()
    {
        $excludePatterns = $this->getExcludePatterns();

        if(!$excludePatterns || !is_array($excludePatterns)) { return false; }

        $bool = false;
        // Support for operators, more characters should be first in array
       // $operators = ['<=', '>=', '<', '>' ];

        foreach($excludePatterns as $item)
        {
           $type = (isset($item['type'])) ? trim($item["type"]) : '';
           if ('filesize' === $type)
           {
               $filesize =  $this->getFileSize();

               // This indicates remote files / virtual / will not work with that.
               if ($filesize <= 0)
               {
                  return false;
               }

               $item_value = explode(' ', $item['value']);
               if (! is_array($item_value) && count($item_value) <> 3)
               {
                 return false;
               }

               $operator = $item_value[0];
               $value = $item_value[1];
               $bytes = $item_value[2];

               if ('B' == $bytes)
               {
                 $compare_bytes = $value;
               }
               else
               {
                $compare_bytes = (int) UtilHelper::convertExclusionFileSizeToBytes($value . $bytes);
               }
               // About version_compare for this
              if ('>' == $operator &&  $filesize > $compare_bytes)
              {
                 $bool = true;
              }
              elseif ('<' == $operator &&  $filesize < $compare_bytes)
              {
                 $bool = true;
              }

              if (true === $bool)
              {
                $this->processable_status = self::P_EXCLUDE_FILESIZE;
                return $bool;
              }
           }
        }
        // Convert fileSize to bytes.

    }

    protected function checkDateExcluded()
		{
			$excludePatterns = $this->getExcludePatterns();
			if (! $excludePatterns || ! is_array($excludePatterns) ) // no patterns, nothing excluded
				return false;

			$bool = false;

			foreach($excludePatterns as $item) {
					$type = (isset($item['type'])) ? trim($item["type"]) : '';
					if($type == "date") {

              $check_date = ['date' => $item['value'], 'when' => $item['dateWhen']];
              return $check_date;
						}
			 }

			 return $bool;
		}

    protected function isFileSizeOK()
    {
        if ($this->is_virtual() || $this->getFileSize() > 0 )
        {
           return true;
        }
        else {
          $this->processable_status = static::P_IMAGE_ZERO_SIZE;
          return false;
        }
    }

    protected function setVirtualToReal($fullpath)
    {
      $this->resetStatus();
      $this->fullpath = $fullpath;
      $this->directory = null; //reset directory
      $this->is_virtual = false; // stops being virtual
      $this->setFileInfo();
    }

		private function isProcessableSize($width, $height, $excludePattern)
		{

				$ranges = preg_split("/(x|×|X)/",$excludePattern);
				$widthBounds = explode("-", $ranges[0]);
				$minWidth = intval($widthBounds[0]);
				$maxWidth = (!isset($widthBounds[1])) ? intval($widthBounds[0]) : intval($widthBounds[1]);

				$heightBounds = isset($ranges[1]) ? explode("-", $ranges[1]) : false;
				$minHeight = $maxHeight = 0;

				if ($heightBounds)
				{
					$minHeight = intval($heightBounds[0]);
					$maxHeight = (!isset($heightBounds[1])) ? intval($heightBounds[0]) : intval($heightBounds[1]);
				}

				if(   $width >= $minWidth && $width <= $maxWidth
					 && ( $heightBounds === false
							 || ($height >= $minHeight && $height <= $maxHeight) )) {
						return false;
				}
				return true;
		}


    /** Convert Image Meta to A Class */
    protected function toClass()
    {
        return $this->image_meta->toClass();
    }


    protected function fs()
    {
       return \wpSPAATG()->filesystem();
    }

		protected function createParamList($args = array())
		{
		 $result = array(
             'url' => isset($args['url']) ? $args['url'] : $this->getUrl(),
             'image' => true,
             'webp' => false,
             'avif' => false,
             'resize' => 0,
         );

     $result = apply_filters('spaatg/image/imageparamlist', $result, $this->id, $this);
		 return $result;

		}



} // model
