/**
  Track visible elemnts on the screen.

  You can register for triggers on:

    `focusChanged`  the top element we're focusing on

    `seenElement`   if we've seen the element

  @class Eyeline
  @namespace Discourse
  @module Discourse
  @uses RSVP.EventTarget
**/
Discourse.Eyeline = function Eyeline(selector) {
  this.selector = selector;
};


/**
  Call this to analyze the positions of all the nodes in a set 

  returns: a hash with top, bottom and onScreen items 
          {top: , bottom:, onScreen:}
 **/
Discourse.Eyeline.analyze = function(rows) {
  var current, goingUp, i, increment, offset, 
      winHeight, winOffset, detected, onScreen,
      bottom, top, outerHeight;
  
  if (rows.length === 0) return;
  
  i = parseInt(rows.length / 2, 10);
  increment = parseInt(rows.length / 4, 10);
  goingUp = undefined;
  winOffset = window.pageYOffset || $('html').scrollTop();
  winHeight = window.innerHeight || $(window).height();
  
  while (true) {
    if (i === 0 || (i >= rows.length - 1)) {
      break;
    }
    current = $(rows[i]);
    offset = current.offset();
   
    if (offset.top - winHeight < winOffset) {
      if (offset.top + current.outerHeight() - window.innerHeight > winOffset) {
        break;
      } else {
        i = i + increment;
        if (goingUp !== undefined && increment === 1 && !goingUp) {
          break;
        }
        goingUp = true;
      }
    } else {
      i = i - increment;
      if (goingUp !== undefined && increment === 1 && goingUp) {
        break;
      }
      goingUp = false;
    }
    if (increment > 1) {
      increment = parseInt(increment / 2, 10);
      goingUp = undefined;
    }
    if (increment === 0) {
      increment = 1;
      goingUp = undefined;
    }
  }
  
  onScreen = [];
  bottom = i;
  // quick analysis of whats on screen
  while(true) {
    if(i < 0) { break;}
    
    current = $(rows[i]);
    offset = current.offset();
    outerHeight = current.outerHeight();
    
    // on screen
    if(offset.top > winOffset && offset.top + outerHeight < winOffset + winHeight) {
      onScreen.unshift(i);
    } else {
      
      if(offset.top < winOffset) {
        top = i;
        break;
      } else {
        // bottom
      }
    }
    i -=1;
  }
  
  return({top: top, bottom: bottom, onScreen: onScreen});

};


/**
  Call this whenever you want to consider what is being seen by the browser

  @method update
**/
Discourse.Eyeline.prototype.update = function() {
  var $elements, atBottom, bottomOffset, docViewBottom, docViewTop, documentHeight, foundElement, windowHeight,
    _this = this;

  docViewTop = $(window).scrollTop();
  windowHeight = $(window).height();
  docViewBottom = docViewTop + windowHeight;
  documentHeight = $(document).height();
  $elements = $(this.selector);
  atBottom = false;

  if (bottomOffset = $elements.last().offset()) {
    atBottom = (bottomOffset.top <= docViewBottom) && (bottomOffset.top >= docViewTop);
  }

  // Whether we've seen any elements in this search
  foundElement = false;

  return $elements.each(function(i, elem) {
    var $elem, elemBottom, elemTop, markSeen;

    $elem = $(elem);
    elemTop = $elem.offset().top;
    elemBottom = elemTop + $elem.height();
    markSeen = false;
    // It's seen if...

    // ...the element is vertically within the top and botom
    if ((elemTop <= docViewBottom) && (elemTop >= docViewTop)) markSeen = true;

    // ...the element top is above the top and the bottom is below the bottom (large elements)
    if ((elemTop <= docViewTop) && (elemBottom >= docViewBottom)) markSeen = true;

    // ...we're at the bottom and the bottom of the element is visible (large bottom elements)
    if (atBottom && (elemBottom >= docViewTop)) markSeen = true;

    if (!markSeen) return true;

    // If you hit the bottom we mark all the elements as seen. Otherwise, just the first one
    if (!atBottom) {
      _this.trigger('saw', {
        detail: $elem
      });
      if (i === 0) {
        _this.trigger('sawTop', { detail: $elem });
      }
      return false;
    }
    if (i === 0) {
      _this.trigger('sawTop', { detail: $elem });
    }
    if (i === ($elements.length - 1)) {
      return _this.trigger('sawBottom', { detail: $elem });
    }
  });
};


/**
  Call this when we know aren't loading any more elements. Mark the rest as seen

  @method flushRest
**/
Discourse.Eyeline.prototype.flushRest = function() {
  var _this = this;
  return $(this.selector).each(function(i, elem) {
    var $elem;
    $elem = $(elem);
    return _this.trigger('saw', { detail: $elem });
  });
};

RSVP.EventTarget.mixin(Discourse.Eyeline.prototype);


