// interface file
// by Melnychuk Oleg  [http://my.opera.com/melnichuck]

/******************************************************

	MAP GUI CLASS
	provides interface manipulations

******************************************************/

function Interface() {
	this._curr_tool = "";
	this._prev_tool = "";
	
	this.scrollbars_visible = this.isMSIE6();
}

/****************************************
	PUBLIC METHODS
*****************************************/
	
// interface initialization
Interface.prototype.initialize = function() {
	// append the map image holder
	$(CONFIG.layer.content).prepend('<img src="" id="' + CONFIG.layer.map.substring(1) + '" alt="" />');
	this._map = $(CONFIG.layer.map);
	
	this.update();
	this.setCurrentTool('default');
	this.setMapCursor();
};

// sizing sidebar to all available height
Interface.prototype.setSidebarHeight = function() {
	$(CONFIG.layer.sidebar).height(this.getSidebarHeight());
};

// sizing active sidebar item contents to all available height
Interface.prototype.setSidebarActiveItemHeight = function() {
	var sidebarItem			= $(CONFIG.layer.sidebar).find('.item');
	var activeItemContents	= $(CONFIG.layer.sidebar).find('.active .content');
	var sidebarHeaders		= $(CONFIG.layer.sidebar).find('h2');
	var sidebarItemMargins	= sidebarItem.outerHeight({ margin:true }) - sidebarItem.height();
	var contents_height		= this.getSidebarHeight();
	
	// substracting headers
	for (var i=0; i < sidebarHeaders.length; i++) {
		contents_height -= sidebarHeaders.outerHeight({ margin:true });
		// only one margin will be substracted
		contents_height -= (sidebarItemMargins % 2 === 0) ? (sidebarItemMargins / 2) : sidebarItemMargins;
	}
	
	// substractiong contents paddings
	contents_height -= activeItemContents.outerHeight({ margin:true }) - activeItemContents.height();
	// applying calculated height and add one 'margin'
	activeItemContents.height(contents_height - 1);
};

Interface.prototype.fitItemHeight = function() {
	var fit_elements = $(CONFIG.fit_height);
	var parent_height = 0;
	var element_height = 0;
	
	for (var i=0; i < fit_elements.length; i++) {
		// get the element's parent height
		parent_height = $(fit_elements[i]).parent().height();
		// suppose the element has the max height without margins
		element_height = parent_height - $(fit_elements[i]).outerHeight({ margin:true });
		// all element's neighbors (placed on the same level)
		var siblings = $(fit_elements[i]).siblings();
		
		// substract the height of all siblings
		for (var j=0; j < siblings.length; j++) {
			element_height -= $(siblings[j]).outerHeight({ margin:true });
		}
		// set the height to the element
		$(fit_elements[i]).height(element_height - CONFIG.content_margin);
	}
};

// activate current tool
Interface.prototype.setActiveItem = function(item) {
	this.getActiveItem(item).removeClass('active');
	$(item).addClass('active');
};

// map content area sizing
Interface.prototype.setContentSize = function() {
	var content = $(CONFIG.layer.content);
	var scrollWidth = this.scrollbars_visible ? this.scrollbarWidth() : 0;
	
	// setting content dimensions
	content.width($('body').outerWidth() - scrollWidth - $(CONFIG.layer.sidebar).outerWidth({margin:true}) - CONFIG.content_margin);
	content.height(this.getSidebarHeight() - CONFIG.content_margin);
};

// whether to show the sidetoolbar
// STATIC method
Interface.fetchContents = function(insert_to, query, cleanup, no_query, bug_type) {
	// any cleanup needed?
	if (cleanup) { $(insert_to).empty(); }
	
	if (no_query) {
		appendContent(query, insert_to);
	} else {
		$.ajax({
			url: CONFIG.host.name + CONFIG.host.usertab,
			async: true,
			data: { 
				forwhat: query,
				bugType: bug_type,
				x: CONFIG.coords.mainX,
				y: CONFIG.coords.mainY
			},
			success: function(res) {
				appendContent(res, insert_to);
			}
		});
	}
};

function appendContent(res, insert_to) {
	$(insert_to).append(res.xhtml);
	//alert(res.xhtml);
				
	// is there any javascript passed?
	if (res.js) {
		// trying to evaluate embedded script
		try {
			eval(res.js);
		} catch (err) {
			alert(err.message);
		}
	}
	// alter all empty # attributes
	$("a[href=#]").attr({ href: "javascript:void(0);" });
}

// updates all unit dimensions after resizing
Interface.prototype.update = function() {
	this.setSidebarHeight();
	this.setSidebarActiveItemHeight();
	this.fitItemHeight();
	this.setContentSize();
};

// save the currently selected tool to CONFIG and activate it
Interface.prototype.setCurrentTool = function(tool) {
	if (tool == 'default') {
		// setting up the previous tool id,
		// as far, as it's the initialization stage, let's set it to the current tool
		this._curr_tool = CONFIG.default_tool || CONFIG.tools[0];
		this._prev_tool = this._curr_tool;
	} else {
		this._curr_tool = this.getToolName(tool);
		// let's find out the currently active tool and get it's id
		this._prev_tool = this.getActiveItem(tool).find("a").attr("id");
		
		// whether or not enable drag functionality to the map
		if (this._curr_tool == CONFIG.tool.move) {
			// add drag and drop functionality to the map
			this._map.draggable({
				stop: function() {
					var image = Map.dragMap(getDragOffset());
					var mini_img = Map.getMini();
					Interface.setMapImage(image);
					Interface.setMiniImage(mini_img);
				}
			});
		} else {
			// disable drag-n-drop functionality for the map
			this._map.draggable("disable");
		}
	}
	
	this.setActiveItem($(this._curr_tool).parent());
};

// set up the map layer to represent the right cursor for the current tool
Interface.prototype.setMapCursor = function(tool) {
	this._map.removeClass();
	
	if (arguments.length > 0) {
		this._map.addClass(this.getIDValue(tool));
	} else {
		this._map.addClass(this.getIDValue(this._curr_tool));
	}
};

// get the click coordinates and save them to CONFIG
Interface.prototype.getClickCoordinates = function(e) {
	var offset = this._map.offset();

	if (!e) { var e = window.event; }

	if (e.pageX || e.pageY) {
		CONFIG.coords.mainX = e.pageX - offset.left;
		CONFIG.coords.mainY = e.pageY - offset.top;
		return true;
	} else {
		return false;
	}
};

// set the map image for the current state
Interface.setMapImage = function(JsonRespond) {
	if (JsonRespond) {
		$(CONFIG.layer.map).fadeTo('fast', 0.5).attr('src', JsonRespond.uri);
		
		$(CONFIG.layer.scale).find("li").width(JsonRespond.sbpixels);
		
		var span_element = $(CONFIG.layer.scale).find("span");
		
		for (var i=0; i<4; i++) {
			$(span_element[i]).text(JsonRespond.sbmeters * i);
		}
	}
};

Interface.setMiniImage = function(jsResp) {
	if (jsResp) {
		var viewport_x	= parseInt(jsResp.x, 10);
		var viewport_y	= parseInt(jsResp.y, 10);
		var viewport_w	= parseInt(jsResp.w, 10);
		var viewport_h	= parseInt(jsResp.h, 10);
		var img_w = 0;
		var img_h = 0;
		var delta_x = 0;
		var delta_y = 0;
		
		if (jsResp.uri) {
			var mini_img = new Image();
			
			mini_img.src = jsResp.uri;
			img_w = mini_img.width;
			img_h = mini_img.height;
			
			$(CONFIG.layer.minimap).remove();
			$(CONFIG.layer.miniholder).prepend('<img src="'+mini_img.src+'" id="minimap" alt="" />').fadeTo(0, 0.9);
			//$(CONFIG.layer.minimap).width(img_width);
			//$(CONFIG.layer.minimap).height(img_height);
		}
		// checking dimensions
		viewport_w = (viewport_w >= img_w) ? img_w : viewport_w;
		viewport_h = (viewport_h >= img_h) ? img_h : viewport_h;
		
		// checking start coordinates
		viewport_x = (viewport_x <= 0) ? 0 : viewport_x;
		viewport_y = (viewport_y <= 0) ? 0 : viewport_y;
		
		// checking the viewport overflow after the offset
		delta_x = img_w - viewport_x;
		delat_y = img_h - viewport_y;
		
		//viewport_w = (viewport_w > delta_x) ? delta_x : viewport_w;
		//viewport_h = (viewport_h > delta_y) ? delta_y : viewport_h;
		
		// the viewport is too small
		if (viewport_w <= 10) {
			$(CONFIG.layer.viewport).removeClass().addClass('dot');
		} else {
			$(CONFIG.layer.viewport).removeClass();
		}
		
		$(CONFIG.layer.viewport).css({
			top: viewport_y +"px",
			left: viewport_x +"px",
			width: viewport_w-1 +"px",
			height: viewport_h-1 +"px"
		});
	} else {
		return false;
	}
};

function getDragOffset() {
	var c = $(CONFIG.layer.map);
	var dx = 0, dy = 0;
	var cntrx = 0, cntry = 0;
	var pos = c.position();
	
	// computing the shift coordinates
	cntrx = parseInt(c.width() / 2, 10);
	cntry = parseInt(c.height() / 2, 10);

	// get map offset
	dx = cntrx - pos.left;
	dy = cntry - pos.top;
	
	return {X: dx, Y: dy};
};

// display objects affected by click
Interface.prototype.showFoundObjects = function(foundObjects) {
	var self = this;
	
	// show the search results table
	$(CONFIG.layer.results).slideDown('fast');
	
	// remove all rows from the results list
	self.clearResults();
	self.resetMapOffset();
	
	// something was found!
	if (foundObjects && foundObjects.length > 0) {
		// adding new rows with the search results
		for (var i=0; i < foundObjects.length; i++) {
			self.appendResults('<a href="javascript:void(0)" rel="'+foundObjects[i].la+';'+foundObjects[i].id+'">' +foundObjects[i].na+ '</a>');
		}
		
		// activate current result
		$(CONFIG.layer.results).find('a').click(function(evt) {
			var attribute = $(this).attr('rel');
			//alert(attribute);
			var layer_type = attribute.substring(0, attribute.indexOf(';'));
			var obj_id = attribute.substring(attribute.indexOf(';')+1, attribute.length);
			
			// making current result active
			self.setActiveItem($(this).parent());
			
			// sending the click coordinates and viewport
			var objImageURI = Map.showObject(layer_type, obj_id);
			Interface.setMapImage(objImageURI);
			Interface.setMiniImage(Map.getMini());
		});
	} else { // nothing was found
		self.appendResults(MSG.Error.nothing_found);
	}
};

// clear search results list
Interface.prototype.clearResults = function() {
	$(CONFIG.layer.results).find('ul').empty();
};

// appending the row to a table
Interface.prototype.appendResults = function(str) {
	$(CONFIG.layer.results).find('ul').append("<li>" +str+ "</li>\n");
};

// privileged method to get viewport height
Interface.prototype.getViewportHeight = function() {
	var vheight = self.innerHeight || jQuery.boxModel && document.documentElement.clientHeight || document.body.clientHeight;
	
	if (vheight > CONFIG.iface.min_viewport_height) {
		this.hideScrollBars();
		return vheight;
	} else {
		this.showScrollBars();
		return CONFIG.iface.min_viewport_height; 
	}
};

// sizing sidebar to fit all available height
Interface.prototype.getSidebarHeight = function() {
	var body = $('body');
	
	// other elements padding to substract
	body = body.outerHeight() - body.height();
	var sidebar	= $(CONFIG.layer.sidebar).outerHeight({margin:true}) - $(CONFIG.layer.sidebar).height();
	
	// calculating sidebar's height
	return this.getViewportHeight() - $(CONFIG.layer.header).outerHeight({margin:true}) - $(CONFIG.layer.footer).outerHeight() - body - sidebar;
};

// getting the current active object
Interface.prototype.getActiveItem = function(item) {
	return $(item).parent().find('.active');
};

Interface.prototype.scrollbarWidth = function() {
	// Scrollbalken im Body ausschalten
	document.body.style.overflow = 'hidden';
	var width = document.body.clientWidth;
	
	// Scrollbalken
	document.body.style.overflow = 'scroll';
	
	width -= document.body.clientWidth;
	
	// Der IE im Standardmode
	if (!width) { width = document.body.offsetWidth-document.body.clientWidth; }
	
	// ursprungliche Einstellungen
	document.body.style.overflow = '';
	
	return width;
};

Interface.prototype.resetMapOffset = function() {
	$(CONFIG.layer.map).css({top:0, left:0});
};

Interface.prototype.hideScrollBars = function() {
	$('body').removeClass('.showScroll');
	$('body').addClass('.hideScroll');
	this.scrollbars_visible = false;
};

Interface.prototype.showScrollBars = function() {
	$('body').removeClass('.hideScroll');
	$('body').addClass('.showScroll');
	this.scrollbars_visible = true;
};

// return tool name in the format: #tool-name
Interface.prototype.getToolName = function(tool) {
	return "#" + $(tool).attr('id');
};

Interface.prototype.getIDValue = function(item) {
	return $(item).attr("id");
};

Interface.prototype.isMSIE6 = function() {
	return jQuery.browser.msie && parseInt(jQuery.browser.version, 10) == 6;
};

Interface.prototype.setStatus = function(status_obj) {
	$(CONFIG.layer.stat).text(status_obj.msg);
	$(CONFIG.layer.stat).removeClass().addClass(status_obj.cls);
};