text: 'use strict';
  
  const Ref = require('./ref');
  
  
  const internals = {};
  
  
  internals.extendedCheckForValue = function (value, insensitive) {
  
      const valueType = typeof value;
  
      if (valueType === 'object') {
          if (value instanceof Date) {
              return (item) => {
  
                  return item instanceof Date && value.getTime() === item.getTime();
              };
          }
  
          if (Buffer.isBuffer(value)) {
              return (item) => {
  
                  return Buffer.isBuffer(item) && value.length === item.length && value.toString('binary') === item.toString('binary');
              };
          }
      }
      else if (insensitive && valueType === 'string') {
          const lowercaseValue = value.toLowerCase();
          return (item) => {
  
              return typeof item === 'string' && lowercaseValue === item.toLowerCase();
          };
      }
  
      return null;
  };
  
  
  module.exports = class InternalSet {
  
      constructor(from) {
  
          this._set = new Set(from);
          this._hasRef = false;
      }
  
      add(value, refs) {
  
          const isRef = Ref.isRef(value);
          if (!isRef && this.has(value, null, null, false)) {
  
              return this;
          }
  
          if (refs !== undefined) { // If it's a merge, we don't have any refs
              Ref.push(refs, value);
          }
  
          this._set.add(value);
  
          this._hasRef |= isRef;
  
          return this;
      }
  
      merge(add, remove) {
  
          for (const item of add._set) {
              this.add(item);
          }
  
          for (const item of remove._set) {
              this.remove(item);
          }
  
          return this;
      }
  
      remove(value) {
  
          this._set.delete(value);
          return this;
      }
  
      has(value, state, options, insensitive) {
  
          return !!this.get(value, state, options, insensitive);
      }
  
      get(value, state, options, insensitive) {
  
          if (!this._set.size) {
              return false;
          }
  
          const hasValue = this._set.has(value);
          if (hasValue) {
              return { value };
          }
  
          const extendedCheck = internals.extendedCheckForValue(value, insensitive);
          if (!extendedCheck) {
              if (state && this._hasRef) {
                  for (let item of this._set) {
                      if (Ref.isRef(item)) {
                          item = [].concat(item(state.reference || state.parent, options));
                          const found = item.indexOf(value);
                          if (found >= 0) {
                              return { value: item[found] };
                          }
                      }
                  }
              }
  
              return false;
          }
  
          return this._has(value, state, options, extendedCheck);
      }
  
      _has(value, state, options, check) {
  
          const checkRef = !!(state && this._hasRef);
  
          const isReallyEqual = function (item) {
  
              if (value === item) {
                  return true;
              }
  
              return check(item);
          };
  
          for (let item of this._set) {
              if (checkRef && Ref.isRef(item)) { // Only resolve references if there is a state, otherwise it's a merge
                  item = item(state.reference || state.parent, options);
  
                  if (Array.isArray(item)) {
                      const found = item.findIndex(isReallyEqual);
                      if (found >= 0) {
                          return {
                              value: item[found]
                          };
                      }
  
                      continue;
                  }
              }
  
              if (isReallyEqual(item)) {
                  return {
                      value: item
                  };
              }
          }
  
          return false;
      }
  
      values(options) {
  
          if (options && options.stripUndefined) {
              const values = [];
  
              for (const item of this._set) {
                  if (item !== undefined) {
                      values.push(item);
                  }
              }
  
              return values;
          }
  
          return Array.from(this._set);
      }
  
      slice() {
  
          const set = new InternalSet(this._set);
          set._hasRef = this._hasRef;
          return set;
      }
  
      concat(source) {
  
          const set = new InternalSet([...this._set, ...source._set]);
          set._hasRef = !!(this._hasRef | source._hasRef);
          return set;
      }
  };
  