/************************************************************
* cTableCanvas
* Drawing canvas using a html-table
* date: 2008/06/20 
* author: manuel ruelke
* THIS CODE IS FREE TO USE. IFYOU FIND IT USEFUL, DROP ME LINE.
* manuel at ruelke.net

usage example:

    // create the canvas object
    var canvas = new cTableCacheCanvas();

    // initialize the 50 by 40 canvas with a pixel size of 2x2
    canvas.Init(50, 40, 2, 2);

    // clear the screen (fill color black)
    canvas.ClearScreen("#000000");

    // set the pixel at 10,5 to be magenta
    canvas.PutPixel(10,5,"#FF00FF");

************************************************************/

function cTableCacheCanvas()
{
    // private enum
    var SPR_X = 0;      // sprite table accessors
    var SPR_Y = 1;
    var SPR_C = 2;
    var SPR_CHUNK = 3;
    
    // public enum
    this.MODE_WRAP_X = 1;  // draw wrap mode (x wrap)
    
    // member
    var m_width = 0;    // width of canvas in pixels
    var m_height = 0;   // height of canvas in pixels
    
    var m_pxWidth = 0;  // width of canvas pixel in real screen pixels
    var m_pxHeight = 0// height of canvas pixel in real screen pixels

    var m_idMatrix;     // holds the ids for the "pixels"
    var m_cellCache;    // references to the cells

    var m_errorOccured; // error message only once

    // public methods

    /******************************************************************
    Initializes the canvas
    @param _width width of the canvas
    @param _height height of the canvas
    @param _pixelWidth width of the canvas pixels in real screen pixels
    @param _pixelHeight height of the canvas pixels in real screen pixels
    ******************************************************************/
    this.Init = function(_width, _height, _pixelWidth, _pixelHeight)
    {
        m_width = _width;
        m_height = _height;
        m_pxWidth = _pixelWidth;
        m_pxHeight = _pixelHeight;
        CreateIdMatrix();
        CreateTableCanvas();
        CreateCellReferences();
        
        m_errorOccured = false;
    }

    /******************************************************************
    Clears the screen using a clear color
    @param _col clear color
    ******************************************************************/
    this.ClearScreen = function(_col)
    {
        var imax = m_width*m_height;
        for (var i = imax-1; i >= 0; i--)
            PutPixelFast(i, _col);
    }
    
    /******************************************************************
    Sets a pixel on the canvas, ignores pixels outside of the screen
    @param _x   x-coord of the pixel in canvas coords
    @param _y   y-coord of the pixel in canvas coords
    @param _col color of the pixel
    ******************************************************************/
    this.PutPixel = function (_x, _y, _col)
    {
        if (_x < || _y < || _x >= m_width || _y >= m_height )
            return;
        // I am sacrificing all out-of-bounds checks for speed :-(
        m_cellCache[_y*m_width+_x].style.backgroundColor = _col;
    }

    /******************************************************************
    Draws a sprite object on the canvas
    
    this is a sample of a sprite object:
    
        SpriteTest = new function()
        {
            // color table
            this.p=new Array("#41DEFF","#006980","#00768F","#004959");  
            
            // triplets consisting of x, y, color id
            this.d=new Array(0,0,0,0,1,1,1,0,2,1,1,3);
            
            // sprite width and height
            this.w=2;
            this.h=2;   
        }    
        
        canvas.DrawSprite(SpriteTest, 5, 5);
        
    @param _spr sprite object
    @param _x   x-coord of the pixel in canvas coords
    @param _y   y-coord of the pixel in canvas coords
    ******************************************************************/
    this.DrawSprite = function(_spr, _x, _y)
    {
        for (var i = _spr.d.length - SPR_CHUNK; i >= 0; i-=SPR_CHUNK)
        {
            this.PutPixel(_x + _spr.d[i+SPR_X], _y + _spr.d[i+SPR_Y], _spr.p[_spr.d[i+SPR_C]]);
        }
    }    
    
    /******************************************************************
    Draws all non-transparent pixels of a sprite object
    
    @param _spr sprite object
    @param _x   x-coord of the pixel in canvas coords
    @param _y   y-coord of the pixel in canvas coords
    @param _col color to use
    ******************************************************************/
    
    this.DrawSpriteMask = function(_spr, _x, _y, _col)
    {
        for (var i = _spr.d.length - SPR_CHUNK; i >= 0; i-=SPR_CHUNK)
        {
            this.PutPixel(_x + _spr.d[i+SPR_X], _y + _spr.d[i+SPR_Y], _col);
        }
    }        
    
    /******************************************************************
    Draws a sprite using a certain drawing mode
    @param _spr sprite object
    @param _x   x-coord of the pixel in canvas coords
    @param _y   y-coord of the pixel in canvas coords
    @param _mode only MODE_WRAP_X is implemented (wrap drawing in x direction)
    ******************************************************************/
    this.DrawSpriteMode = function(_spr, _x, _y, _mode)
    {
        for (var i = _spr.d.length - SPR_CHUNK; i >= 0; i-=SPR_CHUNK)
        {
            var x = _x + _spr.d[i+SPR_X];
            var y = _y + _spr.d[i+SPR_Y];
            if (_mode == this.MODE_WRAP_X)
            {
                if (x >= m_width)
                    x-=m_width;
                if (x < 0)
                    x+=m_width;
            }
            this.PutPixel(x, y, _spr.p[_spr.d[i+SPR_C]]);
        }
    }        

    // private methods

    /******************************************************************
    Create the html code for the canvas
    ******************************************************************/
    function CreateTableCanvas()
    {
        var d = document;
        // first open the container and the table
        var tablewidth = m_width * m_pxWidth;
        var tableheight = m_height * m_pxHeight;
   
        d.writeln("<div>");
        d.writeln('<table border="0" cellspacing="0" cellpadding="0" width="'+tablewidth+'" height="'+tableheight+'" style="width:'+tablewidth+'; height:'+tableheight+';">');
        
        // create the lines and the columns
        
        for (var y = 0; y < m_height; y++)
        {
            d.writeln("<tr>");
            for (var x = 0; x < m_width; x++)
            {
                d.writeln('<td id="'+m_idMatrix[y][x]+'" style="background-color: #000000;"></td>');
            }
            d.writeln("</tr>");
        }

        // close the table and container
        d.writeln("</table>")
        d.writeln("</div>");
    }

    /******************************************************************
    // create the id matrix to access the "pixels"
    ******************************************************************/
    function CreateIdMatrix()
    {
        // create the array
        m_idMatrix = new Array(m_height);
        for (var i = 0; i < m_height; i++)
        {
            m_idMatrix[inew Array(m_width);
        }
        // create the ids
        for (var y = 0; y < m_height; y++)
        {
            for (var x = 0; x < m_width; x++)
            {
                // create a unique id
                m_idMatrix[y][x"id" + x + "." + y;
            }
        }
    }
    
    /******************************************************************
    // create the references to the cells
    ******************************************************************/
    function CreateCellReferences()
    {
        // create the array
        m_cellCache = new Array(m_height*m_width);
        // create the ids
        for (var y = 0; y < m_height; y++)
        {
            for (var x = 0; x < m_width; x++)
            {
                // create a unique id
                var reference = document.getElementById(m_idMatrix[y][x]);
                if (reference)
                    m_cellCache[y*m_width+x= document.getElementById(m_idMatrix[y][x]);
                else
                {
                    if (m_errorOccured != true)
                    {
                        alert("Error while creating the cell references! The table cells are not present!");                
                        m_errorOccured = true;
                    }
                }
            }
        }
    }
    
    /******************************************************************
    // changes the color of a pixel, sets the color directly
    // without using the cell lookup table
    ******************************************************************/
    function PutPixelFast(_i, _col)
    {
        // I am sacrificing all out-of-bounds checks for speed :-(
        m_cellCache[_i].style.backgroundColor = _col;
    }        
}