var ImageZoom = Class.create();

ImageZoom.prototype =
{
	/**
	 * Creates a new image zoom object.
	 *
	 * The image zoom object enables scrolling and panning of the image.
	 *
	 * The object needs a div, identified by _div_id, an image, identified by
	 * _image_id, and the ORIGINAL image width and height, identified by
	 * _image_width and _image_height resp.
	 *
	 */
	
	initialize: function(_div_id, _image_id, _image_width, _image_height)
	{
		// Adapt zoom img such that it is of the same size as the live view image
		
		var width = null;
		var height = null;
		var elements = $$('div.view_module');
		var len = elements.length;
		
		for (var i = 0; i < len; i++)
		{
			var element = elements[i];
			if (element.id == "SiteCamStreaming")
			{
				// SiteCamStreaming takes precedence over any other view module
				
				width = element.childNodes[0].getAttribute("width");
				height = element.childNodes[0].getAttribute("height");
				
				break;
			}
			else
			{
				width = element.childNodes[0].width;
				height = element.childNodes[0].height;
			}
		}
		
		/*if (element == null)
		{
			// We are using the streaming video object
			
			width = element.getAttribute("width");
			height = element.getAttribute("height");
		}
		else
		{
			// We are using SittycamPro/FTPRefresh/etc.
			
			width = element.width;
			height = element.height;
		}*/
		
		var div = $(_div_id);
		var img = $(_image_id);
		
		div.style.width = width + 'px';
		div.style.height = height + 'px';
	
		img.style.width = width + 'px';
		img.style.height = height + 'px';
		
		//
		
		this.div = $(_div_id);
		this.image = $(_image_id);
		
		this.canvas_x = 0;
		this.canvas_y = 0;
		this.canvas_width = this.div.getWidth();
		this.canvas_height = this.div.getHeight();
		
		// Store the actual image's size
		
		this.image_width = _image_width;
		this.image_height = _image_height;
		
		// Store scale
		
		this.min_scale = 1.0;
		this.max_scale = this.image_width / this.div.getWidth();
		this.scale = this.max_scale;
		this.scale_ratio = (this.image_width / this.image_height) / (this.canvas_width / this.canvas_height);
		
		//
		
		this.moving = false;
		
		// Some constants
		
		this.WHEEL_DELTA_MODIFIER = 0.005;
		
		if (navigator.userAgent.indexOf("Firefox") != -1 || navigator.userAgent.indexOf("Opera") != -1)
		{
			// Firefox/Opera use a mouse scroll event which has a much smaller wheel delta
			
			this.WHEEL_DELTA_MODIFIER *= 10;
		}
			
		// Setup listeners
		
		this.div.observe('mousedown', this.startMove.bind(this));
		this.div.observe('mouseup', this.stopMove.bind(this));
		this.div.observe('mouseout', this.stopMove.bind(this));
		this.div.observe('mousemove', this.move.bind(this));
		this.div.observe('DOMMouseScroll', this.scroll.bind(this));
		this.div.observe('mousewheel', this.scroll.bind(this));
		
		// Sliders/controls
		
		this.DEFAULT_ZOOM_DELTA = 0.1;
		this.DEFAULT_PAN = 24;
		
		// Tilt control
	
		this.tilt_slider = new Control.Slider('tilt_handle', 'tilt_slider',
		{
			range: $R(0, 1),
			axis: 'vertical'
		});
		
		this.tilt_slider.options.onSlide = this.tiltBar.bind(this);
		
		$('tilt_up').observe('click', this.panUp.bind(this));
		$('tilt_down').observe('click', this.panDown.bind(this));
		
		// Pan control
		
		this.pan_slider = new Control.Slider('pan_handle', 'pan_slider',
		{
			range: $R(0, 1),
			axis: 'horizontal'
		});
		
		this.pan_slider.options.onSlide = this.panBar.bind(this);
		
		$('pan_left').observe('click', this.panLeft.bind(this));
		$('pan_right').observe('click', this.panRight.bind(this));
		
		// Zoom control
		
		this.zoom_slider = new Control.Slider('zoom_handle', 'zoom_slider',
		{
			range: $R(0, 1),
			axis: 'horizontal'
		});
		
		this.zoom_slider.options.onSlide = this.zoomBar.bind(this);
		$('zoom_left').observe('click', this.zoomOut.bind(this));
		$('zoom_right').observe('click', this.zoomIn.bind(this));
	},
	
	
	// Callback for a mouse start move
	
	startMove: function(e)
	{
		if (this.moving)
			return;
      	
		this.start_x = e.clientX;
		this.start_y = e.clientY;
		
		this.moving = true;
		e.stop();
    },


	// Callback for a mouse stop move
	
    stopMove: function(e)
	{
		if (!this.moving)
			return;
		
		// Update the image coordinates
		
		var dx = e.clientX - this.start_x;
		var dy = e.clientY - this.start_y;
		
		this.set_bounded_canvas_position(this.canvas_x + dx, this.canvas_y + dy);
		
      	this.moving = false;
      	e.stop();
		
		this.calculate_sliders();
    },

	
	// Mouse move callback
	
    move: function(e)
	{
		if (!this.moving)
			return;
			
		// Update image
		
		var dx = e.clientX - this.start_x;
		var dy = e.clientY - this.start_y;
		
		var new_x = Math.min(this.canvas_x + dx, 0);
		var new_y = Math.min(this.canvas_y + dy, 0);
		
		new_x = Math.max(new_x, this.div.getWidth() - this.canvas_width);
		new_y = Math.max(new_y, this.div.getHeight() - this.canvas_height);
		
		// We're not using set_bounded_canvas_position, because canvas_x and canvas_y
		// must not be updated now
		 
		this.set_canvas_position(new_x, new_y);
		
		e.stop();		
	},
	
	
	// Mouse scroll wheel callback
	
	scroll: function(e)
	{
		// WebKit includes both e.offsetX and e.layerX whereas
		// IE and Fx use either e.offsetX or e.layerX respectively
		
		var offset_x = (e.offsetX ? e.offsetX : e.layerX);
		var offset_y = (e.offsetY ? e.offsetY : e.layerY);
		
		// Get wheel delta
		
		var delta = ((e.detail) ? -e.detail : e.wheelDelta) / 3.0;
		
		if (delta > 0)
		{
			// The scroll wheel is scrolled towards the screen; we zoom in (decrease the scale)
			
			this.set_scale(this.scale - this.WHEEL_DELTA_MODIFIER * delta, offset_x, offset_y);
		}
		else
		{
			// The scroll wheel is scrolled away from the screen; we zoom out (increase the scale)
			
			this.set_scale(this.scale - this.WHEEL_DELTA_MODIFIER * delta, offset_x, offset_y);
		}
		
		// Do not propagate event to prevent the browser scrolling
		
		Event.stop(e);
	},
	
	
	// Pans up
	
	panUp: function(e)
	{
		this.set_bounded_canvas_position(this.canvas_x, this.canvas_y + this.DEFAULT_PAN);
		this.calculate_sliders();
	},
	
	
	// Pans down
	
	panDown: function(e)
	{
		this.set_bounded_canvas_position(this.canvas_x, this.canvas_y - this.DEFAULT_PAN);
		this.calculate_sliders();
	},
	
	
	// Pans left
	
	panLeft: function(e)
	{
		this.set_bounded_canvas_position(this.canvas_x + this.DEFAULT_PAN, this.canvas_y);
		this.calculate_sliders();
	},
	
	
	// Pans right
	
	panRight: function(e)
	{
		this.set_bounded_canvas_position(this.canvas_x - this.DEFAULT_PAN, this.canvas_y);
		this.calculate_sliders();
	},
	
	
	// Zooms in
	
	zoomIn: function(e)
	{
		// We do not have any offsets, so we take the center of the image
		
		var center = this.get_div_center();
		
		this.set_scale(this.scale - this.DEFAULT_ZOOM_DELTA, center[0], center[1]);
		this.calculate_sliders();
	},
	
	
	// Zooms out
	
	zoomOut: function(e)
	{
		// We do not have any offsets, so we take the center of the image
		
		var center = this.get_div_center();
		
		this.set_scale(this.scale + this.DEFAULT_ZOOM_DELTA, center[0], center[1]);	
		this.calculate_sliders();
	},
	
	
	// Handles zoom bar drags
	
	zoomBar: function(v)
	{
		// Calculate new scale based on slider value
		
		var center = this.get_div_center();
		var new_scale = this.min_scale + ((1.0 - v) * (this.max_scale - this.min_scale));
		this.set_scale(new_scale, center[0], center[1]);
	},
	
	
	// Handles tilt bar drags
	
	tiltBar: function(v)
	{
		// Map slider position to canvas y position
		
		var new_y = -(v * (this.canvas_height - this.div.getHeight()));
		this.set_bounded_canvas_position(this.canvas_x, new_y);
	},
	
	
	// Handles pan bar drags
	
	panBar: function(v)
	{
		var new_x = -(v * (this.canvas_width - this.div.getWidth()));
		this.set_bounded_canvas_position(new_x, this.canvas_y);
	},
	
	
	// Sets the new zoom level. The zoom level will be bounded by this.min_scale and this.max_scale
	
	set_scale: function(zoom_level, offset_x, offset_y)
	{
		this.scale = Math.max(Math.min(this.max_scale, zoom_level), this.min_scale);
		
		// Adjust the canvas size
		
		var origin_x = offset_x + this.canvas_x;
		var origin_y = offset_y + this.canvas_y;
		
		var left_proportion = offset_x / this.canvas_width;
		var top_proportion = offset_y / this.canvas_height;
		
		this.canvas_width = this.image_width / this.scale;
		this.canvas_height = (this.image_height / this.scale) * this.scale_ratio;
		
		var to_left = left_proportion * this.canvas_width;
		var to_top = top_proportion * this.canvas_height;
		
		this.set_canvas_dimensions(this.canvas_width, this.canvas_height);
		
		// We may need to adjust the canvas top-left
		
		this.set_bounded_canvas_position(origin_x - to_left, origin_y - to_top);
		
		// Adjust sliders
		
		this.calculate_sliders();
	},
	
	
	// Sets the position of the canvas (i.e. the image in the div)
	
	set_canvas_position: function(x, y)
	{
		this.image.setStyle({top: Math.floor(y), left: Math.floor(x)});
	},
	
	
	// Sets the size of the canvas (i.e. the image in the div)
	
	set_canvas_dimensions: function(w, h)
	{
		this.image.setStyle({width: Math.floor(w), height: Math.floor(h)});
	},
	
	
	// Sets the position of the canvas within bounds (s.t. the containing div does not contain
	// empty spaces)
	
	set_bounded_canvas_position: function(x, y)
	{
		var new_x = Math.min(x, 0);
		var new_y = Math.min(y, 0);
		
		new_x = Math.max(new_x, this.div.getWidth() - this.canvas_width);
		new_y = Math.max(new_y, this.div.getHeight() - this.canvas_height);
		
		this.canvas_x = new_x;
		this.canvas_y = new_y;
		
		this.image.setStyle({top: Math.floor(new_y), left: Math.floor(new_x)});
	},
	
	
	// Returns the center of the div relative to the canvas
	
	get_div_center: function()
	{
		var center = new Array();
		center[0] = -this.canvas_x + Math.floor(this.div.getWidth() / 2);
		center[1] = -this.canvas_y + Math.floor(this.div.getHeight() / 2);
		
		return center;
	},
		// 
		// 
		// // Returns offset of an event
		// 
		// get_event_offset: function(e)
		// {
		// 	var offset = new Array();
		// 	
		// 	offset[0] = (e.offsetX ? e.offsetX : e.layerX);
		// 	offset[1] = (e.offsetY ? e.offsetY : e.layerY);
		// 	
		// 	return offset;
		// },
	
	
	// Sets the sliders based on the current canvas
	
	calculate_sliders: function()
	{
		// Tilt
		
		var v = -this.canvas_y / (this.canvas_height - this.div.getHeight());
		if (isNaN(v))
			v = 0.5;
		this.tilt_slider.setValue(v);
		
		// Pan
		
		v = -this.canvas_x / (this.canvas_width - this.div.getWidth());
		if (isNaN(v))
			v = 0.5;
		this.pan_slider.setValue(v);
		
		// Zoom
		
		v = 1.0 - (this.scale - this.min_scale) / (this.max_scale - this.min_scale);
		this.zoom_slider.setValue(v);
	}
}
