﻿/*

*/
String.prototype.pad = function(l, s, t)
{
/// <summary>
/// Jonas Raoni Soares Silva
/// http://jsfromhell.com/string/pad
/// </summary>
    return s || (s = " "), (l -= this.length) > 0 ? (s = new Array(Math.ceil(l / s.length)
        + 1).join(s)).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2))
        + this + s.substr(0, l - t) : this;
};

function World(zoom, tilesize)
{
/// <summary>
/// Helper object for calculations regarding the 'world' tile-space
/// </summary>
    this.Zoom = zoom;
    this.TileSize = tilesize;
    this.Columns = Math.pow(2, this.Zoom);
    this.Rows = Math.pow(2, this.Zoom);
    this.PixelSize = this.ColumnCount * this.TileSize;
}

function Viewport(width, height, tilesize)
{
/// <summary>
/// Helper object for calculations regarding the 'viewport' tile-space
/// </summary>
    this.TileSize = tilesize;
    this.Width = width;
    this.Height = height;
    this.Columns = Math.floor(width/tilesize);
    this.Rows = Math.floor(height/tilesize);
}

function Point(x,y)
{
/// <summary>Point</summary>
    this.X = x;
    this.Y = y;
    
    this.ToBase2 = function()
    {
    /// <summary>(Y,X) 111,000 = 101010 (pattern is YXYXYX)</summary>
        var binary='';
        for (var p = 0; p < this.X.length; p++)
        {
            binary = binary + this.Y.charAt(p) + this.X.charAt(p);
        }
        return binary;
    }
}

function Tile(quadkey,zoom)
{
/// <summary>Tile class</summary>
/// <remarks>Models a tile</remarks>
    this.Quadkey = quadkey;
    this.Zoom   = zoom;
    this.Column = 0;
    this.Row    = 0;    
    
    //this.Latitude  = 0;
    //this.Longitude = 0;
    
    this.GetTopLeftQuadkey = function (deltaX, deltaY)
    {
    /// <summary>
    /// TODO: need different functions for the 'get relative' when choosing the new value
    ///       and when looping through
    /// </summary>
        var column = this.Column + deltaX;
        var row    = this.Row    + deltaY;
        
        var world = new World (zoom, TILE_SIZE);
        var viewport = new Viewport (512, 512, TILE_SIZE);
        if ( (column + viewport.Columns) > world.Columns) 
        {
            //alert ('column was ' + column + ' but now ' + (world.Columns - viewport.Columns) + ' ' + world.Columns + '-' +  viewport.Columns);
            column = world.Columns - viewport.Columns; 
        }
        else if (column < 0)
        {
            column = 0;
        } 
        if ( (row + viewport.Rows) > world.Rows) 
        {
            row = world.Rows - viewport.Rows; 
        }
        else if (row < 0)
        {
            row = 0;
        } 
        
        
        var binaryColumn = this.Base10ToBase2(column);    //Column.toString(2).pad(this.Zoom * 2, '0');
        var binaryRow    = this.Base10ToBase2(row);       //Row.toString(2).pad(this.Zoom * 2, '0');
        binXY = new Point (binaryColumn, binaryRow);
        var b2 = binXY.ToBase2();                //XYToBase2(binXY);
        var b4 = this.Base2ToBase4 (b2);         //parseInt(b2,2).toString(4).pad(zoom, '0')
        var relativeQuadkey = Base4ToQuadkey(b4);
        return relativeQuadkey;
    }
    this.GetRelativeQuadkey = function (deltaX, deltaY)
    {
    /// <summary>
    /// 
    /// </summary>
//        var qkb4   = QuadkeyToBase4 (this.Quadkey);
//        var binXY  = Base2ToXY (this.Base4ToBase2(qkb4)); //parseInt (qkb4,4).toString(2).pad(this.Zoom * 2, '0'));
//        var BinaryColumn = binXY.X;
//        var BinaryRow    = binXY.Y;
//        var Column = parseInt(binXY.X,2);
//        var Row    = parseInt(binXY.Y,2);
        
        var column = this.Column + deltaX;
        var row    = this.Row    + deltaY;
        
        var binaryColumn = this.Base10ToBase2(column);    //Column.toString(2).pad(this.Zoom * 2, '0');
        var binaryRow    = this.Base10ToBase2(row);       //Row.toString(2).pad(this.Zoom * 2, '0');
        binXY = new Point (binaryColumn, binaryRow);
        var b2 = binXY.ToBase2();                //XYToBase2(binXY);
        var b4 = this.Base2ToBase4 (b2);         //parseInt(b2,2).toString(4).pad(zoom, '0')
        var relativeQuadkey = Base4ToQuadkey(b4);
        return relativeQuadkey;
    }
    
    this.GetRelativeQuadkeyFromPixels = function (clickX, clickY)
    {
    /// <summary>
    /// Knowing this quadkey (assuming it is top-left), pass in a clicked-point and 
    /// work out which Quadkey was clicked
    /// </summary> 
        var column = this.Column + Math.floor(clickX / (TILE_SIZE/2) );
        var row    = this.Row    + Math.floor(clickY / (TILE_SIZE/2) );
        
        //alert(this.Column + ',' +this.Row +' cols adjusted for pixels: ' +row+','+column);
        
        var world = new World (zoom + 1, TILE_SIZE);
        var viewport = new Viewport (512, 512, TILE_SIZE);
        if ( (column + viewport.Columns) > world.Columns) 
        {
            //alert ('columns');
            column = world.Columns - viewport.Columns; 
        }
        else if (column < 0)
        {
            column = 0;
        } 
        if ( (row + viewport.Rows) > world.Rows) 
        {
            row = world.Rows - viewport.Rows; 
        }
        else if (row < 0)
        {
            row = 0;
        }
        
        var binaryColumn = this.Base10ToBase2(column);    //Column.toString(2).pad(this.Zoom * 2, '0');
        var binaryRow    = this.Base10ToBase2(row);       //Row.toString(2).pad(this.Zoom * 2, '0');
        binXY = new Point (binaryColumn, binaryRow);
        var b2 = binXY.ToBase2();                //XYToBase2(binXY);
        var b4 = this.Base2ToBase4 (b2);         //parseInt(b2,2).toString(4).pad(zoom, '0')
        var relativeQuadkey = Base4ToQuadkey(b4);
        return relativeQuadkey;
    }
    
    this.Base2ToBase4 = function (binary)
    {
    /// <example>
    /// 001111 = 033
    /// 011011 = 123
    /// 101111 = 233
    /// </example>
        return parseInt(binary,2).toString(4).pad(this.Zoom, '0');
    }
    
    this.Base4ToBase2 = function (base4)
    {
    /// <example>
    /// 231 = 101101
    /// 333 = 111111
    /// </example>
        return parseInt(base4,4).toString(2).pad(this.Zoom * 2, '0');
    }
    
    this.Base10ToBase2 = function (base10)
    {
    /// <example>
    /// 10 = 1010
    /// 15 = 1111
    /// </example>
        return base10.toString(2).pad(this.Zoom * 2, '0');
    }
    
    // "constructor" logic at end, after functions are added
    var qkb4   = QuadkeyToBase4 (this.Quadkey);
    var binXY  = Base2ToXY (this.Base4ToBase2(qkb4)); //parseInt (qkb4,4).toString(2).pad(this.Zoom * 2, '0'));
    var BinaryColumn = binXY.X;
    var BinaryRow    = binXY.Y;
    this.Column = parseInt(binXY.X,2);
    this.Row    = parseInt(binXY.Y,2);
}
