/*

  Jonathon Bellew for Something Splendid, based on
  earlier works by Michal Migurski <mike-gsv@teczno.com>
  
*/   

var globalmouse;

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

function prepareViewer(imageViewer, tileDir, tileSize)
{
    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;
        
        }
    }
    
    var width = imageViewer.offsetWidth;
    var height = imageViewer.offsetHeight;
    var zoomLevel = -1; // guaranteed at least one increment below, so start at less-than-zero
    var fullSize = tileSize * Math.pow(2, zoomLevel); // full pixel size of the image at this zoom level
    do {
        zoomLevel += 1;
        fullSize *= 2;
    } while(fullSize < Math.max(width, height));

    var center = {'x': ((fullSize - width) / -2), 'y': ((fullSize - height) / -2)}; // top-left pixel of viewer, if it were to be centered in the view window

    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,

         // zero or higher; big number == big image, lots of tiles
         '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.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);
}


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

    imageViewer.tiles = [];
    
    var rows = Math.ceil(dim.height / dim.tileSize) + 1;
    var cols = Math.ceil(dim.width / dim.tileSize) + 1;
    
    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.ondblclick = jonnozoom;
    positionTiles(imageViewer, {'x': 0, 'y': 0}); // x, y should match imageViewer.start x, y
}

function positionTiles(imageViewer, mouse)
{
    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
                do {
                    tile.c -= tiles.length;
                    tile.x = (tile.c * dim.tileSize) + dim.x + (mouse.x - start.x);
                    wrappedAround = true;

                } while(tile.x > dim.width);

            } else {
                // tile may be too far to the right
                // if it is, shift it to the far-right until it's within the viewer window
                while(tile.x < (-1 * dim.tileSize)) {
                    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
                do {
                    tile.r -= tiles[c].length;
                    tile.y = (tile.r * dim.tileSize) + dim.y + (mouse.y - start.y);
                    wrappedAround = true;

                } while(tile.y > dim.height);

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

                }
            }

            statusTextLines.push('tile '+r+','+c+' at '+tile.c+','+tile.r);
            
            // 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);
            setTileImage(tile, false);

            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
    var src = dim.tileDir+'/zine-'+dim.zoomLevel+'-'+tile.c+'-'+tile.r+'.jpg';

    // has the image been scrolled too far in any particular direction?
    var left = tile.c < 0;
    var high = tile.r < 0;
    var right = tile.c >= Math.pow(2, tile.imageViewer.dimensions.zoomLevel);
    var low = tile.r >= Math.pow(2, tile.imageViewer.dimensions.zoomLevel);
    var outside = high || left || low || right;

         if(nullOverride)     { src = 'tiles/none.gif';          }

    // note this "outside" clause overrides all those below
    else if(outside)          { src = 'tiles/none.gif';   }
                                                      
    else if(high && left)     { src = 'tiles/none.gif';  }
    else if(low  && left)     { src = 'tiles/none.gif';  }
    else if(high && right)    { src = 'tiles/none.gif';  }
    else if(low  && right)    { src = 'tiles/none.gif';  }
    else if(high)             { src = 'tiles/none.gif';  }
    else if(right)            { src = 'tiles/none.gif';  }
    else if(low)              { src = 'tiles/none.gif';  }
    else if(left)             { src = 'tiles/none.gif';  }

    tile.img.src = 'tiles/blank.gif';
    tile.img.src = src;
}

function moveViewer(event)
{
    // alert('moving');
    var imageViewer = this.imageViewer;
    var ev = getEvent(event);
    var mouse = localizeCoordinates(imageViewer, {'x': ev.clientX, 'y': ev.clientY});

    displayStatus(imageViewer, '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 getScrollXY() {
  var scrOfX = 0, scrOfY = 0;
  if( typeof( window.pageYOffset ) == 'number' ) {
    //Netscape compliant
    scrOfY = window.pageYOffset;
    scrOfX = window.pageXOffset;
  } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
    //DOM compliant
    scrOfY = document.body.scrollTop;
    scrOfX = document.body.scrollLeft;
  } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
    //IE6 standards compliant mode
    scrOfY = document.documentElement.scrollTop;
    scrOfX = document.documentElement.scrollLeft;
  }
  return [ scrOfX, scrOfY ];
}

function localizeCoordinates(imageViewer, client)
{

    // // MINE
    // 
    // var curleft = curtop = 0;
    // obj = imageViewer;
    // if (obj.offsetParent) {
    //  do {
    //        curleft += obj.offsetLeft;
    //        curtop += obj.offsetTop;
    //      } while (obj = obj.offsetParent);
    // 
    // 
    // }
    // 
    // scrolls = getScrollXY();
    // scrollX = scrolls[0];
    // scrollY = scrolls[1];
    // 
    // var local = {'x': client.x - curleft + scrollX, 'y': client.y - curtop + scrollY};
    //       
    // // console.debug(local);
    // return local;

    // THEIRS
    
    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)
{
    // alert('press');
    var imageViewer = this.imageViewer;
    var dim = imageViewer.dimensions;
    var ev = getEvent(event);
    var mouse = localizeCoordinates(imageViewer, {'x': ev.clientX, 'y': ev.clientY});
    // globalmouse = mouse;
    globalmouse = {'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 jonnozoom(event) {
  var imageViewer = this.imageViewer;
  var dim = imageViewer.dimensions;
  var ev = getEvent(event);
  var mouse = localizeCoordinates(imageViewer, {'x': ev.clientX, 'y': ev.clientY});
  // console.debug(mouse);
  zoomImage(document.getElementById('imageViewer'), mouse, 1), 
  releaseViewer(event);  
}

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;

            dim.x += (mouse.x - imageViewer.start.x);
            dim.y += (mouse.y - imageViewer.start.y);
        }

        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 zoomImage(imageViewer, mouse, direction)
{
  var dim = imageViewer.dimensions;  
  if (dim.zoomLevel + direction < 7) {

    
    if(mouse == undefined) {
        var mouse = {'x': dim.width / 2, 'y': dim.height / 2};
    }

    // console.debug(dim.zoomLevel);

    var pos = {'before': {'x': 0, 'y': 0}};

    // pixel position within the image is a function of the
    // upper-left-hand corner of the viewe in the page (pos.before),
    // the click position (event), and the image position within
    // the viewer (dim).
    pos.before.x = (mouse.x - pos.before.x) - dim.x;
    pos.before.y = (mouse.y - pos.before.y) - dim.y;
    pos.before.width = pos.before.height = Math.pow(2, dim.zoomLevel) * dim.tileSize;
    
    var statusMessage = ['at current zoom level, image is '+pos.before.width+' pixels wide',
                         '...mouse position is now '+pos.before.x+','+pos.before.y+' in the full image at zoom '+dim.zoomLevel,
                         '...with the corner at '+dim.x+','+dim.y];

    if(dim.zoomLevel + direction >= 0) {
        pos.after = {'width': (pos.before.width * Math.pow(2, direction)), 'height': (pos.before.height * Math.pow(2, direction))};
        statusMessage.push('at zoom level '+(dim.zoomLevel + direction)+', image is '+pos.after.width+' pixels wide');

        pos.after.x = pos.before.x * Math.pow(2, direction);
        pos.after.y = pos.before.y * Math.pow(2, direction);
        statusMessage.push('...so the current mouse position would be '+pos.after.x+','+pos.after.y);

        pos.after.left = mouse.x - pos.after.x;
        pos.after.top = mouse.y - pos.after.y;
        statusMessage.push('...with the corner at '+pos.after.left+','+pos.after.top);
        
        dim.x = pos.after.left;
        dim.y = pos.after.top;
        dim.zoomLevel += direction;
        
        imageViewer.start = mouse;
        positionTiles(imageViewer, mouse);
    }

  }

}

function zoomImageUp(imageViewer, mouse)
{
    zoomImage(imageViewer, mouse, 1);
}

function zoomImageDown(imageViewer, mouse)
{
    zoomImage(imageViewer, mouse, -1);
}

// NEW JONNO STUFF


function hookEvent(element, eventName, callback)
{
  if(typeof(element) == "string")
    element = document.getElementById(element);
  if(element == null)
    return;
  if(element.addEventListener)
  {
    if(eventName == 'mousewheel')
      element.addEventListener('DOMMouseScroll', callback, false);  
    element.addEventListener(eventName, callback, false);
  }
  else if(element.attachEvent)
    element.attachEvent("on" + eventName, callback);
}

function cancelEvent(e)
{
  e = e ? e : window.event;
  if(e.stopPropagation)
    e.stopPropagation();
  if(e.preventDefault)
    e.preventDefault();
  e.cancelBubble = true;
  e.cancel = true;
  e.returnValue = false;
  return false;
}

var lockscroll = false;

function unlockscroll() {
  lockscroll = false;
}

function scrollHandle(e)
{
  e = e ? e : window.event;
  var raw = e.detail ? e.detail : e.wheelDelta;
  var normal = e.detail ? e.detail * -1 : e.wheelDelta / 40;
  if (!lockscroll) {
    if (normal > 0) {
      direction = 1;
    }
    else {
      direction = -1;
    }
    // console.debug(normal);
    var ev = getEvent(e);
    var mouse = localizeCoordinates(document.getElementById('imageViewer'), {'x': ev.clientX, 'y': ev.clientY});
    // console.debug(mouse);
    zoomImage(document.getElementById('imageViewer'), mouse, direction), 

    lockscroll = true;
    setTimeout(unlockscroll, 1000);
  }
  // document.getElementById('scrollContent').innerHTML = "<br/>&nbsp;Raw Value: " + raw + "<br/>&nbsp;Normalized Value: " + normal;

  cancelEvent(e);
}


function doZoomIn() {
  zoomImageUp(el, undefined);
  return false;
}

function doZoomOut() {
  zoomImageDown(el, undefined);
  return false;
}

