var openedModal = null;

var AcModalType = {
  HTML      : "html",
  EXTERNAL  : "external",
  IMAGE     : "image",
  IFRAME    : "iframe"
};

(function($) {
  
  var defaults = {
    title           : "Popup Window",
    contentType     : AcModalType.HTML,
    initialWidth    : 48,
    modalWidth      : 640,
    initialHeight   : 48,
    modalHeight     : 480,
    positionOffsetX : 0,
    positionOffsetY : 0,
    animated        : true,
    fadeBackdrop    : true,
    src             : "",
    contentBounds   : {
      top   : 35,
      left  : 15,
      right : 15,
      bottom: 15
    },
    onBeforeShow : function(api, ui) {
      // return value can be a new src
      return null;
    },
    onOpenAnimation : function(args) {
      var $popup = this,
          $closeButton = $popup.find("a.modal_close"),
          $popupBody = $popup.find("div#modal_popup_body"),
          $backdrop = $("div#modal_backdrop");
      
      $popup.show().css({
        top   : args.startingPosY,
        left  : args.startingPosX
      });
      $popup.animate({
        height : args.height + "px",
        width  : args.width + "px",
        top    : args.finalPosY,
        left   : args.finalPosX
      }, 1000, function() {
        $closeButton.fadeIn(250);
        $popupBody.fadeIn(250);
      });
    },
    onCloseAnimation : function(args) {
      var $popup = this,
          $closeButton = $popup.find("a.modal_close"),
          $backdrop = $("div#modal_backdrop");
          
      $closeButton.hide();
      $popup.animate({
        height  : "0px",
        width   : "0px",
        top     : $backdrop.height() / 2,
        left    : $backdrop.width() / 2,
        opacity : 0
      }, 300, function() {
        $popup.hide().css("opacity", "1.0");
      });
    },
    closeCallback : function() {
    }
  };
  
  AcModal = function(selection, options) {
    this.elements = selection;
    this.options = options;
    
    this.preloadedImages = new Array();
    this.bodyOverflow = "auto";
    this.isOpen = false;
    
    this.init();
  };
  AcModal.prototype = {
    init : function() {
      var modal     = this,
          options   = this.options,
          eventData = {
            modal   : modal,
            options : options
          },
          $backdrop = $("div#modal_backdrop"),
          $popup = $("div#modal_popup"),
          $body = $("body"),
          imagePreloadIndex = 0;
          
      if($backdrop.length == 0) {
        $backdrop = $("<div />").attr("id", "modal_backdrop");
        $backdrop.prependTo($body);
      }
      if($popup.length == 0) {
        var $title = $("<div />").attr("id", "modal_popup_title"),
            $popupBody = $("<div />").attr("id", "modal_popup_body"),
            $closeButton = $("<a />").attr({
              href  : "#",
              "class" : "modal_close",
              id    : "modal_popup_close"
            });
        
        $popup = $("<div />").attr("id", "modal_popup").css("display", "none");
        $closeButton.appendTo($title);
        $title.appendTo($popup);
        $popupBody.appendTo($popup);
        
        $popup.appendTo($body);
        $closeButton.bind("click.acModal", eventData, modal.close);
      }
          
      if(this.elements.length > 0) {
        this.elements.each(function() {
          var $modalTrigger = $(this),
              src = options.src;
            
          if(!src || src.length == 0) {
            src = $modalTrigger.attr("href");
          }
        
          if(options.contentType == AcModalType.IMAGE) {
            var image = new Image();
            image.src = src;
            modal.preloadedImages[imagePreloadIndex] = image;
            $modalTrigger.data("index", imagePreloadIndex);
            imagePreloadIndex = imagePreloadIndex + 1;
          }
        
          $modalTrigger.bind("click.acModal", eventData, modal.open);
        });
      }

      $(window).bind("resize.acModal", eventData, modal.onWindowResized);
    },
    
    destroy : function() {
      var modal   = this,
          options = this.options,
          $backdrop = $("div#modal_backdrop"),
          $popup = $("div#modal_popup");

      $backdrop.remove();
      $popup.remove();
          
      $(window).unbind(".acModal");
    },

    // API accessable open method
    show : function(url, title) {
      var modal   = this,
          options = this.options;

      if(url) {
        options.src = url;
      }

      if(title) {
        options.title = title;
      }

      modal.open();
    },

    open : function(event) {
      var modal, options, $modalTrigger, rel;

      if(event) {
        event.preventDefault();
        modal = event.data.modal;
        options = event.data.options;
        $modalTrigger = $(this);
        rel = $modalTrigger.attr("rel");
      } else {
        modal = this;
        options = this.options;
      }
      
      var $backdrop     = $("div#modal_backdrop"),
          $popup        = $("div#modal_popup"),
          $title        = $popup.find("div#modal_popup_title"),
          $popupBody    = $popup.find("div#modal_popup_body"),
          $closeButton  = $title.find("a#modal_popup_close"),
          $body         = $("body"),
          src           = options.src,
          modalSizeAdjustments = undefined;
    
      if(modal.isOpen) return;

      var ui = {
        popup : $popup,
        trigger : $modalTrigger
      };
      var temp = options.onBeforeShow(modal, ui);

      if(temp && temp.length > 0) {
        src = temp;
      }
      
      var $titleSpan = $("<span />").text(options.title);
      $title.find("span").remove();
      $titleSpan.appendTo($title);
      
      $popupBody.css({
        height : "auto",
        width  : "auto"
      });
      
      if($modalTrigger && (!src || src.length == 0)) {
        src = $modalTrigger.attr("href");
      }

      switch(options.contentType) {
        case AcModalType.HTML:
          if(rel) {
            var $rel = $("#" + rel);
            modal.renderContentFromContainer($popupBody, $rel);
          }
          break;
          
        case AcModalType.EXTERNAL:
          $popupBody.load(src);
          break;
          
        case AcModalType.IFRAME:
          modal.renderIframeContent($popupBody, src);
          break;
          
        case AcModalType.IMAGE:
          modalSizeAdjustments = modal.renderImage.apply($modalTrigger, [event]);
          break;
      }
      
      if(modalSizeAdjustments) {
        options.modalWidth = modalSizeAdjustments.modalWidth;
        options.modalHeight = modalSizeAdjustments.modalHeight;
      }
      
      var dimensions      = modal.calculateDimensions(options),
          backdropWidth   = $(window).width(),
          backdropHeight  = $(window).height(),
          startingPosX    = (backdropWidth/2 - options.initialWidth/2) + options.positionOffsetX,
          startingPosY    = (backdropHeight/2 - options.initialHeight/2) + options.positionOffsetY,
          finalPosX       = dimensions.left,
          finalPosY       = dimensions.top,
          modalWidth      = dimensions.width,
          modalHeight     = dimensions.height;
      
      modal.bodyOverflow = $body.css("overflow");
      $body.css("overflow", "hidden");

      if(dimensions.scroll) {
        $popupBody.css({
          overflow : "auto",
          height   : "auto",
          width    : "auto"
        });
      } else {
        $popupBody.css("overflow", "hidden");
      }
      
      if(options.fadeBackdrop) {
        $backdrop.fadeIn(500);
      } else {
        $backdrop.show();
      }
      
      if(options.animated) {
        var animationData = {
          startingPosX : startingPosX,
          startingPosY : startingPosY,
          height       : modalHeight,
          width        : modalWidth,
          finalPosX    : finalPosX,
          finalPosY    : finalPosY
        };
        options.onOpenAnimation.apply($popup, [animationData]);
      } else {
        $popupBody.show();
        $popup.css({
          height : modalHeight + "px",
          width  : modalWidth + "px",
          top    : finalPosY,
          left   : finalPosX
        }).show();
        $closeButton.show();
      }
      
      modal.isOpen = true;
      openedModal = modal;
    },

    close : function(event) {
      var modal = openedModal, 
          options, 
          $closeButton;

      if(event) {
        event.preventDefault();
        options = event.data.options;
      } else {
        options = openedModal.options;
      }

      var $backdrop     = $("div#modal_backdrop"),
          $popup        = $("div#modal_popup"),
          $title        = $popup.find("div#modal_popup_title"),
          $titleSpan    = $title.find("span"),
          $popupBody    = $popup.find("div#modal_popup_body"),
          $body         = $("body");
      
      if(options.animated) {
        var animationData = {
          initialHeight : options.modalHeight,
          initialWidth  : options.modalWidth
        };
        options.onCloseAnimation.apply($popup, [animationData]);
      } else {
        $popup.hide();
      }
      $popupBody.children().remove();
      $popupBody.hide();
      $titleSpan.remove();
      
      if(options.fadeBackdrop) {
        $backdrop.fadeOut(300, function() {
          $backdrop.hide();
        });
      } else {
        $backdrop.hide();
      }
      
      $body.css("overflow", modal.bodyOverflow);
      modal.isOpen = false;
      openedModal = null;

      modal.options.closeCallback();
    },
    
    renderIframeContent : function($container, src) {
      var $frame = $("<iframe />").attr({
            frameborder : 0,
            id : "modal_iframe",
            src : src
          });

      $frame.appendTo($container);
    },
    
    renderContentFromContainer : function($dest, $src) {
      var $srcContent = $src.children().detach();
      $srcContent.appendTo($dest);
    },
    
    renderImage : function(event) {
      var modal           = event.data.modal,
          options         = event.data.options,
          $modalTrigger   = $(this),
          $popup          = $("div#modal_popup"),
          $popupBody      = $popup.find("div#modal_popup_body"),
          imageIndex      = $modalTrigger.data("index"),
          image           = modal.preloadedImages[imageIndex],
          imageDimensions = {
            width   : image.width,
            height  : image.height
          },
          sizeAdjustments = {
            modalWidth  : 0,
            modalHeight : 0
          };
          
      $popupBody.css({
        height : imageDimensions.height,
        width  : imageDimensions.width
      });
          
      sizeAdjustments.modalWidth  = imageDimensions.width + options.contentBounds.left + options.contentBounds.right;
      sizeAdjustments.modalHeight = imageDimensions.height + options.contentBounds.top + options.contentBounds.bottom;
      
      $(image).appendTo($popupBody);
      return sizeAdjustments;
    },

    onWindowResized : function(event) {
      var modal      = event.data.modal,
          options    = event.data.options,
          $popup     = $("div#modal_popup"),
          $popupBody = $popup.find("div#modal_popup_body");

      if(!modal.isOpen) return;

      if(options.contentType == AcModalType.IMAGE) {
        var $image = $popupBody.find("img");

        options.modalWidth  = $image.width() + options.contentBounds.left + options.contentBounds.right;
        options.modalHeight = $image.height() + options.contentBounds.top + options.contentBounds.bottom;
      }

      var dimensions = modal.calculateDimensions(options);

      $popup.css({
        height : dimensions.height,
        width  : dimensions.width,
        top    : dimensions.top,
        left   : dimensions.left
      });

      if(dimensions.scroll) {
        $popupBody.css({
          overflow : "auto",
          height   : "auto",
          width    : "auto"
        });
      } else {
        $popupBody.css({
          overflow : "hidden"
        });
      }
    },

    calculateDimensions : function(options) {
      var backdropWidth   = $(window).width(),
          backdropHeight  = $(window).height();
          modalWidth      = options.modalWidth,
          modalHeight     = options.modalHeight,
          overrideOffsetY = false,
          overrideOffsetX = false,
          setBodyScroll   = false;

      if(backdropWidth < (modalWidth + 25)) {
        modalWidth = backdropWidth - 30;
        overrideOffsetX = true;
        setBodyScroll = options.contentType == AcModalType.IMAGE;
      }
      if(backdropHeight < (modalHeight + 25)) {
        modalHeight = backdropHeight - 35;
        overrideOffsetY = true;
        setBodyScroll = options.contentType == AcModalType.IMAGE;
      }

      var newPosX = (backdropWidth/2 - modalWidth/2),
          newPosY = (backdropHeight/2 - modalHeight/2);

      if(overrideOffsetX) {
        newPosX = 10;
      } else {
        newPosX = newPosX + options.positionOffsetX;
      }

      if(overrideOffsetY) {
        newPosY = 25;
      } else {
        newPosY = newPosY + options.positionOffsetY;
      }

      var result = {
        height : modalHeight,
        width  : modalWidth,
        top    : newPosY,
        left   : newPosX,
        scroll : setBodyScroll
      };

      return result;
    }
  };
  
  $.fn.acModal = function(options) {
    var settings = {};
    $.extend(settings, defaults, options);
    return new AcModal(this, settings);
  };

  $.acModal = function(options) {
    var settings = {};
    $.extend(settings, defaults, options);
    return new AcModal([], settings);
  };
  
})(jQuery);
