function Comments(i, user, isPrevelegedUser, deleteUrl, screenUrl, treeUrl)
{
	var id = i;
	var main = jq("#" + id);
	var that = this;
	var annotations;
	var data = {};
	var emptyHTML;
	var allowLevel = Comments.allow;
	var idPrefix = "comment";
	var parser = new XMLParser();
	data.currentComment = null;
	
	this.getData = function(){
		return data;
	}
	
	this.getAnnotations = function(){
		return annotations;
	}
	
	this.init = function()
	{
		parseAnnotations();
		annotations.form = id + "-form";
		annotations.isPrevelegedUser = (isPrevelegedUser == "true");
		annotations.deleteUrl = deleteUrl;
		annotations.screenUrl = screenUrl;
		annotations.treeUrl = treeUrl;
		initAddForm();
		Form.forms[annotations.form].addAnnotation("onSuccess", this.onAddForm);
		Form.forms[annotations.form].addAnnotation("onErrorResponse", this.onFormError);
		Form.forms[annotations.form].addAnnotation("onError", Comments.onError);
		jq("#" + id + "-form .close-button").click(function(){
			closeAddForm();
			return false;
		});
		emptyHTML = jq("." + annotations.emptyClass, main).html();
		emptyHTML = emptyHTML.replace(annotations.emptyClass, annotations.commentClass);
		initMain();
	}
	
	function initComment(comment, d){
		var a = parseCommentAnnotations(comment);
		var container = jq("." + annotations.containerClass, comment).eq(0);
		if(annotations.sort == "desc")
			container = jq("." + annotations.mainClass + " ." + annotations.containerClass, main);
		var subCount = jq("." + annotations.commentClass, container).length;
		var subPosition = jq("." + annotations.subPosistionClass, container).eq(0);
		var id;
		if(d == null)
			id = parseInt(a.id);
		else
			id = d.id;
		var count;
		if(d == null)
			count = parseInt(a.count);
		else
			count = d.count;
		var subLoaded = true;
		if(count > 0 && subCount == 0)
			subLoaded = false;
		if(annotations.sort == "desc")
			subLoaded = true;
		if(d == null)
			data[id] = {
				count:count,
				owner:parseInt(a.ownerId),
				parent:parseInt(a.parentId),
				screen:a.screen == "true",
				subLoaded:subLoaded
			};
		else
			data[id] = d;
		data[id].container = container;
		data[id].position = jq("." + annotations.positionClass, comment).eq(0);
		data[id].comment=comment;
		data[id].countContainer = jq("." + annotations.countClass, comment).eq(0);
		data[id].subPosition = subPosition;
	}
	
	this.lazyCommentInit = function(comments,id){
		initCommentAndParent(id);
	}
	
	function initCommentAndParent(id){
		while(id > 0){
			if(data[id] != undefined)
				break;
			var pc = jq("#" + idPrefix + id);
			if(pc.length == 0)
				break;
			initComment(pc, null);
			id = data[id].parent;
		}
	}
	
	function initAddForm(){
		var form = jq("#" + Form.forms[annotations.form].getId());
		form.css({position:"absolute"});
	}
	
	 function initMain(){
	 	var container = jq("." + annotations.mainClass + " ." + annotations.containerClass, main);
	 	var subPosition = jq("." + annotations.mainClass + " ." + annotations.subPosistionClass, main);
	 	jq("." + annotations.mainClass, main).each(function(i, comment){
			comment = jq(comment);
			var id = "main" + i;
			data[id] = {
				parent:0,
				comment:comment,
				container:container,
				position:jq("." + annotations.positionClass, comment).eq(0),
				subPosition:subPosition
			};
			jq(".reply", comment).click(function(){
				that.onReplyClick(id);
				return false;
			});
		});
	}
	
	this.onAddForm = function(response, data){
		var cdata = parseXMLComment(response.comment[0]);
		Comments.getOtherFields(cdata, response.comment[0]);
		addComment(cdata, true);
		Form.forms[annotations.form].loadingSwitch(false);
		jq("textarea", jq("#" + annotations.form)).val("");
		closeAddForm();
	}
	
	this.onFormError = function (d, data){
		Comments.onErrorResponse(d, data);
	}
	
	this.addNewComment = function(response){
		var cdata = parseXMLComment(response.comment[0]);
		if(cdata.parent == 0)
			cdata.parent = "main" + 0;
		Comments.getOtherFields(cdata, response.comment[0]);
		addComment(cdata, true);
		Form.forms[annotations.form].loadingSwitch(false);
		jq("textarea", jq("#" + annotations.form)).val("");
		closeAddForm();
	}
	
	function addComment(d, newComment){
		if(d.parent == 0)
			d.parent = data.currentComment;
		var comment = null;
		if(annotations.sort == "asc"){
			
			data[d.parent].container.append(emptyHTML);
			comment = jq("." + annotations.commentClass + ":last", data[d.parent].container);
		}else{
			data[d.parent].subPosition.after(emptyHTML);
			comment = jq("." + annotations.commentClass + ":first", data[d.parent].container);
		}
		comment.attr("id", idPrefix + d.id);
		initComment(comment, d);
		fillComment(d.id);
		if(newComment)
			updateThread(d.id, newComment);
	}
	
	function loadSubComments(id){
		jq.get(annotations.treeUrl, {parent:id}, function(response){
			onLoadSubComments(id, response);
		});
	}
	
	function onLoadSubComments(id, response){
		data[id].showButton.attr("class", annotations.showThreadClass + "NA");
		data[id].hideButton.attr("class", annotations.hideThreadClass);
		var o = parser.deserialize(response);
		var a = o[i];
		data[id].subLoaded = true;
		if(o == null || o == undefined)
			return;
		var d = o.comments.comments;
		if(d != undefined){
			data[id].subLoaded = true;
			for(var i = 0; i < d.length; i++){
				var cdata = parseXMLComment(d[i]);
				Comments.getOtherFields(cdata, d[i]);
				addComment(cdata, false);
			}
			//updateCount(id);
		}
	}
	
	function parseXMLComment(xmlComment){
		var date = Comments.parseDate(xmlComment.date);
		var screen = xmlComment.screen == "true";
		var ddd = {
			text:xmlComment.text,
			count:parseInt(xmlComment.count),
			id:parseInt(xmlComment.id),
			parent:parseInt(xmlComment.parentId),
			date:date,
			displayName:xmlComment.ownerDisplayName,
			login:xmlComment.ownerLogin,
			owner:parseInt(xmlComment.ownerId),
			screen:screen
		};
		if(xmlComment.parentOwnerLogin){
			ddd.parentOwnerLogin = xmlComment.parentOwnerLogin;
			ddd.parentOwnerDisplayName = xmlComment.parentOwnerDisplayName;
			ddd.parentOwnerId = xmlComment.parentOwnerId;
			ddd.parentText = xmlComment.parentText;
			ddd.parentDate = xmlComment.parentDate;
		}
		return ddd;
	}
	
	function updateThread(id, newComment){
		id = data[id].parent;
		while(id != 0 && data[id] != undefined){
			var s = id + "";
			var ignor = s.indexOf("main") != -1;
			data[id].count ++;
			var hideThreadButton = getButton(annotations.hideThreadClass, id);
			if(!ignor){
				if(annotations.sort != "desc")
					hideThreadButton.attr("class", annotations.hideThreadClass);
				if(data[id].count > 0){
					data[id].countContainer.html(Comments.countHTML(data[id].count));
					data[id].countContainer.show();
				}
				else
					data[id].countContainer.hide();
			}
			id = data[id].parent;
		}
	}
	
	function updateCount(id){
		if(data[id].count >= data[id].subLoaded)
			return;
		while(id != 0){
			data[id].count = data[id].count + 1;
			if(data[id].count > 0){
				data[id].countContainer.html(Comments.countHTML(data[id].count));
				data[id].countContainer.show();
			}
			else
				data[id].countContainer.hide();
			id = data[id].parent;
		}
	}
	
	function openAddForm(id){
		var form = jq("#" + Form.forms[annotations.form].getId());
		var s = id + "";
		if(s.indexOf("main") != -1)
			s = 0;
		if(parseInt(s) == parseInt(jq("input[@name=parent]", form).val()) && form.css("display") == "block"){
			closeAddForm();
			return;
		}
		var pos = data[id].position;
		pos.height(form.height());
		jq("input[@name=parent]", form).val(s);
		form.css({top:pos.offsetTop(),left:pos.offsetLeft(),width:pos.width(), display:"block"});
	}
	
	function closeAddForm(){
		Comments.onCloseForm(data, data.currentComment);
		jq("#" + Form.forms[annotations.form].getId()).css({display:"none"});
		if(data.currentComment != null)
			data[data.currentComment].position.height(0);
	}
	
	this.closeForm = closeAddForm;
	
	function parseAnnotations(){
		annotations = {};
		var a = jq(".annotations", main);
		if(a.length == 0)
			return;
		annotations = parseString(a.html());
		if(annotations.user == "0")
			annotations.user = 0;
		else
			annotations.user = parseInt(annotations.user);
		annotations.commentClass = "commentItem";
		annotations.containerClass="subcomments";
		annotations.subPosistionClass="subcomments-position";
		annotations.emptyClass="empty";
		if(annotations.countClass == undefined)
			annotations.countClass="count";
		annotations.mainClass="main";
		annotations.positionClass="position";
		if(annotations.dateClass == undefined)
			annotations.dateClass="date";
		if(annotations.addClass == undefined)
			annotations.addClass="reply";
		if(annotations.textClass == undefined)
			annotations.textClass="text";
		if(annotations.buttonsClass == undefined)
			annotations.buttonsClass="buttons";
		if(annotations.sort == undefined)
			annotations.sort = "asc";
	}
	
	function parseCommentAnnotations(comment){
		var a = jq(".annotation", comment);
		if(a.length == 0)
			return {};
		return parseString(a.html());
	}
	
	function parseString(str){
		var result = {};
		str = jq.trim(str);
		if(str == "")
			return result;
		var arr = str.split(",");
		for(var i = 0; i < arr.length; i++){
			var pare = jq.trim(arr[i]).split("=");
			result[jq.trim(pare[0])] = jq.trim(pare[1]);
		}
		return result;
	}
	
	function findLevel(i, level){
		if(level == undefined)
			level = 0;
		if(data[i].parent == 0 || data[data[i].parent] == undefined)
			return level;
		level++;
		return findLevel(data[i].parent, level); 
	}
	
	function fillComment(id){
		var level = findLevel(id);
		var addButton = getButton(annotations.addClass, id);
		if(level >= allowLevel && annotations.sort != "desc")
			addButton.remove();
		else{
			addButton.attr("class", annotations.addClass);
			addButton.click(function(){
				that.onReplyClick(id, main);
				return false;
			});
		}
		var showThreadButton = getButton(annotations.showThreadClass, id);
		var hideThreadButton = getButton(annotations.hideThreadClass, id);
		
		if(!data[id].subLoaded && data[id].count > 0){
			showThreadButton.attr("class", annotations.showThreadClass);
		}
		if(data[id].subLoaded && data[id].count > 0){
			hideThreadButton.attr("class", annotations.hideThreadClass);
		}
		
		jq("." + annotations.textClass, data[id].comment).html(data[id].text);
		
		showThreadButton.click(function(){
			that.onThreadClick(id, true);
			return false;
		});
		hideThreadButton.click(function(){
			that.onThreadClick(id, false);
			return false;
		});
		var deleteButton = getButton(annotations.deleteClass, id);
		if(isPrevelegedUser || data[id].owner == user){
			deleteButton.attr("class", annotations.deleteClass, id);
			deleteButton.click(function(){
				that.onDeleteClick(id);
				return false;
			});
		}
		else
			deleteButton.remove();
		var screenButton = getButton(annotations.screenClass, id);
		var unscreenButton = getButton(annotations.unscreenClass, id);
		if(screenButton.length > 0 && unscreenButton.length > 0){
			if(isPrevelegedUser || data[id].owner == user){
				if(data[id].screen){
					screenButton.attr("class", annotations.screenClass + "NA");
					unscreenButton.attr("class", annotations.unscreenClass);
				}else{
					screenButton.attr("class", annotations.screenClass);
					unscreenButton.attr("class", annotations.unscreenClass + "NA");
				}
				screenButton.click(function(){
					that.onScreenClick(id);
					return false;
				});
				unscreenButton.click(function(){
					that.onScreenClick(id);
					return false;
				});
			}else{
				screenButton.remove();
				unscreenButton.remove();
			}
		}
		if(data[id].count > 0){
			data[id].countContainer.html(Comments.countHTML(data[id].count));
			data[id].countContainer.show();
		}
		var date = jq("." + annotations.dateClass, data[id].comment).eq(0);
		if(date.length > 0)
			date.html(Comments.formatDate(data[id].date));
		Comments.initOtherFields(that, id);
	}
	
	this.onReplyClick = function(id){
		var form = jq("#" + Form.forms[annotations.form].getId());
		var s = id + "";
		if(s.indexOf("main") != -1)
			s = '0';
		if(s != jq("input[@name=parent]", form).val())
			closeAddForm();
		Comments.beforeReplyClick(data, id);
		if(annotations.useLogin && user == 0){
			Comments.showLoginMessage(data, id);
			data.currentComment = id;
			return;
		}
		data.currentComment = id;
		openAddForm(id);
	}
	
	function getButton(clazz, id){
		var buttons = jq("." + annotations.buttonsClass, data[id].comment).eq(0);
		return jq("." + clazz + ", ." + clazz + "NA", buttons);
	}
	
	this.onThreadClick = function(id, show){
		if(show)
			data[id].container.show();
		else
			data[id].container.hide();
		closeAddForm();
		if(data[id].showButton == undefined){
			data[id].showButton = getButton(annotations.showThreadClass, id)
			data[id].hideButton = getButton(annotations.hideThreadClass, id)
		}
		if(show){
			if(!data[id].subLoaded && Comments.useSubLoad){
				loadSubComments(id);
			}else{
				data[id].showButton.attr("class", annotations.showThreadClass + "NA");
				data[id].hideButton.attr("class", annotations.hideThreadClass);
				data[id].hideButton.attr("class", annotations.hideThreadClass);
			}
		}else{
			data[id].hideButton.attr("class", annotations.hideThreadClass + "NA");
			data[id].showButton.attr("class", annotations.showThreadClass);
		}
	}
	
	this.onDeleteClick = function(id){
		if(data[id].owner != user && !isPrevelegedUser)
			return;
		if(!confirm("Вы точно хотите удалить комментарий?"))
			return;
		jq.get(annotations.deleteUrl, {id:id}, function(){
			closeAddForm();
			updateDeleteThread(id);
			data[id].comment.remove();
			data[id] = undefined;
			data.currentComment = null;
		});
	}
	
	function updateDeleteThread(id){
		var c = data[id].count;
		while(data[id] != undefined){
			var parent = data[id].parent;
			if(data[id].parent == 0 || data[data[id].parent] == undefined || ( data[id].parent.indexOf && data[id].parent.indexOf("main") != -1))
				return;
			data[parent].count -= c + 1;
			if(data[parent].count < 0)
				data[parent].count = 0;
			if(data[parent].count == 0){
				if(data[parent].showButton == undefined){
					data[parent].showButton = getButton(annotations.showThreadClass, parent)
					data[parent].hideButton = getButton(annotations.hideThreadClass, parent)
				}
				data[parent].showButton.attr("class", annotations.showThreadClass + "NA");
				data[parent].hideButton.attr("class", annotations.hideThreadClass + "NA");
			}
			if(data[parent].count > 0){
				data[parent].countContainer.html(Comments.countHTML(data[parent].count));
				data[parent].countContainer.show();
			}
			else
				data[parent].countContainer.hide();
			id = data[id].parent;
		}
	}
	
	this.onScreenClick = function(id){
		if(data[id].owner != user && isPrevelegedUser)
			return;
		if(data[id].screenButton == undefined){
			data[id].screenButton = getButton(annotations.screenClass, id);
			data[id].unscreenButton = getButton(annotations.unscreenClass, id);
		}
		jq.get(annotations.screenUrl, {id:id}, function(){
			data[id].screen = !data[id].screen;
			if(data[id].screen){
				data[id].screenButton.attr("class", annotations.screenClass + "NA");
				data[id].unscreenButton.attr("class", annotations.unscreenClass);
			}else{
				data[id].screenButton.attr("class", annotations.screenClass);
				data[id].unscreenButton.attr("class", annotations.unscreenClass + "NA");
			}
		});
	}
}

Comments.init = function(selector, user, isPrevelegedUser, deleteUrl, screenUrl, treeUrl){
	var comments = {};
	jq("." + selector).each(function(i, item){
		item = jq(item);
		var id = item.attr("id");
		comments[id] = new Comments(id, user, isPrevelegedUser, deleteUrl, screenUrl, treeUrl);
		comments[id].init();
	});
	return comments;
}

Comments.onReplyClick = function(id, commentId){
	Comments.comments[id].lazyCommentInit(Comments.comments[id], commentId);
	Comments.comments[id].onReplyClick(commentId);
	return false;
}

Comments.beforeReplyClick = function(data, id){}

Comments.onCloseForm = function(data, id){}

Comments.onScreenClick = function(id, commentId){
	Comments.comments[id].lazyCommentInit(Comments.comments[id], commentId);
	Comments.comments[id].onScreenClick(commentId);
	return false;
}

Comments.onShowThreadClick = function(id, commentId){
	Comments.comments[id].lazyCommentInit(Comments.comments[id], commentId);
	Comments.comments[id].onThreadClick(commentId, true);
	return false;
}

Comments.onHideThreadClick = function(id, commentId){
	Comments.comments[id].lazyCommentInit(Comments.comments[id], commentId);
	Comments.comments[id].onThreadClick(commentId, false);
	return false;
}

Comments.onDeleteClick = function(id, commentId){
	Comments.comments[id].lazyCommentInit(Comments.comments[id], commentId);
	Comments.comments[id].onDeleteClick(commentId);
	return false;
}

Comments.showLoginMessage = function(data, id){
	alert("Только авторизованные пользователи могут оставлять комментарии");
}
Comments.formatDate = function(date){
	var str = "";
	var today = new Date();
	if((date.getDate() == today.getDate() || date.getDate() == today.getDate() - 1) && date.getMonth() == today.getMonth() && date.getYear() == today.getYear()){
		if(date.getDate() == today.getDate())
			str = "сегодня";
		else
			str = "вчера";
	}else{
		str = date.getDate() + " " + Comments.months[date.getMonth()];
		if(date.getYear() != today.getYear())
			str += " " + date.getYear();
	}
	str += ", " + date.getHours() + ":" + date.getMinutes();
	return str;
}
Comments.months = ["января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"];
Comments.initOtherFields = function(comments, id){}
Comments.getOtherFields = function(d,commentData){}
Comments.allow = 3;
Comments.countHTML = function(count){return count + '';}
Comments.onError = function(){alert('произошла ошибка при добавлении комментария.');};
Comments.onErrorResponse = function(d, data){};

Comments.parseDate = function(ddd){
	var d = ddd.split("T");
	var date = d[0].split("-");
	var time = d[1].split(":");
	d = [date[0], date[1], date[2], time[0], time[1]];
	for(var i = 0; i < d.length; i++)
		if(d[i].charAt(0) == "0")
			d[i] = d[i].substr(1, d[i].length);
	return new Date(d[0], parseInt(d[1]) -1, d[2], d[3], d[4]);
}