/*
   Class: Gucci.Zoomer
   Zoom and rotate feature of Gucci.
*/

var GRAB_CURSOR = '-moz-grab';
var GRABBING_CURSOR = '-moz-grabbing';

if(Engine.isMSIE){
  GRAB_CURSOR = '/images/grab.cur';
  GRABBING_CURSOR = '/images/grabbing.cur';
}

if(Engine.isKHTML){
  GRAB_CURSOR = 'move';
  GRABBING_CURSOR = 'move';
}

Gucci.Zoomer = Class.create();

Object.extend(Gucci.Zoomer, {
  ZOOM_IN_SPEED: 0.8,
  ZOOM_OUT_SPEED: 0.4,
  FACTOR: 3.9682539,
  expand: true
});

Object.extend(Gucci.Zoomer.prototype, {
  FACTOR: 3.9682539,
  _zoomed: false,
  _zooming: false,
  active: false,
  
  _currentMode: 'full',
  /*
   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
  */
  _zoomInRepositionOffsetX: 0,
  _zoomInRepositionOffsetY: 0,
  
  //FIXME: klemens: exept for element these attribute names suck!
  initialize: function(element, full, style, url, options){	
    this.active = true;
    this.element = $(element);
    this.full = full;
    this.style = style;
    this.url = url;
    this.expand = Gucci.Zoomer.expand;
	
	this.options = {};
	Object.extend(this.options, options || {});

	this.rotatorInstance = this.options.rotator || null;
	this._frames = (this.rotatorInstance && this.rotatorInstance.isInitialized) ? this.rotatorInstance._frames : 1;
	this.isMultiPictureArray = this.rotatorInstance.isMultiPictureArray;
	
	if(this.rotatorInstance && this.rotatorInstance.imagesObj.rtw_zoom)
		this.setZoomMode('full'); // if the rtw_zoom property exists, we have to use the full zoom mode
    else
		this.setZoomMode('centered'); // default zoom mode is centered
	
    new Insertion.Bottom(this.element,
      '<img class="zoomer-image" id="zoomer-'+style+'-image" style="position:absolute;display:none" />'+
      '<div class="zoomer-button zoomer-reset" id="zoomer-'+style+'-reset" style="display:none">' +
      '<div class="content">' + Gucci.getTerm('reset') + '</div><div class="end"> </div></div>' +
      '<img class="zoomer-cursor" id="zoomer-'+style+'-cursor" src="/images/zoom-cursor.png" style="position:absolute;display:none" />');
	
	this.zoomImageElement = $('zoomer-'+this.style+'-image');

    $(this.full).setStyle({cursor:'url(/images/zoom-in.cur),-moz-zoom-in'});
    
    this.zoomInEvent = this.zoomIn.bindAsEventListener(this);
    this.zoomOutEvent = this.zoomOut.bindAsEventListener(this);
    Event.observe(this.element,'click',this.zoomInEvent);
    Event.observe('zoomer-'+style+'-reset','click',this.zoomOutEvent);
    
    if(Engine.isKHTML) {
      this.khtmlMouseMoveEvent = this.khtmlMouseMove.bindAsEventListener(this);
      this.khtmlMouseOutEvent  = this.khtmlMouseOut.bindAsEventListener(this);
      Event.observe(this.element,'mousemove',this.khtmlMouseMoveEvent);
      Event.observe(this.element,'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 = 8;
    }
    this._currentMode = mode;
  },
  
  khtmlMouseMove: function(event) {
    if(this._zoomed || this._paused) return;
    var c = Event.localPointer(event, this.element);
    var doubleZoomOffset = Shop.isRTW() ? 260 : 0;
    $('zoomer-'+this.style+'-cursor').show();
    $('zoomer-'+this.style+'-cursor').setStyle({left:c[0]+doubleZoomOffset+12+'px',top:c[1]+15+'px'});
		//check if we are on safari3/leopard
		if (/10.5/.test(navigator.userAgent))
			$('zoomer-'+this.style+'-cursor').hide();    //hides the '+' sign on leaves only the magnifier
  },
  
  khtmlMouseOut: function(){
    $('zoomer-'+this.style+'-cursor').hide();
  },
  
  destroy: function(){
    if(!this.active) return;
    Event.stopObserving(this.element,'click',this.zoomInEvent);
    if ($('zoomer-'+this.style+'-reset'))
      Event.stopObserving('zoomer-'+this.style+'-reset','click',this.zoomOutEvent);
    if(Engine.isKHTML) {
      Event.stopObserving(this.element,'mousemove',this.khtmlMouseMoveEvent);
      Event.stopObserving(this.element,'mouseout',this.khtmlMouseOutEvent);
    }
    this.zoomImageElement.remove();
    $('zoomer-'+this.style+'-reset').remove();
    $('zoomer-'+this.style+'-cursor').remove();
    if($(this.full)) $(this.full).setStyle({cursor:'default'});
    this._views = null;
    /*this._currentView = null;*/
    if($('zoomer-'+this.style+'-back')) $('zoomer-'+this.style+'-back').remove();
    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.currentImageIndex = (this.rotatorInstance && this.rotatorInstance.isInitialized) ? this.rotatorInstance._currentImageIndex : 0;
	
	if(this.isMultiPictureArray)
		this.zoomImageElement.src = this.url[this.currentImageIndex];
	else
		this.zoomImageElement.src = (typeof(this.url) != 'string') ? this.url[this.currentImageIndex] : this.url;
    
	//this.zoomImageElement.src = (typeof(this.url) != 'string') ? this.url[this.currentImageIndex] : this.url;
	
    var c = Event.localPointer(event, this.element);
    var m = [c[0],c[1]];
    if(m[1]<99) m[1] = 99;
    if(m[1]>382) m[1] = 382;
    
    m[0] = m[0]*(this.FACTOR)*(this.expand ? 0.5 : 0.75);   
    m[1] = m[1]*(this.FACTOR)*0.75;
    
    var p = [m[0]+this._zoomInRepositionOffsetX, m[1]+this._zoomInRepositionOffsetY];
    //this.zoomImageElement.setStyle({left:'-'+p[0]+'px',top:'-'+p[1]+'px'});

	this.element.style.zIndex = 3;
	
	var effects = $A();
	if(!this.isMultiPictureArray) {
		// If not MPI, use separate effect for scale up
		$(this.full).deltax = -(this.currentImageIndex)*260;
		effects.push(new Effect.ZoomIn360( this.full, { pointer:c, frames:this._frames, sync:true }));
	} else {
		// If MPI, the things are getting simpler
		effects.push(new Effect.Scale(this.full,this.FACTOR*100,{sync:true,scaleContent:false}));
		effects.push(new Effect.Move(this.full,{x:-m[0],y:-m[1],sync:true}));
	}
    if(this.expand) effects.push(new Effect.Scale(this.element, 200, {scaleContent:false, scaleY:false, sync:true}));
	
    if(Engine.isKHTML) $('zoomer-'+this.style+'-cursor').hide();

    // HACK: this.element.hasClassName('showroom') in rtw also safari has to do the move, but not in shop
	// In search results it shouldn't be moved too. Added a check for GucciSearch object.
    if((Engine.isGecko || this.element.hasClassName('showroom')) && this.expand && typeof(GucciSearch) == 'undefined')
		effects.push(new Effect.Move(this.element, {x:-260, sync:true}));
	
	// Also if it's a search results for one product, we need additional effects
	if(typeof(GucciSearch) != 'undefined' && GucciSearch.productCount == 1) {
		var variants = $('lightbox_0_variants'); 
		var variantsWidth = variants ? variants.getWidth() : 0;
		effects.push(new Effect.Morph($('lightbox_0').down('div.container'), {style: {width: '780px'}, sync:true }));
        effects.push(new Effect.Morph($('lightbox_0'), {style: {width: 780 + variantsWidth + 'px'}, sync:true }));
        effects.push(new Effect.Morph($('lightbox_0_variants'), {style: {left: '780px'}, sync:true }));
        effects.push(new Effect.Morph($('wrapper'), {style: {width: 1317 + variantsWidth + 'px'}, sync:true }));
        effects.push(new Effect.Morph($('layout'), {style: {width: 1317 + variantsWidth + 'px'}, sync:true }));
        effects.push(new Effect.Morph($('lightbox_0_product_image'), { style: {left: '0px'}, sync:true }));
	}
	
    new Effect.Parallel(effects, {
		duration:Gucci.Zoomer.ZOOM_IN_SPEED, 
		transition:Gucci.cubic,
		beforeStart: function(){
			this.full.up().setStyle({width:'520px',overflow:'hidden'});
			
			if(this.rotatorInstance && this.rotatorInstance.isInitialized)
				this.rotatorInstance.rotatorContainer.fade({duration: .25});
		}.bind(this),
		afterFinish: function(){
			this._zooming = false;
			
			if (p[0] < 0) p[0] = 0;
			if (p[1] < 0) p[1] = 0;			
			this.zoomImageElement.setStyle({
				left:'-'+p[0]+'px', 
				top:'-'+p[1]+'px', 
				cursor: GRAB_CURSOR
			}).appear({
				duration:0.25,
				afterFinish: function(){
					new Draggable(this.zoomImageElement,{
				      starteffect: function(){ this.zoomImageElement.style.cursor = GRABBING_CURSOR}.bind(this),
				      endeffect: function(){ this.zoomImageElement.style.cursor = GRAB_CURSOR}.bind(this),
				      snap: function(x,y,draggable) {
				        function constrain(n, lower, upper) {
				          if (n > upper) return upper;
				          else if (n < lower) return lower;
				          else return n;
				        }
				        var p;
				        // FIXME maybe the logic should be in setZoomMode
				        if(this._currentMode == 'centered') {
				           p = [
				            constrain(x, -(1040-(this.expand ? 520 : 260)), 0),
				            constrain(y, -(1343-504), 0)
				            ];
				        } else if(this._currentMode == 'full') {
				          p = [
				           constrain(x, -(1032-(this.expand ? 520 : 260)), 0),
				           constrain(y, -(2000-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)});
					
					$('zoomer-'+this.style+'-reset').appear({delay:0.5, duration:0.25});
					
				}.bind(this)
			});
		}.bind(this)
	});
  },
  
  zoomOut: function(event, resetZ, options){
	if(event) Event.stop(event);
	if(!this._zoomed) return;
	
	// Extending with optional callbacks
	Object.extend(this.options, options || {});
	
	var toX = 260;
	if (typeof(GucciSearch) != 'undefined') {
		if (GucciSearch.productCount == 1) {
			var resetZ = 10000;
			//var toX = 0;
		};
	};
	if(this._fullStyle) {
		$(this.full).setStyle(this._fullStyle);
		this._fullStyle = null;
	}
	resetZ = resetZ || 2;
	
	var effects = $A();
	if(!this.isMultiPictureArray) {
		// If not MPI, use separate effect for scale down
		effects.push(new Effect.ZoomOut360( this.full, { frames: this._frames, sync: true }));
	} else {
		// If MPI, the things are getting simpler
		effects.push(new Effect.Scale(this.full, 100/this.FACTOR, {sync:true}));
		effects.push(new Effect.Move(this.full, {x:-$(this.full).offsetLeft, y:-$(this.full).offsetTop, sync:true}));
	}
	if(this.expand) effects.push(new Effect.Scale(this.element,50,{scaleContent:false,scaleY:false,sync:true}));
	// It shouldn't be moved if we're in search results
	if(this.expand && typeof(GucciSearch) == 'undefined') effects.push(new Effect.Move(this.element,{x:toX,sync:true}));
	
	// Also if it's a search results for one product, we need additional effects
	if(typeof(GucciSearch) != 'undefined' && GucciSearch.productCount == 1) {
		var variants = $('lightbox_0_variants'); 
		var variantsWidth = variants ? variants.getWidth() : 0;
		effects.push(new Effect.Morph($('lightbox_0').down('div.container'), {style: {width: '520px'}, sync:true }));
        effects.push(new Effect.Morph($('lightbox_0'), {style: {width: 520 + variantsWidth + 'px'}, sync:true }));
        effects.push(new Effect.Morph($('lightbox_0_variants'), {style: {left: '520px'}, sync:true }));
        effects.push(new Effect.Morph($('wrapper'), {style: {width: 1057 + variantsWidth + 'px'}, sync:true }));
        effects.push(new Effect.Morph($('layout'), {style: {width: 1057 + variantsWidth + 'px'}, sync:true }));
	}
	
	new Effect.Parallel(effects,{
		duration:Gucci.Zoomer.ZOOM_OUT_SPEED,
		transition:Gucci.cubic,
		queue:Shop.queue,
		delay: .25,
		beforeStart: function(){
			$('zoomer-'+this.style+'-reset').fade({duration:0.15});
			this.zoomImageElement.fade({duration:0.15})
		}.bind(this),
		afterFinish:function(){
			this.element.setStyle({zIndex: resetZ, left: ''});
			Element.undoPositioned(this.element);

			if(this.rotatorInstance && this.rotatorInstance.isInitialized)
				this.rotatorInstance.rotatorContainer.appear({duration: .25});
			
			this.event('afterZoomOut'); // External callback
			this.event('_afterZoomOut'); // Internal callback
			
			this._zoomed = false;
			
		}.bind(this)
	});
  },
  
  zoomOutAndPause: function(options){
    if(this._zoomed){
      this.zoomOut(null, 3, options);
      setTimeout(function(){
        this.pause()
      }.bind(this),450);
    } else {
      this.pause();
    }
  },
  
  zoomOutAndDestroy: function(options){
	// Extending with optional callbacks
	Object.extend(this.options, options || {});
	if(this._zoomed){
		this.zoomOut(null, 3, {
			_afterZoomOut: function(){
				this.destroy();
			}.bind(this)
		});
	} else {
		this.destroy();
	}
  },
  
  pause: function(){
    $(this.full).setStyle({cursor: 'default'});
    this._paused = true;
  },
  
  unpause: function(){
    $(this.full).setStyle({cursor: 'url(/images/zoom-in.cur),-moz-zoom-in'});
    this._paused = false;
  },

  event: function(eventName) {
	if(this.options[eventName]) {
		this.options[eventName](); // Executing callback
		this.options[eventName] = null; // Removing callback after execution
	}
  }

});


/*
Class: Gucci.Rotator
  Renders prev/next button in a HTML element. Clicking on them switches the src attribute of an img tag.

Example:
  (start code)
  var data = {
    wrapperElement: $('showroom_123'),
    imgElement: $('showroom_123').down('img'),
    imagesObj: Object
  };
  $('showroom_123').rotator = new Gucci.Rotator(data);
  (end)
*/
Gucci.Rotator = Class.create({
  rotatorWidth: 61,
  
  initialize: function(options) {
	this.isInitialized = false;
	this.wrapperElement = options.wrapperElement;
    this.imgElement = options.imgElement || null;
    this.imagesObj = options.imagesObj;
    this.identifier = options.identifier || new Date().getTime();
    this._currentImageIndex = 0;
    this._allImagesLoaded = false;
	this.isMultiPictureArray = (this.imagesObj.full360 || this.imagesObj.mpi) ? true : false;
	
	this.images = (this.isMultiPictureArray) ? (this.imagesObj.full360 || this.imagesObj.mpi) : this.imagesObj.full;
	this._frames = (this.isMultiPictureArray) ? this.images.length : this.imagesObj.is360 || null;	
	
	// If no additional images for the product exit, skip initialization
    if(this._frames)
		this.setButtons(options.buttonOptions);
  },
  
  setButtons: function(options) {
    var left = (parseInt(this.wrapperElement.getStyle('width'), 10) / 2 - this.rotatorWidth / 2) + 'px';
    new Insertion.Bottom(this.wrapperElement,
      '<div style="z-index:1000; left: ' + left + '; opacity: 0" class="zoomer-360" id="rotator-'+this.identifier+'-container">'+
      '<div class="zoomer-button zoomer-left" id="rotator-'+this.identifier+'-left"> </div>'+
      '<div class="zoomer-button zoomer-right" id="rotator-'+this.identifier+'-right"> </div>'+
      '<div class="zoomer-loading-indicator" id="rotator-'+this.identifier+'-indicator" style="display:none">LOADING</div>'+
      '</div>'
    );
	
	this.rotatorContainer = $('rotator-'+this.identifier+'-container');
	this.rotateLeftBut = $('rotator-'+this.identifier+'-left');
	this.rotateRightBut = $('rotator-'+this.identifier+'-right');
    
    this.rotateLeftEvent = this.rotateLeft.bindAsEventListener(this);
    this.rotateRightEvent = this.rotateRight.bindAsEventListener(this);
    Event.observe(this.rotateLeftBut, 'click', this.rotateLeftEvent);
    Event.observe(this.rotateRightBut ,'click', this.rotateRightEvent);

	this.rotatorContainer.appear({duration: 0.25});
	this.isInitialized = true;
  },
  
  rotateRight: function(event) {
    this.doRotation(-1);
    Event.stop(event);
  },
  
  rotateLeft: function(event) {
    this.doRotation(+1);
    Event.stop(event);
  },
  
  doRotation: function(rotationSteps) {
	
	if (!this.imgElement) {
      var ele = this.wrapperElement.down('img');
      this.imgElement = ele.src ? ele : null;
    }

	this._currentImageIndex = this.modifyImageIndex(rotationSteps);

	if (this.isMultiPictureArray) {
		this.imgElement.src = this.images[this._currentImageIndex];
		Loader.cacheOrLoad(this.images[this.previousFrameIndex() - 1]);
		Loader.cacheOrLoad(this.images[this.nextFrameIndex() - 1]);
	} else
		this.imgElement.setStyle({left: -(this._currentImageIndex)*260 + 'px'});
  },
  
  modifyImageIndex: function(rotationSteps) {
	var newImageIndex = this._currentImageIndex + rotationSteps;
	if (newImageIndex < 0) {
		newImageIndex = this._frames - 1;
	} else if ( (this._frames - 1) < newImageIndex) {
		newImageIndex = 0;
	}
	return newImageIndex;
  },

  nextFrameIndex: function() {    
    frame = parseInt(this._currentImageIndex) + 1;
    if(frame > this.images.length) frame = 1;
    return frame;
  },

  previousFrameIndex: function() {
    frame = parseInt(this._currentImageIndex) - 1;
    if(frame < 1) frame = this.images.length;
    return frame;
  },
  
  loadAllImages: function() {
    if (this._allImagesLoaded) return;
    this.images.each(function(imageUrl) {
      Loader.cacheOrLoad(imageUrl);
    });
    this._allImagesLoaded = true;
  },
  
  destroy: function() {
	if(!this.isInitialized)
		return;

	Event.stopObserving(this.rotateLeftBut ,'click', this.rotateLeftEvent);
    Event.stopObserving(this.rotateRightBut ,'click', this.rotateRightEvent);
	if (this.rotatorContainer) {
		this.rotatorContainer.fade({duration: .25, afterFinish: function(){
			this.rotatorContainer.remove();
			this.isInitialized = false;
		}.bind(this)});
	}
  }
});
