function Modelo() {
	function Entidad(nombre, llave) {
		this.nombre = nombre;
		this.llave = llave;
		this.instancias = {};
        var este = this;

		function extraer(obj, atrs) {
			var r = [];
			for(var i=0;i<atrs.length;i++) {
				r.push(obj[atrs[i]]);
			}

			return r;
		}

		function satisface(obj, cond) {
            if(este.llave.atributos.length >= 2 && (typeof(cond)).toLowerCase() === 'string') {
                return este.indice(obj) === cond;
            }

			for(var i in cond) {
				if(!cond.hasOwnProperty(i)) continue;

				if(obj[i] != cond[i])
					return false;
			}

			return true;
		}

		this.stdCondicion = function(condicion) {
			var stdCond;
			var t = (typeof(condicion)).toLowerCase();

            // Si es escalar y la llave solo tiene un atributo, lo convierto en objeto
			if(this.llave.atributos.length == 1 && (t === 'string' || t === 'boolean' || t === 'number' || t === 'undefined' || condicion === null)) {
				stdCond = {};
				stdCond[this.llave.nombre] = condicion;
			}

            else if(this.llave.atributos.length >= 2 && t === 'string') {
                stdCond = condicion;
            }

			else {
				stdCond = condicion;
			}

			return stdCond;
		}

		// Obtiene la llave de hash de un objeto
		this.indice = function(obj) {
			var atrs = extraer(obj, this.llave.atributos);
			for(var i=0;i<atrs.length;i++)
				atrs[i] = String(atrs[i]).toString();
			return atrs.join('::');
		}

		this.get = function(condicion) {
			var lista = this.select(condicion);
			if(lista.length == 1) return lista[0];
			else if (lista.length == 0) return null;
			else return null; // throw Exception ???
		}

		this.getHash = function() {
			return this.instancias;
		}

		this.getList = function() {
			var o = this.instancias;
			var L = [];
			for(var i in o) {
				if(!o.hasOwnProperty(i)) continue;
				L.push(o[i]);
			}

			return L;
		}

		this.select = function(condicion) {
			var cond = this.stdCondicion(condicion);

			var f = [];
			for(var i in this.instancias) {
				if(!this.instancias.hasOwnProperty(i)) continue;

				if(satisface(this.instancias[i], cond)) {
					f.push(this.instancias[i]);
				}
			}

			return f;
		}

		this.insertar = function(obj) {
			var indice = this.indice(obj);

			if(this.instancias.hasOwnProperty(indice)) {
				return false;
			}

			this.instancias[ indice ] = obj;
			return true;
		}

		this.modificar = function(obj, condicion) {
			var cond = this.stdCondicion(condicion);

			function fillObj(source, dest) {
				for(var i in source) {
					if(!source.hasOwnProperty(i)) continue;
					dest[i] = source[i];
				}
			}

			var mod = 0;
			var reinsertar = [];
			var eliminar = [];

			for(var i in this.instancias) {
				if(!this.instancias.hasOwnProperty(i)) continue;

				if(satisface(this.instancias[i], cond)) {
					// Por modificar:
					var tochange = this.instancias[i];

					// Eliminar referencia antigua
					eliminar.push(i);

					// Modificar
					fillObj(obj, tochange);

					// Reinsertar
					reinsertar.push(tochange);

					mod++;
				}
			}

			// Eliminar
			for(var i=0;i<eliminar.length;i++) {
				delete this.instancias[eliminar[i]];
			}

			// Reinsertar
			for(var i=0;i<reinsertar.length;i++) {
				this.insertar(reinsertar[i]);
			}

			return mod;
		}

		this.eliminar = function(condicion) {
			var cond = this.stdCondicion(condicion);

			var del = 0;
			var p = [];

			for(var i in this.instancias) {
				if(!this.instancias.hasOwnProperty(i)) continue;

				if(satisface(this.instancias[i], cond)) {
					p.push(i);
				}
			}

			for(var i=0;i<p.length;i++) {
				if(delete this.instancias[p[i]]) {
					del++;
				}
			}

			return del;
		}

		this.limpiar = function() {
			delete this.instancias;
			this.instancias = {};
		}
	}

	function Llave(k) {
		this.nombre = null;
		this.atributos = [];

		if((typeof(k)).toLowerCase() === 'string') {
			this.atributos = [k];
		}
		else {
			this.atributos = k.sort();
		}
		this.nombre = this.atributos.join(',');
	}

	this.data = {};


	// params {nombre:'Persona', llave:'rut'} o {nombre:'Persona', llave:['nombre', 'apellido']}
	this.registrar = function(params) {
		var nombre = params['nombre'];
		var llave = params['llave'];

		if(this.registrado(nombre)) return false;

		var oLlave = new Llave(llave);
		this.data[nombre] = new Entidad(nombre, oLlave);

		return true;
	}

	this.registrado = function(nombre) {
		return this.data.hasOwnProperty(nombre);
	}

	this.insertar = function(entidad, obj) {
		return this.data[entidad].insertar(obj);
	}

	this.modificar = function(entidad, obj, condicion) {
		return this.data[entidad].modificar(obj, condicion);
	}

	this.eliminar = function(entidad, id) {
		return this.data[entidad].eliminar(id);
	}

	this.get = function(entidad, buscados) {
		return this.data[entidad].get(buscados);
	}

	this.getHash = function(entidad) {
		return this.data[entidad].getHash();
	}

	this.getList = function(entidad) {
		return this.data[entidad].getList();
	}

	this.select = function(entidad, buscados) {
		return this.data[entidad].select(buscados);
	}

	this.limpiar = function(entidad) {
		this.data[entidad].limpiar();
	}
    
    this.indice = function(entidad, obj) {
        return this.data[entidad].indice(obj);
    }
}