// FIXME IE does not recognize the namespace, unless it is previously initiated
var fd = {image : { effects : {}, navigation : {}}};

(function(root){

  namespace(namespace);

  function namespace(ns, functions) {
    if(typeof arguments[0] != "string"){
      return namespace_for_root.apply(this, arguments);
    }
    var parts = arguments[0].split("."),
        space = namespaceFor(parts);

    for (var i=1; i < arguments.length; i++) {
      space[functionName(arguments[i])] = arguments[i];
    }
  }

  function namespace_for_root(functions){
    for (var i=0; i < arguments.length; i++) {
      root[functionName(arguments[i])] = arguments[i];
    }
  }

  function functionName(fn) {
    return fn.name ? fn.name : fn.toString().match(/^\s*function\s+([^\s\(]+)/)[1];
  }

  function namespaceFor(parts) {
    var space = root;
    for (var i=0; i < parts.length; i++) {
      space = space[parts[i]] = space[parts[i]] || {};
    }
    return space;
  }

}(this)); // <- change this if you want to change the scope of namespace

(function(){
  namespace("fd.image", view, start, images, addEffect);

  var _view, _images; // where the image is displayed

  function view(v){
    if(v !== undefined){ _view = v; return fd.image;}
    return _view;
  }

  function images(v){
    if(v !== undefined){ _images = v; return fd.image;}
    return _images;
  }

  function start(callback){
    _images.find(".image").hide();
    _images.show();
    fd.image.navigation.init(_images);
    if(callback) {callback();}
    return fd.images;
  }

  function addEffect(fn, options){
    fn(options);
    return fd.image;
  }

}());

(function(){
  namespace("fd.image.effects", orient);

  var oriented_image, default_offset = 20;

  function orient(options){
    options = options || {};

    if(options.offset != null){ default_offset = options.offset; }

    $(".image img").css({visibility: "hidden"});
    $(window).resize(function(){
      var image = fd.image.navigation.selected();
      image.trigger("fd:image:show", image);});

    $(document.body).bind("fd:image:show", do_orient);
  }

  // TODO support iframes for video
  function do_orient(event, image){

    if($(image).find("img").length === 0) { return true;}

    if(!$(image).find("img")[0].complete){
      return setTimeout(function(){do_orient(event,image);}, 200);
    }

    var view        = fd.image.view(),
        img         = $(image).find("img").show(),
        width       = img.attr("width"),
        text_height = default_offset + $(image).find(".caption").outerHeight(true) + view.find(".description").outerHeight(true),
        height      = img.attr("height"),
        image_r     = (width /(height)),
        view_r      = (view.outerWidth(true) /(view.height()-text_height)),
        o           = ["100%", "auto"],
        // use window height for an IE workaround
        view_height = Math.min($(window).height(), view.height());

    o = (image_r >= view_r) ? ["100%", "auto"] : ['auto', view_height - text_height];
    img.css({width:o[0], height:o[1], visibility: 'visible'});
    if(img.width() > $(window).width()){ setTimeout(function(){do_orient(event, image);});}
  }
}());


(function(){
  namespace("fd.image.navigation", init, select, selected, next, prev, setClick);

  var visible, images, clickFunction;

  var touches = [];

  function init(imgs){
    images = imgs;
    if(images.find(".image").length > 1){
      if(window.Touch){
        images.find(".image").bind("touchstart", function(e){touchAllowed = true; touchEvent(e);});
        images.find(".image").bind("touchmove", touchEvent);
        images.find(".image").bind("touchend", function(){touchAllowed = false;});
      } else {
        images.find(".image").live("click", click);
      }
    } else {
      images.find(".image").css("cursor","default");
    }

    $("#images_next").live("click", next);
    $("#images_previous").live("click", prev);

    select(defaultImage(imgs)).trigger("fd:image:show", visible);
  }

  var touchTimer, touchAllowed = false;
  function touchEvent(e){
    if(touchTimer){clearTimeout(touchTimer);}
    if(touchAllowed === false){ return false;}
    if(e.originalEvent.touches.length > 1){ return true;} //not a single finger swipe

    touches.push(e.originalEvent.changedTouches[0].screenX);
    touchTimer = setTimeout(function(){touches = [];}, 250);

    if(touches.length > 2){
      var y = touches[0],
          x = touches[touches.length - 1];

      touches = [];
      if(Math.abs(x-y) < 20){ return true; }

      if(x < y){ touchAllowed = false; return next(e); }
      if(x > y){ touchAllowed = false; return prev(e); }
    }

    return true;
  }

  function defaultImage(images){
    var hash = window.location.hash.match(/#\!\/(.*)/),
        image = 0;
    if(hash) {image = $("#"+hash.pop()).index();}
    return image;
  }

  function setClick(fn){
    clickFunction = fn;
    return fd.image.navigation;
  }

  function selected(){
    return visible;
  }

  function select(index){
    var t = images.find(".image:eq("+index+")");
    if(t[0]){
      if(visible) {hide.apply(visible);}
      show.apply(t);
    }
    return t;
  }

  function direction(d){
    var image_to_load = $(this)[d]()
    if (image_to_load[0] === undefined){
      if(d == "next" && nextCollection){
        $(this).fadeOut(100);
        window.location.href = nextCollection;
        return;
      }
      if(d == "prev" && prevCollection){
        $(this).fadeOut(100);
        window.location.href = prevCollection;
        return;
      }

      $(this).fadeOut(100, function(){$(this).fadeIn();});

    } else {
      hide.apply(this);
      show.apply(image_to_load);
    }
  }

  function which_direction(e){
    return (e.offsetX >= $(e.target).width()/2) ? "next" : "prev";
  }

  function next(e){
    e.stopPropagation();
    direction.apply(images.find(".image:visible")[0], ["next"]);
    return false;
  }

  function prev(e){
    e.stopPropagation();
    direction.apply(images.find(".image:visible")[0], ["prev"]);
    return false;
  }

  function click(e){
    e.stopPropagation();
    var d = "next"
    if(clickFunction){ d = clickFunction(e); }
    direction.apply(this, [d]);
    return false;
  }

  function show(){
    visible = this;
    $(this)
    .addClass("selected")
    .fadeIn(250, function(){ visible.trigger("fd:image:show", visible)})
    .trigger("fd:image:show", visible);

    window.location.hash = "#!/"+$(this).attr("id");
   }

  function hide(){
    if(this === undefined){ return false;}
    $(this).hide().removeClass("selected").trigger("fd:image:hide", this);
  }

}());

