UiState = { hovered: 'hovered', unhovered: 'unhovered', disabled: 'disabled' };
TriState = { checked: 'checked', unchecked: 'unchecked', undefined: 'undefined' };

var TriStateCheckbox = {

	_init: function () {
		this.classes = this.options.classes;

		var value = this.element.attr('value');
		if (!value) {
			value = TriState.unchecked;
			this.element.val(value);
		}

		this.disabled = this.element[0].disabled;
		this.hovered = false;

		var uiState = this._getUiState();
		var cssClass = this.classes[uiState][value];

		var wrapper = $('<div/>', { 'class': cssClass, width: this.options.width, height: this.options.height });
		this.element.wrap(wrapper);
		this.tristateDiv = this.element.parent('div');

		this._bindHandlers();

		this._trigger('checkedchange', 0, value);
	},

	//Handlers Start

	_bindHandlers: function () {
		var that = this;
		this.tristateDiv.click(function () {
			that.changeState();
			that._trigger('checkedchange', 0, that.element.val());
		});

		this.tristateDiv.hover(createDelegate(this, this._handleHover), createDelegate(this, this._handleUnhover));
		this.tristateDiv.mousedown(createDelegate(this, this._handleMouseDown));
		this.tristateDiv.mouseup(createDelegate(this, this._handleMouseUp));
	},

	_handleHover: function () {
		this.hovered = true;

		if (this.disabled) {
			return;
		}

		var triState = this._getTriState();
		this.tristateDiv.removeClass(this.classes.unhovered[triState]);
		this.tristateDiv.addClass(this.classes.hovered[triState]);
	},

	_handleUnhover: function () {
		this.hovered = false;

		if (this.disabled) {
			return;
		}

		var triState = this._getTriState();
		this.tristateDiv.removeClass(this.classes.hovered[triState]);
		this.tristateDiv.addClass(this.classes.unhovered[triState]);
	},

	_handleMouseDown: function () {
		if (this.disabled) {
			return;
		}

		var triState = this._getTriState();
		this.tristateDiv.removeClass(this.classes.hovered[triState]);
		this.tristateDiv.addClass(this.classes.down[triState]);
	},

	_handleMouseUp: function () {
		if (this.disabled) {
			return;
		}

		var triState = this._getTriState();
		this.tristateDiv.removeClass(this.classes.down[triState]);
		this.tristateDiv.addClass(this.classes.hovered[triState]);
	},

	//Handlers End

	//Public Methods Start

	changeState: function () {
		if (this.disabled) {
			return;
		}

		var uiState = this._getUiState();
		var classes = [this.classes[uiState].unchecked, this.classes[uiState].checked];
		var values = ['unchecked', 'checked'];

		if (this.options.allowUndefinedState) {
			classes.push(this.classes[uiState].undefined);
			values.push('undefined');
		}

		this._shiftClassCircularly(classes, values);
	},

	check: function () {
		this._setTriState('checked');
	},

	uncheck: function () {
		this._setTriState('unchecked');
	},

	getDisabled: function () {
		return this.disabled;
	},

	setDisabled: function (disabled) {
		this.disabled = disabled;
		this.element[0].disabled = disabled;
	},

	//Public Methods End

	//Private Methods Start

	_getTriState: function () {
		return this.element.val();
	},

	_setTriState: function (triState) {
		if (this.disabled) {
			return;
		}

		var uiState = this._getUiState();

		this.tristateDiv.removeClass();
		this.tristateDiv.addClass(this.classes[uiState][triState]);

		this.element.val(triState);
	},

	_getUiState: function () {
		if (this.disabled) {
			return UiState.disabled;
		}
		if (this.hovered) {
			return UiState.hovered;
		}
		return UiState.unhovered;
	},

	_shiftClassCircularly: function (classes, values) {
		var initialLength = classes.length;

		//Add the first element to the end of the array to imitate circular shift
		classes.push(classes[0]);
		values.push(values[0]);

		for (var i = 0; i < initialLength; i++) {
			if (this.tristateDiv.hasClass(classes[i])) {

				this.tristateDiv.removeClass(classes[i]);
				this.tristateDiv.addClass(classes[i + 1]);
				this.element.val(values[i + 1]);

				return;
			}
		}
	},

	//Private Methods End

	options: {
		width: 13,
		height: 13,
		allowUndefinedState: true,
		disabled: false,
		classes: {
			disabled: { checked: 'tri-state-checkbox-disabled-checked', unchecked: 'tri-state-checkbox-disabled-unchecked', undefined: 'tri-state-checkbox-disabled-undefined' },
			hovered: { checked: 'tri-state-checkbox-hover-checked', unchecked: 'tri-state-checkbox-hover-unchecked', undefined: 'tri-state-checkbox-hover-undefined' },
			unhovered: { checked: 'tri-state-checkbox-checked', unchecked: 'tri-state-checkbox-unchecked', undefined: 'tri-state-checkbox-undefined' },
			down: { checked: 'tri-state-checkbox-down-checked', unchecked: 'tri-state-checkbox-down-unchecked', undefined: 'tri-state-checkbox-down-undefined' }
		}
	}
};

$.widget('ui.tristate', TriStateCheckbox);

