(function($) {
  // static constructs
  $.phoenix = $.phoenix || {};
  
  $.phoenix.slidebox = {
    version: '1.0.0',
  
    conf: {    
      effect: 'slide',
      direction: 'top',
      duration: 300,
      events: 'mouseover,mouseout',      
      api: false
    },
  
    addEffect: function(name, loadFn, hideFn) {
      effects[name] = [loadFn, hideFn];
    }
  };

  var effects = {
    slide: [
      function(done) {
        var conf = this.getConf();        
        params = {};
        if(typeof conf.direction != "string") {
          params[conf.direction[0]] = conf.max[0];
          params[conf.direction[1]] = conf.max[1];
        } else {
          params[conf.direction] = this.getCoverLength();
        }
        this.getBox().stop().animate(params,{queue: false,duration: conf.duration});
        done.call();
      },
      function(done) {
        var conf = this.getConf();
        params = {};
        if(typeof conf.direction != "string") {
          if(conf.min) {
            params[conf.direction[0]] = conf.min[0];
            params[conf.direction[1]] = conf.min[1];
          } else {
            params[conf.direction[0]] = "0px";
            params[conf.direction[1]] = "0px";
          }
        } else {
          params[conf.direction] = this.getMinCoverLength();
        }
        this.getBox().stop().animate(params,{queue: false,duration: conf.duration});
        done.call();
      }
    ]
  };

  function SlideBox(trigger, opts) {
    var self = this, box;

    trigger.data("slidebox", self);

    // get the cover box
    var m = $(".cover", trigger);
    box = m ? m : null;

    if(trigger) {
      evt = opts.events.split(/,\s*/);
      trigger.bind(evt[0], function(e) {

        e.target = this;
        self.show(e);
        box.hover(self.show, function(e) { self.hide(); } );
      });

      trigger.bind(evt[1], function() {
        self.hide();
      });
    }

    // API methods
    $.extend(self, {
      show: function(e) {
        if(e) { trigger = $(e.target); }        

        function show() {          

          // onBeforeShow
          var p = {proceed: true};
          $(self).trigger("onBeforeShow", p);
          if(p.proceed === false) { return self; }

          // invoke effect
          effects[opts.effect][0].call(self, function() {
            // onShow
            $(self).trigger("onShow");
          });          
        }

        show();

        return self;
      },

      hide: function() {
        function hide() {

          // onBeforeHide
          var p = {proceed: true};
          $(self).trigger("onBeforeHide", p);
          if(p.proceed === false) { return; }

          // invoke effect
          effects[opts.effect][1].call(self, function() {
            // onHide
            $(self).trigger("onHide");
          });
        }

        hide();

        return self;
      },

      getConf: function() {
        return opts;
      },

      getBox: function() {
        return box;
      },

      getTrigger: function() {
        return trigger;
      },

      getCoverLength: function() {
        if(!opts.max) {
          width = box.parent().width();
          height = box.parent().height();

          return (opts.direction == 'left' || opts.direction == 'right') ? width : height;
        } else {
          return opts.max;
        }        
      },

      getMinCoverLength: function() {
        if(!opts.min) {
          return 0;
        } else {
          return opts.min;
        }
      },

      // callback functions
      onBeforeShow: function(fn) {
        return bind("onBeforeShow", fn);
      },

      onBeforeHide: function(fn) {
        return bind("onBeforeHide", fn);
      },

      onShow: function(fn) {
        return bind("onShow", fn);
      },

      onHide: function(fn) {
        return bind("onHide", fn);
      }
      
    });
  }

  // jQuery plugin implementation
  $.prototype.slidebox = function(conf) {
    // return existing instance
    var api = this.eq(typeof conf == "number" ? conf : 0).data("slidebox");
    if(api) { return api; }

    // setup options
    var opts = $.extend(true, {}, $.phoenix.slidebox.conf);

    if($.isFunction(conf)) {
      conf = {onBeforeShow: conf};
    }

    $.extend(true, opts, conf);

    this.each(function() {
      api = new SlideBox($(this), opts);
    });

    return opts.api ? api : this;
  };
  
})(jQuery);
