MsgThread = Class.create(BasicControl, {
	initialize: function($super, config) {
		this.config = {
			cntr: null,
			effectsDuration: 0.2,
			sticked: false,
			scrollTo: null
		};

		Object.extend(this, {
			loaded: {},
			cache: {},
			formCntr: null,
			form: null,
			currRpl: {
				dsc: null, trigger: null, toggleTarget: null, action: null
			}
		});

		$super('msg_thread', config);
	},

	onReady: function() {
		var trigger, element;

		this.prepare(this.config.cntr);

		(this.formCntr = $('reply-form').remove().removeClassName('x-hidden')).hide();

		if (trigger = $(this.config.cntr).select('.stick').first())
		{
			this.addAction(
				trigger, 'click', this.stick.bind(
					this, this.parseId(trigger.readAttribute('id')), trigger
				), true
			);
		}

		this.assignForm();

		if (this.config.scrollTo && (element = $(this.config.scrollTo)))
		{
			element.scrollTo();
		}

		if (element = $('btn-topic-remove'))
		{
			element.observe('click', function() {
				pConfirm(this.getString('cfrm_delete'), function() {
					$('delete_form').submit();
				});
			}.bind(this));
		}
	},

	prepare: function(cntr) {
		if (cntr = $(cntr))
		{
			Element.select(cntr, '.show-replies').each(function(trigger) {
				this.addAction(
					trigger = $(trigger), 'click', this.toggleReplies.bind(
						this, this.parseId(trigger.readAttribute('id')), trigger
					), true
				);
			}.bind(this));

			Element.select(cntr, '.replies').each(function(rplCntr) {
				if ((rplCntr = $(rplCntr)).empty())
				{
					rplCntr.hide();
				}
				else
				{
					rplCntr.show();
					this.loaded[this.parseId(rplCntr.readAttribute('id')).key] = true;
				}
			}.bind(this));

			Element.select(cntr, '.reply-btn').each(function(trigger) {
				this.addAction(
					trigger = $(trigger), 'click', this.toggleForm.bind(
						this, this.parseId(trigger.readAttribute('id')), trigger, false, null
					), true
				);
			}.bind(this));

			// Edit buttons processing.
			Element.select(cntr, '.edit-btn').each(function(trigger) {
				new Countdown(trigger.down('.countdown'), {
					onFinish: function(trigger) {
						var prev;

						// Hide separator between reply and edit button in msg_thread.tpl
						if ((prev = trigger.previous()) && prev.hasClassName('reply-btn'))
						{
							prev.addClassName('bg-none');
						}

						// Remove edit button.
						if (trigger.parentNode)
						{
							trigger.remove();
						}
					}.curry(trigger)
				});

				this.addAction(trigger = $(trigger), 'click', function(trigger) {
					var dsc = this.parseId(trigger.readAttribute('id')), cntr = this.getEl('frm', dsc);

					this.freeze();
					cntr.startWaiting('bigWaiting');

					new Ajax.Request(this.getUrl({replyId: dsc.parentId}), {
							method: 'GET',
							onFailure: function(cntr) {
								pAlert(
									this.getString('err_internal'), {
										title: this.getString('error')
								});

								this.unfreeze();
								cntr.stopWaiting();
							}.bind(this, cntr),
							onSuccess: function(cntr, transport) {
								var data;

								if (!(data = this.evalResponse(transport)) || data.error)
								{
									pAlert(data.error ? data.error : this.getString('err_internal'), {
										title: this.getString('error')
									});
								}
								else
								{
									this.toggleForm(
										dsc, trigger, true, {message: data.data}
									)
								}

								this.unfreeze();
								cntr.stopWaiting();
							}.bind(this, cntr)
						}
					);
				}.bind(this, trigger), true);
			}.bind(this));

			Element.select(cntr, '.remove-btn').each(function(trigger) {
				this.addAction(trigger = $(trigger), 'click', function(trigger) {
					pConfirm(this.getString('cfrm_delete'), function(trigger) {
						var dsc = this.parseId(trigger.readAttribute('id'));

						if (dsc.topicId == dsc.parentId)
						{
							dsc.parentId = undefined;
						}

						this.loadReplies(
							dsc, null, {'delete': dsc.id}, function(dsc, data) {
								if (data.error)
								{
									pAlert(data.error, {title: this.getString('error')});
									return false;
								}
								else if (data.postsCount)
								{
									$H(data.postsCount).each(function(dsc, pair) {
										var trigger = this.getEl('rpl-trg', {
											topicId: dsc.topicId, parentId: pair.key
										});

										if (trigger)
										{
											trigger.removeClassName('x-hidden');
											trigger.down('span.trigger').update(this.getString('hide_replies'));
											trigger.down('span.counter').update('(' + pair.value + ')');
										}
									}.bind(this, dsc));
								}

								return true;
							}.bind(this, dsc), null, null, false
						);
					}.bind(this, trigger));
				}.bind(this, trigger), true);
			}.bind(this));
		}
	},

	assignForm: function() {
		this.form = this.formCntr.down('form');

		this.addAction(this.formCntr.down('input[name=cancel]'), 'click', this.toggleForm.bind(this));
		this.addAction(this.formCntr.down('input[name=submit]'), 'click', this.submitForm.bind(this), true);
	},

	getEl: function(name, dsc) {
		return $(name + '-' + dsc.topicId + (dsc.parentId ? '-' + dsc.parentId : ''));
	},

	parseId: function(id) {
		var matches, data = {
			topicId: null, parentId: null, key: null
		};

		if (matches = id.match(/^([a-z\-]+)\-(\d+)(\-(\d+)(\-(\d+))*)*$/))
		{
			data = {
				topicId: matches[2], parentId: matches[4], id: matches[6], key: matches[2] + matches[4] + matches[6]
			}
		}

		return data;
	},

	toggleReplies: function(dsc, trigger) {
		var rplCntr;

		if (rplCntr = this.getEl('rpl', dsc))
		{
			this.freeze();

			if (rplCntr.visible())
			{
				trigger.down('span.trigger').update(this.getString('show_replies'));

				Effect.BlindUp(rplCntr, {
					duration: this.config.effectsDuration, afterFinish: this.unfreeze.bind(this)
				});
			}
			else
			{
				this.loadReplies(dsc, null, null, function() {
					trigger.down('span.trigger').update(this.getString('hide_replies'));
					this.unfreeze();
					return true;
				}.bind(this, trigger));
			}
		}
	},

	/**
	 * Auxiliary method for MsgThread#loadReplies.
	 * @private
	 */
	_onRplLoadSuccess: function(msgCntr, rplCntr, useEffects) {
		if (('undefined' == typeof useEffects) || useEffects)
		{
			Effect.BlindDown(rplCntr, {duration: this.config.effectsDuration});
		}
		else
		{
			rplCntr.show();
		}
	},

	/**
	 * Auxiliary method for MsgThread#loadReplies.
	 * @private
	 */
	_onRplLoadFailure: function(msgCntr, rplCntr) {
		rplCntr.hide();
	},

	loadReplies: function(dsc, post, get, onSucess, onFailure, useEffects) {
		var msgCntr, rplCntr;

		if ((msgCntr = this.getEl('msg', dsc)) && (rplCntr = this.getEl('rpl', dsc)))
		{
			if (('undefined' == typeof this.loaded[dsc.key]) || post || get)
			{
				var url = this.getUrl(dsc, {lvlLimit: 1}, get);

				var process = function(msgCntr, rplCntr, onSucess, useEffects, data) {
					if (!onSucess || onSucess(data))
					{
						// IE6 BUG: when big container is opened
						// (~18 replies), all it's content become invisible for some reasons.
						if (Prototype.Browser.IE)
						{
							rplCntr.setStyle({zoom: 1});
						}

						rplCntr.update(data.content);

						this.prepare(rplCntr);

						this._onRplLoadSuccess(msgCntr, rplCntr, useEffects);
					}
				}.bind(this, msgCntr, rplCntr, onSucess, useEffects);

				if (('undefined' != typeof this.cache[url]) && !post)
				{
					process(this.cache[url]);
				}
				else
				{
					msgCntr.startWaiting('bigWaiting');

					new Ajax.Request(url, {
							method: post ? 'POST' : 'GET',
							postBody: post,
							onFailure: function(msgCntr, rplCntr, onFailure, transport) {
								if (!onFailure || onFailure(transport))
								{
									this._onRplLoadFailure(msgCntr, rplCntr);
								}

								msgCntr.stopWaiting();
							}.bind(this, msgCntr, rplCntr, onFailure),
							onSuccess: function(url, msgCntr, rplCntr, process, transport) {
								var data;

								if (data = this.evalResponse(transport))
								{
									process(this.cache[url] = data);
								}
								else
								{
									this._onRplLoadFailure(msgCntr, rplCntr);
								}

								msgCntr.stopWaiting();
							}.bind(this, url, msgCntr, rplCntr, process)
						}
					);
				}

				this.loaded[dsc.key] = true;
			}
			else
			{
				if (!onSucess || onSucess())
				{
					this._onRplLoadSuccess(msgCntr, rplCntr, useEffects);
				}
			}
		}
	},

	/**
	 * Auxiliary method for MsgThread#toggleForm.
	 * @private
	 */
	_showForm: function(dsc, trigger, toggleTarget, values) {
		var errField, targetCntr;

		// Show previous form show/hide trigger.
		if (this.currRpl.trigger)
		{
			this.currRpl.trigger[
				true || !this.currRpl.dsc.parentId ? 'show' : 'setStyle'
			]({visibility: 'visible'});
		}

		if (trigger && dsc)
		{// Another form is opened.
			//this.form.reset();

			// Clear form manually.
			this.form.getElements().each(function(sys, el) {
				var name = el.readAttribute('name');

				if (name && !sys[name] && ('button' != el.readAttribute('type')) && ('submit' != el.readAttribute('type')))
				{
					el.value = '';
				}
			}.curry({'f_in': true, '_ia': true, '_iap': true, '_it_fs': true}));

			// Set form fields values if specified.
			if (values)
			{
				$H(values).each(function(pair) {
					this.form[pair.key].value = pair.value;
				}.bind(this));

				this.currRpl.action = 'edit';
			}
			else
			{
				this.currRpl.action = 'add';
			}

			if (errField = this.form.down('.field-error'))
			{
				errField.remove();
			}

			// Store active form data.
			this.currRpl.dsc = dsc;
			this.currRpl.trigger = trigger;
			this.currRpl.toggleTarget = toggleTarget;

			// Hide form show/hide trigger.
			this.currRpl.trigger[
				true || !this.currRpl.dsc.parentId ? 'hide' : 'setStyle'
			]({visibility: 'hidden'});

			// Insert the form right after the 'frm-[*]-[*]' (target container).
			(targetCntr = this.getEl('frm', dsc)).insert({after: this.formCntr});

			// Hide target container if nedded and show current form and.
			if (toggleTarget)
			{
				targetCntr.hide();
			}

			Effect.BlindDown(this.formCntr, {
				duration: this.config.effectsDuration, afterFinish: this.unfreeze.bind(this)
			});
		}
		else
		{// Form closed with pressing of 'cancel' button.
			if (this.currRpl.toggleTarget)
			{
				// Show taget container if it was hidden for form showing.
				this.getEl('frm', this.currRpl.dsc).show();
				this.unfreeze();
			}
			else
			{
				this.unfreeze();
			}
		}
	},

	/**
	 * @param {Boolean} toggleTarget used to specify if it is needed to toggle
	 * the target container too, for example, to hide/show post and show/hide edit form.
	 * @param {Object} values hash with the form field values to set.
	 */
	toggleForm: function(dsc, trigger, toggleTarget, values, useEffects) {
		var showForm = this._showForm.bind(this, dsc, trigger, toggleTarget, values);

		this.freeze();

		if (this.formCntr.visible())
		{
			// Hide current form and show target container if nedded.
			if (false !== useEffects)
			{
				Effect.BlindUp(this.formCntr, {
					duration: this.config.effectsDuration,
					afterFinish: function(toggleTarget, showForm) {
						if (toggleTarget)
						{
							this.getEl('frm', this.currRpl.dsc).show();
						}

						showForm();
					}.bind(this, toggleTarget, showForm)
				});
			}
			else
			{
				this.formCntr.hide();

				if (toggleTarget)
				{
					this.getEl('frm', this.currRpl.dsc).show();
				}

				showForm();
			}
		}
		else
		{
			showForm();
		}
	},

	submitForm: function() {
		var post = Form.serializeElements(this.form.getElements());

		if ('edit' == this.currRpl.action)
		{
			this.freeze();
			this.formCntr.startWaiting('bigWaiting');

			new Ajax.Request(this.getUrl({id: this.currRpl.dsc.parentId}), {
				method: 'POST',
				postBody: post,
				onFailure: function(transport) {
					pAlert(this.getString('err_internal'), {
						title: this.getString('error')
					});

					this.formCntr.stopWaiting();
					this.unfreeze();
				}.bind(this),
				onSuccess: function(transport) {
					var data;

					if (!(data = this.evalResponse(transport)) || data.error)
					{
						pAlert(data.error || this.getString('err_internal'), {
							title: this.getString('error')
						});
					}
					else
					{
						if (data.validationFailed)
						{
							this.formCntr.update(data.content);
							this.assignForm();
						}
						else
						{
							// Update edited elements.
							$H(data.fields).each(function(pair) {
								this.getEl('cnt-' + pair.key, this.currRpl.dsc).update(pair.value);
							}.bind(this));

							this.toggleForm();
						}
					}

					this.formCntr.stopWaiting();
					this.unfreeze();
				}.bind(this)
			});
		}
		else
		{
			this.freeze();

			this.loadReplies(
				this.currRpl.dsc, post, {lvlLimit: this.currRpl.dsc.parentId ? 1 : 2}, function(data) {
					var trigger;

					this.cache = {};

					this.unfreeze();

					if (data.validationFailed)
					{
						this.formCntr.update(data.content);
						this.assignForm();
						return false;
					}
					else if (data.error)
					{
						pAlert(data.error, {title: this.getString('error')});
						return false;
					}
					else
					{
						// When the thread is big, blinfUp effect in this
						// case can be really slow so target link may appear
						// just after 2-3 seconds when overlay would be hidden.
						this.toggleForm(undefined, undefined, undefined, undefined, !(data.repliesCount && (12 < data.repliesCount)));

						if (trigger = this.getEl('rpl-trg', this.currRpl.dsc))
						{
							trigger.down('span.trigger').update(this.getString('hide_replies'));
						}

						if (data.postsCount)
						{
							$H(data.postsCount).each(function(dsc, pair) {
								var trigger = this.getEl('rpl-trg', {
									topicId: this.currRpl.dsc.topicId, parentId: pair.key
								});

								if (trigger)
								{
									trigger.removeClassName('x-hidden');
									trigger.down('span.trigger').update(this.getString('hide_replies'));
									trigger.down('span.counter').update('(' + pair.value + ')');
								}
							}.bind(this, this.currRpl.dsc));

							if (data.lastPost)
							{
								(function(id) {
									$(id) && $(id).scrollTo();
								}.curry(data.lastPost)).delay(0.3);
							}

							if ($('bkmb_show_popup'))
							{
								Element.replace('bkmb_show_popup', this.getString('bkmb_replied'));
							}
						}

						return true;
					}
				}.bind(this), this.unfreeze.bind(this)
			);
		}
	},

	setPage: function(page) {
		this.loaded = {};

		this.loadReplies(
			this.parseId(this.config.cntr), null, {page: page, lvlLimit: 2}, null, null, false
		);
	},

	stick: function(dcs, trigger) {
		var msgCntr, form = $('edit_form');

		if (msgCntr = this.getEl('msg', dcs))
		{
			form.elements['sticked'].checked = !this.config.sticked;

			msgCntr.startWaiting('bigWaiting');

			new Ajax.Request(this.getUrl(dcs), {
					method: 'POST',
					postBody: Form.serializeElements(form.getElements()),
					onFailure: function(msgCntr, transport) {
						msgCntr.stopWaiting();
					}.bind(this, msgCntr),
					onSuccess: function(msgCntr, form, transport) {
						trigger.update(
							this.getString((
								this.config.sticked = !this.config.sticked
							) ? 'unstick' : 'stick')
						);
						msgCntr.stopWaiting();
					}.bind(this, msgCntr, form)
				}
			);
		}
	}
});