/**
 *   SimpleCaptcha implements a CAPTCHA in client side JavaScript using a dictionary of
 *     of 191 short words.  If the browser supports the 'canvas' tag, this tag is used
 *     to create a graphic containing the hidden word. Most modern browsers support
 *     this tag.  Windows Internet Explorer does not.  In this case, a pre made image
 *     is used but it's reference is exposed in the 'src' attribute of the CATPTCHA
 *     'img' tag.
 *
 *   This Echo component can be used from the server side.  In this case, any short
 *     word can be used.  Once again, if the browser does not support the 'canvas'
 *     tag a word from the built-in dictionary is used along with the pre-made JPEG
 *     CAPTCHA image.  The client will update the server as to which word was choosen.
 *
 *   CAPTCHA is considered to be a 'glass shield' type of security. It can be used to
 *     impede attacks but can be easily broken manually, programatically , or with
 *     image processing software.  This implementation is particulary vunerable because
 *     all of the source code is exposed on the client side.
 *
 *   Original implementation by Jonathan Feaster, http://www.archreality.com/jcap/
 *
 *   Author: Will Gilbert; May, 2009
 *   Copyrighted with MIT License
 */


// Informagen =================================================================================
// Ensure that the 'Informagen' and 'Informagen.Sync' namespaces exists

if (!Core.get(window, ["Informagen", "Sync"])) {
    Core.set(window, ["Informagen", "Sync"], {});
}


// Informagen.SimpleCaptcha ==================================================================

Informagen.SimpleCaptcha = Core.extend(Echo.Component, {

    $load : function() {
        Echo.ComponentFactory.registerType("Informagen.SimpleCaptcha", this);
    },

    componentType : "Informagen.SimpleCaptcha",

    // Used for evaluating a string against a CAPTCHA by FreeClient applictions
    
    evaluate : function(string) {
        return this.get("string") === string;
    },

    focusable : false
    
});


// Informagen.Sync.SimpleCaptcha =============================================================
//  The name of this object is irrelevant; see '$load' function.  However, keeping this 
//    class as a member of the namespace 'Informagen.Sync' adds consistency.


Informagen.Sync.SimpleCaptcha = Core.extend(Echo.Render.ComponentSync, {


    // Register this class as the 'Sync' class for the 'Informagen.SimpleCaptcha' class

    $load: function() {
        Echo.Render.registerPeer("Informagen.SimpleCaptcha", this);
    },


    _divElement: null,
    _imgElement: null,
    _useImages: false,
    _string: null,

    // Cannot update during the renderUpdate transaction.Update the component 
    //    property in a different JavaScript context using Scheduler; 
    
    _updateProperty: function(propertyName, oldValue, newValue) {
        
        // Create an update event
         var updateEvent = {type: "componentUpdate", 
                            parent: this.component, 
                            propertyName: propertyName, 
                            oldValue: oldValue, 
                            newValue: newValue};
                            
        // Get a reference to the 'client', can't use 'this' inside the Scheduler class
        
        var client = this.component.application.client;
        
        // For RemoteClient, process the property update explicitly; for
        //   FreeClient, simply set the property
        
        if(client._processClientUpdate)
            Core.Web.Scheduler.run( function() {client._processClientUpdate(updateEvent)});
        else
            this.component.set(propertyName, newValue);
    },

    /***************************************************************************
     * Implement the Component superclass methods: renderAdd, renderUpdate and
     *    renderDispose
     */

    renderAdd: function(update, parentElement) {
        this._divElement = document.createElement("div");
        this._divElement.id = this.component.renderId;
        
        this._imgElement = document.createElement("img");
        this._divElement.appendChild(this._imgElement);
        
        parentElement.appendChild(this._divElement);

        this.renderUpdate(update);
    },
    
    renderUpdate: function(update) {
            
        // Generate a random integer between 1 and 191, inclusive
        var index = (Math.floor(Math.random()*191)) + 1;
        this._string = this.component.render("string", null);
        this._useImages = this.component.render("useImages", false);
       
        if(!this._string || this._useImages) {
            var oldValue = this._string;
            this._string = this._names[index-1];
            
            // Update the component property outside of the 'renderUpdate' context
            this._updateProperty("string", oldValue, this._string);
        }
             
        this._imgElement.width = Echo.Sync.Extent.toPixels(this.component.render("width", 145), true);
        this._imgElement.height = Echo.Sync.Extent.toPixels(this.component.render("height", 40), false);

        var canvas = document.createElement("canvas");

        if (canvas.getContext &&  !this._useImages) {
            this._imgElement.src = this._createImgSrc(canvas, this._string);
        } else {
            var imageFileName = index + ".jpg";
            var image = this.client.getResourceUrl("SimpleCAPTCHA", imageFileName);
            Echo.Sync.ImageReference.renderImg(image, this._imgElement);
        }

        delete canvas;
        return false;
    },
    
    renderDispose: function(update) {
        delete this._divElement;
    },
    

    _createImgSrc: function(canvas, string) {

        canvas.width = 290;
        canvas.height = 80;

        var context = canvas.getContext('2d');
        
        // This adds the text functions to the 'context'
        CanvasTextFunctions.enable(context);

        // Create two complementary color gradients 
        var gradient = context.createLinearGradient(0, 0, 290, 80);
        gradient.addColorStop(0.0, '#f22');
        gradient.addColorStop(0.5, '#880');
        gradient.addColorStop(1.0, '#22f');
        
        var fgradient = context.createLinearGradient(0, 0, 290, 80);
        fgradient.addColorStop(0.0, '#0ff');
        fgradient.addColorStop(0.5, '#0f0');
        fgradient.addColorStop(1.0, '#ff0');
        
        // Fill a rectangle with the gradient
        context.fillStyle = gradient;
        context.fillRect(0, 0, 290, 80);
 
        var font = "sans";
        var fontsize = 48;
        
      
        var y = context.fontAscent(font, fontsize) + 10;
        context.strokeStyle = fgradient;
        context.drawTextCenter( font, fontsize, 290/2, y, string);
        context.drawTextCenter( font, fontsize, (290 + Math.random()*15)/2, y, string);

        context.strokeStyle = gradient;
        context.drawTextCenter( font, fontsize, (290 + Math.random()*10)/2, y, string);

        // Scratch up the image with some random lines
        context.strokeStyle = fgradient;
        for(var i=0; i<15; i++) {
            context.beginPath();
            context.moveTo(Math.random()*290, Math.random()*80);
            context.lineTo(Math.random()*290, Math.random()*80);
            context.stroke();
            context.closePath();
         }

 
        // Convert the canvas to a data URL which will be used to render the 'img' tag
       return canvas.toDataURL();
    
    },

    
    // String values used in the 191 premade CAPTCHA JPEG images; [0] -> 1.jpg.
    //  NB: Not very secure!!! Crackers could just refer to the table and to the img src
         
    _names : ["polish", "past",  "part",  "when","much",  "seed",  "soap",  "glove", "sticky","soap",
              "profit", "bent",  "collar","where","weight","again", "weight","boat",  "small", "profit",
              "sound",  "chin",  "flag",  "body", "salt",  "birth", "crime", "false", "sleep", "square",
              "canvas", "mine",  "safe",  "mark", "degree","bell",  "color", "expert","rule",  "parcel",
              "degree", "waste", "after", "army", "moon",  "brain", "news",  "silver","rain",  "stiff",
              "horse",  "smile", "shirt", "this", "grip",  "sharp", "knot",  "neck",  "woman", "smell",
              "round",  "linen", "same",  "right","adjust","jewel", "bell",  "pocket","green", "mother",
              "mine",   "rice",  "loss",  "tail", "foot",  "porter","spring","desire","screw", "spade",
              "bent",   "letter","glass", "sugar","fear",  "every", "muscle","right", "rate",  "butter",
              "sail",   "summer","snake", "wheel","sheep", "glove", "poison","tooth", "bucket","wood",
              "great",  "school","sudden","wind", "step",  "credit","pain",  "design","front", "push",
              "seem",   "cord",  "sound", "scale","with",  "wind",  "cloth", "screw", "garden","west",
              "judge",  "goat",  "animal","warm", "join",  "turn",  "school","white", "keep",  "basin",
              "tooth",  "face",  "range", "tight","nail",  "seem",  "female","public","potato","idea",
              "snake",  "flower","narrow","still","hope",  "glass", "lock",  "hand",  "face",  "fear",
              "copper", "debt",  "shoe",  "paint","butter","roll",  "blood", "story", "doubt", "meat",
              "offer",  "clean", "memory","like", "wrong", "jump",  "amount","regret","free",  "crush",
              "pull",   "dress", "door",  "male", "black", "please","flag",  "fact",  "nose",  "taste",
              "snake",  "cold",  "attack","crush","canvas","shame", "book",  "wound", "nation","fire",
              "good"]

});

