/*
  GSV 1.0, by Michal Migurski <mike-gsv@teczno.com>
  $Id: viewer.js,v 1.1 2009/01/11 02:08:01 wannamak Exp $

  Modified by Keith@Wannamaker.org
  Modification reuse prohibited without permission 
  
  License:
    Copyright (c) 2005 Michal Migurski <mike-gsv@teczno.com>
    
    Redistribution and use in source form, with or without modification,
    are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.
    2. The name of the author may not be used to endorse or promote products
       derived from this software without specific prior written permission.
    
    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/   

function hideContextMenu() {
    var imageViewer = document.body.imageViewers[0];
    if (imageViewer != null) {
      if (imageViewer.offa_contextMenu.style.visibility == 'visible') {
        imageViewer.offa_contextMenu.style.visibility = "hidden";
      }
      if (imageViewer.ona_contextMenu.style.visibility == 'visible') {
        imageViewer.ona_contextMenu.style.visibility = "hidden";
      }
    }
}

function getEvent(event)
{
    if(event == undefined) {
        return window.event;
    }
    
    return event;
}

function prepareViewer(imageViewer, tileDir, tileSize, cfg)
{
    for(var child = imageViewer.firstChild; child; child = child.nextSibling) {
        if(child.className == 'surface') {
            imageViewer.activeSurface = child;
            child.imageViewer = imageViewer;
        
        } else if(child.className == 'well') {
            imageViewer.tileWell = child;
            child.imageViewer = imageViewer;
        
        } else if(child.className == 'status') {
            imageViewer.status = child;
            child.imageViewer = imageViewer;
        } 
    }

    imageViewer.ona_contextMenu = document.getElementById("ona_contextmenu");
    imageViewer.offa_contextMenu = document.getElementById("offa_contextmenu");
    
    var width = 750;
    var height = 500;

    var zoomLevel = 1; 
    // our viewer is not square
    var fullHeight = 3 * tileSize;
    var fullWidth = 4 * tileSize;
    var center = { 'x': -1, 'y': -tileSize/2 };

    imageViewer.style.width = width+'px';
    imageViewer.style.height = height+'px';
    
    var top = 0;
    var left = 0;
    for(var node = imageViewer; node; node = node.offsetParent) {
        top += node.offsetTop;
        left += node.offsetLeft;
    }

    imageViewer.dimensions = {

         // width and height of the viewer in pixels
         'width': width, 'height': height,

         // position of the viewer in the document, from the upper-left corner
         'top': top, 'left': left,

         // location and height of each tile; they're always square
         'tileDir': tileDir, 
         'tileSize': tileSize,

         // >= 1, bigger # = zoomed out
         'zoomLevel': zoomLevel,

         // initial viewer position
         // defined as window-relative x,y coordinate of upper-left hand corner of complete image
         // usually negative. constant until zoomLevel changes
         'x': center.x, 'y': center.y

         };
         
    imageViewer.cfg = cfg;
    imageViewer.start = {'x': 0, 'y': 0}; // this is reset each time that the mouse is pressed anew
    imageViewer.pressed = false;

    if(document.body.imageViewers == undefined) {
        document.body.imageViewers = [imageViewer];
        document.body.onmouseup = releaseViewer;
    } else {
        document.body.imageViewers.push(imageViewer);
    }

    prepareTiles(imageViewer);
}

//called from zoom or lookup
function update() {
  var iv = document.body.imageViewers[0];

  iv.dimensions.x = - 1;
  iv.dimensions.y = - (iv.dimensions.tileSize/2);
  iv.start.x = 0;
  iv.start.y = 0;
  
  var tiles = iv.tiles;
  for(var c = 0; c < tiles.length; c += 1) {
    for(var r = 0; r < tiles[c].length; r += 1) {
       var tile = tiles[c][r];
       tile.c = c;
       tile.r = r;
    }
  }
  
  positionTiles(iv, {'x': 0, 'y': 0}, true);
}

function prepareTiles(imageViewer)
{
    var activeSurface = imageViewer.activeSurface;
    var tileWell = imageViewer.tileWell;
    var dim = imageViewer.dimensions;

    imageViewer.tiles = [];
    
    var rows = 3;
    var cols = 4; 
    
//    displayStatus(imageViewer, 'rows: '+rows+', cols: '+cols);
    
    for(var c = 0; c < cols; c += 1) {
        var tileCol = [];
    
        for(var r = 0; r < rows; r += 1) {

            var tile = {'c': c, 
                        'r': r, 
                        'img': document.createElement('img'), 
                        'imageViewer': imageViewer
                        };

            tile.img.className = 'tile';
            tile.img.style.width = dim.tileSize+'px';
            tile.img.style.height = dim.tileSize+'px';
            setTileImage(tile, true);
            tileWell.appendChild(tile.img);
            tileCol.push(tile);
        }
        
        imageViewer.tiles.push(tileCol);
    }
    
    activeSurface.onmousedown = pressViewer;
    activeSurface.oncontextmenu = triggerContextMenu;
    positionTiles(imageViewer, {'x': 0, 'y': 0}); // x, y should match imageViewer.start x, y
}

function triggerContextMenu(event) {
    var imageViewer = document.body.imageViewers[0];
    var dim = imageViewer.dimensions;
    var ev = getEvent(event);
    var mouse = localizeCoordinates(imageViewer, {'x': ev.clientX, 'y': ev.clientY});
    //todo hard-coded
    var geo = imageViewer.cfg.getGeo(mouse.x, mouse.y, 750, 500, imageViewer.dimensions.zoomLevel);
    imageViewer.cfg.params.lastContextLongitude = geo[1];
    imageViewer.cfg.params.lastContextLatitude = geo[0];
    imageViewer.cfg.params.lastContextLeft = ev.clientX + document.body.scrollLeft;
    imageViewer.cfg.params.lastContextTop = ev.clientY + document.body.scrollTop;
    contextRequest(imageViewer);
    return false;
}

function positionTiles(imageViewer, mouse, reloadall)
{
    var tiles = imageViewer.tiles;
    var dim = imageViewer.dimensions;
    var start = imageViewer.start;

    var statusTextLines = [];
//    statusTextLines.push('imageViewer.dimensions x,y: '+dim.x+','+dim.y);
    
    for(var c = 0; c < tiles.length; c += 1) {
        for(var r = 0; r < tiles[c].length; r += 1) {

            var tile = tiles[c][r];
            
            // wrappedAround will become true if any tile has to be wrapped around
            var wrappedAround = false;

            tile.x = (tile.c * dim.tileSize) + dim.x + (mouse.x - start.x);
            tile.y = (tile.r * dim.tileSize) + dim.y + (mouse.y - start.y);
            
            if(tile.x > dim.width) {
                // tile is too far to the right
                // shift it to the far-left until it's within the viewer window
                setTileImage(tile, true);
                do {
//                    statusTextLines.push('tile ' + tile.c + ' ' + tile.r + ' is too far right');
                    tile.c -= tiles.length;
                    tile.x = (tile.c * dim.tileSize) + dim.x + (mouse.x - start.x);
                    wrappedAround = true;

                } while(tile.x > dim.width);
//statusTextLines.push('new tile is ' + tile.c + ' ' + tile.r);

            } else {
                // tile may be too far to the left
                // if it is, shift it to the far-right until it's within the viewer window
                if (tile.x < (-1 * dim.tileSize)) {
                  setTileImage(tile, true);
                }
                while(tile.x < (-1 * dim.tileSize)) {
//statusTextLines.push('tile ' + tile.c + ' ' + tile.r + ' is too far left - shift it right');
                    tile.c += tiles.length;
                    tile.x = (tile.c * dim.tileSize) + dim.x + (mouse.x - start.x);
                    wrappedAround = true;

                }
            }
            
            if(tile.y > dim.height) {
                // tile is too far down
                // shift it to the very top until it's within the viewer window
                setTileImage(tile, true);
//statusTextLines.push('tile ' + tile.c + ' ' + tile.r + ' is too far down');
                do {
//statusTextLines.push('tile ' + tile.c + ' ' + tile.r + ' is too far right - shift it left');
                    tile.r -= tiles[c].length;
                    tile.y = (tile.r * dim.tileSize) + dim.y + (mouse.y - start.y);
                    wrappedAround = true;

                } while(tile.y > dim.height);
//statusTextLines.push('new tile is ' + tile.c + ' ' + tile.r);

            } else {
                // tile may be too far up
                // if it is, shift it to the very bottom until it's within the viewer window
                if (tile.y < (-1 * dim.tileSize)) {
                  setTileImage(tile, true);
                }
                while(tile.y < (-1 * dim.tileSize)) {
//statusTextLines.push('tile ' + tile.c + ' ' + tile.r + ' is too far up- shift it down');
                
                    tile.r += tiles[c].length;
                    tile.y = (tile.r * dim.tileSize) + dim.y + (mouse.y - start.y);
                    wrappedAround = true;

                }
            }

            
            // set the tile image once to *maybe* null, then again to
            // definitely the correct tile. this removes the wraparound
            // artifacts seen over slower connections.
            //setTileImage(tile, wrappedAround);
            if (reloadall || tile.img.src.indexOf('/sectional/blank.png') > -1) {
              setTileImage(tile, false);
            }
//statusTextLines.push('tile '+c+','+r+' at '+tile.c+','+tile.r+ ' with src = ' + tile.img.src);

            tile.img.style.top = tile.y+'px';
            tile.img.style.left = tile.x+'px';
        }
    }
    
//    displayStatus(imageViewer, statusTextLines.join('<br>'));
}

function setTileImage(tile, nullOverride)
{
    var dim = tile.imageViewer.dimensions;

    // request a particular image slice
    //dim.zoomLevel
    //tile.c
    //tile.r
    var src = '/sectional/tiles?x='+(tile.c-1)+'&y='+(tile.r-1)+'&z='+dim.zoomLevel+
      '&la=' + tile.imageViewer.cfg.params.latitude +
      '&lo=' + tile.imageViewer.cfg.params.longitude;
    if(nullOverride)     { src = '/sectional/blank.png';            }

    tile.img.src = src;
}

function moveViewer(event)
{
    var imageViewer = this.imageViewer;
    var ev = getEvent(event);
    var mouse = localizeCoordinates(imageViewer, {'x': ev.clientX, 'y': ev.clientY});
    
//    displayStatus(imageViewer, 'move viewer.  mouse at: '+mouse.x+', '+mouse.y+', '+(imageViewer.tiles.length * imageViewer.tiles[0].length)+' tiles to process');
    positionTiles(imageViewer, {'x': mouse.x, 'y': mouse.y});
}

function localizeCoordinates(imageViewer, client)
{
    var local = {'x': client.x, 'y': client.y};

    for(var node = imageViewer; node; node = node.offsetParent) {
        local.x -= node.offsetLeft;
        local.y -= node.offsetTop;
    }
    
    return local;
}

function pressViewer(event)
{
    hideContextMenu();
    
    var imageViewer = this.imageViewer;
    var dim = imageViewer.dimensions;
    var ev = getEvent(event);
    var mouse = localizeCoordinates(imageViewer, {'x': ev.clientX, 'y': ev.clientY});

    imageViewer.pressed = true;
    imageViewer.tileWell.style.cursor = imageViewer.activeSurface.style.cursor = 'move';
    
    imageViewer.start = {'x': mouse.x, 'y': mouse.y};
    this.onmousemove = moveViewer;

//    displayStatus(imageViewer, 'mouse pressed at '+mouse.x+','+mouse.y);
}

function releaseViewer(event)
{
    var ev = getEvent(event);
    
    for(var i = 0; i < document.body.imageViewers.length; i += 1) {
        var imageViewer = document.body.imageViewers[i];
        var mouse = localizeCoordinates(imageViewer, {'x': ev.clientX, 'y': ev.clientY});
        var dim = imageViewer.dimensions;

        if(imageViewer.pressed) {
            imageViewer.activeSurface.onmousemove = null;
            imageViewer.tileWell.style.cursor = imageViewer.activeSurface.style.cursor = 'default';
            imageViewer.pressed = false;

            var offsetx = (mouse.x - imageViewer.start.x);
            var offsety = (mouse.y - imageViewer.start.y);
            
            if (offsetx != 0 || offsety != 0) {
              //does not hurt to update but may save time
              dim.x += offsetx;
              dim.y += offsety;

              imageViewer.cfg.updateEstimatedLocation(
                (imageViewer.start.x - mouse.x) * imageViewer.dimensions.zoomLevel,
                (imageViewer.start.y - mouse.y) * imageViewer.dimensions.zoomLevel);

//              displayStatus(imageViewer, 'move : ' + xoffset + ' ' + yoffset + ' ' +
//                               'new estimated center: ' + imageViewer.estimatedLocation.longitude + ' ' +
//                                                          imageViewer.estimatedLocation.latitude);
            }
        }


//        displayStatus(imageViewer, 'mouse dragged from '+imageViewer.start.x+', '+imageViewer.start.y+' to '+mouse.x+','+mouse.y+'. image: '+dim.x+','+dim.y);
    }
}

function displayStatus(imageViewer, message)
{
    imageViewer.status.innerHTML = message;
}

function dumpInfo(imageViewer)
{
    var dim = imageViewer.dimensions;
    var tiles = imageViewer.tiles;

    var statusTextLines = ['imageViewer '+(i + 1), 'current window position: '+dim.x+','+dim.y+'.', '----'];

    for(var c = 0; c < tiles.length; c += 1) {
        for(var r = 0; r < tiles[c].length; r += 1) {
            statusTextLines.push('image ('+c+','+r+') has tile ('+dim.zoomLevel+','+tiles[c][r].c+','+tiles[c][r].r+')');
        }
    }
    
    alert(statusTextLines.join("\n"));
}

function dumpAllInfo()
{
    for(var i = 0; i < document.body.imageViewers.length; i += 1) {
        dumpInfo(document.body.imageViewers[i]);
    }
}

function zoomImageUp(imageViewer)
{
   if (imageViewer.dimensions.zoomLevel > 1) {
     hideContextMenu();
     imageViewer.dimensions.zoomLevel --;
//      displayStatus(imageViewer, 'est lat lon: ' + imageViewer.estimatedLocation.latitude + ' '+ imageViewer.estimatedLocation.longitude);
     imageViewer.cfg.resetToEstimate();
     update();
   }
}

function zoomImageDown(imageViewer)
{
    if (imageViewer.dimensions.zoomLevel < 3) {
      hideContextMenu();
      imageViewer.dimensions.zoomLevel ++;
      imageViewer.cfg.resetToEstimate();
//      displayStatus(imageViewer, 'est lat lon: ' + imageViewer.estimatedLocation.latitude + ' '+ imageViewer.estimatedLocation.longitude);
      update();
    }
}
;