/**
 * @depends prototype.js
 * @depends effects.js
 * @depends g-ext.js
 */
 
/*
   Class: Gucci.Zoomer
   Zoom and rotate feature of Gucci.
*/

var ZOOM_CURSOR = '-moz-zoom-in';
var GRAB_CURSOR = '-moz-grab';
var GRABBING_CURSOR = '-moz-grabbing';

if(Prototype.Browser.IE){
  ZOOM_CURSOR = '/images/zoom-in.cur';
  GRAB_CURSOR = '/images/grab.cur';
  GRABBING_CURSOR = '/images/grabbing.cur';
}

if(Prototype.Browser.WebKit){
  ZOOM_CURSOR = '-webkit-zoom-in';
  GRAB_CURSOR = 'move';
  GRABBING_CURSOR = 'move';
}

Gucci.Zoomer = Class.create();
Object.extend(Gucci.Zoomer.prototype, {
   /*
   the _zoomInRepositionOffset properties control the behavior when the user
   clicks in the full image, where the zoomer zooms in. Eg. if she clicks
   at the very top of a handbag image, the offset ensures that it will zoom
   to the handbag in the center, and not to the white space
   
   use setZoomMode to change this properties
  */
  
  initialize: function(element, url, parentWidth){
	  this.FACTOR = 2;
  	this._zoomed = false;
  	this._zooming = false;
  	this._dragged = false;
  	this._zoomInRepositionOffsetX = 0;
  	this._zoomInRepositionOffsetY = 0;
  	this._zoomWidth = parentWidth || 780;
	
    this.active = true;
    this.element = $(element).down('.full_image_wrapper');
    this.full = $(element).down('.full_image_wrapper img');
    this.url = url;
	
  	this.options = Object.extend({}, arguments[2] || {});
		this.setZoomMode('full');
	
  	this.zoomImageElement = new Element('img', {className: 'zoomer-image'}).hide();
  	this.zoomCursorElement = new Element('img', {className: 'zoomer-cursor', src: '/images/zoom-cursor.png'}).hide();
  	this.zoomResetButton = new Element('div', {className: 'zoomer-button zoomer-reset'}).update('<div class="content">' + Gucci.getTerm('reset') + '</div><div class="end"></div>').hide();
  	this.element.insert({'bottom': this.zoomImageElement});
  	this.element.insert({'bottom': this.zoomCursorElement});
  	this.element.insert({'bottom': this.zoomResetButton});
    
    $(this.full).setStyle({cursor: ZOOM_CURSOR});
    
  	this.toggleZoomEvent = this.toggleZoom.bindAsEventListener(this);
	this.zoomInEvent = this.zoomIn.bindAsEventListener(this);
    this.element.observe('click', this.zoomInEvent);
    this.zoomResetButton.observe('click', this.toggleZoomEvent);
    
    if(Prototype.Browser.WebKit) {
      this.khtmlMouseMoveEvent = this.khtmlMouseMove.bindAsEventListener(this);
      this.khtmlMouseOutEvent  = this.khtmlMouseOut.bindAsEventListener(this);
      this.element.observe('mousemove', this.khtmlMouseMoveEvent);
      this.element.observe('mouseout', this.khtmlMouseOutEvent);
    }
    
  },
  
  /*
  Property: setZoomMode
    Sets the mode that should be used for zooming.
    
    Currently available modes:
    
    * centered - should be used for "normal" products where the product is placed
                 in the center of the image. Eg. shoes
                 The zoom image format is: 1040x1344 px
    * full     - should be used for products, where the product is not placed in the
                 center of the zoom image, but uses also space at the top and the bottom. E.g trousers
                 The zoom image format is: 1032x2000 px
    
  Parameters:
    mode - String - mode to be used
  */
  setZoomMode: function(mode) {
    if(mode == "centered") {
      this._zoomInRepositionOffsetX = 5;
      this._zoomInRepositionOffsetY = -297;      
    } else if(mode == "full") {
      this._zoomInRepositionOffsetX = 0;
      this._zoomInRepositionOffsetY = 0;
    }
    this._currentMode = mode;
  },
  
  khtmlMouseMove: function(event) {
    if(this._zoomed || this._paused) return;
    var c = Event.localPointer(event, this.element);
    var doubleZoomOffset = 260;
    this.zoomCursorElement.setStyle({left:c[0]+doubleZoomOffset+12+'px',top:c[1]+15+'px'}).show();
		//check if we are on safari3/leopard
		if (/10.5/.test(navigator.userAgent))
			this.zoomCursorElement.hide();    //hides the '+' sign on leaves only the magnifier
  },
  
  khtmlMouseOut: function(){
    this.zoomCursorElement.hide();
  },
  
  destroy: function(){
  	if(!this.active) return;
	
  	this.element.stopObserving('click', this.zoomInEvent);
  	this.zoomResetButton.stopObserving('click', this.toggleZoomEvent);
	
  	if(Prototype.Browser.WebKit) {
  		this.element.stopObserving('mousemove',this.khtmlMouseMoveEvent);
  		this.element.stopObserving('mouseout',this.khtmlMouseOutEvent);
  	}
	
  	this.zoomImageElement.remove();
  	this.zoomCursorElement.remove();
  	this.zoomResetButton.remove();
	
  	if($(this.full)) $(this.full).setStyle({cursor:'pointer'});
	
  	this._views = null;
	
  	this.active = false;
  		this.event('afterDestroy'); // External callback
  },
  
  zoomIn: function(event){
    if(this._zoomed || this._paused || !this.zoomImageElement) return;

    this._zoomed = true;
    this._zooming = true;

		this.zoomImageElement.src = this.url;
    
    //var localPoint = Event.localPointer(event, this.element);
    var mappedPoint = [0, 0];
    
    /*if(mappedPoint[1] < 98) mappedPoint[1] = 0;
    if(mappedPoint[1] > 406) mappedPoint[1] = 504;*/
  
    var p = [mappedPoint[0] + this._zoomInRepositionOffsetX, mappedPoint[1] + this._zoomInRepositionOffsetY];
	
  	var effect = [
  	  new Effect.Scale(this.full, this.FACTOR * 100, { sync: true, scaleContent: false }),
  	  new Effect.Move(this.full, { x: -mappedPoint[0], y: -mappedPoint[1], sync: true }),
  	  new Effect.Scale(this.element, 200, { scaleContent: false, scaleY: false, sync: true }),
  	  new Effect.Morph(this.element.up(), { style: 'width:'+ this._zoomWidth + 'px', sync: true }),
  	  new Effect.Morph('snippets_container', { style : {'width': $('snippets_container').getWidth() + 260 + 'px'} })
  	]
	
    if(Prototype.Browser.WebKit) this.zoomCursorElement.hide();

    new Effect.Parallel(effect, {
      duration: 1.5,
      //transition: Effect.Transitions.linear,
  		afterUpdate: document.fire.bind(document, 'zoomer:updated'),
  		beforeStart: function(){
  			this.element.setStyle({overflow:'hidden'});
  		}.bind(this),
  		afterFinish: function(){
  			this.zoomImageElement.setStyle({
  				left:'-'+p[0]+'px', 
  				top:'-'+p[1]+'px', 
  				cursor: GRAB_CURSOR
  			}).appear({
  				duration: 0.25,
  				afterFinish: function(){
  				  
  				  this.zoomResetButton.appear({duration: .15});
  				  
  				  new Draggable(this.zoomImageElement,{
  			      starteffect: function(){ 
  			        this.zoomImageElement.style.cursor = GRABBING_CURSOR; 
  			      }.bind(this),
  			      endeffect: function(){ 
  			        this._dragged = true;
  			        (function(){this.zoomImageElement.setStyle('cursor:' + GRAB_CURSOR)}.bind(this)).delay(.1);
  			      }.bind(this),
  			      snap: function(x, y, draggable) {
  			        this.element.fire('zoomer:dragged', { 'element': this.element });
                
                function constrain(n, lower, upper) {
                  if (n > upper) return upper;
                  else if (n < lower) return lower;
                  else return n;
                };
                
  			        var p = [
    		          constrain(x, 0, 0),
    		          constrain(y, -(1008 - 504), 0)
  		          ];

  						  this._fullStyle = { 
  						    left: ((this.full.deltax) ? (this.full.deltax * Gucci.Zoomer.FACTOR) : 0) + p[0] + this._zoomInRepositionOffsetX + 'px', 
  						    top: p[1] + this._zoomInRepositionOffsetY + 'px'
  						  };
  						  
  			        return p;

  				    }.bind(this)});
					
  					this._zooming = false;
  					this.element.fire('zoomer:zoomedIn', { 'element': this.element });
					  
  				}.bind(this)
  			});
  		}.bind(this)
  	});
  },
  
  zoomOut: function(event){
    if(event) Event.stop(event);
    if(!this._zoomed) return;
	  
  	this._zooming = true;
  	
  	// Extending with optional callbacks
  	Object.extend(this.options, arguments[1] || {});
  	this.element.fire('zoomer:beforeZoomOut', { 'element': this.element });
   
  	if(this._fullStyle) {
  		$(this.full).setStyle(this._fullStyle);
  		this._fullStyle = null;
  	}
  	
  	if(Prototype.Browser.IE && (/MSIE 6\./.test(navigator.userAgent))) {
      this.element.up('.showroom').down('.shadow-bottom').setStyle({'width': (this._zoomWidth - 260) + 'px'});
    }

  	var effect = [
      new Effect.Scale(this.full, 100/this.FACTOR, { sync: true }),
      new Effect.Move(this.full, { x: -$(this.full).offsetLeft, y: -$(this.full).offsetTop, sync: true }),
      new Effect.Scale(this.element,50,{ scaleContent: false, scaleY: false, sync: true }),
      new Effect.Move(this.element,{ x: 0, sync: true }),
      new Effect.Morph(this.element.up(), { style: 'width: ' + (this._zoomWidth - 260) + 'px', sync: true })
  	];
  	
  	new Effect.Parallel(effect, {
      afterUpdate: document.fire.bind(document, 'zoomer:updated'),
      beforeStart: function(){
       this.zoomImageElement.fade({duration: 0.15});
       this.zoomResetButton.fade({duration: 0.15});
      }.bind(this),
      afterFinish:function(){
       var fullWidth = FashionLoop.getFullWidth();
       new Effect.Morph('snippets_container', { style : {'width': (fullWidth + 65) + 'px'} });        
       
       Element.undoPositioned(this.element);
       this.event('afterZoomOut'); // External callback
       this.event('_afterZoomOut'); // Internal callback
       this.element.fire('zoomer:zoomedOut', { 'element': this.element });
              this._zooming = false;
       this._zoomed = false;
      }.bind(this)
  	});
  },

  toggleZoom: function(event){
  	if(this._zooming) return;
	
  	if(this._dragged) {
  		this._dragged = false;
  		return;
  	}
  	
  	this[this._zoomed ? 'zoomOut' : 'zoomIn'](event);
  },
  
  zoomOutAndDestroy: function(options){
    // Extending with optional callbacks
    Object.extend(this.options, options || {});
    if(this._zoomed){
      this.zoomOut(null, {
        _afterZoomOut: function(){
          this.destroy();
        }.bind(this)
      });
    } else {
      this.destroy();
    }
  },
  
  event: function(eventName) {
    if(this.options[eventName]) {
      this.options[eventName](); // Executing callback
      this.options[eventName] = null; // Removing callback after execution
    }
  }
});