package org.jboss.seam.captcha;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.Random;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.contexts.Contexts;

/**
 * Default CAPTCHA algorithm, a simple addition problem. May be
 * extended and customized.
 * 
 * @author Gavin King
 *
 */
@Name("org.jboss.seam.captcha.captcha")
@Scope(ScopeType.SESSION)
@Install(precedence=Install.BUILT_IN)
@BypassInterceptors
public class Captcha implements Serializable
{
   private static Random random = new Random( System.currentTimeMillis() );
   
   private String correctResponse;
   private String challenge;
   private String response;
   
   /**
    * Initialize the challenge and correct response.
    * May be overridden and customized by a subclass.
    */
   @Create
   public void init()
   {
       int x = random.nextInt(50);
       int y = random.nextInt(50);
       setCorrectResponse( Integer.toString( x + y ) );
       setChallenge( Integer.toString(x) + " + " + Integer.toString(y) + " =" );
   }
   
   /**
    * Set the challenge question
    */
   protected void setChallenge(String challenge) 
   {
       this.challenge = challenge;
   }
   
   /**
    * Get the challenge question
    */
   protected String getChallenge()
   {
       return challenge;
   }
   
   /**
    * Set the correct response
    */
   protected void setCorrectResponse(String correctResponse) 
   {
       this.correctResponse = correctResponse;
   }
   
   /**
    * Validate that the entered response is the correct
    * response
    */
   public boolean validateResponse(String response)
   {
      boolean valid = response!=null && 
                      correctResponse!=null && 
                      response.trim().equals(correctResponse);
      if (!valid) 
      {
         init();
      }
      return valid;
   }
   
   @CaptchaResponse
   public String getResponse()
   {
      return response;
   }

   public void setResponse(String input)
   {
      this.response = input;
   }
   
   /**
    * Render the challenge question as an image.
    * May be overridden by subclasses to achieve
    * a stronger CAPTCHA.
    */
   public BufferedImage renderChallenge() 
   {
      BufferedImage challenge = new BufferedImage(70, 20, BufferedImage.TYPE_BYTE_GRAY);
      Graphics graphics = challenge.getGraphics();
      graphics.setColor( getChallengeBackgroundColor() );
      graphics.fillRect(0, 0, getChallengeImageWidth(), 20);
      graphics.setColor( getChallengeTextColor() );
      graphics.drawString( getChallenge(), 5, 15 );
      return challenge;
   }

   /**
    * May be overridden by subclasses
    * @return the width, in pixels, of the challenge question
    */
   protected int getChallengeImageWidth() {
      return 70;
   }
   
   /**
    * May be overridden by subclasses
    * @return the background color of the challenge image
    */
   protected Color getChallengeBackgroundColor()
   {
      return Color.WHITE;
   }

   /**
    * May be overridden by subclasses
    * @return @return the foreground color of the challenge image
    */
   protected Color getChallengeTextColor() 
   {
      return Color.BLACK;
   }
   
   public static Captcha instance()
   {
      if ( !Contexts.isSessionContextActive() )
      {
         throw new IllegalStateException("No page context active");
      }
      return (Captcha) Component.getInstance(Captcha.class, ScopeType.SESSION);
   }

}
