Ext.define("Terrasoft.core.mixins.EntityBaseViewModelMixin", {
	extend: "Terrasoft.core.BaseObject",
	alternateClassName: "Terrasoft.EntityBaseViewModelMixin",

	/**
  * Base entity schema reference.
  * @type {Terrasoft.BaseEntitySchema}
  */
	entitySchema: null,

	/**
  * Uniqie identifier column name.
  * @type {String}
  */
	primaryColumnName: "",

	/**
  * Primary image column name.
  * @type {String}
  */
	primaryImageColumnName: "",

	/**
  * Display value column name.
  * @type {String}
  */
	primaryDisplayColumnName: "",

	/**
  * Delete query instance.
  * @private
  * @type {Terrasoft.DeleteQuery}
  */
	deleteQuery: null,

	/**
  * Insert query instance.
  * @private
  * @type {Terrasoft.InsertQuery}
  */
	insertQuery: null,

	/**
  * Update query instance.
  * @private
  * @type {Terrasoft.UpdateQuery}
  */
	updateQuery: null,

	//region Methods: Private

	/**
  * @private
  */
	_getEntityDataModel: function () {
		return this.dataModelCollection && this.dataModelCollection.first();
	},

	/**
  * Creates and initializes delete query {@link #deleteQuery}
  * @private
  * @return {Terrasoft.DeleteQuery}
  */
	getDeleteQuery: function () {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var deleteQuery = this.deleteQuery = this.deleteQuery || Ext.create("Terrasoft.DeleteQuery", {
				rootSchema: this.entitySchema
			});
			return deleteQuery;
		} else {
			return this._getEntityDataModel().getDeleteQuery();
		}
	},

	/**
  * Creates and initializes insert query {@link #insertQuery}
  * @private
  * @return {Terrasoft.InsertQuery}
  */
	getInsertQuery: function () {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var insertQuery = this.insertQuery = this.insertQuery || Ext.create("Terrasoft.InsertQuery", {
				rootSchema: this.entitySchema
			});
			return insertQuery;
		} else {
			return this._getEntityDataModel().getInsertQuery();
		}
	},

	/**
  * Creates and initializes update query {@link #updateQuery}
  * @private
  * @return {Terrasoft.UpdateQuery}
  */
	getUpdateQuery: function () {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var updateQuery = this.updateQuery = this.updateQuery || Ext.create("Terrasoft.UpdateQuery", {
				rootSchema: this.entitySchema,
				isForceUpdate: this.get("IsProcessMode") || false
			});
			return updateQuery;
		} else {
			return this._getEntityDataModel().getUpdateQuery(this.get("IsProcessMode"));
		}
	},

	/**
  * Sets the column values from the entity.
  * @private
  * @param {Terrasoft.BaseViewModel} entity An instance of an entity.
  * @param {Object} options
  */
	setColumnValues: function (entity, options) {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			Terrasoft.each(this.columns, function (column, columnName) {
				if (column.type === Terrasoft.ViewModelColumnType.ENTITY_COLUMN && column.isCollection !== true) {
					this.setColumnValue(columnName, entity.get(columnName), options);
				}
			}, this);
		} else {
			Terrasoft.each(this.columns, function (column, columnName) {
				if (entity.columns[columnName] && column.type === Terrasoft.ViewModelColumnType.ENTITY_COLUMN && column.isCollection !== true) {
					this.setColumnValue(columnName, entity.get(columnName), options);
				}
			}, this);
		}
	},

	/**
  * Gets the entitySchema column corresponding to the model column.
  * @private
  * @deprecated
  * @param {String} modelColumnName Name of the view model column.
  * @return {Object} column The entitySchema column.
  */
	getSafeEntityColumn: function (modelColumnName) {
		window.console.warn(Ext.String.format(Terrasoft.Resources.ObsoleteMessages.ObsoleteMethodMessage, "getSafeEntityColumn", "findEntityColumn"));
		return this.findEntityColumn(modelColumnName);
	},

	/**
  * Handles result of lookup query.
  * @private
  * @param {Object} config Request config.
  * @param {Object} response Response from lookup query.
  */
	getLookupDataCallback: function (config, response) {
		this.removeLookupDataRequestInstanceId(config && config.columnName);
		this.isListFullyLoaded = !response.collection.getCount();
		var objects = {};
		var viewModelCollection = response.collection;
		var params = {
			collection: viewModelCollection,
			columnName: config.columnName,
			filterValue: config.filterValue,
			isLookupEdit: config.isLookupEdit,
			objects: objects
		};
		this.onLookupDataLoaded(params);
		config.list.loadAll(objects);
		Ext.callback(config.callback, config.scope || this);
	},

	/**
  * Sets display values for default values of entitySchema lookup columns.
  * @private
  * @param {Function} callback Callback-function.
  * @param {Object} scope Callback execution scope.
  */
	_setLookupColumnsDefaultDisplayValues: function (callback, scope) {
		var entityColumns = this._getEntityColumnsWithDefaultValues();
		if (!Terrasoft.useStaticFileContent) {
			callback.call(scope, entityColumns);
			return;
		}
		var batch = this._getLookupDisplayValueByPrimaryColumnBatchQuery(entityColumns);
		if (Ext.isEmpty(batch.queries)) {
			callback.call(scope, entityColumns);
			return;
		}
		batch.execute(function (response) {
			if (response && response.success) {
				Terrasoft.each(batch.queriesMap, function (esqIndex, columnName) {
					var entityColumn = this.findEntityColumn(columnName);
					var displayValueResult = response.queryResults[esqIndex] && response.queryResults[esqIndex].rows[0] && response.queryResults[esqIndex].rows[0].displayValue;
					if (displayValueResult) {
						entityColumn.defaultValue.value.displayValue = displayValueResult;
					} else {
						entityColumn.defaultValue.value.displayValue = Terrasoft.emptyString;
					}
				}, this);
			}
			callback.call(scope, entityColumns);
		}, this);
	},

	/**
  * Sets constant and system value default values for entitySchema columns.
  * @private
  * @param {Object} entityColumns entitySchema columns which have default values.
  * @param {Function} callback Callback-function to set system settings default column values.
  * @param {Object} scope Callback execution scope.
  */
	_setEntitySchemaColumnDefaultValues: function (entityColumns, callback, scope) {
		var sysSettingsToQuery = [];
		var columnToSettingMap = {};
		Terrasoft.each(entityColumns, function (column, columnName) {
			var defaultValue = column.defaultValue;
			switch (defaultValue.source) {
				case Terrasoft.EntitySchemaColumnDefSource.CONST:
					this.set(columnName, defaultValue.value);
					break;
				case Terrasoft.EntitySchemaColumnDefSource.SYSTEM_VALUE:
					this.set(columnName, this.getSysDefaultValue(defaultValue.value));
					break;
				case Terrasoft.EntitySchemaColumnDefSource.SETTINGS:
					var settingName = defaultValue.value;
					if (!Ext.Array.contains(sysSettingsToQuery, settingName)) {
						sysSettingsToQuery.push(settingName);
					}
					columnToSettingMap[columnName] = settingName;
					break;
				default:
					break;
			}
		}, this);
		callback.call(scope, columnToSettingMap, sysSettingsToQuery);
	},

	/**
  * Gets entitySchema columns which have default values.
  * @private
  * @return {Object} entitySchema columns which have default values.
  */
	_getEntityColumnsWithDefaultValues: function () {
		var entityColumns = {};
		Terrasoft.each(this.columns, function (column, columnName) {
			if (column.type !== Terrasoft.ViewModelColumnType.ENTITY_COLUMN) {
				return;
			}
			if (Terrasoft.contains(this.ignoredSystemColumns, column.columnPath)) {
				return;
			}
			var entityColumn = this.findEntityColumn(columnName);
			if (entityColumn && entityColumn.defaultValue) {
				entityColumns[columnName] = entityColumn;
			}
		}, this);
		return entityColumns;
	},

	/**
  * Gets BatchQuery instance for load primary display column value by primary column value.
  * @param {Object} entityColumns entitySchema columns which have default values.
  * @return {Terrasoft.BatchQuery} BatchQuery instance for load primary display column value
  * by primary column value.
  */
	_getLookupDisplayValueByPrimaryColumnBatchQuery: function (entityColumns) {
		var batch = Ext.create("Terrasoft.BatchQuery");
		Terrasoft.each(entityColumns, function (column, columnName) {
			var isLookup = Terrasoft.isLookupDataValueType(column.dataValueType) || column.isLookup;
			var defaultValue = column.defaultValue;
			var hasDefaultValue = defaultValue && defaultValue.value && defaultValue.value.value;
			if (hasDefaultValue && isLookup && defaultValue.source === Terrasoft.EntitySchemaColumnDefSource.CONST && !defaultValue.value.displayValue) {
				var esq = this._getLookupDisplayValueByPrimaryColumnQuery(defaultValue.value.value, column.referenceSchemaName);
				batch.addNamedQuery(esq, columnName, Terrasoft.emptyFn, this);
			}
		}, this);
		return batch;
	},

	/**
  * Gets EntitySchemaQuery instance for load primary display column value by primary column value.
  * @private
  * @param {String} primaryColumnValue Primary column value.
  * @param {String} schemaName Root schema name.
  * @return {Terrasoft.EntitySchemaQuery} EntitySchemaQuery instance for load primary display column value
  * by primary column value.
  */
	_getLookupDisplayValueByPrimaryColumnQuery: function (primaryColumnValue, schemaName) {
		var entitySchemaQuery = Ext.create("Terrasoft.EntitySchemaQuery", {
			rootSchemaName: schemaName
		});
		entitySchemaQuery.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_DISPLAY_COLUMN, "displayValue");
		entitySchemaQuery.enablePrimaryColumnFilter(primaryColumnValue);
		return entitySchemaQuery;
	},

	/**
  * Sets primary entity model name for user defined entity columns with column path.
  * @private
  */
	_setColumnsDataModelName: function () {
		var model = this._getEntityDataModel();
		if (model) {
			Terrasoft.each(this.columns, function (column) {
				if (column.type === Terrasoft.ViewModelColumnType.ENTITY_COLUMN && Ext.isEmpty(column.modelName) && !Ext.isEmpty(column.columnPath)) {
					column.modelName = model.name;
				}
			}, this);
		}
	},

	//endregion

	//region Methods: Public

	/**
  * Initializes primaryColumnName, primaryDisplayColumnName, primaryImageColumnName fields from entity schema,
  * when it is set to {@link #entitySchema}.
  */
	initEntitySchemaProperties: function () {
		if (this.entitySchema) {
			this.primaryColumnName = this.entitySchema.primaryColumnName;
			this.primaryDisplayColumnName = this.entitySchema.primaryDisplayColumnName;
			this.primaryImageColumnName = this.entitySchema.primaryImageColumnName;
		}
	},

	/**
  * Gets the entitySchema column corresponding to the model column.
  * @param {String} modelColumnName Name of the view model column.
  * @return {Object} column The entitySchema column.
  */
	findEntityColumn: function (modelColumnName) {
		var result = null;
		var modelColumn = this.getColumnByName(modelColumnName);
		var columnPath = modelColumn ? modelColumn.columnPath : modelColumnName;
		var entitySchema = this.entitySchema;
		if (entitySchema && entitySchema.isColumnExist(columnPath)) {
			result = entitySchema.getColumnByName(columnPath);
		}
		return result;
	},

	/**
  * Sets default values for entitySchema.
  * @param {Function} callback Callback-function called after default values initialization.
  * @param {Object} scope Callback execution scope.
  */
	setDefaultValues: function (callback, scope) {
		scope = scope || this;
		if (!this.entitySchema) {
			Ext.callback(callback, scope, [this]);
			return;
		}
		this._setLookupColumnsDefaultDisplayValues(function (entityColumns) {
			this._setEntitySchemaColumnDefaultValues(entityColumns, function (columnToSettingMap, sysSettingsToQuery) {
				if (sysSettingsToQuery.length === 0) {
					Ext.callback(callback, scope, [this]);
					return;
				}
				Terrasoft.SysSettings.querySysSettings(sysSettingsToQuery, function (settings) {
					Terrasoft.each(columnToSettingMap, function (settingName, columnName) {
						this.setColumnValue(columnName, settings[settingName]);
					}, this);
					Ext.callback(callback, scope, [this]);
				}, this);
			}, this);
		}, this);
	},

	/**
  * Adds a macros column that represents primary value column.
  * @protected
  * @param {Terrasoft.EntitySchemaQuery} entitySchemaQuery A query to which the column will be added.
  */
	addPrimaryValueColumn: function (entitySchemaQuery) {
		entitySchemaQuery.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_COLUMN, "value");
	},

	/**
  * Adds a macros column that represents primary dispaly column.
  * @protected
  * @param {Terrasoft.EntitySchemaQuery} entitySchemaQuery A query to which the column will be added.
  */
	addPrimaryDisplayColumn: function (entitySchemaQuery) {
		var primaryDisplayColumn = entitySchemaQuery.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_DISPLAY_COLUMN, "displayValue");
		primaryDisplayColumn.orderPosition = 1;
		primaryDisplayColumn.orderDirection = Terrasoft.OrderDirection.ASC;
	},

	/**
  * Adds a filter for primary display value column.
  * @protected
  * @param {Terrasoft.EntitySchemaQuery} entitySchemaQuery A query to be filtered.
  * @param {String} filterValue A search value to be used in the filter.
  */
	addLookupQueryFilter: function (entitySchemaQuery, filterValue) {
		var comparisonType = this.getLookupComparisonType();
		var lookupFilter = entitySchemaQuery.createPrimaryDisplayColumnFilterWithParameter(comparisonType, filterValue, Terrasoft.DataValueType.TEXT);
		entitySchemaQuery.filters.add("LookupFilter", lookupFilter);
		lookupFilter.isEnabled = Boolean(filterValue);
	},

	/**
  * Checks if column has a proper lookup dataValueType.
  * @protected
  * @param {Terrasoft.EntitySchemaColumn} column A column to be checked.
  * @throws {Terrasoft.UnsupportedTypeException} Will throw if column dataValueType is incorrect.
  */
	checkColumnLookupType: function (column) {
		var isLookup = Terrasoft.isLookupDataValueType(column.dataValueType) || column.isLookup;
		if (!isLookup) {
			throw new Terrasoft.UnsupportedTypeException({
				message: Terrasoft.Resources.BaseViewModel.columnUnsupportedTypeException
			});
		}
	},

	/**
  * Returns a configuration object to be used for creation of the query to a lookup.
  * @protected
  * @param {Terrasoft.EntitySchemaColumn} column A lookup column that refers to the schema that will be be used
  * in the query.
  * @return {Object} A configuration object for creation of a query to the lookup.
  */
	getLookupQueryConfig: function (column) {
		if (!column) {
			throw new Terrasoft.ItemNotFoundException();
		}
		this.checkColumnLookupType(column);
		var config = {
			rootSchemaName: column.referenceSchemaName,
			rowCount: Terrasoft.SysSettings.lookupRowCount,
			isPageable: true,
			useRecordDeactivation: true
		};
		return config;
	},

	/**
  * Returns {@link Terrasoft.EntitySchemaQuery query instance} for load lookup column data.
  * @param {String} filterValue Filter for primaryDisplayColumn.
  * @param {String} columnName ViewModel column name.
  * @return {Terrasoft.EntitySchemaQuery}
  * @throws {Terrasoft.ItemNotFoundException} Will throw if schema does not contain column with name columnName.
  * @throws {Terrasoft.UnsupportedTypeException} Will throw if column with name columnName is not lookup.
  */
	getLookupQuery: function (filterValue, columnName) {
		this.abortLoadLookupDataRequest(columnName);
		var column = this.getColumnByName(columnName);
		var queryConfig = this.getLookupQueryConfig(column);
		var entitySchemaQuery = Ext.create("Terrasoft.EntitySchemaQuery", queryConfig);
		this.addPrimaryValueColumn(entitySchemaQuery);
		this.addPrimaryDisplayColumn(entitySchemaQuery);
		this.addLookupQueryFilter(entitySchemaQuery, filterValue);
		var pagingParams = this.pagingParams;
		if (pagingParams) {
			this.addPagingParameters(entitySchemaQuery, pagingParams);
			pagingParams = null;
		} else {
			if (Terrasoft.useOffsetFetchPaging) {
				entitySchemaQuery.rowsOffset = 0;
			}
		}
		this.saveLookupDataRequestInstanceId(columnName, entitySchemaQuery.instanceId);
		return entitySchemaQuery;
	},

	/**
  * Adds params to ESQ to simulate paging.
  * @protected
  * @param {Terrasoft.EntitySchemaQuery} entitySchemaQuery The Query to EntitySchema.
  * @param {Object} config Request parameters.
  */
	addPagingParameters: function (entitySchemaQuery, config) {
		var hasItems = config && config.list && config.list.getCount();
		if (Terrasoft.useOffsetFetchPaging && hasItems) {
			entitySchemaQuery.rowsOffset = this._getListLength(config.list);
		} else {
			var conditionalValues = entitySchemaQuery.conditionalValues = this.Ext.create("Terrasoft.ColumnValues");
			if (hasItems) {
				var lastRecord = this.getLastRecord(config.list);
				var primaryColumnDisplayValue = lastRecord.displayValue;
				var primaryColumnValue = lastRecord.value;
				conditionalValues.setParameterValue("displayValue", primaryColumnDisplayValue, Terrasoft.DataValueType.TEXT);
				conditionalValues.setParameterValue("value", primaryColumnValue, Terrasoft.DataValueType.GUID);
			}
		}
	},

	/**
  * Loads data for lookup column.
  * @param {String} filterValue Filter for primaryDisplayColumn.
  * @param {Terrasoft.Collection} list Collection for loaded data.
  * @param {String} columnName ViewModel column name.
  * @param {Boolean} isLookupEdit True if lookup, false if combobox.
  * @param {Function} [callback] Callback-function.
  * @param {Object} [scope] Callback execution scope.
  */
	loadLookupData: function (filterValue, list, columnName, isLookupEdit, callback, scope) {
		if (list) {
			list.clear();
		}
		this.pagingParams = null;
		var config = {
			callback: callback,
			columnName: columnName,
			filterValue: filterValue,
			isLookupEdit: isLookupEdit,
			list: list,
			scope: scope
		};
		var lookupQuery = this.getLookupQuery(filterValue, columnName);
		lookupQuery.getEntityCollection(this.getLookupDataCallback.bind(this, config));
	},

	/**
  * Returns EntitySchemaQuery instance for load predictable lookup column data.
  * @param {String} filterValue Filter for primaryDisplayColumn.
  * @param {String} columnName ViewModel column name.
  * @return {Terrasoft.EntitySchemaQuery} EntitySchemaQuery instance for load predictable lookup column data.
  */
	getPredictableLookupQuery: function (filterValue, columnName) {
		var esq = this.getLookupQuery(filterValue, columnName);
		this.appendPredictableQueryColumns(esq);
		this.appendPredictableQueryConditions(esq);
		return esq;
	},

	/**
  * Loads predictable data for lookup column.
  * @param {String} filterValue Filter for primaryDisplayColumn.
  * @param {Terrasoft.Collection} list Collection for loaded data.
  * @param {String} columnName ViewModel column name.
  * @param {Boolean} isLookupEdit True if lookup, false if combobox.
  * @param {Function} [callback] Callback-function.
  * @param {Object} [scope] Callback execution scope.
  */
	loadPredictableData: function (filterValue, list, columnName, isLookupEdit, callback, scope) {
		if (list) {
			list.clear();
		}
		var lookupQuery = this.getPredictableLookupQuery(filterValue, columnName);
		lookupQuery.getEntityCollection(this.getLookupDataCallback.bind(this, {
			callback: callback,
			columnName: columnName,
			filterValue: filterValue,
			isLookupEdit: isLookupEdit,
			list: list,
			scope: scope
		}));
	},

	/**
  * Loads data for lookup column with params.
  * @param {Object} params Query params.
  * @param {String} columnName ViewModel column name.
  * @param {Boolean} isLookupEdit True if lookup, false if combobox.
  * @param {Function} [callback] Callback-function.
  * @param {Object} [scope] Callback execution scope.
  */
	loadLookupDataWithParams: function (params, columnName, isLookupEdit, callback, scope) {
		if (this.isListFullyLoaded) {
			return;
		}
		if (params) {
			var filterValue = params.filterValue || "";
			var list = params.list || {};
			var listView = params.listView || {};
			var config = {
				callback: callback,
				columnName: columnName,
				filterValue: filterValue,
				isLookupEdit: isLookupEdit,
				list: list,
				scope: scope,
				listView: listView
			};
			this.pagingParams = params;
			var lookupQuery = this.getLookupQuery(filterValue, columnName);
			lookupQuery.getEntityCollection(this.getLookupDataCallback.bind(this, config));
		}
	},

	/**
  * Gets the url for the image
  * @return {String|null} url of the requested image
  */
	getSchemaImageUrl: function () {
		var imageColumnName, primaryImageColumnValue, imageConfig;
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var entitySchemaPrimaryImageColumnName = this.entitySchema ? this.entitySchema.primaryImageColumnName : null;
			imageColumnName = this.primaryImageColumnName || entitySchemaPrimaryImageColumnName;
			primaryImageColumnValue = this.get(imageColumnName);
			if (!primaryImageColumnValue) {
				return null;
			}
			imageConfig = {
				source: Terrasoft.ImageSources.SYS_IMAGE,
				params: {
					primaryColumnValue: primaryImageColumnValue.value
				}
			};
			return Terrasoft.ImageUrlBuilder.getUrl(imageConfig);
		} else {
			var entityDataModel = this._getEntityDataModel();
			imageColumnName = this.primaryImageColumnName || entityDataModel.getEntityPrimaryImageColumnName();
			primaryImageColumnValue = this.get(imageColumnName);
			if (!primaryImageColumnValue) {
				return null;
			}
			imageConfig = {
				source: Terrasoft.ImageSources.SYS_IMAGE,
				params: {
					primaryColumnValue: primaryImageColumnValue.value
				}
			};
			return Terrasoft.ImageUrlBuilder.getUrl(imageConfig);
		}
	},

	/**
  * Forms a request to save the entity.
  * @return  {Terrasoft.BaseQuery|null} Request to save the entity
  */
	getSaveQuery: function () {
		var query = null;
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			if (!this.validate()) {
				return;
			}
			var entitySchema = this.entitySchema;
			if (entitySchema == null) {
				throw new Terrasoft.exceptions.ArgumentNullOrEmptyException({ argumentName: "entitySchema" });
			}
			if (this.isDeleted) {
				throw Terrasoft.InvalidOperationException();
			}
			if (this.isNew) {
				query = this.getInsertQuery();
			} else {
				query = this.getUpdateQuery();
				query.enablePrimaryColumnFilter(this.get(this.primaryColumnName));
			}
			var columnValues = query.columnValues;
			columnValues.clear();
			for (var columnName in this.changedValues) {
				var column = this.columns[columnName];
				if (!column || column.type !== Terrasoft.ViewModelColumnType.ENTITY_COLUMN) {
					continue;
				}
				var columnPath = column.columnPath;
				if (!entitySchema.isColumnExist(columnPath)) {
					continue;
				}
				var columnValue = this.get(columnName);
				if (column.isLookup) {
					columnValue = columnValue ? columnValue.value : null;
				}
				columnValues.setParameterValue(columnPath, columnValue, this.getColumnDataType(columnName));
			}
			return query;
		} else {
			if (!this.validate()) {
				return null;
			}
			if (this.isDeleted) {
				throw Terrasoft.InvalidObjectState();
			}
			var entityDataModel = this._getEntityDataModel();
			if (this.isNew) {
				query = this.getInsertQuery();
			} else {
				query = this.getUpdateQuery();
				var primaryColumnValue = this.get(this.primaryColumnName);
				query.enablePrimaryColumnFilter(primaryColumnValue);
			}
			return entityDataModel.getSaveQuery({
				primaryColumnValue: this.get(this.primaryColumnName),
				changedValues: this.changedValues,
				columns: this.columns,
				saveQuery: query,
				isForceUpdate: this.get("IsProcessMode"),
				isNew: this.isNew
			});
		}
	},

	/**
  * Saves the entity on the server. If the entity already exists, the data will be updated,
  * otherwise a new entity is added.
  * @param {Function} callback The function that will be called when a response is received from the server
  * @param {Object} scope The context in which the callback function is called
  */
	saveEntity: function (callback, scope) {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var query = this.getSaveQuery();
			query.execute(function (result) {
				if (result.success) {
					this.isNew = false;
					this.changedValues = null;
				}
				if (Ext.isFunction(callback)) {
					callback.call(scope || this, result);
				}
			}, this);
		} else {
			var config = {
				saveQuery: this.getSaveQuery()
			};
			var entityDataModel = this._getEntityDataModel();
			entityDataModel.save(config, function (result) {
				if (result.success) {
					this.isNew = false;
					this.changedValues = null;
				}
				Ext.callback(callback, scope || this, [result]);
			}, this);
		}
	},

	/**
  * Removes the current entity on the server
  * @param {Function} callback The function that will be called when a response is received from the server
  * @param {Object} scope The context in which the callback function is called
  */
	deleteEntity: function (callback, scope) {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			if (this.entitySchema == null) {
				return;
			}
			if (this.isNew || this.isDeleted) {
				throw Terrasoft.InvalidOperationException();
			}
			var primaryColumnValue = this.get(this.primaryColumnName);
			var deleteQuery = this.getDeleteQuery();
			try {
				deleteQuery.enablePrimaryColumnFilter(primaryColumnValue);
				deleteQuery.execute(function (result) {
					if (result.success) {
						this.isDeleted = true;
					}
					callback.call(scope || this, result);
				}, this);
			} finally {
				deleteQuery.disablePrimaryColumnFilter();
			}
		} else {
			if (this.isNew || this.isDeleted) {
				throw Terrasoft.InvalidObjectState();
			}
			var entityDataModel = this._getEntityDataModel();
			var config = {
				deleteQuery: this.getDeleteQuery(),
				primaryColumnValue: this.get(this.primaryColumnName)
			};
			entityDataModel.deleteEntity(config, function (result) {
				if (result.success) {
					this.isDeleted = true;
				}
				callback.call(scope || this, result);
			}, this);
		}
	},

	/**
  * Generates an entity select query
  * @return {Terrasoft.EntitySchemaQuery}
  */
	getEntitySchemaQuery: function () {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var entitySchemaQuery = Ext.create("Terrasoft.EntitySchemaQuery", {
				rootSchema: this.entitySchema
			});
			var columns = this.columns;
			Terrasoft.each(columns, function (column, columnName) {
				if (column.columnPath && column.type === Terrasoft.ViewModelColumnType.ENTITY_COLUMN) {
					entitySchemaQuery.addColumn(column.columnPath, columnName);
				}
			}, this);
			return entitySchemaQuery;
		} else {
			var model = this._getEntityDataModel();
			return model.getEntitySchemaQuery(this.columns);
		}
	},

	/**
  * Loads data into the model
  * @param {String} primaryColumnValue Primary key value
  * @param {Function} callback The function that will be called when a response is received from the server
  * @param {Object} scope The context in which the callback function is called
  */
	loadEntity: function (primaryColumnValue, callback, scope) {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var entitySchemaQuery = this.getEntitySchemaQuery();
			entitySchemaQuery.getEntity(primaryColumnValue, function (result) {
				this.isNew = false;
				var entity = result.entity;
				if (entity) {
					this.setColumnValues(entity, { preventValidation: true });
					this.changedValues = {};
				}
				if (callback) {
					callback.call(scope || this, this);
				}
			}, this);
		} else {
			var config = {
				primaryColumnValue: primaryColumnValue,
				entitySchemaQuery: this.getEntitySchemaQuery(),
				columns: this.columns
			};
			var entityDataModel = this._getEntityDataModel();
			entityDataModel.load(config, function (entity) {
				if (entity) {
					this.isNew = false;
					this.setColumnValues(entity, { preventValidation: true });
					this.changedValues = {};
				}
				Ext.callback(callback, scope || this, [this]);
			}, this);
		}
	},

	/**
  * Copies data to the model
  * @param {String} primaryColumnValue Primary key value
  * @param {Function} callback The function that will be called when a response is received from the server
  * @param {Object} scope The context in which the callback function is called
  */
	copyEntity: function (primaryColumnValue, callback, scope) {
		if (Terrasoft.Features.getIsEnabled("BaseViewModelOld")) {
			var entitySchemaQuery = this.getEntitySchemaQuery();
			entitySchemaQuery.getEntity(primaryColumnValue, function (result) {
				var entity = result.entity;
				if (!entity) {
					throw new Terrasoft.ItemNotFoundException();
				}
				this.setDefaultValues(function () {
					this.setCopyColumnValues(entity);
					if (callback) {
						callback.call(scope || this, this);
					}
				}, this);
			}, this);
		} else {
			var config = {
				primaryColumnValue: primaryColumnValue,
				entitySchemaQuery: this.getEntitySchemaQuery()
			};
			var entityDataModel = this._getEntityDataModel();
			entityDataModel.copy(config, function (entity) {
				this.setDefaultValues(function () {
					this.setCopyColumnValues(entity);
					Ext.callback(callback, scope || this, [this]);
				}, this);
			}, this);
		}
	},

	/**
  * Returns entity schema.
  * @return {Terrasoft.BaseEntitySchema|null}
  */
	getEntityViewModelEntitySchema: function () {
		var model = this._getEntityDataModel();
		return model && model.schema || null;
	},

	/**
  * Sets entity schema.
  * @param {Terrasoft.BaseEntitySchema} entitySchema Entity schema.
  */
	setEntityViewModelEntitySchema: function (entitySchema) {
		if (entitySchema) {
			var entityDataModel = this._getEntityDataModel();
			if (entityDataModel && entityDataModel.schema.instanceId === entitySchema.instanceId) {
				return;
			}
			var dataModelName = entitySchema.name || "dataEntityModelKey";
			var dataModelCollection = this.dataModelCollection = Ext.create("Terrasoft.DataModelCollection");
			var config = {};
			config[dataModelName] = {
				entitySchema: entitySchema
			};
			dataModelCollection.init(config);
			this.initEntitySchemaProperties();
		} else {
			if (this.dataModelCollection && this.dataModelCollection.getCount() > 0) {
				this.dataModelCollection.removeByIndex(0);
			}
		}
	},

	//endregion

	//region Methods: Protected

	/**
  * Saves lookup data request instance id in requesting list by key.
  * @protected
  * @param {String} key Request key.
  * @param {String} instanceId Request instance id.
  */
	saveLookupDataRequestInstanceId: function (key, instanceId) {
		this.lookupDataRequestInstanceId[key] = instanceId;
	},

	/**
  * Removes lookup data request instance id from requesting list by key.
  * @protected
  * @param {String} key Request key.
  */
	removeLookupDataRequestInstanceId: function (key) {
		delete this.lookupDataRequestInstanceId[key];
	},

	/**
  * Aborts lookup data request by requesting item key.
  * @protected
  * @param {String} key Requesting item key.
  */
	abortLoadLookupDataRequest: function (key) {
		var requestId = this.lookupDataRequestInstanceId[key];
		if (requestId) {
			Terrasoft.AjaxProvider.abortRequestByInstanceId(requestId);
			this.removeLookupDataRequestInstanceId(key);
		}
	},

	/**
  * Appends predictable columns to predictable lookup query.
  * @protected
  * @param {Terrasoft.EntitySchemaQuery} esq Predictable lookup query.
  */
	appendPredictableQueryColumns: Terrasoft.emptyFn,

	/**
  * Appends predictable query conditions to predictable lookup query.
  * @protected
  * @param {Terrasoft.EntitySchemaQuery} esq Predictable lookup query.
  */
	appendPredictableQueryConditions: Terrasoft.emptyFn

	//endregion
});