<window
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  xmlns:html="http://www.w3.org/1999/xhtml"
  onload="onAlmostLoad();"
  style="background-color:white;"
  width="800"
  height="480"
  onresize="onResize();"
  onkeypress="onKeyPress(event);">

<script type="application/javascript" src="WidgetStack.js"/>
<script type="application/javascript" src="TileManager.js"/>
<script type="application/javascript" src="BrowserView.js"/>
<script type="application/javascript">
<![CDATA[

// We do not endorse the use of globals, but this is just a closed lab
// environment.  What could possibly go wrong? ...
let bv        = null;
let scrollbox = null;
let leftbar   = null;
let rightbar  = null;
let topbar    = null;

function debug() {
  let w = {};
  let h = {};
  scrollbox.getScrolledSize(w, h);
  let container = document.getElementById("tile_container");
  let [x, y] = getScrollboxPosition();
  let [w, h] = [w.value, h.value];
  if (bv) {
    dump('----------------------DEBUG!-------------------------\n');
    dump(bv._browserViewportState.toString() + endl);

    dump(endl);

    let cr = bv._tileManager._criticalRect;
    dump('criticalRect from BV: ' + (cr ? cr.toString() : null) + endl);
    dump('visibleRect from BV : ' + bv._visibleRect.toString() + endl);
    dump('visibleRect from foo: ' + scrollboxToViewportRect(getVisibleRect()) + endl);

    dump(endl);

    dump('container width,height from BV: ' + bv._container.style.width + ', '
                                            + bv._container.style.height + endl);
    dump('container width,height via DOM: ' + container.style.width + ', '
                                            + container.style.height + endl);

    dump(endl);

    dump('scrollbox position    : ' + x + ', ' + y + endl);
    dump('scrollbox scrolledsize: ' + w + ', ' + h + endl);

    dump(endl);

    dump('tilecache capacity: ' + bv._tileManager._tileCache.getCapacity() + endl);
    dump('tilecache size    : ' + bv._tileManager._tileCache.size          + endl);
    dump('tilecache numFree : ' + bv._tileManager._tileCache.numFree       + endl);
    dump('tilecache iBound  : ' + bv._tileManager._tileCache.iBound        + endl);
    dump('tilecache jBound  : ' + bv._tileManager._tileCache.jBound        + endl);
    dump('tilecache _lru    : ' + bv._tileManager._tileCache._lru          + endl);

    dump('-----------------------------------------------------\n');
  }
}

function debugTile(i, j) {
  let tc = bv._tileManager._tileCache;
  let t  = tc.getTile(i, j);

  dump('------ DEBUGGING TILE (' + i + ',' + j + ') --------\n');

  dump('in bounds: ' + tc.inBounds(i, j) + endl);
  dump('occupied : ' + tc._isOccupied(i, j) + endl);
  if (t)
  {
  dump('toString : ' + t.toString(true) + endl);
  dump('free     : ' + t.free + endl);
  dump('dirtyRect: ' + t._dirtyTileCanvasRect + endl);

  let len = tc._tilePool.length;
  for (let k = 0; k < len; ++k)
    if (tc._tilePool[k] === t)
      dump('found in tilePool at index ' + k + endl);
  }

  dump('------------------------------------\n');
}

function onKeyPress(e) {
  const a = 97;   // debug all critical tiles
  const c = 99;   // set tilecache capacity
  const d = 100;  // debug dump
  const f = 102;  // run noop() through forEachIntersectingRect (for timing)
  const i = 105;  // toggle info click mode
  const l = 108;  // restart lazy crawl
  const m = 109;  // fix mouseout
  const t = 116;  // debug given list of tiles separated by space

  switch (e.charCode) {
  case d:
    debug();

    break;
  case l:
    bv._tileManager.restartLazyCrawl(bv._tileManager._criticalRect);

    break;
  case c:
    let cap = parseInt(window.prompt('new capacity'));
    bv._tileManager._tileCache.setCapacity(cap);

    break;
  case f:
    let noop = function noop() { for (let i = 0; i < 10; ++i); };
    bv._tileManager._tileCache.forEachIntersectingRect(bv._tileManager._criticalRect,
                                                       false, noop, window);
    
    break;
  case t:
    let ijstrs = window.prompt('row,col plz').split(' ');
    for each (let ijstr in ijstrs) {
      let [i, j] = ijstr.split(',').map(function (x) parseInt(x));
      debugTile(i, j);
    }

    break;
  case a:
    let cr = bv._tileManager._criticalRect;
    dump('>>>>>> critical rect is ' + (cr ? cr.toString() : cr) + endl);
    if (cr) {
      let starti = cr.left  >> kTileExponentWidth;
      let endi   = cr.right >> kTileExponentWidth;

      let startj = cr.top    >> kTileExponentHeight;
      let endj   = cr.bottom >> kTileExponentHeight;

      for (var jj = startj; jj <= endj; ++jj)
        for (var ii = starti; ii <= endi; ++ii)
          debugTile(ii, jj);
    }

    break;
  case i:
    window.infoMode = !window.infoMode;
    break;
  case m:
    onMouseUp();
    break;
  default:
    break;
  }
}

function onResize(e) {
  if (bv) {
    bv.beginBatchOperation();
    bv.setVisibleRect(scrollboxToViewportRect(getVisibleRect()));
    bv.zoomToPage();
    bv.commitBatchOperation();
  }
}

function onMouseDown(e) {
  if (window.infoMode) {
    let [basex, basey] = getScrollboxPosition();
    let [x, y] = scrollboxToViewportXY(basex + e.clientX, basey + e.clientY);
    let i = x >> kTileExponentWidth;
    let j = y >> kTileExponentHeight;

    debugTile(i, j);
  }

  window._isDragging = true;
  window._dragStart = {x: e.clientX, y: e.clientY};

  bv.pauseRendering();
}

function onMouseUp() {
  window._isDragging = false;
  bv.resumeRendering();
}

function onMouseMove(e) {
  if (window._isDragging) {
    let x = {};
    let y = {};
    let w = {};
    let h = {};
    scrollbox.getPosition(x, y);
    scrollbox.getScrolledSize(w, h);

    let dx = window._dragStart.x - e.clientX;
    let dy = window._dragStart.y - e.clientY;

    // XXX if max(x, 0) > scrollwidth we shouldn't do anything (same for y/height)
    let newX = Math.max(x.value + dx, 0);
    let newY = Math.max(y.value + dy, 0);

    if (newX < w.value || newY < h.value) {
      // clip dx and dy to prevent us from going below 0
      dx = Math.max(dx, -x.value);
      dy = Math.max(dy, -y.value);

      let oldx = x.value;
      let oldy = y.value;

      bv.onBeforeVisibleMove(dx, dy);

      updateBars(oldx, oldy, dx, dy);
      scrollbox.scrollBy(dx, dy);
      
      let [newx, newy] = getScrollboxPosition();
      let realdx = newx - oldx;
      let realdy = newy - oldy;

      updateBars(oldx, oldy, realdx, realdy);
      bv.onAfterVisibleMove(realdx, realdy);
    }
    window._dragStart = {x: e.clientX, y: e.clientY};
  }
}

function onAlmostLoad() {
  window._isDragging = false;
  window.infoMode = false;
  window.setTimeout(onLoad, 1500);
}

function onLoad() {
  // ----------------------------------------------------
  scrollbox = document.getElementById("scrollbox")
		.boxObject
		.QueryInterface(Components.interfaces.nsIScrollBoxObject);
  leftbar  = document.getElementById("left_sidebar");
  rightbar = document.getElementById("right_sidebar");
  topbar   = document.getElementById("top_urlbar");
  // ----------------------------------------------------

  let initX = Math.round(leftbar.getBoundingClientRect().right);
  dump('scrolling to ' + initX + endl);
  scrollbox.scrollTo(initX, 0);
  let [x, y] = getScrollboxPosition();
  dump(' scrolled to ' + x + ',' + y + endl);

  let container = document.getElementById("tile_container");
  container.addEventListener("mousedown", onMouseDown, true);
  container.addEventListener("mouseup",   onMouseUp,   true);
  container.addEventListener("mousemove", onMouseMove, true);

  bv = new BrowserView(container, scrollboxToViewportRect(getVisibleRect()));

  let browser = document.getElementById("googlenews");
  bv.setBrowser(browser, false);
}

function updateBars(x, y, dx, dy) {
return;
  // shouldn't update margin if it doesn't need to be changed
  let sidebars = document.getElementsByClassName("sidebar");
  for (let i = 0; i < sidebars.length; i++) {
    let sidebar = sidebars[i];
    sidebar.style.margin = (y + dy) + "px 0px 0px 0px";
  }

  let urlbar = document.getElementById("top_urlbar");
  urlbar.style.margin = "0px 0px 0px " + (x + dx) + "px";
}

function viewportToScrollboxXY(x, y) {
  return scrollboxToViewportXY(x, y, -1);
}

function scrollboxToViewportXY(x, y) {
  if (!x) x = 0;
  if (!y) y = 0;

  // shield your eyes!
  let direction = (arguments.length >= 3) ? arguments[2] : 1;

  let leftbarcr  = leftbar.getBoundingClientRect();
  let rightbarcr = rightbar.getBoundingClientRect();
  let topbarcr   = topbar.getBoundingClientRect();

  let xtrans = direction * (-leftbarcr.width);
  let ytrans = direction * (-topbarcr.height);
  x += xtrans;
  y += ytrans;

  return [x, y];
}

function scrollboxToBrowserXY(browserView, x, y) {
  [x, y] = scrollboxToViewportXY(x, y);
  return [browserView.viewportToBrowser(x),
          browserView.viewportToBrowser(y)];
}

function scrollboxToViewportRect(rect) {
  let leftbarcr = leftbar.getBoundingClientRect();
  let topbarcr  = topbar.getBoundingClientRect();

  let xtrans = -leftbarcr.width;
  let ytrans = -topbarcr.height;

  rect.translate(xtrans, ytrans);

  return rect;
}

function getScrollboxPosition() {
  let x = {};
  let y = {};
  scrollbox.getPosition(x, y);
  return [x.value, y.value];
}

function getContentScrollValues(browser) {
  let cwu = getBrowserDOMWindowUtils(browser);
  let scrollX = {};
  let scrollY = {};
  cwu.getScrollXY(false, scrollX, scrollY);

  return [scrollX.value, scrollY.value];
}

function getBrowserDOMWindowUtils(browser) {
  return browser.contentWindow
    .QueryInterface(Ci.nsIInterfaceRequestor)
    .getInterface(Ci.nsIDOMWindowUtils);
}

function getBrowserClientRect(browser, el) {
  let [scrollX, scrollY] = getContentScrollValues(browser);
  let r = el.getBoundingClientRect();

  return new wsRect(r.left + scrollX,
		    r.top + scrollY,
                    r.width, r.height);
}

function scrollToElement(browser, el) {
  var elRect = getPagePosition(browser, el);
  bv.browserToViewportRect(elRect);
  elRect.round();
  this.scrollTo(elRect.x, elRect.y);
}

function zoomToElement(aElement) {
  const margin = 15;

  let elRect = getBrowserClientRect(browser, aElement);
  let elWidth = elRect.width;
  let vrWidth = bv.visibleRect.width;
  /* Try to set zoom-level such that once zoomed element is as wide
   *  as the visible rect */
  let zoomLevel = vrtWidth / (elWidth + (2 * margin));

  bv.beginBatchOperation();

  bv.setZoomLevel(zoomLevel);

  /* If zoomLevel ends up clamped to less than asked for, calculate
   * how many more screen pixels will fit horizontally in addition to
   * element's width. This ensures that more of the webpage is
   * showing instead of the navbar. Bug 480595. */
  let xpadding = Math.max(margin, vrWidth - bv.browserToViewport(elWidth));

  // XXX TODO these arguments are wrong, we still have to transform the coordinates
  // from viewport to scrollbox before sending them to scrollTo
  this.scrollTo(Math.floor(Math.max(bv.browserToViewport(elRect.x) - xpadding, 0)),
                Math.floor(Math.max(bv.browserToViewport(elRect.y) - margin, 0)));

  bv.commitBatchOperation();
}

function zoomFromElement(browser, aElement) {
  let elRect = getBrowserClientRect(browser, aElement);

  bv.beginBatchOperation();

  // pan to the element
  // don't bother with x since we're zooming all the way out
  bv.zoomToPage();

  // XXX have this center the element on the page
  // XXX TODO these arguments are wrong, we still have to transform the coordinates
  // from viewport to scrollbox before sending them to scrollTo
  this.scrollTo(0, Math.floor(Math.max(0, bv.browserToViewport(elRect.y))));

  bv.commitBatchOperation();
}

/**
 * Retrieve the content element for a given point in client coordinates
 * (relative to the top left corner of the chrome window).
 */
function elementFromPoint(browser, browserView, x, y) {
  [x, y] = scrollboxToBrowserXY(browserView, x, y);
  let cwu = getBrowserDOMWindowUtils(browser);
  return cwu.elementFromPoint(x, y,
                              true,   /* ignore root scroll frame*/
                              false); /* don't flush layout */
}

/* ensures that a given content element is visible */
function ensureElementIsVisible(browser, aElement) {
  let elRect = getBrowserClientRect(browser, aElement);

  bv.browserToViewportRect(elRect);

  let curRect = bv.visibleRect;
  let newx = curRect.x;
  let newy = curRect.y;

  if (elRect.x < curRect.x || elRect.width > curRect.width) {
    newx = elRect.x;
  } else if (elRect.x + elRect.width > curRect.x + curRect.width) {
    newx = elRect.x - curRect.width + elRect.width;
  }

  if (elRect.y < curRect.y || elRect.height > curRect.height) {
    newy = elRect.y;
  } else if (elRect.y + elRect.height > curRect.y + curRect.height) {
    newy = elRect.y - curRect.height + elRect.height;
  }

  // XXX TODO these arguments are wrong, we still have to transform the coordinates
  // from viewport to scrollbox before sending them to scrollTo
  this.scrollTo(newx, newy);
}

// this is a mehful way of getting the visible rect in scrollbox coordinates
// that we use in this here lab environment and hopefully nowhere in real fennec
function getVisibleRect() {
  let w = window.innerWidth;
  let h = window.innerHeight;

  let [x, y] = getScrollboxPosition();

  return new wsRect(x, y, w, h);
}

]]>
</script>

<scrollbox id="scrollbox" style="-moz-box-orient: vertical; overflow: scroll;" flex="1">
  <hbox id="top_urlbar" style="background-color: pink"><textbox flex="1"/></hbox>
  <hbox style="position: relative">
  <vbox id="left_sidebar" class="sidebar" style="background-color: red"><button label="left sidebar"/></vbox>
  <box>
    <html:div id="tile_container" style="position: relative; width: 800px; height: 480px; overflow: -moz-hidden-unscrollable;"/>
  </box>
  <vbox id="right_sidebar" class="sidebar" style="background-color: blue"><button label="right sidebar"/></vbox>
  </hbox>
</scrollbox>

  <box>
    <html:div style="position: relative; overflow: hidden; max-width: 0px; max-height: 0px; visibility: hidden;">
    <html:div id="browsers" style="position: absolute;">
        <!-- <browser id="googlenews" src="http://www.webhamster.com/" type="content" style="width: 1024px; height: 614px"/> -->
	<iframe id="googlenews" src="http://news.google.com/" type="content" style="width: 1024px; height: 614px"/>
    </html:div>
    </html:div>
  </box>

</window>
