User = {
	data: {},
	permanent: {},
	init: function() {
		$$('.beg-pardon').each(function(el) {
			el.href = 'javascript:void(0)';
			el.observe('click', main.begPardon.bind(main));
		});
		$$('.auth-protected').each(function(el) {
			el.observe('click', User.checkLogin.bindAsEventListener(User));
		});
	},
	checkLogin: function(e) {
		if (!this.isAuthenticated()) {
			var link = e.element();
			this.afterLogin = link.onclick? link.onclick : function() {
				location = link.href;
			};
			this.showLoginForm();
			e.stop();
		}
	},
	showLoginForm: function(e, escapeIsDisabled) {
		if (!escapeIsDisabled && e != 'escapeIsDisabled') { // Preventing arguments like events from spoiling the thing
			this.loginFormObserver = this.hideLoginForm.bindAsEventListener(this);
			Event.observe(window, 'keydown', this.loginFormObserver);
		}
		main.showGrid();
		$('user-auth-form').show();
		$('auth-form-password').value = '';
		if ($('auth-form-username').value) {
			$('auth-form-password').focus()
		} else {
			$('auth-form-username').focus()
		}
		e && e.stop();
	},
	hideLoginForm: function(e) {
		if (e && e.keyCode != Event.KEY_ESC) {
			return;
		}
		if (this.loginFormObserver) {
			Event.stopObserving(window, 'keydown', this.loginFormObserver);
			delete this.loginFormObserver;
		}
		if (this.afterLogin) {
			delete this.afterLogin;
		}
		$('auth-error').update('');
		main.hideGrid();
		$('user-auth-form').hide();
	},
	showRegForm: function() {
		this.regFormObserver = this.hideRegForm.bindAsEventListener(this);
		Event.observe(window, 'keydown', this.regFormObserver);
		main.showGrid();
		$('user-reg-form').show().focusFirstElement();
	},
	hideRegForm: function(e) {
		if (e && e.keyCode != Event.KEY_ESC) {
			return;
		}
		if (this.regFormObserver) {
			Event.stopObserving(window, 'keydown', this.regFormObserver);
			delete this.regFormObserver;
		}
		main.hideGrid();
		$('user-reg-form').hide();
	},
	login: function(e) {
		var form = e.findElement('form');
		var parameters = {
			requestedData: this.stateChange.collectData().toJSON()
		};
		parameters = Object.extend(parameters, form.serialize('hash'));
		if (!Prototype.Browser.Opera) {
			form.disable();
		}
		new Ajax.Request(form.action, {
			method: 'post',
			parameters: parameters,
			onSuccess: location.reload.bind(location, true),
			onComplete: this.loginComplete.bind(this, form)
		});
		e.stop();
		return false;
	},
	logout: function(e) {
		new Ajax.Request(this.logoutUrl, {
			onSuccess: this.logoutSuccess.bind(this)
		});
		e.stop();
	},
	register: function(e) {
		var form = e.findElement('form');
		form.request({
			onSuccess: location.reload.bind(location),
			onFailure: this.registerFailure.bind(this, form)
		})
		if (!Prototype.Browser.Opera) {
			form.disable();
		}
		e.stop();
	},
	loginComplete: function(form, request) {
		result = request.responseText.evalJSON();
		form.enable();
		if (!result.success) {
			$('auth-form-password').focus();
			$('auth-error').update(result.reason);
		}
	},
	logoutSuccess: function(request) {
		location.reload(true);
	},
	registerFailure: function(form, request) {
		result = request.responseText.evalJSON();
		form.select('ul').invoke('remove');
		globalErrorList = new Element('ul').addClassName('error_list');
		var list;
		for (fieldname in result.errors) {
			var error = new Element('li').update(result.errors[fieldname]);
			var field = $(this.regFormName+'_'+fieldname);
			if (!field) {
				// global error
				list = globalErrorList;
			} else {
				list = new Element('ul').addClassName('error_list');
				field.up().insert({bottom: list});
			}
			list.insert(error);
		}
		form.down('.global-error-list-container').insert(globalErrorList);
		form.enable();
	},
	makeFriends: function(e) {
		new Ajax.Request(this.makeFriendsLink.href, {
			onComplete: this.makeFriendsSuccess.bind(this)
		});
		e.stop();
	},
	makeFriendsSuccess: function(response) {
		this.makeFriendsLink.replace(response.responseText);
		delete this.makeFriendsLink;
	},
	hasCredential: function(credential) {
		return this.data.credentials &&
			this.data.credentials.indexOf(credential) != -1;
	},
	hasSA: function() {
		return this.data.hasSA;
	},
	getPreference: function(preference) {
		return this.data.preferences &&
			this.data.preferences[preference];
	},
	setLocalPreference: function(preference, value) {
		return this.data.preferences[preference] = value;
	},
	setPreference: function(preference, newValue) {
		var oldValue = this.getPreference(preference);
		var id = preference+'-'+newValue.toString()+'-link';
		var link = $(id);
		new Ajax.Request(link.href);
		this.data.preferences[preference] = newValue;
		this.stateChange.preferenceChanged(preference, oldValue, newValue);
		return this;
	},
	togglePreference: function(preference) {
		var old = this.getPreference(preference);
		var oldClass = '.'+preference+'-'+old.toString();
		var newClass = '.'+preference+'-'+(!old).toString();
		$$(oldClass).invoke('toggle');
		$$(newClass).invoke('toggle');
		this.setPreference(preference, !old);
		return this;
	},
	isAuthenticated: function() {
		return this.data.isAuthenticated;
	},
	attachEffect: function() {
		var pcl = $('profileCollapseLink');
		if (!pcl) {
			return;	
		}
		if (this.profileCollapseLinkObserver) {
			pcl.stopObserving('click', this.profileCollapseLinkObserver);
		}
		if (this.isAuthenticated()) {
			this.profileCollapseLinkObserver = Effect.toggle.bind(Effect, 'profileCollapse', 'blind');
		} else {
			this.profileCollapseLinkObserver = this.showLoginForm.bind(this, false);
		}
		pcl.observe('click', this.profileCollapseLinkObserver);
	},
	attachKeyNav: function() {
		Event.observe(document, 'keydown', this.handleKeyNav.bindAsEventListener(this));
	},
	handleKeyNav: function(e) {
		switch (e.keyCode) {
			case 222: // '
				if (e.altKey) {
					this.toggleSA();
					Event.stop(e);
				} else if (e.ctrlKey) {
					this.toggleLogin();
					Event.stop(e);
				}
				break;
		}
		if (e.altKey && e.ctrlKey && e.shiftKey) {
			var lp = {
				S: {
					login: 'Dmitry.Stolyarov',
					password: 'qweasd'
				},
				H: {
					login: 'Dmitry.Shurupov',
					password: 'qweasd'
				},
				D: {
					login: 'Denis.Gorbachev',
					password: 'asdf'
				}
			}
			switch (e.keyCode) {
				case 83: // S
				case 72: // H
				case 68: // D
					var key = String.fromCharCode(e.keyCode);
					var loginner = (function() {
						$('loginFormUsername').value = lp[key].login;
						$('loginFormPassword').value = lp[key].password;
						this.login($('loginFormReal'));
					}).bind(this);
					if (User.isAuthenticated()) {
						var request = this.logout();
						var interval = setInterval(function () {
							if (request._complete) {
								loginner();
								clearInterval(interval);
							}
						}, 200);
					} else {
						loginner();
					}
					break;
			}
		}
	},
	toggleSA: function(el) {
		if (el) $(el).blur();
		if (!this.isAuthenticated()) {
			this.afterLogin = this.stateChange.showSA.bind(this.stateChange);
			this.toggleLogin();
			return;
		}
		if (!this.hasSA()) return false;
		this.getPreference('ShowSpecialAbilities')? this.stateChange.hideSA() : this.stateChange.showSA();
	},
	toggleLogin: function(el) {
		if (el) $(el).blur();
		this.isAuthenticated()? this.logout() : this.showLoginForm();
	},
	stateChangeHandlers: {
		updateProfile: function() {
			this.attachEffect();
			Effect.toggle('profileCollapse', 'blind')
			var elements = $$('#profileCollapseLink span');
			elements[0].update(this.data.displayName);
			$('avatar').src = this.data.avatar;
			$('commentary-counter').update(this.data.commentaryCounter)
			$('update-counter-holder').update(this.data.updateCounter);
			$('update-counter-wrapper').show();
			$('votesHTML').update(this.data.votesHTML);
			$('profileExitLink').show();
		},
		defaultProfile: function() {
			var defaults = [
				['#profileCollapseLink span', '<em>Войти</em>']
			]
			var field, selector, elements;
			for (var i = 0; i < defaults.length; i++) {
				selector = defaults[i][0];
				if (elements = $$(selector)) {
					elements.invoke('update', defaults[i][1]);
				}
			}
			var pc = $('profileCollapse');
			if (pc.visible()) {
				Effect.BlindUp('profileCollapse');
			}
			$('profileExitLink').hide();
		},
		updateSA: function(data) {
			data = data.collectSAData;
			this.data.hasSA = data;
		},
		collectSAData: function() {
			return {
				func: 'collectSAData',
				arguments: {
					moduleName: this.permanent.moduleName,
					actionName: this.permanent.actionName,
					uri: location.href
				}
			}
		},
		redefineVisualEffects: function() {
			var showEffects = User.getPreference('ShowVisualEffects');
			if (showEffects === false/*preventing 'undefined' from screwing my effects*/) {
				Object.redefine(Effect.Base.prototype, {
					'start': function(options) {
						options.duration = 0;
						this._original_start(options);
					}
				}) 
			}
		},
		unRedefineVisualEffects: function() {
			Object.unredefine(Effect.Base.prototype);
		}
	}
};
User.stateChange = {
	loadFuncs: [],
	loginFuncs: [],
	logoutFuncs: [],
	dataFuncs: [],
	showSAFuncs: [],
	hideSAFuncs: [],
	preferenceChangeFuncs: {},
	append: function() {
		var o;
		for (var i=0; i<arguments.length; i++) {
			o = arguments[i];
			if (typeof o == 'function') {
				o = {
					load: o,
					login: o,
					logout: o
				}
			}
			if (typeof o.data == 'object') {
				o.data = function() {return o.data};
			}
			if (o.load) {
				if (document.loaded) {
					o.load.call(this);
				} else {
					this.loadFuncs.push(o.load);
				}
			}
			if (o.login) this.loginFuncs.push(o.login);
			if (o.logout) this.logoutFuncs.push(o.logout);
			if (o.data) this.dataFuncs.push(o.data);
			if (o.showSA) this.showSAFuncs.push(o.showSA);
			if (o.hideSA) this.hideSAFuncs.push(o.hideSA);
			if (o.preferenceChange) {
				for (var preference in o.preferenceChange) {
					if (!this.preferenceChangeFuncs[preference]) {
						this.preferenceChangeFuncs[preference] = [];
					}
					this.preferenceChangeFuncs[preference].push(o.preferenceChange[preference]);
				}
			}
		}
	},
	load: function() {
		this.loadFuncs.invoke('call', this);
		delete this.loadFuncs;
	},
	login: function(result) {
		this.loginFuncs.invoke('call', this, result);
		if (!User.hasSA()) {
			$('show-sa-wrapper').hide();
			$('hide-sa-wrapper').hide();
		} else {
			if (User.getPreference('ShowSpecialAbilities')) {
				this.showSA(true);
			} else {
				this.hideSA(true);
			}
		}
	},
	logout: function() {
		this.hideSA(true);
		this.logoutFuncs.invoke('call', this);
	},
	collectData: function() {
		var requestedData = [];
		this.dataFuncs.each(
			function(func) {
				requestedData.push(func());
			}
		);
		requestedData = requestedData.compact();
		return requestedData;
	},
	showSA: function(blockUpdatePreference) {
		if (!User.hasSA()) return;
		if (!blockUpdatePreference) {
			User.setLocalPreference('ShowSpecialAbilities', true);
			new Ajax.Request($('show-sa-link').href);
		}
		$('show-sa-wrapper').hide();
		$('hide-sa-wrapper').show();
		for (var i=0; i<this.showSAFuncs.length; i++) {
			this.showSAFuncs[i]();
		}
		return false;
	},
	hideSA: function(blockUpdatePreference) {
		if (!blockUpdatePreference) {
			User.setLocalPreference('ShowSpecialAbilities', false);
			new Ajax.Request($('hide-sa-link').href);
		}
		$('hide-sa-wrapper').hide();
		if (User.hasSA()) {
			$('show-sa-wrapper').show();
		}
		this.hideSAFuncs.invoke('call', this);
		return false;
	},
	preferenceChanged: function(preference, oldValue, newValue) {
		var preferenceFuncs = this.preferenceChangeFuncs[preference];
		if (!preferenceFuncs) return;
		preferenceFuncs.invoke('call', this, oldValue, newValue);
	}
}
main.waitingRoom.append('domLoaded', User.stateChange.load.bind(User.stateChange));
/* Default state change handlers */

User.stateChange.append(
	{
		load: User.stateChangeHandlers.redefineVisualEffects.bind(User),
		login: User.stateChangeHandlers.redefineVisualEffects.bind(User),
		logout: User.stateChangeHandlers.unRedefineVisualEffects.bind(User),
		preferenceChange: {
			ShowVisualEffects: function(oldValue, newValue) {
				var funcNames = ['redefineVisualEffects', 'unRedefineVisualEffects']
				User.stateChangeHandlers[funcNames[(+newValue)/*type conversion*/]]();
			}
		}
	},{
		preferenceChange: {
			ShowPreferences: function(oldValue, newValue) {
				$('user-preferences')[newValue? 'show' : 'hide']();
			}
		}
	},{
		load: User.init.bind(User)
	}
);
