/*!
 * @author electricessence / https://github.com/electricessence/
 * Licensing: MIT https://github.com/electricessence/TypeScript.NET/blob/master/LICENSE.md
 */
"use strict";

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Types_1 = require("../Types");
var LinkedNodeList_1 = require("./LinkedNodeList");
var ArgumentException_1 = require("../Exceptions/ArgumentException");
var ArgumentNullException_1 = require("../Exceptions/ArgumentNullException");
var forEach_1 = require("./Enumeration/forEach");
var Enumerator_1 = require("./Enumeration/Enumerator");
var Utility_1 = require("../Disposable/Utility");
var OTHER = 'other';

var Set = function () {
    function Set(source) {
        _classCallCheck(this, Set);

        this._count = 0;
        if (source) this.unionWith(source);
    }

    _createClass(Set, [{
        key: "_getSet",
        value: function _getSet() {
            var s = this._set;
            if (!s) this._set = s = new LinkedNodeList_1.default();
            return s;
        }
    }, {
        key: "exceptWith",
        value: function exceptWith(other) {
            var _this = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            forEach_1.default(other, function (v) {
                _this.remove(v);
            });
        }
    }, {
        key: "intersectWith",
        value: function intersectWith(other) {
            var _this2 = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            if (other instanceof Set) {
                var s = this._set;
                if (s) s.forEach(function (n) {
                    if (!other.contains(n.value)) _this2.remove(n.value);
                });
            } else {
                Utility_1.using(new Set(other), function (o) {
                    return _this2.intersectWith(o);
                });
            }
        }
    }, {
        key: "isProperSubsetOf",
        value: function isProperSubsetOf(other) {
            var _this3 = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            return other instanceof Set ? other.isProperSupersetOf(this) : Utility_1.using(new Set(other), function (o) {
                return o.isProperSupersetOf(_this3);
            });
        }
    }, {
        key: "isProperSupersetOf",
        value: function isProperSupersetOf(other) {
            var _this4 = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            var result = true,
                count;
            if (other instanceof Set) {
                result = this.isSupersetOf(other);
                count = other._count;
            } else {
                Utility_1.using(new Set(), function (o) {
                    forEach_1.default(other, function (v) {
                        o.add(v);
                        return result = _this4.contains(v);
                    });
                    count = o._count;
                });
            }
            return result && this._count > count;
        }
    }, {
        key: "isSubsetOf",
        value: function isSubsetOf(other) {
            var _this5 = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            return other instanceof Set ? other.isSupersetOf(this) : Utility_1.using(new Set(other), function (o) {
                return o.isSupersetOf(_this5);
            });
        }
    }, {
        key: "isSupersetOf",
        value: function isSupersetOf(other) {
            var _this6 = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            var result = true;
            forEach_1.default(other, function (v) {
                return result = _this6.contains(v);
            });
            return result;
        }
    }, {
        key: "overlaps",
        value: function overlaps(other) {
            var _this7 = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            var result = false;
            forEach_1.default(other, function (v) {
                return !(result = _this7.contains(v));
            });
            return result;
        }
    }, {
        key: "setEquals",
        value: function setEquals(other) {
            if (!other) throw new ArgumentNullException_1.default(OTHER);
            return this._count == (other instanceof Set ? other._count : Utility_1.using(new Set(other), function (o) {
                return o._count;
            })) && this.isSubsetOf(other);
        }
    }, {
        key: "symmetricExceptWith",
        value: function symmetricExceptWith(other) {
            var _this8 = this;

            if (!other) throw new ArgumentNullException_1.default(OTHER);
            if (other instanceof Set) {
                forEach_1.default(other, function (v) {
                    if (_this8.contains(v)) _this8.remove(v);else _this8.add(v);
                });
            } else {
                Utility_1.using(new Set(other), function (o) {
                    return _this8.symmetricExceptWith(o);
                });
            }
        }
    }, {
        key: "unionWith",
        value: function unionWith(other) {
            var _this9 = this;

            forEach_1.default(other, function (v) {
                _this9.add(v);
            });
        }
    }, {
        key: "add",
        value: function add(item) {
            if (!this.contains(item)) {
                var type = typeof item === "undefined" ? "undefined" : _typeof(item);
                if (!Types_1.default.isPrimitive(type)) throw new ArgumentException_1.default("item", "A Set can only index primitives.  Complex objects require a HashSet.");
                var r = this._registry;
                var t = r && r[type];
                if (!r) this._registry = r = {};
                if (!t) r[type] = t = {};
                var node = { value: item };
                this._getSet().addNode(node);
                t[item] = node;
                ++this._count;
            }
        }
    }, {
        key: "clear",
        value: function clear() {
            var _ = this;
            _._count = 0;
            wipe(_._registry, 2);
            var s = _._set;
            return s ? s.clear() : 0;
        }
    }, {
        key: "dispose",
        value: function dispose() {
            this.clear();
            this._set = null;
            this._registry = null;
        }
    }, {
        key: "_getNode",
        value: function _getNode(item) {
            var r = this._registry,
                t = r && r[typeof item === "undefined" ? "undefined" : _typeof(item)];
            return t && t[item];
        }
    }, {
        key: "contains",
        value: function contains(item) {
            return !(!this._count || !this._getNode(item));
        }
    }, {
        key: "copyTo",
        value: function copyTo(array) {
            var index = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];

            if (!array) throw new ArgumentNullException_1.default('array');
            var s = this._set,
                c = this._count;
            if (!s || !c) return array;
            var minLength = index + c;
            if (array.length < minLength) array.length = minLength;
            return LinkedNodeList_1.default.copyValues(s, array, index);
        }
    }, {
        key: "toArray",
        value: function toArray() {
            var s = this._set;
            return s ? s.map(function (n) {
                return n.value;
            }) : [];
        }
    }, {
        key: "remove",
        value: function remove(item) {
            var r = this._registry,
                t = r && r[typeof item === "undefined" ? "undefined" : _typeof(item)],
                node = t && t[item];
            if (node) {
                delete t[item];
                var s = this._set;
                if (s && s.removeNode(node)) {
                    --this._count;
                    return 1;
                }
            }
            return 0;
        }
    }, {
        key: "getEnumerator",
        value: function getEnumerator() {
            var s = this._set;
            return s && this._count ? LinkedNodeList_1.default.valueEnumeratorFrom(s) : Enumerator_1.empty;
        }
    }, {
        key: "count",
        get: function get() {
            return this._count;
        }
    }, {
        key: "isReadOnly",
        get: function get() {
            return true;
        }
    }]);

    return Set;
}();

Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Set;
function wipe(map) {
    var depth = arguments.length <= 1 || arguments[1] === undefined ? 1 : arguments[1];

    if (map && depth) {
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
            for (var _iterator = Object.keys(map)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
                var key = _step.value;

                var v = map[key];
                delete map[key];
                wipe(v, depth - 1);
            }
        } catch (err) {
            _didIteratorError = true;
            _iteratorError = err;
        } finally {
            try {
                if (!_iteratorNormalCompletion && _iterator.return) {
                    _iterator.return();
                }
            } finally {
                if (_didIteratorError) {
                    throw _iteratorError;
                }
            }
        }
    }
}
//# sourceMappingURL=Set.js.map
