<?php namespace ProcessWire;

/**
 * ProcessWire Image Fieldtype
 *
 * Field that stores one or more image files. 
 *
 * For documentation about the fields used in this class, please see:  
 * /wire/core/Fieldtype.php
 * /wire/core/FieldtypeMulti.php
 * 
 * ProcessWire 3.x, Copyright 2023 by Ryan Cramer
 * https://processwire.com
 *
 *
 */

class FieldtypeImage extends FieldtypeFile implements FieldtypeHasFiles, FieldtypeHasPageimages {
	
	/**
	 * File schema is configured to store dimensions for image files 'width', 'height', 'ratio' (flag)
	 *
	 */
	const fileSchemaDimensions = 256;

	/**
	 * Get module info
	 * 
	 * @return array
	 * 
	 */
	public static function getModuleInfo() {
		return array(
			'title' => 'Images',
			'version' => 102,
			'summary' => 'Field that stores one or more GIF, JPG, or PNG images',
			'permanent' => true, 
		);
	}

	/**
	 * Get blank value
	 * 
	 * @param Page $page
	 * @param Field $field
	 * @return Pageimages|Pagefiles
	 * 
	 */
	public function getBlankValue(Page $page, Field $field) {
		/** @var Pageimages $pageimages */
		$pageimages = $this->wire(new Pageimages($page));
		$pageimages->setField($field);
		$pageimages->setTrackChanges(true); 
		return $pageimages; 
	}

	/**
	 * @param Pagefiles $pagefiles
	 * @param string $filename
	 * @return Pageimage|Pagefile
	 * 
	 */
	protected function getBlankPagefile(Pagefiles $pagefiles, $filename) {
		return $this->wire(new Pageimage($pagefiles, $filename)); 
	}

	/**
	 * Get default file extensions 
	 * @return string
	 * 
	 */
	protected function getDefaultFileExtensions() {
		return "gif jpg jpeg png";
	}
	
	/**
	 * Check and update database schema according to current version and features
	 *
	 * @param Field $field
	 * @param array $schema Updated directly
	 * @param int $fileSchema The fileSchema version flags integer
	 * @return int Updated fileSchema flags integer
	 * @since 3.0.154
	 *
	 */
	protected function updateDatabaseSchema(Field $field, array &$schema, $fileSchema) {
		
		$fileSchema = parent::updateDatabaseSchema($field, $schema, $fileSchema); 

		$hasDimensions = $fileSchema & self::fileSchemaDimensions;
		
		$schema['width'] = 'int';
		$schema['height'] = 'int';
		$schema['ratio'] = 'decimal(4,2)';
		
		$schema['keys']['width'] = 'index (width)';
		$schema['keys']['height'] = 'index (height)';
		$schema['keys']['ratio'] = 'index (ratio)';
		
		if(!$hasDimensions) {
			$numErrors = 0;
			if($this->wire()->database->tableExists($field->getTable())) {
				$columns = array('width', 'height', 'ratio');
				foreach($columns as $column) {
					if(!$this->addColumn($field, $column, $schema)) $numErrors++;
					if($numErrors) break;
				}
			} else {
				// new field being created that is getting initial schema to create table
			}
			if(!$numErrors) {
				$fileSchema = $fileSchema | self::fileSchemaDimensions;
			}
		}
		
		return $fileSchema;
	}
	
	/**
	 * Convert individual Pagefile to array for storage in DB
	 *
	 * @param Page $page
	 * @param Field $field
	 * @param Pagefile|Pageimage $pagefile
	 * @return array
	 *
	 */
	protected function sleepFile(Page $page, Field $field, Pagefile $pagefile) {
		
		$item = parent::sleepFile($page, $field, $pagefile); 

		$fileSchema = (int) $field->get('fileSchema');

		if($fileSchema & self::fileSchemaDimensions) {
			$info = $pagefile->getImageInfo(true);
			
			if(strpos($info['width'], '%')) $info['width'] = (-1 * (int) rtrim($info['width'], '%'));
			if(strpos($info['height'], '%')) $info['height'] = (-1 * (int) rtrim($info['height'], '%'));
			
			$item['width'] = (int) $info['width'];
			$item['height'] = (int) $info['height'];
			$item['ratio'] = number_format($pagefile->ratio, 2, '.', '');
		}
		
		return $item;
	}


	/**
	 * Wakeup individual file converting array of data to Pagefile and adding it to Pagefiles
	 *
	 * @param Page $page
	 * @param Field $field
	 * @param Pagefiles $pagefiles
	 * @param array $a Data from DB
	 * @return Pagefile
	 *
	 */
	protected function wakeupFile(Page $page, Field $field, Pagefiles $pagefiles, array $a) {
	
		/** @var Pageimage $pagefile */
		$pagefile = parent::wakeupFile($page, $field, $pagefiles, $a); 
		$info = false;
		
		if(!empty($a['width'])) {
			// dimension info already present in DB: populate to Pageimage object
			$info = array('width' => (int) $a['width'], 'height' => (int) $a['height']);
		} else if(self::autoUpdateOnWakeup && (((int) $field->get('fileSchema')) & self::fileSchemaDimensions) && $pagefile->width()) {
			// dimension info not yet in DB: detect and populate in DB
			list($width, $height, $ratio) = array($pagefile->width(), $pagefile->height(), $pagefile->ratio);
			if(strpos($width, '%')) $width = (-1 * (int) rtrim($width, '%'));
			if(strpos($height, '%')) $height = (-1 * (int) rtrim($height, '%'));
			$info = array('width' => $width, 'height' => $height, 'ratio' => $ratio);
			$this->saveFileCols($page, $field, $pagefile, $info);
		}
		
		if($info) {
			unset($info['ratio']); // ratio not stored in runtime image info
			$w = isset($info['width']) ? (int) $info['width'] : 0;
			$h = isset($info['height']) ? (int) $info['height'] : 0;
			if($w === -100 && $h === -100) {
				// we were previously unable to determine width and height
				// so do not populate the cached values, forcing it to re-check
				// this only occurs on SVG files
			} else {
				$pagefile->setImageInfo($info);
			}
		}

		return $pagefile;
	}

	/**
	 * Export a Pageimages value to a portable PHP array
	 * 
	 * @param Page $page
	 * @param Field $field
	 * @param array|float|int|null|object|string $value
	 * @param array $options
	 * @return array
	 * 
	 */
	public function ___exportValue(Page $page, Field $field, $value, array $options = array()) {
		/** @var Pageimages $pagefiles */
		$pagefiles = $value; 
		$value = parent::___exportValue($page, $field, $value, $options); 
		if(empty($options['system'])) {
			foreach($value as $k => $v) {
				$img = $pagefiles->get($v['name']);
				$value[$k]['width'] = $img->width();
				$value[$k]['height'] = $img->height();
			}
		}
		
		if(!empty($options['FieldtypeImage'])) {
			$o = $options['FieldtypeImage']; 
			if(!empty($o['variations'])) {
				// include URLs to image variations
				foreach($value as $k => $v) {
					if(empty($options['system'])) {
						$img = $pagefiles->get($v['name']);
					} else {
						$img = $pagefiles->get($k); 
					}
					$variations = array();
					foreach($img->getVariations() as $variation) {
						/** @var Pageimage $variation */
						$variations[$variation->name] = $variation->httpUrl();
					}
					$value[$k]['variations'] = $variations;
					
				}
			}
		}
		
		return $value; 	
	}
	
	/**
	 * Get array of full path/file for all files managed by given page and field (also includes image variations)
	 *
	 * #pw-internal For FieldtypeHasFiles interface
	 *
	 * @param Page $page
	 * @param Field $field
	 * @return array
	 * @since 3.0.181
	 *
	 */
	public function getFiles(Page $page, Field $field) {
		return parent::getFiles($page, $field);
	}

	/**
	 * Get Pageimages
	 *
	 * @param Page $page
	 * @param Field $field
	 * @return Pageimages
	 *
	 */
	public function getPageimages(Page $page, Field $field) {
		/** @var Pageimages|Pageimage|null $value */
		$value = $page->get($field->name);
		if($value instanceof Pageimages) return $value;
		$pageimages = $this->getBlankValue($page, $field);
		if($value instanceof Pageimage) $pageimages->add($value);
		return $pageimages;
	}

	/**
	 * Get setup options and setup functions for new fields
	 *
	 * @return array
	 * @since 3.0.213
	 *
	 */
	public function ___getFieldSetups() {
		$setups = parent::___getFieldSetups();
		$setups['single']['title'] = $this->_('Single image');
		$setups['multiple']['title'] = $this->_('Multiple images');
		return $setups;
	}
	
	/**
	 * Get Inputfields to configure fields using this Fieldtype
	 *
	 * @param Field $field
	 * @return InputfieldWrapper
	 *
	 */
	public function ___getConfigInputfields(Field $field) {
		$inputfields = parent::___getConfigInputfields($field);
		return $inputfields;
	}

	/**
	 * Get Inputfields to advanced configure fields using this Fieldtype
	 * 
	 * @param Field $field
	 * @return InputfieldWrapper
	 * 
	 */
	public function ___getConfigAdvancedInputfields(Field $field) {
		$inputfields = parent::___getConfigAdvancedInputfields($field);
		return $inputfields; 
	}

	/*
	public function getInputfield(Page $page, Field $field) {

		// even though we don't want this input field, call it anyway
		parent::getInputfield($page, $field); 

		$inputfield = $this->modules->get("InputfieldImage"); 
		$inputfield->class = $this->className();

		$this->setupHooks($page, $field, $inputfield);

		return $inputfield;
	}
	*/


}
