* @link http://www.pradosoft.com/
 * @copyright Copyright © 2005-2007 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @version $Id$
 * @package System.Web.UI.WebControls
 */
Prado::using('System.Web.UI.WebControls.TImage');
/**
 * TCaptcha class.
 *
 * TCaptcha displays a CAPTCHA (a token displayed as an image) that can be used
 * to determine if the input is entered by a real user instead of some program.
 *
 * Unlike other CAPTCHA scripts, TCaptcha does not need session or cookie.
 *
 * The token (a string consisting of alphanumeric characters) displayed is automatically
 * generated and can be configured in several ways. To specify the length of characters
 * in the token, set {@link setMinTokenLength MinTokenLength} and {@link setMaxTokenLength MaxTokenLength}.
 * To use case-insensitive comparison and generate upper-case-only token, set {@link setCaseSensitive CaseSensitive}
 * to false. Advanced users can try to set {@link setTokenAlphabet TokenAlphabet}, which
 * specifies what characters can appear in tokens.
 *
 * The validation of the token is related with two properties: {@link setTestLimit TestLimit}
 * and {@link setTokenExpiry TokenExpiry}. The former specifies how many times a token can
 * be tested with on the server side, and the latter says when a generated token will expire.
 *
 * To specify the appearance of the generated token image, set {@link setTokenImageTheme TokenImageTheme}
 * to be an integer between 0 and 63. And to adjust the generated image size, set {@link setTokenFontSize TokenFontSize}
 * (you may also set {@link TWebControl::setWidth Width}, but the scaled image may not look good.)
 * By setting {@link setChangingTokenBackground ChangingTokenBackground} to true, the image background
 * of the token will be variating even though the token is the same during postbacks.
 *
 * Upon postback, user input can be validated by calling {@link validate()}.
 * The {@link TCaptchaValidator} control can also be used to do validation, which provides
 * client-side validation besides the server-side validation.  By default, the token will
 * remain the same during multiple postbacks. A new one can be generated by calling
 * {@link regenerateToken()} manually.
 *
 * The following template shows a typical use of TCaptcha control:
 * 
 * 
 * 
 * 
 * 
 *
 * @author Qiang Xue 
 * @version $Id$
 * @package System.Web.UI.WebControls
 * @since 3.1.1
 */
class TCaptcha extends TImage
{
	const MIN_TOKEN_LENGTH=2;
	const MAX_TOKEN_LENGTH=40;
	private $_privateKey;
	private $_validated=false;
	/**
	 * @return integer the theme of the token image. Defaults to 0.
	 */
	public function getTokenImageTheme()
	{
		return $this->getViewState('TokenImageTheme',0);
	}
	/**
	 * Sets the theme of the token image.
	 * You may test each theme to find out the one you like the most.
	 * Below is the explanation of the theme value:
	 * It is treated as a 5-bit integer. Each bit toggles a specific feature of the image.
	 * Bit 0 (the least significant): whether the image is opaque (1) or transparent (0).
	 * Bit 1: whether we should add white noise to the image (1) or not (0).
	 * Bit 2: whether we should add a grid to  the image (1) or not (0).
	 * Bit 3: whether we should add some scribbles to the image (1) or not (0).
	 * Bit 4: whether the image background should be morphed (1) or not (0).
	 * Bit 5: whether the token text should cast a shadow (1) or not (0).
	 * @param integer the theme of the token image. It must be an integer between 0 and 63.
	 */
	public function setTokenImageTheme($value)
	{
		$value=TPropertyValue::ensureInteger($value);
		if($value>=0 && $value<=63)
			$this->setViewState('TokenImageTheme',$value,0);
		else
			throw new TConfigurationException('captcha_tokenimagetheme_invalid',0,63);
	}
	/**
	 * @return integer the font size used for displaying the token in an image. Defaults to 30.
	 */
	public function getTokenFontSize()
	{
		return $this->getViewState('TokenFontSize',30);
	}
	/**
	 * Sets the font size used for displaying the token in an image.
	 * This property affects the generated token image size.
	 * The image width is proportional to this font size.
	 * @param integer the font size used for displaying the token in an image. It must be an integer between 20 and 100.
	 */
	public function setTokenFontSize($value)
	{
		$value=TPropertyValue::ensureInteger($value);
		if($value>=20 && $value<=100)
			$this->setViewState('TokenFontSize',$value,30);
		else
			throw new TConfigurationException('captcha_tokenfontsize_invalid',20,100);
	}
	/**
	 * @return integer the minimum length of the token. Defaults to 4.
	 */
	public function getMinTokenLength()
	{
		return $this->getViewState('MinTokenLength',4);
	}
	/**
	 * @param integer the minimum length of the token. It must be between 2 and 40.
	 */
	public function setMinTokenLength($value)
	{
		$length=TPropertyValue::ensureInteger($value);
		if($length>=self::MIN_TOKEN_LENGTH && $length<=self::MAX_TOKEN_LENGTH)
			$this->setViewState('MinTokenLength',$length,4);
		else
			throw new TConfigurationException('captcha_mintokenlength_invalid',self::MIN_TOKEN_LENGTH,self::MAX_TOKEN_LENGTH);
	}
	/**
	 * @return integer the maximum length of the token. Defaults to 6.
	 */
	public function getMaxTokenLength()
	{
		return $this->getViewState('MaxTokenLength',6);
	}
	/**
	 * @param integer the maximum length of the token. It must be between 2 and 40.
	 */
	public function setMaxTokenLength($value)
	{
		$length=TPropertyValue::ensureInteger($value);
		if($length>=self::MIN_TOKEN_LENGTH && $length<=self::MAX_TOKEN_LENGTH)
			$this->setViewState('MaxTokenLength',$length,6);
		else
			throw new TConfigurationException('captcha_maxtokenlength_invalid',self::MIN_TOKEN_LENGTH,self::MAX_TOKEN_LENGTH);
	}
	/**
	 * @return boolean whether the token should be treated as case-sensitive. Defaults to true.
	 */
	public function getCaseSensitive()
	{
		return $this->getViewState('CaseSensitive',true);
	}
	/**
	 * @param boolean whether the token should be treated as case-sensitive. If false, only upper-case letters will appear in the token.
	 */
	public function setCaseSensitive($value)
	{
		$this->setViewState('CaseSensitive',TPropertyValue::ensureBoolean($value),true);
	}
	/**
	 * @return string the characters that may appear in the token. Defaults to '234578adefhijmnrtABDEFGHJLMNRT'.
	 */
	public function getTokenAlphabet()
	{
		return $this->getViewState('TokenAlphabet','234578adefhijmnrtABDEFGHJLMNRT');
	}
	/**
	 * @param string the characters that may appear in the token. At least 2 characters must be specified.
	 */
	public function setTokenAlphabet($value)
	{
		if(strlen($value)<2)
			throw new TConfigurationException('captcha_tokenalphabet_invalid');
		$this->setViewState('TokenAlphabet',$value,'234578adefhijmnrtABDEFGHJLMNRT');
	}
	/**
	 * @return integer the number of seconds that a generated token will remain valid. Defaults to 600 seconds (10 minutes).
	 */
	public function getTokenExpiry()
	{
		return $this->getViewState('TokenExpiry',600);
	}
	/**
	 * @param integer the number of seconds that a generated token will remain valid. A value smaller than 1 means the token will not expire.
	 */
	public function setTokenExpiry($value)
	{
		$this->setViewState('TokenExpiry',TPropertyValue::ensureInteger($value),600);
	}
	/**
	 * @return boolean whether the background of the token image should be variated during postbacks. Defaults to false.
	 */
	public function getChangingTokenBackground()
	{
		return $this->getViewState('ChangingTokenBackground',false);
	}
	/**
	 * @param boolean whether the background of the token image should be variated during postbacks.
	 */
	public function setChangingTokenBackground($value)
	{
		$this->setViewState('ChangingTokenBackground',TPropertyValue::ensureBoolean($value),false);
	}
	/**
	 * @return integer how many times a generated token can be tested. Defaults to 5.
	 */
	public function getTestLimit()
	{
		return $this->getViewState('TestLimit',5);
	}
	/**
	 * @param integer how many times a generated token can be tested. For unlimited tests, set it to 0.
	 */
	public function setTestLimit($value)
	{
		$this->setViewState('TestLimit',TPropertyValue::ensureInteger($value),5);
	}
	/**
	 * @return boolean whether the currently generated token has expired.
	 */
	public function getIsTokenExpired()
	{
		if(($expiry=$this->getTokenExpiry())>0 && ($start=$this->getViewState('TokenGenerated',0))>0)
			return $expiry+$start