/*global jQBoxa, jQuery, window */
/*jslint white: true, onevar: true, browser: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, maxerr: 50, indent: 2 */
jQBoxa.setRelative = function (popup, target, options) {
  var callbacks, get$, resizeTimer, setPositionName, redraw;

  get$ = function ($el) {
    if (!($el instanceof jQuery)) {
      $el = jQuery($el);
    }
    return $el;
  };

  if (typeof options !== 'object') {
    options = {};
  }

  target = get$(target);

  setPositionName = function (elem, collectionName) {
    var collection, position, offset;
    collection = options.positionCollection[collectionName];
    if (collection.position) {
      position = collection.position;
    }
    if (collection.offset) {
      offset = collection.offset;
    }
    elem.removeClass('relative-left relative-right relative-top relative-bottom')
        .addClass('relative-' + collectionName)
        .positionRelativeTo(target, position, offset);
  };

  checkOverflow = function (elem, collectionName, context, contextDimensions) {
    var positionCollection, popupDimension, newCollectionName, overflow;
    positionCollection = options.positionCollection;
    popupDimension = elem.offset();
    if (positionCollection.left && positionCollection.right) {
      if (collectionName === 'right') {
        popupDimension.width = elem.outerWidth();
        if (!contextDimensions.width) {
          contextDimensions.width = context.outerWidth();
        }
        overflow = (popupDimension.left + popupDimension.width) - (contextDimensions.left + contextDimensions.width);
        if (overflow > 0) {
          newCollectionName = 'left';
        }
      }
      else if (collectionName === 'left') {
        overflow = contextDimensions.left - popupDimension.left;
        if (overflow > 0) {
          newCollectionName = 'right';
        }
      }
    }
    if (!newCollectionName && positionCollection.top && positionCollection.bottom) {
      if (collectionName === 'bottom') {
        popupDimension.height = elem.outerHeight();
        if (!contextDimensions.height) {
          contextDimensions.height = context.outerHeight();
        }
        overflow = (popupDimension.top + popupDimension.height) - (contextDimensions.top + contextDimensions.height);
        if (overflow > 0) {
          newCollectionName = 'top';
        }
      }
      else if (collectionName === 'top') {
        overflow = contextDimensions.top - popupDimension.top;
        if (overflow > 0) {
          newCollectionName = 'bottom';
        }
      }
    }
    if (newCollectionName) {
      return {
        overflow : overflow,
        name : newCollectionName
      };
    }
  };

  callbacks = {
    created : function () {
      this.element.css({
        display  : 'block',
        position : 'absolute'
      });
    },
    //TODO: Change positioncollection from Top, Right, Bottom, Left to Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left, TopLeft?
    redraw : function () {
      var context, contextDimensions, targetPosition, positionCollection, collectionName, collection, overflow, newOverflow, popupDimension, position, offset;
      positionCollection = options.positionCollection;
      if (positionCollection) {
        context = positionCollection.context ? get$(positionCollection.context) : jQuery('html');        
        contextDimensions = context.position();
        targetPosition  = target.position();
        if (positionCollection.left) {
          collectionName = 'left';
          if (positionCollection.right) {
            contextDimensions.width = context.outerWidth();
            contextDimensions.middleX = contextDimensions.left + contextDimensions.width / 2;
            if (targetPosition.left < contextDimensions.middleX) {
              collectionName = 'right';
            }
          }
        }
        else if (positionCollection.right) {
          collectionName = 'right';
        }
        else if (positionCollection.top) {
          collectionName = 'top';
          if (positionCollection.bottom) {
            contextDimensions.height = context.outerHeight();
            contextDimensions.middleY = contextDimensions.top + contextDimensions.height / 2;
            if (targetPosition.top < contextDimensions.middleY) {
              collectionName = 'bottom';
            }
          }
        }
        else if (positionCollection.bottom) {
          collectionName = 'bottom';
        }
        //TODO: What if there is no collectionName? Not show? Fail miserably?
        setPositionName(this.element, collectionName);
      }
      else {
        if (options.position) {
          position = options.position;
        }
        if (options.offset) {
          offset = options.offset;
        }
        //TODO: Are both position and offset really optional? If so - do we have the necessary fallbacks for this to work?
        this.element.positionRelativeTo(target, position, offset);
      }
      if (options.enforce && collectionName) {
        overflow = checkOverflow(this.element, collectionName, context, contextDimensions);
        if (overflow) {
          collectionName = overflow.name;
          setPositionName(this.element, collectionName);
          if (positionCollection[collectionName].important) {
            newOverflow = checkOverflow(this.element, collectionName, context, contextDimensions);
            if (newOverflow && options.minimizeOverflow && newOverflow.overflow < overflow.overflow) {
              popupDimension = this.element.offset();
              if (!(collectionName === 'left' && popupDimension.left < 0) && !(collectionName === 'top' && popupDimension.top < 0)) {
                newOverflow = false;
              }
            }
            if (newOverflow) {
              setPositionName(this.element, newOverflow.name);
            }
          }
        }
      }
    }
  };

  redraw = function () {
    popup.redraw();
  };

  popup.subscribe(callbacks);

  jQuery(window).bind('resize', function () {
    if (resizeTimer) {
      clearTimeout(resizeTimer);
    }
    resizeTimer = setTimeout(redraw, 1000);
    redraw();
  });

  //Enable chaining
  return this;
};
