<?php
/**
 * An abstract base class for the string field types (i.e. Varchar and Text)
 *
 * @package framework
 * @subpackage model
 */
abstract class StringField extends DBField {

	/**
	 * @var boolean
	 */
	protected $nullifyEmpty = true;

	/**
	 * @var array
	 */
	private static $casting = array(
		"LimitCharacters" => "Text",
		'LimitWordCount' => 'Text',
		'LimitWordCountXML' => 'HTMLText',
		"LowerCase" => "Text",
		"UpperCase" => "Text",
		'NoHTML' => 'Text',
	);

	/**
	 * Construct a string type field with a set of optional parameters.
	 *
	 * @param $name string The name of the field
	 * @param $options array An array of options e.g. array('nullifyEmpty'=>false).  See
	 *                       {@link StringField::setOptions()} for information on the available options
	 */
	public function __construct($name = null, $options = array()) {
		// Workaround: The singleton pattern calls this constructor with true/1 as the second parameter, so we
		// must ignore it
		if(is_array($options)){
			$this->setOptions($options);
		}

		parent::__construct($name);
	}
	
	/**
	 * Update the optional parameters for this field.
	 * @param $options array of options
	 * The options allowed are:
	 *   <ul><li>"nullifyEmpty"
	 *       This is a boolean flag.
	 *       True (the default) means that empty strings are automatically converted to nulls to be stored in
	 *       the database. Set it to false to ensure that nulls and empty strings are kept intact in the database.
	 *   </li></ul>
	 * @return unknown_type
	 */
	public function setOptions(array $options = array()) {
		if(array_key_exists("nullifyEmpty", $options)) {
			$this->nullifyEmpty = $options["nullifyEmpty"] ? true : false;
		}
	}
	
	/**
	 * Set whether this field stores empty strings rather than converting
	 * them to null.
	 *
	 * @param $value boolean True if empty strings are to be converted to null
	 */
	public function setNullifyEmpty($value) {
		$this->nullifyEmpty = ($value ? true : false);
	}

	/**
	 * Get whether this field stores empty strings rather than converting
	 * them to null
	 *
	 * @return boolean True if empty strings are to be converted to null
	 */
	public function getNullifyEmpty() {
		return $this->nullifyEmpty;
	}

	/**
	 * (non-PHPdoc)
	 * @see core/model/fieldtypes/DBField#exists()
	 */
	public function exists() {
		return ($this->value || $this->value == '0') || ( !$this->nullifyEmpty && $this->value === '');
	}

	/**
	 * (non-PHPdoc)
	 * @see core/model/fieldtypes/DBField#prepValueForDB($value)
	 */
	public function prepValueForDB($value) {
		if(!$this->nullifyEmpty && $value === '') {
			return DB::getConn()->prepStringForDB($value);
		} else {
			return parent::prepValueForDB($value);
		}
	}

	/**
	 * @return string
	 */
	public function forTemplate() {
		return nl2br($this->XML());
	}
	
	/**
	 * Limit this field's content by a number of characters.
	 * This makes use of strip_tags() to avoid malforming the
	 * HTML tags in the string of text.
	 *
	 * @param int $limit Number of characters to limit by
	 * @param string $add Ellipsis to add to the end of truncated string
	 * @return string
	 */
	public function LimitCharacters($limit = 20, $add = '...') {
		$value = trim($this->value);

		if($this->stat('escape_type') == 'xml') {
			$value = strip_tags($value);
			$value = html_entity_decode($value, ENT_COMPAT, 'UTF-8');
			$value = (mb_strlen($value) > $limit) ? mb_substr($value, 0, $limit) . $add : $value;
			// Avoid encoding all multibyte characters as HTML entities by using htmlspecialchars().
			$value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
		} else {
			$value = (mb_strlen($value) > $limit) ? mb_substr($value, 0, $limit) . $add : $value;
		}

		return $value;
	}


	/**
	 * Limit this field's content by a number of words.
	 *
	 * CAUTION: This is not XML safe. Please use
	 * {@link LimitWordCountXML()} instead.
	 *
	 * @param int $numWords Number of words to limit by.
	 * @param string $add Ellipsis to add to the end of truncated string.
	 *
	 * @return string
	 */
	public function LimitWordCount($numWords = 26, $add = '...') {
		$this->value = trim(Convert::xml2raw($this->value));
		$ret = explode(' ', $this->value, $numWords + 1);

		if(count($ret) <= $numWords - 1) {
			$ret = $this->value;
		} else {
			array_pop($ret);
			$ret = implode(' ', $ret) . $add;
		}

		return $ret;
	}

	/**
	 * Limit the number of words of the current field's
	 * content. This is XML safe, so characters like &
	 * are converted to &amp;
	 *
	 * @param int $numWords Number of words to limit by.
	 * @param string $add Ellipsis to add to the end of truncated string.
	 *
	 * @return string
	 */
	public function LimitWordCountXML($numWords = 26, $add = '...') {
		$ret = $this->LimitWordCount($numWords, $add);

		return Convert::raw2xml($ret);
	}

	/**
	 * Converts the current value for this StringField to lowercase.
	 *
	 * @return string
	 */
	public function LowerCase() {
		return mb_strtolower($this->value);
	}

	/**
	 * Converts the current value for this StringField to uppercase.
	 * @return string 
	 */ 
	public function UpperCase() {
		return mb_strtoupper($this->value);
	}
	
	/**
	 * Return the value of the field stripped of html tags.
	 *
	 * @return string
	 */
	public function NoHTML() {
		return strip_tags($this->value);
	}
}
