(function () {
    "use strict";
    /**
     * @class MultiMap
     * @classdesc A Map supporting arbitrary multiple values with a single key.
     * (key --> [values])
     * [Reference: MultiMap in Google Guava libraries](https://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap)
     * @desc
     * #### Example -
     * ```js
     * var MultiMap = require("dsjslib").MultiMap
     * var mmap=new MultiMap()
     * ```
     */
    function MultiMap() {
        this._mmap = {
            'wrapped' : Object.create(null),
            'get' : function (k) {
                return this.wrapped[k];
            },
            'set' : function (k, v) {
                this.wrapped[k] = v;
            },
            'delete' : function (k) {
                delete this.wrapped[k];
            },
            'keys' : function () {
                return Object.keys(this.wrapped);
            }

        };
        this.size = 0;
    }

    /**
     * Insert a key value pair into the Map.
     * If the key is already present, the value will be added to the existing list,
     * otherwise, an array is created and the value is added to the array
     * @memberOf MultiMap.prototype
     * @instance
     * @param key {*}
     * @param value {*}
     * @returns {MultiMap} this
     */
    MultiMap.prototype.put = function (key, value) {
        var values = this._mmap.get(key);
        values = values || [];
        values.push(value);
        this._mmap.set(key, values);
        this.size++;
        return this;

    };

    /**
     * Search for key and return associated value array or empty array
     * This method never returns null even if the key is not present
     * Any changes made to the returned array modify the underlying array in the MultiMap as well
     * @memberOf MultiMap.prototype
     * @param key
     * @return {Array} associated value Array
     */
    MultiMap.prototype.get = function (key) {
        if (!this._mmap.get(key)) {
            this._mmap.set(key, []);

        }
        return this._mmap.get(key);

    };
    /**
     * If a value is provided, only that value is removed from the list
     * If value is not provided, key and all values are deleted from the MultiMap
     * @memberOf MultiMap.prototype
     * @param key {*}
     * @param value {*}
     * @return {Array} last value associated with the key
     */
    MultiMap.prototype.remove = function (key, value) {
        var values = this._mmap.get(key),
            index = null,
            lasVal;
        if (values) {
            if (typeof value !== 'undefined') {
                values.every(function (v, i) {
                    if (v === value) {
                        index = i;
                        return false;
                    }
                    return true;
                });
                lasVal = index !== null ? values.splice(index, 1) : [];
            } else {
                this._mmap['delete'](key);
                lasVal = values;
            }
        }
        return lasVal;

    };

    /**
     * Test if a key is present in Map or not. This doesn't modify the Map to create empty array if
     * the key was not present.
     * @memberOf MultiMap.prototype
     * @param key
     * @returns {Boolean} true if key is present, false otherwise
     */
    MultiMap.prototype.hasKey = function (key) {
        var v = this._mmap.get(key);
        return (typeof v !== 'undefined') && v !== null;
    };

    /**
     * Return a list of all key, value pairs.
     * The key value pairs are returned as objects {'key':<K>,'value':<V>}
     * Note that for keys associated with multiple values there is one object per value returned in the entry
     * for example for key1->val1,val2,val3 , the entries will be
     * [{'key':key1,'value':val1},{'key':key1,'value':val2},{'key':key1,'value':val3}]
     * @memberOf MultiMap.prototype
     * @instance
     * @returns {Array} Array of objects  {'key':<K>,'value':<V>}
     */
    MultiMap.prototype.entries = function () {
        var entries = [];

        this._mmap.keys().forEach(function (k) {
            this._mmap.get(k).forEach(function (v) {
                entries.push({'key' : k, 'value' : v});
            });

        }, this);

        return entries;

    };
    module.exports = MultiMap;
}());