
var PIXELBLOCKS = {

    _logService  : null,
    _favIconURL : null,
    _savePath : null,

    /*
     * log
     *
     * Log debug messages to the Javascript console.
     * Note: javascript.options.showInConsole must be enabled
     */
    log : function (message) {
        //if (!this._debug) { return; }
        this._logService.logStringMessage("Pixel Blocks: " + message);
    },
    
    
    start : function (favIconURL, savePath) {
        this._favIconURL = favIconURL;
        this._savePath = savePath;
        
        //Initialize Logging
        this._logService = Components.classes['@mozilla.org/consoleservice;1'].getService();
        this._logService.QueryInterface(Components.interfaces.nsIConsoleService);

        PIXELBLOCKS.log("Starting Pixel Blocks Test");
        
        // We'll be needing a <canvas> element to do the image processing... It requires a
        // docshell (?) to work, so we'll stash it inside a hidden <iframe>.
        
        // Create an iframe, make it hidden, and secure it against untrusted content.
        var iframe = document.createElement('iframe');
        iframe.setAttribute("type", "content");
        
        
        // Insert the iframe into the window, creating the doc shell.
        document.documentElement.appendChild(iframe);
        
        // When we insert the iframe into the window, it immediately starts loading
        // about:blank, which we don't need and could even hurt us (for example
        // by triggering bugs like bug 344305), so cancel that load.
        var webNav = iframe.docShell.QueryInterface(Components.interfaces.nsIWebNavigation);
        webNav.stop(Ci.nsIWebNavigation.STOP_NETWORK);
        
        // TODO: trunk hack to avoid flashing white in content bug?
        iframe.setAttribute("hidden", "true");
               
        this._canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
        iframe.appendChild(this._canvas);
        
        //img = PIXELBLOCKS.downloadFavIcon("http://www.google.com/favicon.ico",false);
        //img = PIXELBLOCKS.downloadFavIcon("http://www.google.com/calendar/images/favicon.ico",false);
        //img = PIXELBLOCKS.downloadFavIcon("http://www.digg.com/favicon.ico",false); 
        //img = PIXELBLOCKS.downloadFavIcon("http://mail.google.com/mail/images/favicon.ico",false);
        //img = PIXELBLOCKS.downloadFavIcon("http://developer.mozilla.org/favicon.ico",false);
        //img = PIXELBLOCKS.downloadFavIcon("http://www.yahoo.com/favicon.ico",false);
        //img = PIXELBLOCKS.downloadFavIcon("http://www.msn.com/favicon.ico",false);
        
        img = PIXELBLOCKS.downloadFavIcon(this._favIconURL);
    },
    
    downloadFavIcon : function (url){
    
        PIXELBLOCKS.log("1/4  Downloading the favicon");
        
        var imgsrc = url;
        
        // Get the original image being used
        var img = new Image();
        img.src = imgsrc;
        
        if (!img.complete) {
            // The onload listener should always have the data, but if it somehow doesn't we'll just give up.
            /*if (!byEvent) {
                CHROMATABS.log("...favicon not yet loaded, deferring processing to onload handler.");
                img.addEventListener("load", function() { CHROMATABS.colorizeTab(tab, true); }, false);
            } else {
                CHROMATABS.log("...favicon not yet loaded, but we're the onload handler! WTF!!!.");
            }*/
            img.addEventListener("load", function() { PIXELBLOCKS.downloadFavIconComplete(img); }, false);
            //PIXELBLOCKS.log("Set event listener for download complete");
            return null;
        }
        else{
            //the image was in cache
            //PIXELBLOCKS.log("The favicon was in cache");
            PIXELBLOCKS.downloadFavIconComplete(img);
        }  
    },
    
    downloadFavIconComplete : function (img){
        
        PIXELBLOCKS.log("2/4  Favicon download is complete");
        
        var canvas = PIXELBLOCKS._canvas;
	    var ctx = canvas.getContext("2d");
        
        // Set canvas size to favicon size (we need to check if the source is actually bigger)
        canvas.setAttribute("width", 16);
        canvas.setAttribute("height", 16);
    
        // Draw original image to the canvas.
        ctx.drawImage(img, 0, 0, 16, 16);
        
        // Get the raw pixels. This returns an array of integers, 4 per pixel.
        // R, G, B, A,  R, G, B, A,  ...
        var pixelData = ctx.getImageData(0,0,16,16).data;  
        PIXELBLOCKS.createFusedIcon(pixelData);
          
    },
    
    createFusedIcon : function (pixelData){
    
        PIXELBLOCKS.log("3/4  Creating the fused icon");
    
        var canvas = PIXELBLOCKS._canvas;
	    var ctx = canvas.getContext("2d");
    
        //create a high res version of the favicon
        canvas.setAttribute("width", 972);
        canvas.setAttribute("height", 972);
    
    
        var pC = new Array(255); //pixelBlock color data
        var pL = new Array(255); //pixelBlock location data
        
        
        //reorder the pixel data to the draw order (it was top left to right down each row
        //it needs to be bottom left to right going up each row).
        
        var rowMarker = 256; //start in the left position of the bottom row
        var spotMarker = rowMarker;
        for (var i = 0; i < 1024; i=i+4) {

            if(i%64 == 0){
                rowMarker = rowMarker-16; //move up a row
                spotMarker = rowMarker;
            }
            
            pC[spotMarker] = new Array(4);
            pC[spotMarker][0] = pixelData[i];
            pC[spotMarker][1] = pixelData[i+1];
            pC[spotMarker][2] = pixelData[i+2];
            pC[spotMarker][3] = pixelData[i+3];
            
            spotMarker = spotMarker+1;         
        }
                        
        pL = PIXELBLOCKS.createGrid();
        
        //draw the fused icon, one pixel block at a time
        for(var i=0; i<256; i++){
            
            //set the color of the block
            ctx.fillStyle = "rgba(" + pC[i][0] + "," + pC[i][1] + "," + pC[i][2] + "," + pC[i][3] + ")";
            
            //set the intensity of the shadow
            var shadow = 0.5;
            
            //draw the front surface
            //ctx.fillStyle = "rgba(0,255,0,128)";
            ctx.beginPath();
            ctx.moveTo(pL[i][0][0],pL[i][0][1]);
            ctx.lineTo(pL[i][1][0],pL[i][1][1]);
            ctx.lineTo(pL[i][2][0],pL[i][2][1]);
            ctx.lineTo(pL[i][3][0],pL[i][3][1]);
            ctx.lineTo(pL[i][0][0],pL[i][0][1]);
            ctx.fill();
            
            //draw the top surface
            //ctx.fillStyle = "rgba(255,0,0,128)";
            ctx.beginPath();
            ctx.moveTo(pL[i][0][0],pL[i][0][1]);
            ctx.lineTo(pL[i][1][0],pL[i][1][1]);
            ctx.lineTo(pL[i][5][0],pL[i][5][1]);
            ctx.lineTo(pL[i][4][0],pL[i][4][1]);
            ctx.lineTo(pL[i][0][0],pL[i][0][1]);
            ctx.fill();
            
            //draw a shadow on the top surface
            if(!pC[i][3] < 1){
                ctx.fillStyle = "rgba(0,0,0,"+ shadow +")";
                ctx.beginPath();
                ctx.moveTo(pL[i][0][0],pL[i][0][1]);
                ctx.lineTo(pL[i][1][0],pL[i][1][1]);
                ctx.lineTo(pL[i][5][0],pL[i][5][1]);
                ctx.lineTo(pL[i][4][0],pL[i][4][1]);
                ctx.lineTo(pL[i][0][0],pL[i][0][1]);
                ctx.fill();
            }
            
            //draw the right side surface
            //ctx.fillStyle = "rgba(0,0,255,128)";
            ctx.beginPath();
            ctx.moveTo(pL[i][1][0],pL[i][1][1]);
            ctx.lineTo(pL[i][5][0],pL[i][5][1]);
            ctx.lineTo(pL[i][6][0],pL[i][6][1]);
            ctx.lineTo(pL[i][2][0],pL[i][2][1]);
            ctx.lineTo(pL[i][1][0],pL[i][1][1]);
            ctx.fill();
            
            //draw a shadow on the right side surface
            if(!pC[i][3] < 1){
                ctx.fillStyle = "rgba(0,0,0,"+ shadow +")";
                ctx.beginPath();
                ctx.moveTo(pL[i][1][0],pL[i][1][1]);
                ctx.lineTo(pL[i][5][0],pL[i][5][1]);
                ctx.lineTo(pL[i][6][0],pL[i][6][1]);
                ctx.lineTo(pL[i][2][0],pL[i][2][1]);
                ctx.lineTo(pL[i][1][0],pL[i][1][1]);
                ctx.fill();
            }
        }
    
        //save the canvas to a png
        //PIXELBLOCKS.saveCanvas(PIXELBLOCKS._canvas,"/Users/Alex/Documents/Mozilla/Tasks/PixelBLocks/pixelBlocksTest.png");
        PIXELBLOCKS.saveCanvas(PIXELBLOCKS._canvas,PIXELBLOCKS._savePath);
    },
    
    saveCanvas: function (canvas, destFile) {
         PIXELBLOCKS.log("4/4  Converting the fused icon to a png");
         
         // convert string filepath to an nsIFile
          var file = Components.classes["@mozilla.org/file/local;1"]
                               .createInstance(Components.interfaces.nsILocalFile);
          file.initWithPath(destFile);
        
          // create a data url from the canvas and then create URIs of the source and targets  
          var io = Components.classes["@mozilla.org/network/io-service;1"]
                             .getService(Components.interfaces.nsIIOService);
          var source = io.newURI(canvas.toDataURL("image/png", ""), "UTF8", null);
          var target = io.newFileURI(file)
          
          // prepare to save the canvas data
          var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
                                  .createInstance(Components.interfaces.nsIWebBrowserPersist);
          
          persist.persistFlags = Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
          persist.persistFlags |= Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
          
          // displays a download dialog (remove these 3 lines for silent download)
          var xfer = Components.classes["@mozilla.org/transfer;1"]
                               .createInstance(Components.interfaces.nsITransfer);
          xfer.init(source, target, "", null, null, null, persist);
          persist.progressListener = xfer;
          
          // save the canvas data to the file
          persist.saveURI(source, null, null, null, null, file);
    },
    
    
    createGrid: function (){
        
        var pL = new Array(255);
        
        //set the points for a 2 dimensional view
        var x = 0;
        var y = 16;
        var s = 48;  //stride
        var oX = 102; //horizontal offset so icon is in the center of the canvas
        var oY = -25;
        
        for(var i=0; i<=255; i++){
            
            if(i%16 == 0){ //move up a row
                x = 0;
                y = y-1;
            }
            
            pL[i] = new Array(8);
            pL[i][0] = [x*s+oX,y*s+oY];
            pL[i][1] = [(x+1)*s+oX,y*s+oY];
            pL[i][2] = [(x+1)*s+oX,(y+1)*s+oY];
            pL[i][3] = [x*s+oX,(y+1)*s+oY];
            pL[i][4] = [pL[i][0][0],pL[i][0][1]];
            pL[i][5] = [pL[i][1][0],pL[i][1][1]];
            pL[i][6] = [pL[i][2][0],pL[i][2][1]];
            pL[i][7] = [pL[i][3][0],pL[i][3][1]];
            
            //move one space left
            x=x+1;
        }
        
        //enter the third dimension
        //variables for setting 3D view
        var view = [.25,.25,.25];
        
        for(var i=0; i<=255; i++){
            pL[i][0] = [PIXELBLOCKS.mX(pL[i][0],s,view),PIXELBLOCKS.mY(pL[i][0],s,view)];
            pL[i][1] = [PIXELBLOCKS.mX(pL[i][1],s,view),PIXELBLOCKS.mY(pL[i][1],s,view)];
            pL[i][2] = [PIXELBLOCKS.mX(pL[i][2],s,view),PIXELBLOCKS.mY(pL[i][2],s,view)];
            pL[i][3] = [PIXELBLOCKS.mX(pL[i][3],s,view),PIXELBLOCKS.mY(pL[i][3],s,view)];
            pL[i][4] = [PIXELBLOCKS.mX(pL[i][4],0,view),PIXELBLOCKS.mY(pL[i][4],0,view)];
            pL[i][5] = [PIXELBLOCKS.mX(pL[i][5],0,view),PIXELBLOCKS.mY(pL[i][5],0,view)];
            pL[i][6] = [PIXELBLOCKS.mX(pL[i][6],0,view),PIXELBLOCKS.mY(pL[i][6],0,view)];
            pL[i][7] = [PIXELBLOCKS.mX(pL[i][7],0,view),PIXELBLOCKS.mY(pL[i][7],0,view)];
        }
        
        //return the grid data
        return pL;
    },
    
    //change in x
    mX : function (point,z,view) {
        
        var x = point[0];
        var b = view[1];
        
        return x - (z * b);
    },
    
    //change in y
    mY : function (point,z,view) {
        
        var x = point[0];
        var y = point[1];
        var a = view[0];
        var c = view[2];
        
        return y + (x * a) + (z * c);
    },


};

