// Third Party & Utilities
function $(o) {return document.getElementById(o);}

Function.prototype.bind = function(thisObj, var_args) {
  if (typeof(this) != "function") {
    throw new Error("Bind must be called as a method of a function object.");
  }

  var self = this;
  var staticArgs = Array.prototype.splice.call(arguments, 1, arguments.length);

  return function() {
    // make a copy of staticArgs (don't modify it because it gets reused for
    // every invocation).
    var args = staticArgs.concat();

    // add all the new arguments
    for (var i = 0; i < arguments.length; i++) {
      args.push(arguments[i]);
    }

    // invoke the original function with the correct thisObj and the combined
    // list of static and dynamic arguments.
    return self.apply(thisObj, args);
  };
};

function getPosition(node) {
  var x = 0, y = 0;
  
  while(node) {
    x += parseInt(node.offsetLeft);
    y += parseInt(node.offsetTop);
    node = node.offsetParent;
  }
  
  return {
    x : x,
    y : y
  }
}

function constrain(a, min, max) {
  return (a < min) ? min : ((a > max) ? max : a);
}

function max(a, b) {
  return (a > b) ? a : b;
}

function getInt(a) {
  var v = parseInt(a);
  return (isNaN(v)) ? 0 : v;
}

function scrollTop() {
  return max(window.scrollY, document.body.parentNode.scrollTop);
}

function MenuController() {
  window.addEventListener('scroll', this.handleScroll.bind(this), false);
  window.addEventListener('resize', this.handleResize.bind(this), false);
  this.loaded = false;
  this.items = {};
  this.menuHeight = null;
  this.highlighted = null;
  this.lastTop = 0;
  this.lastItem = null;

  this.menuPos = 0;
  this.menuTarget = 0;
  this.menuAnimation = null;

  this.handleLoad();
  window.addEventListener('load', this.handleLoad.bind(this), false);
}

MenuController.prototype.getLayout = function() {
  this.menuHeight = $('menu').offsetHeight;
  this.items = {};
  
  var menu_nodes = $('menu').getElementsByTagName('a');
  for (var i = 0, node; node = menu_nodes[i]; i++) {
    var name = node.href.split("#").pop();
    this.items[name] = {};
    this.items[name].name = name;
    this.items[name].menu = node;
    this.items[name].menutop = getPosition(node).y;
  }

  var main_nodes = $('main').getElementsByTagName('a');
  for (var i = 0, node; node = main_nodes[i]; i++) {
    if (!node.name) continue;

    var name = node.name;
    var main = node.parentNode;
    this.items[name].main = main;
    this.items[name].top = main.offsetTop;

    /*
    this.items[name].images = [];
    this.items[name].lores = false;

    // Figure out if images are low res.
    var images = main.getElementsByTagName('img');
    for (var u = 0, image; image = images[u]; u++) {
      this.items[name].images.push(image);
      var lores = (image.src.indexOf('.jpg') != -1 && !image.getAttribute('highres'));
      if (lores)
        this.items[name].lores = true;
    }
    */
    this.lastItem = this.items[name];
  }
}

MenuController.prototype.highlight = function() {
  var top = scrollTop();
  var closest = null;
  for (var i in this.items) {
    var item = this.items[i];
    var dist = item.top - top;
    if (dist < 0) dist = -dist * 1.5;

    if (closest == null || dist < closest.dist) {
      closest = item;
      closest.dist = dist;
    }
  }

  if (this.highlighted != closest) {
    if (this.highlighted) {
      this.highlighted.menu.classList.remove('selected');
      this.highlighted.main.classList.remove('selected');
    }
    this.highlighted = closest;

    if (history.replaceState) {
      if (this.highlighted.name == 'hello')
        history.replaceState({}, "", " ");
      else
        history.replaceState({}, this.highlighted.name, this.highlighted.menu.href);
    }

    this.highlighted.menu.classList.add('selected');
    this.highlighted.main.classList.add('selected');
  }
}

MenuController.prototype.fetchHighres = function() {
  /*
  var bottom = scrollTop() + window.innerHeight * 2;
  for (var i in this.items) {
    var item = this.items[i];
    if (item.top < bottom && item.lores) {
      for (var u = 0, image; image = item.images[u]; u++) {
        image.src = image.src.replace(".jpg", ".png");
        window.console.log(image.src);
      }
      item.lores = false;
    } 
  }
  */
}

MenuController.prototype.layoutMenu = function() {
  if (this.menuHeight < window.innerHeight) {
    this.setMenuPos(0);
    return;
  }

  var newtop = this.menuTarget - (scrollTop() - this.lastTop) / 10; // MAGIC CONST
  newtop = constrain(newtop, window.innerHeight - this.menuHeight, 0);
  if (this.highlighted) {
    if (this.highlighted.menutop + newtop < 0)
      newtop = -this.highlighted.menutop;
    else if (this.highlighted.menutop + this.highlighted.menu.offsetHeight + newtop > window.innerHeight) {
      newtop = window.innerHeight - this.highlighted.menutop - this.highlighted.menu.offsetHeight;
    }
  }
  
  newtop = parseInt(newtop);
  if (this.menuTarget != newtop) {
    this.menuTarget = newtop;
    if (!this.menuAnimation)
      this.menuAnimation = setInterval(this.animateMenu.bind(this), 30);
  }
}

MenuController.prototype.setMenuPos = function(y) {
  this.menuPos = y;
  $('menu').style.top = parseInt(y) + 'px';
}

MenuController.prototype.animateMenu = function() {
  window.console.log('interval');
  var pos = this.menuPos;
  var newtop = pos + ((this.menuTarget - pos) / 4.5);
  if (Math.abs(this.menuTarget - pos) < 2) {
    clearInterval(this.menuAnimation);
    this.menuAnimation = null;
    newtop = this.menuTarget;
  }
  this.setMenuPos(newtop);
}

MenuController.prototype.bottomPadding = function() {
  $('main').style.marginBottom = max(window.innerHeight - parseInt(this.lastItem.main.clientHeight), 0) + 'px';
}

MenuController.prototype.handleLoad = function() {
  this.loaded = true;
  this.getLayout();
  this.highlight();
  this.layoutMenu();
  this.bottomPadding();
}

MenuController.prototype.handleScroll = function(e) {
  this.highlight();
  this.layoutMenu();
  this.fetchHighres();
  this.lastTop = scrollTop();
}

MenuController.prototype.handleResize = function() {
  this.layoutMenu();
  this.bottomPadding();
}

