var IndexedObjects = Namespace.declare("com.ceruleanCircle.EAM.2_systems.BasicDataTypes",
    class IndexedObjects {
        constructor() {
            this.index = {};
            this.indexAttribute = "id"
        }

        init(indexAttribute) {
            this.indexAttribute = indexAttribute;
            return this;
        }

        getValues() {
            var values = [];
            for (var i in this.index)
                values.push(this.index[i]);
            return values;
        }

        getKeys() {
            var keys = [];
            for (var i in this.index)
                keys.push(i);
            return keys;
        }

        addArray(array) {
            array.forEach(e => { this.add(e) });
        }

        push(indexedObject) {
            this.add(indexedObject);
            return this.getKeys().length;
        }

        add(indexedObject) {
            var added = (this.lookupObject(indexedObject) == null);
            this.index[this.lookupObjectID(indexedObject)] = indexedObject;
            return added;
        }

        addAlias(alias, indexedObject) {
            this.index[alias] = indexedObject;
            if (indexedObject.indexedObjectAliases == null)
                indexedObject.indexedObjectAliases = [];
            indexedObject.indexedObjectAliases.push(alias)
        }

        remove(indexedObject) {
            this.index[this.lookupObjectID(indexedObject)] = null;
        }

        lookup(id) {
            //finds the object with id
            return this.index[id];
        }
        lookupObjectID(indexedObject) {
            //finds the value of id in indexedObject
            return IndexedObjects.lookupAttribute(indexedObject,this.indexAttribute);
        }
        lookupObject(indexedObject) {
            //return this.constructor.lookup(this.index,id);
            var id = this.lookupObjectID(indexedObject);
            var object = this.lookup(id);
            if (id === object)
                return null;

            return object;
        }

    static lookupAttribute(object, id) {
        if (!id) 
                return object;
        var packages = id.split(".");
        var currentNamespace = object;
        var name = null;
        for (var i in packages) {
            name = packages[i];
            var nextNamespace = currentNamespace[name];
            if (!nextNamespace) {
                //                console.warn(currentNamespace.package, name + " not found");
                return object;
            }
            currentNamespace = nextNamespace;
        }
        return currentNamespace;

    }


    filter(attribute, equalsTo, isExtraxtAttribute) {
            var collection = []
            if (attribute == null)
                attribute = this.indexAttribute;


            for (var i in this.index) {
                var indexedObject = this.index[i];

                var value = indexedObject[attribute];
                if (value != null) {
                    if (equalsTo === "*" || value == equalsTo || equalsTo == null) {
                        var entry = value;
                        /*
                        if (attribute != this.indexAttribute) {
                            entry = {};
                            if (isExtraxtAttribute)
                                entry[attribute] = value;
                            else
                                entry["value"] = indexedObject;

                            entry[this.indexAttribute] = indexedObject[this.indexAttribute];
                        }
                        */
                        collection.push(entry);
                    }
                }
            }
            return collection;
        }
    }
);

var Map = Namespace.declare("com.ceruleanCircle.EAM.2_systems.BasicDataTypes",
    class Map {
        constructor() {
            this.allowCollections = false;
            this.mapIndexAttribute = "mapEntry.mapId";
            this.keys = new IndexedObjects().init(this.mapIndexAttribute);
        }

        init(mapIndexAttribute) {
            this.mapIndexAttribute = mapIndexAttribute;
            return this;
        }

        getMapEntry(key) {
            var indexedObject = this.keys.lookup(key);
            if (indexedObject == null)
                return null;
            return indexedObject.mapEntry;

        }

        getKey(key) {
            var id = IndexedObjects.lookupAttribute(key,this.mapIndexAttribute);
            var indexedObject = this.keys.lookup(id);
            if (indexedObject == null)
                return null;
            return indexedObject.mapEntry.key;

        }

        getAllKeys() {
            var keys = [];
            var allEntries = this.keys.filter("mapEntry");
            return allEntries
        }

        getValue(key) {
            var id = IndexedObjects.lookupAttribute(key,this.mapIndexAttribute);
            var indexedObject = this.keys.lookup(id);
            if (indexedObject == null)
                return null;
            return indexedObject.mapEntry.value;

        }

        set(key, value) {
            var id = IndexedObjects.lookupAttribute(key,this.mapIndexAttribute);
            var indexedObject = this.keys.lookup(id);
            if (indexedObject == null) {
                var mapEnty = { mapEntry:{
                        mapId:id, 
                        key:key, 
                        value: value,
                        mapIndexAttribute:this.mapIndexAttribute,
                        name:id, 
                        type:{name:"com.ceruleanCircle.EAM.2_systems.BasicDataTypes.Map.Entry"}} 
                        }

                this.keys.add(mapEnty)
            } else {
                indexedObject.value = value;
            }
        }

        add(key, value) {

            //		console.log("model2View: "+key[this.keys.indexAttribute]);

            if (!this.allowCollections)
                return;
            var id = IndexedObjects.lookupAttribute(key,this.mapIndexAttribute);
            var indexedObject = this.keys.lookup(id);
            //var indexedObject = this.keys.lookup(key);
            if (indexedObject == null) {
                //this.set(key, [value]);
                this.set(key, Set.from([value]));
            } else {
                if (indexedObject.mapEntry.value == null)
                    indexedObject.mapEntry.value = new Set();

                if (indexedObject.mapEntry.value.push == null) {
                    //not a collection? so make it a collection
                    indexedObject.mapEntry.value = Set.from([indexedObject.value]);
                }
                indexedObject.mapEntry.value.push(value);
            }
        }

        isCollection(key) {
            var indexedObject = this.keys.lookup(key);
            if (indexedObject == null)
                return false;

            if (indexedObject.value == null)
                return false;

            if (indexedObject.value.push == null)
                return false;
            else
                return true;

        }

    }
);

var Set = Namespace.declare("com.ceruleanCircle.EAM.2_systems.BasicDataTypes",
    class Set extends Array {
        push(element) {
          if (this.every(e => {return e != element}))
                return super.push(element);
          return this.length;       
        }
        unshift(element) {
          if (this.every(e => {return e != element}))
                return super.unshift(element);        
          return this.length;       
        }
        static from(array) {
          var newSet = new Set();
          if (Array.isArray(array)) array.forEach(e => {newSet.push(e)});
          else newSet.push(array);
          return newSet;
        }
    }
);