// Sonic Spin Plugin
//
// Version 0.1.0
//
// JavaScript Library: jQuery
// 
// Copyright (c) 2009 John Resig, http://jquery.com/
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(function($) {
	var EVENT_PRELOAD_STATUS = 'ssz_preload_status';
	var EVENT_PRELOAD_END = 'ssz_preload_end';
	var EVENT_CHANGE_FRAME = 'ssz_chage_frame';
	var EVENT_START_SPIN = 'ssz_start_spin';
	var EVENT_STOP_SPIN = 'ssz_stop_spin';
	var DEFAULT_DURATION = 3000;
	var DEFAULT_ZOOM_SPIN_DURATION = 30000;
	var RESET_DURATION = 300;
	var DEFAULT_SIZE = 18;
	var UPDATE_CHECK_TIME = 100;
	var GESTURE_PLAY_COUNT = 20;
	var GESTURE_PLAY_TIME = 200;
	
	$.fn.sonicspin = function(options)
	{
		if (typeof options === "string")
		{
			var args = Array.prototype.slice.call(arguments, 1);
			
			if (this.length == 1) {
				var zoomer = $.data(this[0], 'zoomer');
				if (zoomer == null)
					return;
				return zoomer.spiner[options](args);
			} else {
				return this.each(function()
				{
					var zoomer = $.data(this, 'zoomer');
					if (zoomer == null)
						return;
					return zoomer.spiner[options](args);
				});
			}
		} else {
			return this.each(function()
			{
				var spiner = new Object();
				spiner = {
					init : false,
					imgs : [],
					pos : 0,
					chcnt : 0,
					fwd : true,
					dt : new Date(),
					tm : null,
					loadedImgs : [],
					thumbImgs : [],
					load : false,
					rot : 0,
					rotst : 0,
					
					settings : {
						size : DEFAULT_SIZE,
						duration : DEFAULT_DURATION,
						zoomSpinDuration : DEFAULT_ZOOM_SPIN_DURATION,
						direction : 'left-turn',
						movement : true,
						mousewheel : 'fz',
						contextmenu : true,
						preloadStatus : true
					},
					size : function()
					{
						return this.imgs.length;
					},
					source : function(args)
					{
						if (args.length == 0)
							return this.imgs[this.pos];
						
						return this.imgs[args[0]];
					},
					position : function(args)
					{
						if (args.length == 0)
							return this.pos;
						
						this.pos = args[0];
						this.setFrame();
					},
					movement : function(args)
					{
						if (args == undefined || args.length == 0)
							return this.mov;
						
						this.mov= args[0];
						this.chcnt = 0;
						this.stop();
					},
					spinChangeLength : function(args)
					{
						if (args.length == 0)
							return this.chlen;
						
						this.chlen = args[0];
					},
					forwarding : function(args)
					{
						if (args.length == 0)
							return this.fwd;
						
						this.fwd = args[0];
					},
					duration : function(args)
					{
						if (args.length == 0)
							return this.up * spiner.imgs.length;
						
						if (args.length == 1)
						{
							if (typeof args[0] === "string" && args[0] == "zoomspin")
								return this.zsup * spiner.imgs.length;
							else
								this.up = Math.floor(args[0] / spiner.imgs.length);
						}
						
						if (args.length == 2 && args[0] == "zoomspin")
							this.up = Math.floor(args[1] / spiner.imgs.length);
					},
					isLoaded : function(args)
					{
						return this.load;
					},
					isPlaying : function()
					{
						return (this.tm != null);
					},
					previous : function()
					{
						if (this.dir == 'right-turn')
						{
							if(--this.pos < 0)
								this.pos = this.imgs.length-1;
						} else {
							if(++this.pos >= this.imgs.length)
								this.pos = 0;
						}
						this.setFrame();
					},
					next : function()
					{
						if (this.dir == 'right-turn')
						{
							if(++this.pos >= this.imgs.length)
								this.pos = 0;
						} else {
							if(--this.pos < 0)
								this.pos = this.imgs.length-1;
						}
						this.setFrame();
					},
					setFrame : function()
					{
						if (this.load && this.zoomer.currentPreviewPosition.zoomLevel == 0)
						{
							this.fullZoomImage.src = this.loadedImgs[this.pos].src;
							this.zoomer.path = this.imgs[this.pos];
							this.zoomer.zoomImage = this.zoomer.mrl + this.zoomer.path;
							if (this.zoomer.navigationDisplay != 'none' && this.zoomer.navigationImage == 'thumbNail')
								$(this.zoomer.navigationView.thumbNail).attr('src', this.thumbImgs[this.pos].src);
						} else {
							this.zoomer.changeImage(this.imgs[this.pos], "keepPosition");
						}
						
						$(this.fullZoomImage).triggerHandler(EVENT_CHANGE_FRAME, [this.zoomer, this.pos]);
						
						return true;
					},
					slide : function(args)
					{
						this.stop();
						if (this.dir == 'right-turn')
						{
							if (this.pos > args[0])
								this.previous();
							else if (this.pos < args[0])
								this.next();
						} else {
							if (this.pos > args[0])
								this.next();
							else if (this.pos < args[0])
								this.previous();
						}
					},
					spin : function(args)
					{
						if (args[0] == "stop")
						{
							$(this.zoomer.frame).css('cursor', 'crosshair');
							this.chcnt = 0;
							return;
						}
						
						$(this.zoomer.frame).css('cursor', 'e-resize');
						this.chcnt += args[0];
						if (Math.abs(this.chcnt) > this.chlen) {
							if (this.chcnt < 0)
								this.previous();
							else
								this.next();
							this.chcnt = 0;
							this.stop();
						}
					},
					reset : function(args)
					{
						if (this.zoomer.currentPreviewPosition.zoomLevel == 0)
						{
							if (this.isPlaying() == false)
							{
								if (this.pos == 0)
									return;
								
								this.tostart = true;
								this.saveup = this.up;
								this.up = Math.floor(RESET_DURATION / spiner.imgs.length);
								var fwd = (this.dir == 'right-turn') ? (this.pos > spiner.imgs.length/2) : (this.pos < spiner.imgs.length/2);
								this.play([fwd]);
							} else {
								this.tostart = true;
								this.saveup = this.up;
								this.up = Math.floor(RESET_DURATION / spiner.imgs.length);
							}
						}
						else
						{
							if (this.isPlaying())
								this.stop();
							else
								this.zoomer.reset();
						}
					},
					play : function(args)
					{
						var fwd = args[0];
						if (this.tm != null)
						{
							this.stop();
							if (this.fwd == fwd)
								return;
						}
						
						this.fwd = fwd;
						$(this.fullZoomImage).triggerHandler(EVENT_START_SPIN, [this.zoomer, this.fwd]);
						
						this.lt = this.dt.getTime();
						this.action();
					},
					action : function()
					{
						var newTime = this.dt.getTime();
						if (this.fwd == true)
							this.next();
						else
							this.previous();
						
						if (this.tostart && this.pos == 0)
						{
							this.stop();
							return;
						}
						
						var updateTime = ((this.zoomer.currentPreviewPosition.zoomLevel == 0) ? this.up : this.zsup) - (newTime - this.lt);
						this.lt = newTime;
						
						var me = this;
						this.tm = setTimeout(function(){ me.action(); }, updateTime);
					},
					stop : function()
					{
						if (this.tm == null) {
							return;
						}
						
						if (this.tostart)
						{
							this.tostart = false;
							this.up = this.saveup;
							this.saveup = 0;
						}
						
						clearTimeout(this.tm);
						this.tm = null;
						$(this.fullZoomImage).triggerHandler(EVENT_STOP_SPIN, [this.zoomer, this.fwd]);
					},
					preload : function()
					{
						var zoomer = this.zoomer;
						for(var i = 0; i < this.imgs.length; i++)
						{
							var img = new Image();
							img.src = zoomer.createThumbNailMRL(zoomer.mrl,
																this.imgs[i],
																zoomer.zoomWidth,
																zoomer.zoomHeight,
																zoomer.padColor,
																zoomer.requestOption);
							this.loadedImgs[i] = img;
						}
						
						this.load = false;
						this.preloadStatus();
					},
					preloadStatus : function()
					{
						var cnt = 0;
						for(var i = 0; i < this.loadedImgs.length; i++)
						{
							if (this.loadedImgs[i].complete) cnt++;
						}
						
						$(this.fullZoomImage).triggerHandler(EVENT_PRELOAD_STATUS, [this.zoomer, cnt, this.loadedImgs.length]);
						
						if (cnt < this.loadedImgs.length)
						{
							var me = this;
							setTimeout(function(){ me.preloadStatus(); }, UPDATE_CHECK_TIME);
						} else {
							this.load = true;
							$(this.fullZoomImage).triggerHandler(EVENT_PRELOAD_END, [this.zoomer, cnt, this.loadedImgs.length]);
						}
					},
					preloadThumbNail : function()
					{
						var zoomer = this.zoomer;
						for(var i = 0; i < this.imgs.length; i++)
						{
							if (zoomer.navigationDisplay != 'none' && zoomer.navigationImage == 'thumbNail')
							{
								var zoomImage = zoomer.mrl + this.imgs[i];
								var img = new Image();
								img.src = zoomer.createThumbNailMRL(zoomer.mrl,
																	this.imgs[i],
																	zoomer.navWidth,
																	zoomer.navHeight,
																	zoomer.padColor,
																	zoomer.requestOption);
								this.thumbImgs[i] = img;
							}
						}
					}
				};
				
				if (options != undefined && options.spin)
				{
					$.extend(spiner.settings, options.spin);
					if (options.control == undefined)
					{
						if (spiner.settings.mousewheel != undefined && spiner.settings.contextmenu != undefined)
							options.control = {shiftclick:'zoomout',ctrlclick:'swnavi'};
						else if (spiner.settings.mousewheel != undefined)
							options.control = {contextmenu:'reset',shiftclick:'zoomout',ctrlclick:'swnavi'};
						else if (spiner.settings.contextmenu != undefined)
							options.control = {mousewheel:'fz',shiftclick:'zoomout',ctrlclick:'swnavi'};
					} else {
						if (options.control.mousewheel != undefined)
							delete options.control.mousewheel;
						if (options.control.contextmenu != undefined)
							delete options.control.contextmenu;
					}
				} else {
					options = {control:{shiftclick:'zoomout',ctrlclick:'swnavi'}};
				}
				
				$(this).soniczoom(options);
				
				var zoomer = $.data(this, 'zoomer');
				zoomer.spiner = spiner;
				
				if (spiner.settings.images != undefined)
				{
					spiner.imgs = spiner.settings.images;
					spiner.settings.size = spiner.settings.images.length;
				} else {
					var index = zoomer.path.lastIndexOf('/');
					var folder = zoomer.path.substring(0, index+1);
					var file = zoomer.path.substring(index+1);
					index = file.lastIndexOf('.');
					var num = new Number(file.substring(0, index));
					var ext = file.substring(index);
					for (var i=0; i < spiner.settings.size; i++)
					{
						spiner.imgs[i] = folder + (num+i) + ext;
					}
				}
				
				spiner.fullZoomImage = this;
				spiner.zoomer = zoomer;
				spiner.chlen = Math.floor(spiner.fullZoomImage.width / spiner.imgs.length);
				spiner.up = Math.floor(spiner.settings.duration / spiner.imgs.length);
				spiner.zsup = Math.floor(spiner.settings.zoomSpinDuration / spiner.imgs.length);
				spiner.mov = spiner.settings.movement;
				spiner.dir = spiner.settings.direction;
				
				spiner.preload();
				if (spiner.settings.preloadStatus && spiner.load == false)
				{
					var preloadStatusPanel = document.createElement('div');
					$(preloadStatusPanel).addClass('sonicspin-preloadStatusPanel');
					$(preloadStatusPanel).css({margin : '0',padding : '0',position : 'absolute'});
					
					var preloadStatusBarFrame = document.createElement('div');
					$(preloadStatusBarFrame).addClass('sonicspin-preloadStatusBarFrame');
					$(preloadStatusBarFrame).css({margin : '0',padding : '0',position : 'absolute'});
					
					var preloadStatusBar = $('<img>').attr('src', zoomer.imageDirectory + '/transparent.gif');
					$(preloadStatusBar).addClass('sonicspin-preloadStatusBar');
					$(preloadStatusBar).css({margin:'0',padding:'0',position : 'absolute',borderStyle:'none'});
					$(preloadStatusBarFrame).append(preloadStatusBar);
					
					var preloadStatusString = document.createElement('div');
					$(preloadStatusString).addClass('sonicspin-preloadStatusString');
					$(preloadStatusString).css({margin : '0',padding : '0',position : 'absolute'});
					$(preloadStatusString).append("0%");
					
					$(preloadStatusPanel).append(preloadStatusBarFrame);
					$(preloadStatusPanel).append(preloadStatusString);
					
					$(zoomer.container).append(preloadStatusPanel);
					
					$(preloadStatusBar).width(0).height($(preloadStatusBarFrame).height());
					
					spiner.preloadStatusPanel = preloadStatusPanel;
					spiner.preloadStatusBar = preloadStatusBar;
					spiner.preloadStatusString = preloadStatusString;
					spiner.preloadStatusBarSize = $(preloadStatusBarFrame).width();
					
					$(this).bind('ssz_preload_status', function(event, zm, count, size){
						if (count == size) {
							$(zm.spiner.preloadStatusPanel).remove();
							delete zm.spiner.preloadStatusPanel;
							delete zm.spiner.preloadStatusBar;
							delete zm.spiner.preloadStatusString;
							delete zm.spiner.preloadStatusBarSize;
						} else {
							var progress = count / size;
							$(zm.spiner.preloadStatusBar).width(Math.floor(progress * spiner.preloadStatusBarSize));
							$(zm.spiner.preloadStatusString).empty();
							$(zm.spiner.preloadStatusString).append(Math.floor(progress * 100)+"%");
						}
					});
				}
				
				$(this).bind('sz_moving', function(event, zm, shiftX, shiftY){
					if (zm.spiner.load == true && (zm.spiner.mov == false || zm.currentPreviewPosition.zoomLevel == 0))
					{
						if (zm.spiner.isPlaying() == false)
						{
							if (zm.spiner.rot == 0)
								zm.spiner.rotst = new Date().getTime();
							zm.spiner.rot += shiftX;
							zm.spiner.spin([shiftX]);
						}
						else
							zm.spiner.stop();
						
						return 'end';
					}
					else
						return 'continue';
				});
				
				$(this).bind('sz_moved', function(event, zm){
					if (zm.spiner.load == true && (zm.spiner.mov == false || zm.currentPreviewPosition.zoomLevel == 0))
					{
						if (new Date().getTime() - zm.spiner.rotst < GESTURE_PLAY_TIME && Math.abs(zm.spiner.rot) > GESTURE_PLAY_COUNT)
						{
							zm.spiner.play([(zm.spiner.rot > 0)]);
						}
						zm.spiner.rot = 0;
						zm.spiner.rottm = 0;
						zm.spiner.spin(["stop"]);
						return 'end';
					}
					else
						return 'continue';
				});
				
				$(this).bind('sz_zoomin', function(event, zm, position)
				{
					if (zm.currentPreviewPosition.zoomLevel == 0)
						zm.spiner.stop();
					return 'continue';
				});
				
				$(this).bind('sz_image_info', function(event, zm, size){
					if (zm.spiner.init)
						return;
					
					zm.spiner.init = true;
					zm.spiner.preloadThumbNail();
					
					if (zm.spiner.settings.mousewheel != undefined)
					{
						zm.mouse.mousewheelEvent = function(e)
						{
							var rollCount = zm.mouse.wheel(e);
							if (zm.spiner.settings.mousewheel == 'bz')
								rollCount *= -1;
							
							if (rollCount < 0)
							{
								if (zm.spiner.mov == false || zm.currentPreviewPosition.zoomLevel == 0)
								{
									zm.spiner.stop();
									zm.spiner.previous();
								}
								else
								{
									if (this == zm.navigationView.mainLayer)
									{
										zm.navigationZoomin(zm.getNavigationEventPosition(e));
										zm.mouse.preventEvent(e);
									}
									else
										zm.zoomin(zm.getEventPosition(e));
								}
							}
							else if (rollCount > 0)
							{
								if (zm.spiner.mov == false || zm.currentPreviewPosition.zoomLevel == 0)
								{
									zm.spiner.stop();
									zm.spiner.next();
								}
								else
									zm.zoomout();
							}
						}
						
						var types = ['DOMMouseScroll', 'mousewheel'];
						if (zm.previewZoom.addEventListener)
						{
							for (var i=types.length-1; i >=0; i--)
							{
								zm.previewZoom.addEventListener(types[i], zm.mouse.mousewheelEvent, false);
								if (zm.navigationDisplay != 'none')
									zm.navigationView.mainLayer.addEventListener(types[i], zm.mouse.mousewheelEvent, false);
							}
						}
						else
							$(zm.clickTrigger).bind('mousewheel', zm.mouse.mousewheelEvent);
					}
					if (zm.spiner.settings.contextmenu != undefined)
					{
						$(zm.clickTrigger).bind('contextmenu', function(e)
						{
							zm.mouse.preventEvent(e);
							zm.spiner.reset();
						});
					}
				});
			});
		}
	}
})(jQuery);