var g_stbConditions = [];
var g_stbConditionsColorTimeoutSet = false;
var g_stbConditionsColorLast = '';
(function ($, app) {
	// Conditions Class
	var Conditions = (function () {
		function Conditions(elem) {
			var isFrontend = elem.hasClass('stbConditionsData');

			g_stbConditionsDialog = !isFrontend ? elem : null;

			this.isFrontend = function () {
				return isFrontend;
			};
			this.getDialog = function () {
				return g_stbConditionsDialog;
			};
			this.getEditor = function() {
				return app.Editor.Hot;
			};
			this.getUtils = function() {
				var inctance = !isFrontend
					? this.getEditor().plugin
					: app.getRuleJSInstance(elem.parents('.supsystic-tables-wrap:first').find('.supsystic-table:first'));

				return inctance.utils;
			};
			this.getToolbar = function() {
				return app.Editor.Tb;
			};
			this.getTablesModel = function() {
				return app.Models.Tables;
			};
			if(!isFrontend) {
				this.initDialog();
			}
			this.initConditionsData(elem);
		}
		Conditions.prototype.applyConditionToTable = function() {
			var dialog = this.getDialog(),
				editor = this.getEditor(),
				rangeIndexes = dialog.find('input[name="range"]').val();

			dialog.find('.conditionRulesError').hide();

			if(rangeIndexes && this._indexesToRange(rangeIndexes)) {
				var ruleId = dialog.find('.conditionRuleApply').data('ruleid'),
					conditionType = dialog.find('[name="conditionType"]').val(),
					conditionsData = dialog.find('[name="condition[]"]').serializeArray(),
					stylesData = dialog.find('.conditionStyle').serializeArray(),
					rule = {},
					conditions = [],
					styles = {},
					data = {};

				for(var i = 0; i < conditionsData.length; i++) {
					conditions[i] = conditionsData[i].value;
				}
				for(var j = 0; j < stylesData.length; j++) {
					styles[stylesData[j].name] = stylesData[j].value;
				}
				rule = {
					range: rangeIndexes,
					condition: {
						conditionType: conditionType,
						operatorType: dialog.find('[name="operatorType"]').val(),
						text: this._types[conditionType].type == 'text' ? conditions[0] : null,
						condition: conditions,
						style: styles
					}
				};
				if(ruleId || ruleId === 0) {
					g_stbConditions[ruleId] = rule;
					data = {
						iter: ruleId,
						rule: g_stbConditions[ruleId],
						update: true
					};
				} else {
					g_stbConditions.push(rule);
					data = {
						iter: g_stbConditions.length - 1,
						rule: g_stbConditions[g_stbConditions.length - 1]
					};
				}
				this._addConditionRuleRow(data);
				this.updateRuleForm();
				dialog.find('.conditionRuleApply').data('ruleid', '');
				editor.render();
			} else {
				dialog.find('.noRangeError').show();
			}
		};
		Conditions.prototype._buildRegexp = function(regexp, args) {
			return regexp.replace(/{(\d+)}/g, function(match, number) {
				return typeof args[number] != 'undefined' ? args[number] : match;
			});
		};
		Conditions.prototype.checkCondition = function(condition, cellValue, row, col, tableWrapper) {
			cellValue = cellValue && cellValue[0] === '=' ? this.getTablesModel().getFormulaResult(cellValue, row, col) : cellValue;

			var utils = this.getUtils(),
				curCondition = $.extend(true, {}, condition),
				operator = curCondition.operatorType;

			// Do not try to apply unknown conditions
			if(!this._types[curCondition.conditionType] || !this._operators[curCondition.operatorType]) return false;

			var operatorValue = this._operators[operator].value,
				cellIndexes = null;

			if(curCondition.text && utils.isCell(curCondition.text.toString())) {
				cellIndexes = this._indexesToRange(curCondition.text);

				if(cellIndexes) {
					if(this.isFrontend()) {
						curCondition.text = tableWrapper ? tableWrapper.find('[data-cell-id="' + $.trim(curCondition.text.toUpperCase()) + '"]').data('original-value') : curCondition.text;
					} else {
						curCondition.text = this.getEditor().getDataAtCell(cellIndexes.from.row, cellIndexes.from.col);
						curCondition.text = curCondition.text && curCondition.text[0] === '='
							? this.getTablesModel().getFormulaResult(curCondition.text, cellIndexes.from.row, cellIndexes.from.col)
							: curCondition.text;
					}
				}
			}
			if(curCondition.condition && curCondition.condition.length) {
				for(var j = 0; j < curCondition.condition.length; j++) {
					if(curCondition.condition[j] && utils.isCell(curCondition.condition[j].toString())) {
						cellIndexes = this._indexesToRange(curCondition.condition[j]);

						if(cellIndexes) {
							if(this.isFrontend()) {
								curCondition.condition[j] = tableWrapper ? tableWrapper.find('[data-cell-id="' + $.trim(curCondition.condition[j].toUpperCase()) + '"]').data('original-value') : curCondition.condition[j];
							} else {
								curCondition.condition[j] = this.getEditor().getDataAtCell(cellIndexes.from.row, cellIndexes.from.col);
								curCondition.condition[j] = curCondition.condition[j] && curCondition.condition[j][0] === '='
									? this.getTablesModel().getFormulaResult(curCondition.condition[j], cellIndexes.from.row, cellIndexes.from.col)
									: curCondition.condition[j];
							}
						}
					}
				}
			}
			switch(operator) {
				case 'beginsWith':
				case 'endsWith':
					var regexp = new RegExp(this._buildRegexp(operatorValue, [curCondition.text]));
					return regexp.exec(cellValue);
					break;
				case 'containsText':
					if(cellValue) {
						cellValue = cellValue.toString();
						return cellValue.indexOf(curCondition.text) > -1;
					}
					break;
				case 'notContains':
					if(cellValue) {
						cellValue = cellValue.toString();
						return cellValue.indexOf(curCondition.text) == -1;
					}
					break;
				case 'equal':
				case 'notEqual':
				case 'greaterThan':
				case 'greaterThanOrEqual':
				case 'lessThan':
				case 'lessThanOrEqual':
					var left = cellValue,
						right = curCondition.condition[0];

					right = this._prepareToEval(right);
					left = this._prepareToEval(left);

					if(eval(left + operatorValue + right)) {
						return true;
					}
					break;
				case 'between':
					var valuesArr = [cellValue].concat(curCondition.condition),
						findNaN = false;

					for(var i = 0; i < valuesArr.length; i++) {
						if(isNaN(valuesArr[i])) {
							findNaN = true;
							break;
						} else {
							valuesArr[i] = parseFloat(valuesArr[i]);
						}
					}
					if(!findNaN && eval(this._buildRegexp(operatorValue, valuesArr))) {
						return true;
					}
					break;
				default:
					break;
			}
			return false;
		};
		Conditions.prototype._prepareToEval = function(text) {
			text = text !== undefined && text !== null ? text : '';
			return isNaN(parseFloat(text))
				? '"' + text.replace(/\'/g,"\\'").replace(/\"/g,'\\"') + '"'
				: parseFloat(text);
		};
		Conditions.prototype.setConditionToCell = function(row, col, value, td, conditionsList, tableWrapper) {
			conditionsList = conditionsList ? conditionsList : g_stbConditions;
			tableWrapper = tableWrapper && tableWrapper instanceof jQuery ? tableWrapper : null;

			if(conditionsList && conditionsList.length) {
				for(var i = conditionsList.length - 1; i >= 0; i--) {
					if(conditionsList[i] && conditionsList[i].range) {
						var range = this._indexesToRange(conditionsList[i].range);

						if(!range) continue;

						if(this._isCellInRange({ row: (typeof(g_stbPagination) != 'undefined' && g_stbPagination ? this.getEditor().pageStart + row : row), col: col }, range)) {
							var condition = conditionsList[i].condition;

							if(this.checkCondition(condition, value, row, col, tableWrapper)) {
								this.setStylesToElem(condition.style, td);
							}
						}
					}
				}
			}
		};
		Conditions.prototype.clearElemStyles = function(elem) {
			elem = elem instanceof jQuery ? elem : $(elem);

			elem.css({
				backgroundColor: '',
				color: '',
				fontWeight: '',
				fontStyle: '',
				textDecoration: ''
			});
		};
		Conditions.prototype.setStylesToElem = function(style, elem) {
			elem = elem instanceof jQuery ? elem : $(elem);

			var styleString = elem.get(0).style.cssText;

			if(style.bold == 'on') {
				styleString += 'font-weight: bold !important;';
			}
			if(style.italic == 'on') {
				styleString += 'font-style: italic !important;';
			}
			if(style.underline == 'on') {
				styleString += 'text-decoration: underline !important;';
			}
			if(style.bg_color) {
				styleString += 'background-color: #' + style.bg_color + ' !important;';
			}
			if(style.color) {
				styleString += 'color: #' + style.color + ' !important;';
			}
			elem.css('cssText', styleString);
		};
		Conditions.prototype.openDialog = function() {
			var dialog = this.getDialog(),
				editor = this.getEditor(),
				toolbar = this.getToolbar(),
				range = toolbar.getValidRange(editor.getSelectedRange());

			if(g_stbPagination) {
				range.from.row += editor.pageStart;
				range.to.row += editor.pageStart;
			}

			if(!range) {
				dialog.find('.noRangeError').show();
				return false;
			}
			var rangeIndexes = this._rangeToIndexes(range),
				conditionRulesShell = dialog.find('.conditionRulesShell');

			dialog.find('.conditionRulesError').hide();
			dialog.find('[name="range"]').val(rangeIndexes);
			if(g_stbConditions && g_stbConditions.length) {
				conditionRulesShell.html('');
				for(var i = 0; i < g_stbConditions.length; i++) {
					this._addConditionRuleRow({
						iter: i,
						rule: g_stbConditions[i],
						shell: conditionRulesShell
					});
				}
			} else {
				conditionRulesShell.html(conditionRulesShell.data('def'));
			}
		};
		Conditions.prototype.updateRuleForm = function(ruleId) {
			ruleId = typeof ruleId != 'undefined' ? ruleId : '';

			var dialog = this.getDialog(),
				rule = g_stbConditions[ruleId] ? g_stbConditions[ruleId] : false,
				range = rule ? rule.range : '',
				condition = rule ? rule.condition : [],
				condValues = rule ? rule.condition.condition : [],
				style = rule ? rule.condition.style : [];

			dialog.find('.conditionRuleApply').data('ruleid', ruleId);
			dialog.find('[name="range"]').val(range);
			dialog.find('.conditionPart').each(function() {
				var $field = $(this),
					value = condition[$field.attr('name')] ? condition[$field.attr('name')] : $field.data('def');

				$field.val(value);
				$field.trigger('change');
			});
			dialog.find('.conditionValue').each(function(index, element) {
				var $field = $(this),
					value = condValues[index] ? condValues[index] : $field.data('def');

				$field.val(value);
			});
			dialog.find('.conditionStyle').each(function() {
				var $field = $(this),
					value = style[$field.attr('name')] ? style[$field.attr('name')] : $field.data('def');

				if($field.attr('type') == 'checkbox') {
					value = value == 'on';
					$field.prop('checked', value).iCheck('update');
				} else {
					$field.val(value);
				}
			});
			dialog.find('.conditionColorPicker').each(function() {
				var picker = $(this),
					$field = picker.find('input'),
					color = style[$field.attr('name')] ? style[$field.attr('name')] : $field.data('def');

				$field.val(color);
				picker.find('.conditionColorArea').css({ 'background-color': '#' + color });
				picker.ColorPickerSetColor(color);
			});
		};
		Conditions.prototype.deleteRule = function(ruleId) {
			var dialog = this.getDialog(),
				editor = this.getEditor();

			if(g_stbConditions[ruleId]) {
				delete g_stbConditions[ruleId];
				dialog.find('.conditionRuleRow[data-ruleid="' + ruleId + '"]').fadeOut(300);
				this.updateRuleForm();
				editor.render();
			}
		};
		Conditions.prototype.initConditionsData = function(elem) {
			var dialog = this.isFrontend() ? elem : this.getDialog();

			this._types = dialog.data('types');
			this._operators = dialog.data('operators');
			if(!this.isFrontend()) {
				g_stbConditions = dialog.data('conditions');
			}
		};
		Conditions.prototype.initDialog = function() {
			var self = this,
				dialog = this.getDialog();
			
			dialog.dialog({
				modal:    true,
				autoOpen: false,
				width:    600,
				open: function() {
					self.openDialog();
				},
				close: function() {
					self.updateRuleForm();
					g_stbConditions = self._clearRulesList(g_stbConditions);
				},
				buttons:  {
					Cancel: function () {
						$(this).dialog('close');
					}
				}
			});

			// Conditional Formatting options
			dialog.find('.conditionColorPicker').each(function() {
				var $this = $(this),
					color = $this.find('input').data('def');

				$this.ColorPicker({
					color: color,
					onShow: function (colpkr) {
						$(colpkr).fadeIn(500);
						return false;
					},
					onHide: function (colpkr) {
						$(colpkr).fadeOut(500);
						return false;
					},
					onChange: function (hsb, hex, rgb) {
						var self = this;
						g_stbConditionsColorLast = hex;

						if(!g_stbConditionsColorTimeoutSet) {
							setTimeout(function(){
								g_stbConditionsColorTimeoutSet = false;
								$(self).find('.colorpicker_submit').trigger('click');
							}, 500);
							g_stbConditionsColorTimeoutSet = true;
						}
					},
					onSubmit: function(hsb, hex, rgb, el) {
						$(el).find('.conditionColorArea').css('backgroundColor', '#' + g_stbConditionsColorLast);
						$(el).find('input').val(g_stbConditionsColorLast);
					}
				});
			});
			dialog.find('select[name="conditionType"]').on('change', function() {
				var operatorOpts = dialog.find('select[name="operatorType"] option');

				operatorOpts.hide();
				operatorOpts.filter('.' + $(this).find('option:selected').data('type')).show();
				operatorOpts.each(function () {
					var $this = $(this);

					if ($this.css('display') != 'none') {
						$this.prop('selected', true);
						return false;
					}
				});
			}).trigger('change');
			dialog.find('select[name="operatorType"]').on('change', function() {
				var input = dialog.find('input[name="condition[]"]').eq(1);

				if($(this).val() == 'between') {
					input.show();
				} else {
					input.hide();
				}
			}).trigger('change');
			dialog.find('.conditionRuleApply').on('click', function() {
				self.applyConditionToTable();
			});
		};
		Conditions.prototype.saveConditions = function() {
			var dialog = this.getDialog();

			app.request({
				module: 'tables',
				action: 'updateConditions'
			}, {
				table_id: dialog.data('id'),
				conditions: g_stbConditions
			}).fail(function (message) {
				alert(message);
			});
		};
		Conditions.prototype._rangeToIndexes = function(range) {
			var utils = this.getUtils();

			if(range && range.from && range.to && typeof range.from == 'object' && typeof range.to == 'object') {
				var startCoords = utils.translateCellCoords(range.from),
					endCoords = utils.translateCellCoords(range.to);

				return startCoords == endCoords ? startCoords : startCoords + ':' + endCoords;
			}
			return false;
		};
		Conditions.prototype._indexesToRange = function(coords) {
			var utils = this.getUtils(),
				coordsArr = coords.split(':');

			if(utils.isCell(coordsArr[0].toString())) {
				var from = utils.cellCoords(coordsArr[0]),
					to = coordsArr[1] && utils.isCell(coordsArr[1].toString())? utils.cellCoords(coordsArr[1]) : from;
				return { from: from, to: to };
			}
			return false;
		};
		Conditions.prototype._isCellInRange = function(cell, range) {
			return cell.row >= range.from.row
			&& cell.row <= range.to.row
			&& cell.col >= range.from.col
			&& cell.col <= range.to.col;
		};
		Conditions.prototype._addConditionRuleRow = function(data) {
			var self = this,
				dialog = this.getDialog(),
				ruleRow = data.update
					? dialog.find('.conditionRuleRow[data-ruleid="' + data.iter + '"]')
					: dialog.find('#conditionRuleRowExample').clone();

			var ruleRange = ruleRow.find('.ruleRange'),
				condition = data.rule.condition;

			// Do not try to apply unknown conditions
			if(!this._types[condition.conditionType] || !this._operators[condition.operatorType]) return false;

			var conditionRulesShell = data.shell ? data.shell : dialog.find('.conditionRulesShell'),
				value = condition.operatorType == 'between' ? condition.condition : condition.condition[0];

			value = this._types[condition.conditionType].type == 'text' ? condition.text : value;

			if(!data.update) {
				var ruleEditBtn = ruleRow.find('.conditionRuleEdit'),
					ruleDelBtn = ruleRow.find('.conditionRuleDelete');

				ruleRow.removeAttr('id');
				ruleRow.data('ruleid', data.iter);
				ruleRow.attr('data-ruleid', data.iter);
				ruleEditBtn.data('ruleid', data.iter);
				ruleDelBtn.data('ruleid', data.iter);
				ruleEditBtn.on('click', function() {
					self.updateRuleForm($(this).data('ruleid'));
				});
				ruleDelBtn.on('click', function() {
					self.deleteRule($(this).data('ruleid'));
				});
			}
			ruleRange.html(data.rule.range);
			this.setStylesToElem(condition.style, ruleRange);
			ruleRow.find('.ruleCondition').html(
				this._types[condition.conditionType].label + ' ' +
				this._operators[condition.operatorType].label + ' ' +
				value
			);
			if(!data.update) {
				ruleRow.appendTo(conditionRulesShell).show();
			}
		};
		Conditions.prototype._clearRulesList = function(rules) {
			for(var i = 0; i < rules.length; i++) {
				if(!rules[i]) {
					rules.splice(i, 1);
					rules = this._clearRulesList(rules);
					break;
				}
			}
			return rules;
		};

		return Conditions;
	})();

	app.Editor = app.Editor || {};
	app.Editor._Conditions = Conditions;

}(window.jQuery, window.supsystic.Tables));
