(function(parent, functions) {
	var Comment = function() {
		var that = {
			reply: reply,
			erase: erase,
			edit: edit,
			like: like,
			dislike: dislike,
			aprove: aprove,
			cancel: cancel
		},
		url = plus_url + '/modulos/sistema/core/ajax/Comment.ajax.php';

		/* Controladores de ações */
		function reply (element) {
			var comment = build(element);
			
			if (element.hasClass('comment-hidden')) {
				alert("Você não pode responder um comentário oculto.");
				return false;
			}
			
			if (comment.uname && comment.text.indexOf('@') == -1) comment.text = '@' + comment.uname.split(' ', 1)[0].trim() + ' ' + comment.text;
			else console.info("plus.comment::reply: Erro ao localizar nome de usuario para resposta.");
			
			comment.uname = '';
			comment.cdreply = comment.id; // Adiciona código de resposta (comment.id == comment.cdcomment)
			comment.id = undefined;		  // Estamos criando um novo comentário
			
			element.after(form(comment, {submit: reply.send, cancelable: true}));
		}
		
		reply.send = function (event) {
			var redirect = form.element.find("#redirect_page");
			form.old_redirect = redirect.val();
			redirect.val('');									// Evita redirecionamento
			redirect.remove();
			
			if (!form.element.validate().form()) return false;
			
			// Envia dados para atualização do comentário
			$.ajax({
				url: url + "?type=save",
				data: form.element.serialize(),
				method: form.element.attr('method').toLowerCase(),
				success: replied
			});
			
			/* @todo melhor feedback no estado de 'criando' um comentário
			form.element.hide();
			*/			
			return false;
		};
		
		// Voltar formulário para local inicial
		function cancel(event) {
			var formulario = form.element;
			
			// Remover eventos de submissão
			formulario.unbind('submit', reply.send)
				.find("#commentCancel").hide();
			
			// Remover código de resposta do formulário
			formulario.find('input[name="cdreply"]').val('');
			
			// Volta redirecionamento do formulário
			formulario.find("#redirect_page").val(form.old_redirect);
			
			$("#commentsPoster").append(form.element);
		};
		
		function erase (element) {
			var cid = element.attr('data-cid');
			$.getScript(url + '?cdcomment=' + cid + "&type=delete");
			functions.slideFadeRemove.call(element);
		}
		
		function edit (element) {
			var comment = build(element);
			comment.hide();
			
			element.after(form(comment, {submit: edited, store: comment, cancelable: false}));
		}
		
		function like (element) {
			if (element.hasClass("comment-liked") || element.hasClass("comment-disliked")) return false;
			var cid = element.attr('data-cid');
			
			$.ajax({
				url: url,
				context: element,
				success: liked,
				dataType: "json",
				data: {cdcomment: cid, type: "like"}
			});
		}
		
		function dislike (element) {
			if (element.hasClass("comment-liked") || element.hasClass("comment-disliked")) return false;
			var cid = element.attr('data-cid');
			
			$.ajax({
				url: url,
				context: element,
				success: disliked,
				dataType: "json",
				data: {cdcomment: cid, type: "dislike"}
			});
		}
		
		function aprove (element) {
			var cid = element.attr('data-cid');
			
			$.ajax({
				url: url,
				context: element,
				success: aproved,
				dataType: "json",
				data: {cdcomment: cid, type: "aprove"}
			});
		}
		
		
		/* Controladores de conteudo */
		/**
		 * @param string|comment Seta ou retorna valores do formulário de comentário. 
		 * @todo arrajar maneira de pegar um token e inserir dinamicamente no formulário 
		 * **/
		function form (comment, opcoes) {
			(!opcoes) && (opcoes = {});
			var formulario = form.element;
			
			if (comment.cdreply) formulario.find('#cdreply').val(comment.cdreply);
			if (comment.mid) formulario.find('#cdmodule').val(comment.mid);
			if (comment.text) formulario.find('#text').text(comment.text);
			if (comment.uname) formulario.find('#uname').val(comment.uname);
			
			formulario.get(0).reset();											// Limpa formulario
			formulario.find('#cdcomment').val(comment.id || "");
			
			if (!opcoes.cancelable) formulario.find('#commentCancel').hide();
			else formulario.find('#commentCancel').show();
			
			formulario.submit(opcoes.submit);
			return formulario;
		}
		
		
		/* Controladores de eventos */
		function enter(event) {
			var target = event.currentTarget;
			$(target).addClass('commentHover').children(".commentControls").show();
		}

		function leave(event) {
			var target = event.currentTarget;
			$(target).removeClass('commentHover').children(".commentControls").hide();
		}
		
		function rotear (event) {
			var action = event.target.getAttribute('data-action');
			if (that[action]) {
				that[action]($(event.target).parents('.comment'), event);
				event.preventDefault();
			}
		}
		
		/* Controladores de eventos - Callbacks */
		function edited (event) {
			console.info("Não implementado ainda.");
			/*var target = $(event.target),
			comment = this.store,
			uname = target.find('input[name=uname]').val(),
			text = target.find('textarea').val();
			
			comment.text = text;
			comment.uname = uname;
			
			if (comment.text) {
				$[target.attr('method').toLowerCase()](url + "?type=update", comment.serialize());
				comment.children('.commentText').text(text);
				target.unbind('submit', edited);
				target.prev().remove();
				target.remove();
				comment.show();
			} else alert("Comentários vasios não são permitidos!");*/
			
			return false;
		}
		
		function replied (data) {
			var comment = $(data), formulario = form.element;
			
			// Binda informações ao novo comentário
			comment.hover(enter, leave)
					.addClass("comment-new");
			
			// Reseta formulário
			formulario.unbind('submit', reply.send)
				.find("#commentCancel").hide();
			
			formulario.get(0).reset();
			
			// Insere este comentário logo após a resposta
			formulario.before(comment);
			
			// Volta formulário para o topo
			$("#commentsPoster").append(formulario);
			
			// Exibe comentário novo
			comment.fadeIn('slow');
			
			// Volta redirecionamento do formulário
			formulario.find("#redirect_page").val(form.old_redirect);
		}
		
		function liked(data) {
			if (data === true) {
				var counter = this.addClass('comment-liked').find('.commentLikeCount');
				if (counter) {
					var count = parseInt(counter.text());
					if (isNaN(count)) console.error('Impossivel converter para inteiro: ', counter.text());
					else counter.text(++count);
				}
			} else alert(data);
		}
		
		function disliked(data) {
			if (data === true) {
				var counter = this.addClass('comment-disliked').find('.commentDislikeCount');
				if (counter) {
					var count = parseInt(counter.text());
					if (isNaN(count)) console.error('Impossivel converter para inteiro: ', counter.text());
					else counter.text(++count);
				}
			} else alert(data);
		}
		
		function aproved(data) {
			if (data === true) this.removeClass("comment-pending").addClass('comment-aproved');
			else alert(data);
		}
		
		// Helpers
		function build(element) {
			element.id = element.attr('data-cid');
			element.mid = element.attr('data-mid');
			element.iid = element.attr('data-iid');
			element.text = element.children('.commentText').text().trim();
			element.uname = element.find('.commentUsuario').text().trim();
			
			element.serialize = build.serialize;
			return element;
		};
		
		build.serialize = function () {
			return {
				cdcomment: this.id,
				cdmodule: this.mid,
				cditem: this.iid,
				uname: this.uname,
				text: this.text
			};
		};
		
		function intialize() {
			var rules = undefined;
			
			form.element = $("#commentForm");
			
			$('#commentsFlat').click(rotear)
				.children('.comment').hover(enter, leave);
			
			$('#commentToggler').click(functions.toggleFadeNext);
			
			// Regra de validação no caso de notificação
			rules = {
			    	text: {
			    		required: true,
			    		messages: {
			            	required: "O corpo da mensagem é obrigatório."
			            }
		    		}
			};
			
			if (form.element.length) {
				if (plus.form('#commentForm').hasField('email')) 
					rules.email = {
			            required: function () {
			                return $("#notify:checked").length !== 0;
			            },
			            messages: {
			            	required: "Para receber notificações, o email é obrigatório"
			            }
			      	};
			
				plus.form('#commentForm').rules('add', rules);
			}
		};
		
		// Propriedades internas da classe
		form.element = "";
		

		$(document).ready(intialize);

		return that;
	};
	
	if (!functions.toggleFadeNext)
		functions.toggleFadeNext = function() {
			var target = $(this).next();
			if (target.is(':visible')) target.fadeOut();
			else target.fadeIn();
			return false;
		};
	
	if (!functions.slideFadeRemove)
		functions.slideFadeRemove = function() {
			$(this).wrap('<div />').animate({opacity: 0}).animate({height: 0}, functions.remove);
		};

	if (!functions.remove)
		functions.remove = function() {
			$(this).remove();
		};

	parent.comment = Comment();
})(plus, plus.fn);
