var Sidebar = function (elm, foe, originElm)
{
  this.element        = elm;
  this.foeZIndex      = $(foe).css('z-index');
  this.originElm      = originElm || document.body;
  this.hitarea        = null;
  this.jqBox          = $('div.box', this.element);
  this.isInMotion     = false;
  this.isShowing      = false;
  this.x              = 0;
  this.y              = 0;
  this.width          = 0;
  this.height         = 0;
  this.originalX      = 0;
  this.originalY      = 0;
  this.offsetX        = 0;
  this.offsetY        = 0;
  this.originalZIndex = $(this.element).css('z-index');
  this.ptTween        = null;

  this.fnCache                = {};
  //this.fnCache.onCancel       = EventRunner.scope(this, this.onCancel);
  this.fnCache.onFinishCancel = EventRunner.scope(this, this.onFinishCancel);
  this.fnCache.onFinishEnter  = EventRunner.scope(this, this.onFinishEnter);
  this.fnCache.onFinishLeave  = EventRunner.scope(this, this.onFinishLeave);
  this.fnCache.onMouseLeave   = EventRunner.scope(this, this.onMouseLeave);
  this.fnCache.onUpdate       = EventRunner.scope(this, this.onUpdate);

  this.constructHitarea();
  this.onResize(null);

  EventRunner.register(window, 'resize', this.onResize, this);
  EventRunner.register(this.hitarea, 'mouseenter', this.onMouseEnter, this);
}


// STATICs
// _______________________________________________________________________


Sidebar.INT_HITAREA_Z_INDEX = 999;
Sidebar.SEC_ANIMATE_ENTER   = 0.2;
Sidebar.PIX_DELTA_X         = 40;
Sidebar.STR_EASING_GO       = 'easeInQuad';
Sidebar.STR_EASING_BACK     = 'easeOutQuad';


// INIT Methods
// _______________________________________________________________________


Sidebar.prototype.constructHitarea = function ()
{
  // init instance variables
  var jq      = $(this.element);
  var pos     = jq.position();
  this.width  = jq.outerWidth();
  this.height = jq.outerHeight();
  this.x      = this.originalX = pos.left;
  this.y      = this.originalY = pos.top;
  // build hitarea
  var div   = document.createElement('div');
  div.style.position    = 'absolute';
  div.style.background  = 'transparent';
  div.style.zIndex      = Sidebar.INT_HITAREA_Z_INDEX;
  div.style.width       = this.width + 'px';
  div.style.height      = this.height + 'px';
  div.style.top         = this.y  + 'px';
//  // temporary
  // div.style.opacity = 0.3;
  // div.style.backgroundColor = 'red';
  // div.style.filter = "alpha(opacity=30)";
 
  // insert and asign
  document.body.appendChild(div);
  this.hitarea = div;
}


// VIEW Methods
// _______________________________________________________________________


// EVENT Methods
// _______________________________________________________________________


Sidebar.prototype.onFinishEnter = function (ev)
{
//  console.log('onFinishEnter');
  this.isInMotion = false;
  this.isShowing  = true;
  this.x          = this.originalX;

  this.hitarea.style.display = 'none';
  // this.hitarea.parentNode.removeChild(this.hitarea);
  $(this.element).bind('mouseleave', this.fnCache.onMouseLeave);
}


Sidebar.prototype.onFinishLeave = function (ev)
{
//  console.log('onFinishLeave: zIndex => ' + this.originalZIndex);
  this.isInMotion = false;
  this.isShowing  = false;
  this.x          = this.originalX;
  this.jqBox.css({'visibility' :'hidden'});
  $(this.element).unbind('mouseleave');
  this.element.style.zIndex = this.originalZIndex;
  this.hitarea.style.display = 'block';
  // document.body.appendChild(this.hitarea);
}


Sidebar.prototype.onMouseEnter = function (ev)
{
//  console.log('enter');
  if (this.isInMotion)
  {
    ev.stopPropagation();
    return;
  }
  if (this.isShowing)
  {
    this.onMouseLeave(ev);
    return;
  }
  this.isInMotion = true;
  this.isShowing  = false;
  this.jqBox.css({'visibility' :'visible'});
  //$(this.hitarea).bind('mouseleave', this.fnCache.onCancel);
  // private method
  var self = this;
  var func = function ()
  {
    self.element.style.zIndex = self.foeZIndex + 1;
    JSTweener.addTween(
      self,
      {
        'delay'       : 0,
        'time'        : Sidebar.SEC_ANIMATE_ENTER,
        'x'           : self.originalX,
        'transition'  : Sidebar.STR_EASING_BACK,
        'onUpdate'    : self.fnCache.onUpdate,
        'onComplete'  : self.fnCache.onFinishEnter
      }
    );
  }
  // first animation
  JSTweener.addTween(
    this,
    {
      'delay'       : 0,
      'time'        : Sidebar.SEC_ANIMATE_ENTER,
      'x'           : Sidebar.PIX_DELTA_X + this.x,
      'transition'  : Sidebar.STR_EASING_GO,
      'onUpdate'    : this.fnCache.onUpdate,
      'onComplete'  : func
    }
  );
}


Sidebar.prototype.onMouseLeave = function (ev)
{
//  console.log('leave');
  if (this.isInMotion)
  {
    ev.stopPropagation();
    return;
  }
  if (!this.isShowing)
  {
    this.onMouseEnter(ev);
    return;
  }

  this.isInMotion = true;
  this.isShowing  = true;
  // private method
  var self = this;
  var func = function ()
  {
    self.element.style.zIndex = self.foeZIndex - 1;
    JSTweener.addTween(
      self,
      {
        'delay'       : 0,
        'time'        : Sidebar.SEC_ANIMATE_ENTER,
        'x'           : self.originalX,
        'transition'  : Sidebar.STR_EASING_BACK,
        'onUpdate'    : self.fnCache.onUpdate,
        'onComplete'  : self.fnCache.onFinishLeave
      }
    );
  }
  // first animation
  JSTweener.addTween(
    this,
    {
      'delay'       : 0,
      'time'        : Sidebar.SEC_ANIMATE_ENTER,
      'x'           : Sidebar.PIX_DELTA_X + this.x,
      'transition'  : Sidebar.STR_EASING_GO,
      'onUpdate'    : this.fnCache.onUpdate,
      'onComplete'  : func
    }
  );
}


Sidebar.prototype.onResize = function (ev)
{
  //console.log('onResize');
  var docW    = $(document.body).outerWidth();
  var contW   = $(this.originElm).outerWidth();
  var offsetX = 0;
  if (docW > contW)
  {
    offsetX = Math.round((docW - contW) / 2);
  }
  this.hitarea.style.left = (offsetX + this.x) + 'px';
  this.offsetX = offsetX;
}


Sidebar.prototype.onUpdate = function (ev)
{
  //console.log('\tupdate: ' + this.x);
  this.element.style.left = this.x + 'px';
}


// UTILITY Methods
// _______________________________________________________________________


// FUTURE REFERENCEs...
// _______________________________________________________________________

// JSTWeener doesn't have removeTween method... so that I cannot stop
// animation in the middle....

//Sidebar.prototype.onCancel = function (ev)
//{
//  console.log('onCancel => ' + this.ptTween);
//  this.isInMotion = true;
//  this.isShowing  = false;
//}
//
//
//Sidebar.prototype.onFinishCancel = function (ev)
//{
//  console.log('onFinishCancel');
//  this.isInMotion = false;
//  this.isShowing  = false;
//  this.x          = this.originalX;
//  this.element.style.zIndex = this.originalZIndex;
//  $(this.hitarea).unbind('mouseleave');
//}


