Source: mvc/BaseProxy.js

const collect = require('collect.js');
const Subject = require('./Subject.js').Subject;

/**
 * The class provide the basic method for handling domain object in a proxy, by having a collection o them.
 * The collections of object are expected to be of the samme class.
 */
class BaseProxy extends Subject {
	constructor() {
		super();
		this._objects = collect({});
		this._dirtyUid = collect({}); // set
		this.cls = this.constructor.name;
		this.sortBy = 'uid';
		this.sortAsc = true;
	}

	/**
	 * Add an object to the collection.
	 * @param {Object} object
	 * 	The object to add, which must have this json format:
	 * 	<code>{["class":"SomeClass","uid":"someValue", ....]}</code>
	 * @param {string} cls
	 * 	Override the class property of the object
	 */
	add(object, cls) {
		if (cls === undefined) {
			cls = this.cls;
		}
		if (object.uid === undefined) {
			throw new Error("Object must have an uid property");
		}
		if (object.class === undefined) {
			throw new Error("Object must have a class property");
		}
		if (object.class !== cls) {
			throw new Error("Proxy and object must been of same class");
		}
		this._objects.put(object.uid, object);
		this._dirtyUid.forget(object.uid);
	}

	/**
	 * Add a collections of object.
	 * @param {collect}rows
	 * 	The collections
	 * @param {string} cls
	 * 	The overriden name of the cls.
	 */
	addAll(rows, cls) {
		this.clear();
		rows.each(row => {
			this.add(row, cls);
		});
	}

	/**
	 * Remove an object from the collection by uid
	 * @param {int} uid
	 * 	The uid of the object.
	 */
	remove(uid) {
		this._objects.forget(uid);
	}

	/**
	 * Gets an object by uid
	 * @param {int} uid
	 * 	The given uid
	 * @returns {Object|null}
	 * 	If found the object otherwise null
	 */
	get(uid) {
		let result = this._objects.get(uid);
		if (result == null) {
			throw Error('Object not found: '+uid);
		}
		return result;
	}

	/**
	 * Tells whether an object with uid exists or not
	 * @param {int} uid
	 * 	The given uid
	 * @returns {true|false}
	 * 	True if found otherwise false
	 */
	has(uid) {
		return this._objects.has(uid);
	}

	/**
	 * Gets all the object in the collection and names and values of the filter
	 * @returns {Object}
	 * 	An object on the format:
	 * 	<code>{filter:{someName:someValue, ...}, objects:{uid:someValue,...}}</code>
	 */
	getAll() {
		let result = Object.assign({}, this.filterBy);
		if (this.sortAsc) {
			result['objects'] = this._objects.values().sortBy(this.sortBy);
		}
		else {
			result['objects'] = this._objects.values().sortByDesc(this.sortBy);
		}
		return result;
	}

	/**
	 * Tells the current size of the collections of object
	 * @returns {int}
	 * 	The number of objects
	 */
	size() {
		return this._objects.count();
	}

	/**
	 * Sets a property in this or inherited object.
	 * @param {string} name
	 * 	The name of the property
	 * @param {any} value
	 * 	The value of the property
	 */
	setProperty(name, value) {
		if (name in this) {
			this[name] = value;
		}
		else {
			throw Error('Property not found: '+name);
		}
	}

	/**
	 * Sets a property in this or inherited object.
	 * The property and value is fecthed from ElementValue, but if the property in this or inherited object has
	 * appended the name ByElement, the property is passed the full element.
	 * @param {ElementValue} element
	 * 	The property name and value.
	 */
	setPropertyByElement(element) {
		/*
		Setting the property must follow this orderly path, which allows for properties with same
		name as propeties in objects off this proxy.
		 */
		if (element.property+'ByElement' in this) {
			this[element.property+'ByElement'] = element;
		}
		else if (!isNaN(element.uid)) {
			let object = this._objects.get(element.uid);
			object[element.property] = element.value;
			this._dirtyUid.put(element.uid, element.uid);
		}
		else if (element.property in this) {
			this[element.property] = element.value;
		}
		else {
			throw Error('Property not found: '+element.property);
		}
	}

	/**
	 * Gets the default filter for this proxy, which is empty.
	 * @returns {{}}
	 * 	The empty object
	 */
	get filterBy() {
		return {};
	}

	/**
	 * Remove objects from the collection.
	 */
	clear() {
		this._objects = collect({});
		this._dirtyUid = collect({});
	}

	/**
	 * Tells if an object by the uid has been modified
	 * @param {int} uid
	 * @returns {boolean}
	 * 	True if found otherwise false
	 */
	isDirty(uid) {
		return this._dirtyUid.get(uid) != null;
	}
}
exports.BaseProxy = BaseProxy;