;(function($) {
	
$.fn.extend({
	conversation: function(options) {
		options = $.extend({}, $.Conversation.defaults, options);
		
		return this.each(function() {
			//new $.Conversation(this, options);
			new $.Conversation(this, options);
		});
	},
	scrollToBottom: function() {
		this.animate({scrollTop: this[0].scrollHeight}, 1000);	
	},
	scrollToElement: function(element) {
		// Requires jquery.scrollTo
		if(this.scrollTo)		
			this.scrollTo(element);
	},
	canSeeChild: function(elem) {
	    var docViewTop = $(this).scrollTop();
	    var docViewBottom = docViewTop + $(this).height();
	
	    var elemTop = $(elem).position().top;
	    var elemBottom = elemTop + $(elem).height();
	
	    return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)
	      && (elemBottom <= docViewBottom) &&  (elemTop >= docViewTop) );		
	}

});	

$.Conversation = function(wrapper, options)
{		
	this.id = options.id;

	if(!this.id) return;
	
	var thisObj = this;
	var parentDiv = $(wrapper);
	thisObj.options = options;
	if(thisObj.options.scrollDiv) thisObj.options.scrollDiv = $(thisObj.options.scrollDiv);
	var newCommentDiv = (this.options.disableReply?0: (this.options.newCommentButton? $(this.options.newCommentButton) : 0));
	var mergeDiv = (this.options.mergeButton? $(this.options.mergeButton) : 0);		
		
	var childrenToLoad = [];
	var children = [];
	
	var highestId = 0; // used to pull updates from server

	thisObj.getMainConversation = function()
	{
		return thisObj;
	};	
	
	thisObj.isRoot = function(){ return 1; };
	
	thisObj.loadNext = function()
	{
		if(childrenToLoad.length)
			thisObj.addChild(childrenToLoad.pop());
	};
	
	thisObj.addChild = function(id, isFromUpdate)
	{
		children.push(new $.Conversation.Item(id, thisObj, isFromUpdate));
	};
	
	thisObj.removeChild = function(id)
	{
		for(var key in children)
			if(children[key].id == id)
			{	
				children[key].destroy();	
				delete children[key];
			}	
	};
	
	thisObj.infanticide = function(conversation_id)
	{
		for(var key in children)
			if(children[key].conversationId == conversation_id)
			{	
				children[key].destroy();	
				delete children[key];
			}
	};	
	
	thisObj.getContainer = function() {	return parentDiv; };			
	thisObj.getWrapper = function() { return parentDiv; };	
	
	thisObj.openResponse = function() {
		if(newCommentDiv.css('display') == 'block')
		{
			thisObj.hideReply();		
			new $.Conversation.Response(thisObj);			
		}			
	};
	
	thisObj.showReply = function()
	{
		newCommentDiv.css('display', 'block');
		//for(var key in children)
		//	children[key].showReply();
	};
	
	thisObj.hideReply = function()
	{
		newCommentDiv.css('display', 'none');
		//for(var key in children)
		//	children[key].hideReply();
	};	
	
	thisObj.setHighestId = function(id)
	{
		if(id > highestId) highestId = id;
	};
	
	thisObj.applyUpdate = function(update)
	{
		if(update.parent_id == 0)
		{
			var child = 0;
			// do we already have this child?
			for(var key in children)
				if(children[key].id == update.id) child = children[key];
				
			if(update.code == 'add')
			{	
				if(!child)			
					thisObj.addChild(update.id, 1);	
			}
			else if(update.code == 'remove')
			{
				if(child)
					thisObj.removeChild(update.id);	
			}
		}
		else
		{
			for(var key in children)
				children[key].applyUpdate(update);
		}
	};
	
	if(thisObj.options.loadWithAjax) initializeAjax();
	else initializeNonAjax();	
	
	function initializeAjax()
	{
		// get the array of root items and then
		// Each of the items will make their own individual AJAX calls
		// to get their HTML
		
		$.post(thisObj.options.getConversationUrl, {conversation_id: thisObj.id, merged_ids: thisObj.options.mergedIds}, function(data){ 
			childrenToLoad = jQuery.parseJSON(data);
			loadChildren(); 
		});			
		
		initialize();
	}
	
	function initializeNonAjax()
	{
		// The HTML is already on the page.
		// Each element in the tree is responsible for parsing its own structure from the HTML


		parentDiv.children(thisObj.options.itemDivClass).each(function(index, item) {
			item = $(item);
			// We get the item id from the <div ... item="45"> item attribute			
			childrenToLoad.push(item.attr('itemid'));			
		});
		
		// Load like normal.
		loadChildren(childrenToLoad);
		
		initialize();
	}
	
	function initialize()
	{
		if(newCommentDiv)
		{
			newCommentDiv.click(function(){				
				thisObj.openResponse();
			});	
		}
		if(mergeDiv)
		{
			mergeDiv.click(function(){
				$.post(thisObj.options.mergeConversationUrl, {merged_id: thisObj.id}, function(success){ 
					if(success)
					  fadeMergeButton();
					else
					  alert('There was an error :-(');
				});								
			});	
		}
		queueUpdate();
	}
	
	function fadeMergeButton()
	{		
		mergeDiv.animate({opacity:0},1200);
	}
	
	function queueUpdate()
	{
		if(thisObj.options.updateFrequency)
			window.setTimeout(requestUpdate, thisObj.options.updateFrequency * 1000);
	}
	
	function requestUpdate()
	{
	
		$.post(thisObj.options.requestUpdateUrl, 
			{conversation_id: thisObj.id, merged_ids: thisObj.options.mergedIds, highest_id: highestId}, 
			function(data){ 
				data = jQuery.parseJSON(data);
				
				if(data && data.length)
				{
					for(var x=0; x < data.length; x++)
					 thisObj.applyUpdate(data[x]);
				}
			}
		);		
				
		queueUpdate();
	}

	function loadChildren()
	{			
		//childrenToLoad.reverse();

		// Load in a chain sequence so ajax calls dont get out of order		
		thisObj.loadNext();
	}
};

$.Conversation.Item = function(id, parent, fromUpdate)
{		
	this.id = id;

	// Note: this isn't necessarily the same id as the root $.Conversation object
	// Because it may be coming from a merged conversation
	
	this.conversationId = 0;
	
	var thisObj = this;		
	var thisDiv;
	var childrenDiv;
	var contentDiv;
	var replyDiv;
	var responseDiv;
	var parentDiv = parent.getContainer();	
	var titleDiv;
	
	var deleteDiv = 0;

	//alert("ID: "+id+" parent: "+parent.id+" div: "+parentDiv);
	
	thisObj.options = parent.options;			

	var childrenToLoad = [];
	var children = [];
	
	thisObj.getMainConversation = function()
	{
		return parent.getMainConversation();
	};
	
	thisObj.isRoot = function(){ return 0; };
	
	thisObj.addChild = function(id, isFromUpdate)
	{
		children.push(new $.Conversation.Item(id, thisObj, isFromUpdate));
	};	
	
	thisObj.removeChild = function(id)
	{
		for(var key in children)
			if(children[key].id == id)
			{	
				children[key].destroy();	
				delete children[key];
			}	
	};

	thisObj.destroy = function()
	{
		thisDiv.remove();
	};
	
	thisObj.loadNext = function()
	{
		if(childrenToLoad.length)
			thisObj.addChild(childrenToLoad.pop());
	};
	
	thisObj.getContainer = function() {	return childrenDiv; };			
	thisObj.getWrapper = function() { return thisDiv; };

	thisObj.openResponse = function()
	{		

		if(replyDiv.css('display') == 'block')
		{
			thisObj.hideReply();		
			
			// Will reply to parent, but show box below this item
			if(thisObj.options.replyToParent && !parent.isRoot())
				new $.Conversation.Response(parent, thisObj);
			else
				new $.Conversation.Response(thisObj);
		}

	};	
			
	thisObj.showReply = function()
	{
		replyDiv.css('display', 'block');
		//for(var key in children)
		//	children[key].showReply();
	};
	
	thisObj.hideReply = function()
	{
		replyDiv.css('display', 'none');
		//for(var key in children)
		//	children[key].hideReply();
	};
	
	thisObj.setHighestId = function(id)
	{
		parent.setHighestId(id);
	};	
	
	thisObj.applyUpdate = function(update)
	{
		if(update.parent_id == thisObj.id)
		{
			var child = 0;
			// do we already have this child?
			for(var key in children)
				if(children[key].id == update.id) child = children[key];
				
			if(update.code == 'add')
			{	
				if(!child)			
					thisObj.addChild(update.id, 1);	
			}
			else if(update.code == 'remove')
			{
				if(child)
					thisObj.removeChild(update.id);	
			}
		}
		else
		{
			for(var key in children)
				children[key].applyUpdate(update);
		}
	};	
	
	
	if(thisObj.options.loadWithAjax) initializeAjax();
	else initializeNonAjax();

	function initializeAjax()
	{		
		thisDiv = $('<div class="loading"></div>');
		parentDiv.append(thisDiv);			
				
		$.post(thisObj.options.getItemHtmlUrl, { item_id: id },
			function(data){
				newDiv = $(data);
				thisDiv.replaceWith(newDiv);
				thisDiv = newDiv;
	
				//scroll();
				
				parseHtml();
				initialize();
			}
		);		
	}
	
	function initializeNonAjax()
	{	
		parseHtml();
				
		// The html is already in the DOM.  Just initialize
		initialize();
	}
	
	function initialize()
	{		
		// Update conversation's highestId
		thisObj.setHighestId(id);
		
		// Add click events
		if(thisObj.options.disableReply) thisObj.hideReply();		
		else initializeReplyButton();				
		
		// Get the children ID's and start loading them
		loadChildren();								

		// Tell parent to continue with next
		parent.loadNext();	
		
		if(fromUpdate)
		  checkIfVisible();
		  
		showTitle();
		
		if(deleteDiv) initializeDeleteButton();
	}		
	
	function showTitle()
	{
		if(titleDiv)
		{
			// Only show the title if it's from a foreign conversation (merged)
			if(thisObj.conversationId != thisObj.getMainConversation().id)
			{
				titleDiv.css('display', 'block');	
				if(thisObj.options.isOwner)
				{
					deleteDiv.css('display', 'block');
				}
			}
				
		}	
	}
	
	function checkIfVisible()
	{
		if(thisObj.options.scrollDiv)
		{
			if(thisObj.options.scrollDiv.canSeeChild(thisDiv))
			{
				//alert("New message detected on-screen");	
				
			}	
			else
			{
				thisObj.options.scrollDiv.scrollToElement(thisDiv);
				//alert("New message detected off-screen");	
			}
			
		}
	}
		
	function parseHtml()
	{
		// Pick apart the HTML
		// To find our element Objects
		if(!thisDiv) // in case the html wasnt loaded via ajax
			thisDiv = parentDiv.children('[itemid='+id+']');
		if(!thisDiv) return;	
		thisObj.conversationId = thisDiv.attr('conversationid');
			
		contentDiv = thisDiv.children(thisObj.options.contentDivClass);		
		replyDiv = contentDiv.find('.reply');	
		thisDiv.children('div').each(function(index, item) {
			//alert($(item).attr('class'));
			
		});		
		childrenDiv = thisDiv.children(thisObj.options.childrenDivClass);	
		
		if(thisObj.options.titleDivClass) titleDiv = contentDiv.find(thisObj.options.titleDivClass);
		else titleDiv = 0;
		
		if(thisObj.options.isOwner) 
		  deleteDiv = contentDiv.find('.delete');
		
	}
	
	function initializeReplyButton()
	{
		replyDiv.click(function() {	thisObj.openResponse(); });			
	}	
	
	function initializeDeleteButton()
	{
		deleteDiv.css('display', 'block');
		if(deleteDiv)
		{
			if(thisObj.getMainConversation().id != thisObj.conversationId)
				deleteDiv.click(function() { unmergeConversation(); });	
			else
				deleteDiv.click(function() { deleteItem(); });
		}
	}
	
	function unmergeConversation()
	{
				$.post(thisObj.options.unmergeConversationUrl, { conversation_id: thisObj.getMainConversation().id, merged_id: thisObj.conversationId },
					function(success){
						if(success)
						{
							thisObj.getMainConversation().infanticide(thisObj.conversationId);
						}
					}
				);			
	}
	
	function deleteItem()
	{
				$.post(thisObj.options.deleteItemUrl, { item_id: thisObj.id },
					function(success){
						if(success)
						{
							parent.removeChild(thisObj.id);
						}
					}
				);			
	}

	function loadChildren()
	{
		if(thisObj.options.loadWithAjax)
		{

			
			$.post(thisObj.options.getChildrenUrl, { parent_id: id },
				function(children){
					children = jQuery.parseJSON(children);
					//childrenToLoad = children.reverse();
					childrenToLoad = children;
					thisObj.loadNext();
				}
			);				
		}
		else
		{		
			// Search for children we need to load
			childrenDiv.children(thisObj.options.itemDivClass).each(function(index, item) {
				item = $(item);
				// We get the item id from the <div ... item="45"> item attribute		
				childrenToLoad.push(item.attr('itemid'));			
			});
					
			childrenToLoad.reverse();
			thisObj.loadNext();
		}
	}
	
	function scroll()
	{
		if(thisObj.options.scrollDiv)
		{
			thisObj.options.scrollDiv.scrollToBottom();
		}		
	}

};


// displayUnder is optional. 
// if specified, will show the box under (displayUnder) object, but reply to (parent)
$.Conversation.Response = function(parent, displayUnder)
{	
	var id = parent.id;
	var type = (parent.isRoot()?'conversation':'item');
	
	this.reply_to = id;
	this.reply_to_type = type;
	
	var thisObj = this;
	var thisDiv;
	var submitDiv;
	var closeDiv;
	var displayObj = (displayUnder?displayUnder:parent);
	var parentDiv = displayObj.getWrapper();	
	var textarea;	
	
	thisObj.options = parent.options;
	
	initializeAjax();
	
	function initializeAjax()
	{
		thisDiv = $('<div class="loading"></div>');
		if(parent.isRoot())
		{
			parentDiv.append(thisDiv);
			scroll();
		}
		else
		{
			parentDiv.children(thisObj.options.contentDivClass).after(thisDiv);
		}
		
		// Get the html from the server
		$.get(thisObj.options.getResponseHtmlUrl, function(data) {
			
			newDiv = $(data);
			
			
			
			thisDiv.replaceWith(newDiv);
			thisDiv = newDiv;
			
			//responseDiv.animate({height: origheight}, 800);
			
			initialize();						
		});		
	}
	
	function parseHtml()
	{
		if(!thisDiv) 
			thisDiv = parent.getWrapper().children(thisObj.options.responseDivClass);
		if(!thisDiv) return;
		
		submitDiv = thisDiv.find('.submit');
		closeDiv = thisDiv.find('.close');
		textarea = thisDiv.find('textarea');
	}
	
	function initialize()
	{
		parseHtml();
		
		initializeCloseButton();
		initializeSubmitButton();
		
		textarea.focus();
		
		if(type=='conversation') 
		  scroll();
	}
	
	function initializeCloseButton()
	{
		closeDiv.mouseover(function() {
			closeDiv.addClass('close-hover');
		});
		
		closeDiv.mouseout(function() {
			closeDiv.removeClass('close-hover');
		});
		
		closeDiv.click(function() {	close(); });			
	}	
	
	function initializeSubmitButton()
	{
		submitDiv.click(function() {
			submit();			
		});
	}
	
	function errorHandle(error_code)
	{

		if(error_code == 0)
		{
		  alert( "Did you have something to say?  Your message is empty" );
		}
		else if(error_code == -1)
		{
		  captcha();
		}
		else
		  alert( "Unknown error" );		  		
		
	}
	
	function captcha()
	{
		// should open a small captcha window in the response box		
	}
	
	function submit()
	{
		var value = textarea.val();
		if(value.length)
		{
			$.post(thisObj.options.postResponseUrl, { reply_to: thisObj.reply_to, reply_to_type: thisObj.reply_to_type, message: textarea.val() },
				function(data){
					if(data)
					{
						parent.addChild(data);
						close();
					}
					else
						errorHandle(data);
											
					//alert(data);
				}
			);	
		}	
		else
		{
			errorHandle(0);			
		}
	}
	
	function close()
	{
		thisDiv.animate(
			{opacity: 0}, 
			800, 
			function() {
				thisDiv.remove();
			}
		);
		
		displayObj.showReply();
	}
	
	function scroll()
	{
		if(thisObj.options.scrollDiv)
		{
			//thisObj.options.scrollDiv.scrollToBottom();
			thisObj.options.scrollDiv.scrollToElement(thisDiv);
		}		
	}	
};

$.Conversation.defaults = {
	getConversationUrl: ('site_url' + 'ajax/conversation_ajax/getConversation'),
	getChildrenUrl: ('site_url' + 'ajax/conversation_ajax/getChildren'),
	getItemHtmlUrl: ('site_url' + 'ajax/conversation_ajax/getItemHtml'),
	getResponseHtmlUrl: ('site_url' + 'ajax/conversation_ajax/getResponseHtml'),
	postResponseUrl: ('site_url' + 'ajax/conversation_ajax/postResponse'),
	requestUpdateUrl: ('site_url' + 'ajax/conversation_ajax/requestUpdate'),
	mergeConversationUrl: ('site_url' + 'ajax/conversation_ajax/mergeConversation'),
	unmergeConversationUrl: ('site_url' + 'ajax/conversation_ajax/unmergeConversation'),
	deleteItemUrl: ('site_url' + 'ajax/conversation_ajax/deleteItem'),
	updateFrequency: 20, // 0 = no updates
	itemDivClass: '.conversationitem',
	childrenDivClass: '.itemchildren',
	contentDivClass: '.itemcontent',		
	responseDivClass: '.response',
	titleDivClass: '.conversationtitle', // or 0.  Must be a child of contentDivClass
	loadWithAjax: 1,
	scrollDiv: '#conversation_wrapper', // or 0
	disableReply: 0,
	replyToParent: 1, // Replies to highest tier of comment.  effectively limits the conversation to 2 tiers.  otherwise supports unlimited tiers
	newCommentButton: '#conversation_newcomment', // or 0.  This is the button that adds new comments into the feed.
	mergeButton: 0, // '#conversation_merge'
	isOwner:0, // 1 = can delete merged feeds and other comments
	id:0,
	mergedIds: [] // todo: finish implementation.
	// if given an array of mergedIds, this conversation will
	// display and respond to other conversations from this single window
	// however, the mechanisms for users to choose which conversations
	// to merge is not yet implemented.
	// 1.  add button that says "Add this feed to my page" or similar
	// 2.  add (x) next to feed title
	
};

})(jQuery);

