<?php namespace ProcessWire;

/**
 * ProcessWire Datetime Fieldtype
 *
 * Holds date and optionally time values. 
 *
 * For documentation about the fields used in this class, please see:  
 * /wire/core/Fieldtype.php
 * 
 * ProcessWire 3.x, Copyright 2016 by Ryan Cramer
 * https://processwire.com
 *
 */

class FieldtypeDatetime extends FieldtypeText {

	const defaultDateOutputFormat = 'Y-m-d'; 

	public static function getModuleInfo() {
		return array(
			'title' => 'Datetime',
			'version' => 104,
			'summary' => 'Field that stores a date and optionally time',
			);
	}

	/**
	 * Return all predefined PHP date() formats for use as dates
	 * 
	 * Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.
	 * 
	 * @deprecated Use WireDateTime class instead
	 * @return array
	 *
	 */
	static public function getDateFormats() {
		return WireDateTime::_getDateFormats();
	}

	/**
	 * Return all predefined PHP date() formats for use as times
	 *
	 * Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.
	 *
	 * @deprecated Use WireDateTime class instead
	 * @return array
	 * 
	 */
	static public function getTimeFormats() {
		return WireDateTime::_getTimeFormats();
	}

	/**
	 * Given a date/time string and expected format, convert it to a unix timestamp
	 *
	 * Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.
	 * 
	 * @param string $str Date/time string
	 * @param string $format Format of the date/time string in PHP date syntax
	 * @return int
	 * @deprecated Use WireDateTime class instead
	 *
	 */
	static public function stringToTimestamp($str, $format) {
		$wdt = new WireDateTime();
		return $wdt->stringToTimestamp($str, $format);
	}

	/**
	 * Format a date with the given PHP date() or PHP strftime() format
	 * 
	 * Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.
	 *
 	 * @param int $value Unix timestamp of date
	 * @param string $format date() or strftime() format string to use for formatting
	 * @return string Formatted date string
	 * @deprecated Use WireDateTime class instead
	 *
	 */
	static public function formatDate($value, $format) {
		$wdt = new WireDateTime();
		return $wdt->formatDate($value, $format);
	}

	/**
	 * Given a date() format, convert it to either 'js', 'strftime' or 'regex' format
	 * 
	 * Note: this method moved to the WireDateTime class and is kept here for backwards compatibility.
	 *
	 * @param string $format PHP date() format
	 * @param string $type New format to convert to: either 'js', 'strftime', or 'regex' 
	 * @return string
	 * @deprecated Use WireDateTime class instead
	 *
	 */
	static public function convertDateFormat($format, $type) {
		$wdt = new WireDateTime();
		return $wdt->convertDateFormat($format, $type);
	}

	/*********************************************************************************************************************************
	 *********************************************************************************************************************************/

	/**
	 * Initlialize this module
	 *
	 */
	public function init() {
		parent::init();
		$this->allowTextFormatters(false);
	}

	/**
	 * Return the Inputfield used for date/time (InputfieldDatetime)
	 * 
	 * @param Page $page
	 * @param Field $field
	 * @return InputfieldDatetime
	 *
	 */
	public function getInputfield(Page $page, Field $field) {
		$inputfield = $this->modules->get('InputfieldDatetime'); 
		$inputfield->class = $this->className();
		return $inputfield; 
	}

	/**
	 * Sanitize value, per Fieldtype interface
	 * 
	 * @param Page $page
	 * @param Field $field
	 * @param string|int|\DateTime $value
	 * @return int
	 *	
 	 */
	public function sanitizeValue(Page $page, Field $field, $value) {
		return $this->_sanitizeValue($value); 
	}

	/**
	 * Sanitize a value assumed to be either a timestamp or in strtotime() compatible format
	 * 
	 * @param string|int|\DateTime
	 * @return int
	 *
	 */
	protected function _sanitizeValue($value) {
		if(empty($value)) return '';
		if($value instanceof \DateTime) return $value->getTimestamp();
		// already a timestamp
		if(ctype_digit(ltrim($value, '-'))) return (int) $value; 
		return strtotime($value); 
	}

	/**
	 * Format the value for output, according to selected format and language
	 * 
	 * @param Page $page
	 * @param Field $field
	 * @param int $value
	 * @return string
	 *
	 */
	public function ___formatValue(Page $page, Field $field, $value) {
		$format = '';
		if($this->languages && !$this->user->language->isDefault()) $format = $field->get("dateOutputFormat{$this->user->language}"); 
		if(!$format) $format = $field->get('dateOutputFormat');
		return $this->wire('datetime')->formatDate($value, $format);
	}

	public function ___exportValue(Page $page, Field $field, $value, array $options = array()) {
		if(!$value) return '';
		return date('Y-m-d H:i:s', $value); 
	}

	/**
	 * Match a date/time value in the database, as used by PageFinder
	 * 
	 * @param DatabaseQuerySelect $query
	 * @param string $table
	 * @param string $subfield
	 * @param string $operator
	 * @param int|string $value
	 * @return DatabaseQuerySelect
	 *
	 */
	public function getMatchQuery($query, $table, $subfield, $operator, $value) {
		$value = (int) $this->_sanitizeValue($value); 
		if($value) $value = date('Y-m-d H:i:s', $value); 
			else $value = '';
		$database = $this->wire('database');
		if($database->isOperator($operator)) {
			$table = $database->escapeTable($table);
			$subfield = $database->escapeCol($subfield);
			$value = $database->escapeStr($value);
			$query->where("$table.{$subfield}{$operator}'$value'");
		}

		return $query; 
	}

	/**
	 * Return database schema used by this field
	 * 
	 * @param Field $field
	 * @return array
	 *
	 */
	public function getDatabaseSchema(Field $field) {
		$schema = parent::getDatabaseSchema($field); 
		$schema['data'] = 'datetime NOT NULL';
		unset($schema['keys']['data_exact']); 
		$schema['keys']['data'] = 'KEY data (data)'; // wipe out keys from parent
		return $schema;
	}

	/**
	 * Convert value from timestamp to Y-m-d H:i:s date string
	 * 
	 * @param Page $page
	 * @param Field $field
	 * @param string|int $value
	 * @return string
	 *
	 */
	public function ___sleepValue(Page $page, Field $field, $value) {
		$value = $this->_sanitizeValue($value); 
		return is_int($value) ? date('Y-m-d H:i:s', $value) : '';
	}

	/**
	 * Convert value from Y-m-d H:i:s string to timestamp
	 * 
	 * @param Page $page
	 * @param Field $field
	 * @param string $value
	 * @return int
	 *
	 */
	public function ___wakeupValue(Page $page, Field $field, $value) {
		if(empty($value)) return '';
		$value = strtotime($value); 
		if($value === false) $value = '';
		return $value; 
	}

	/**
	 * Field configuration screen
	 * 
	 * @param Field $field
	 * @return InputfieldWrapper
	 *
	 */
	public function ___getConfigInputfields(Field $field) {

		$inputfields = parent::___getConfigInputfields($field);
		$wdt = $this->wire('datetime');

		/** @var InputfieldSelect $f */
		$f = $this->modules->get('InputfieldSelect'); 
		$f->attr('name', '_dateOutputFormat'); 
		$f->label = $this->_('Date Output Format'); 
		$f->description = $this->_('Select the format to be used when outputting dates with this field.') . ' ';
		$f->description .= $this->_('For relative date/time formats, see the Time Output Format.'); 
		$f->icon = 'calendar';
		$f->addOption('', $this->_('None'));
		$f->columnWidth = 50;
		$date = strtotime('2016-04-08 5:10:02 PM');
		$found = false;
		foreach($wdt->getDateFormats() as $format) {
			$dateFormatted = $wdt->formatDate($date, $format); 	
			if($format == 'U') $dateFormatted .= " " . $this->_('(unix timestamp)');
			$f->addOption($format, $dateFormatted); 
			if(!$found && strpos($field->get('dateOutputFormat'), $format) !== false) {
				$f->attr('value', $format); 
				$found = true; 
			}
		}
		$f->attr('onchange', "$('#Inputfield_dateOutputFormat').val($(this).val() + ' ' + $('#Inputfield__timeOutputFormat').val());"); 
		$inputfields->add($f); 

		$f = $this->modules->get('InputfieldSelect'); 
		$f->attr('name', '_timeOutputFormat'); 
		$f->label = $this->_('Time Output Format'); 
		$f->description = $this->_('If you want to output time in addition to the date, select the format to be used when outputting time with this field. This will be combined with the date format.'); 
		$f->icon = 'clock-o';
		$f->addOption('', $this->_('None'));
		$f->columnWidth = 50;
		$date = strtotime('5:10:02 PM');
		$found = false;
		foreach($wdt->getTimeFormats() as $format) {
			$timeFormatted = $wdt->formatDate($date, $format); 	
			$f->addOption($format, $timeFormatted); 
			if(!$found && strpos($field->get('dateOutputFormat'), $format) !== false) {
				$f->attr('value', $format); 
				$found = true; 
			}
		}
		$f->attr('onchange', "$('#Inputfield_dateOutputFormat').val($('#Inputfield__dateOutputFormat').val() + ' ' + $(this).val());"); 
		//$f->collapsed = Inputfield::collapsedBlank;
		$inputfields->add($f);

		/** @var InputfieldText $f */
		$f = $this->modules->get("InputfieldText"); 
		$f->attr('name', 'dateOutputFormat'); 
		$f->attr('value', $field->get('dateOutputFormat')); 
		$f->attr('size', 20); 
		$f->label = $this->_('Date/Time Output Format Code');
		$f->description = $this->_('The date/time will be output according to the format below. This is automatically built from the date/time selections above, but you may change it as needed to suit your needs.') . ' ';
		$f->description .= $this->_('See the [PHP date](http://www.php.net/manual/en/function.date.php) function reference for more information on how to customize this format. Alternatively, you may use a [PHP strftime](http://www.php.net/manual/en/function.strftime.php) format if desired for localization.'); 
		$f->icon = 'code';
		$f->collapsed = Inputfield::collapsedYes; 
		if($this->languages) {
			$f->useLanguages = true; 
			foreach($this->languages as $language) {
				if($language->isDefault()) continue; 
				$f->set("value$language", (string) $field->get('dateOutputFormat' . $language)); 
			}
		}
		$inputfields->add($f); 

		return $inputfields; 
	}
}

