import { setSafeProperty, hasSafeProperty, getSafeProperty } from './customs.js';
import { isObject } from './is.js';
/**
 * A map facade on a bare object.
 *
 * The small number of methods needed to implement a scope,
 * forwarding on to the SafeProperty functions. Over time, the codebase
 * will stop using this method, as all objects will be Maps, rather than
 * more security prone objects.
 */

export class ObjectWrappingMap {
  constructor(object) {
    this.wrappedObject = object;
  }

  keys() {
    return Object.keys(this.wrappedObject);
  }

  get(key) {
    return getSafeProperty(this.wrappedObject, key);
  }

  set(key, value) {
    setSafeProperty(this.wrappedObject, key, value);
    return this;
  }

  has(key) {
    return hasSafeProperty(this.wrappedObject, key);
  }

}
/**
 * Creates an empty map, or whatever your platform's polyfill is.
 *
 * @returns an empty Map or Map like object.
 */

export function createEmptyMap() {
  return new Map();
}
/**
 * Creates a Map from the given object.
 *
 * @param { Map | { [key: string]: unknown } | undefined } mapOrObject
 * @returns
 */

export function createMap(mapOrObject) {
  if (!mapOrObject) {
    return createEmptyMap();
  }

  if (isMap(mapOrObject)) {
    return mapOrObject;
  }

  if (isObject(mapOrObject)) {
    return new ObjectWrappingMap(mapOrObject);
  }

  throw new Error('createMap can create maps from objects or Maps');
}
/**
 * Unwraps a map into an object.
 *
 * @param {Map} map
 * @returns { [key: string]: unknown }
 */

export function toObject(map) {
  if (map instanceof ObjectWrappingMap) {
    return map.wrappedObject;
  }

  var object = {};

  for (var key of map.keys()) {
    var value = map.get(key);
    setSafeProperty(object, key, value);
  }

  return object;
}
/**
 * Returns `true` if the passed object appears to be a Map (i.e. duck typing).
 *
 * Methods looked for are `get`, `set`, `keys` and `has`.
 *
 * @param {Map | object} object
 * @returns
 */

export function isMap(object) {
  // We can use the fast instanceof, or a slower duck typing check.
  // The duck typing method needs to cover enough methods to not be confused with DenseMatrix.
  if (!object) {
    return false;
  }

  return object instanceof Map || object instanceof ObjectWrappingMap || typeof object.set === 'function' && typeof object.get === 'function' && typeof object.keys === 'function' && typeof object.has === 'function';
}
/**
 * Copies the contents of key-value pairs from each `objects` in to `map`.
 *
 * Object is `objects` can be a `Map` or object.
 *
 * This is the `Map` analog to `Object.assign`.
 */

export function assign(map) {
  for (var _len = arguments.length, objects = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    objects[_key - 1] = arguments[_key];
  }

  for (var args of objects) {
    if (!args) {
      continue;
    }

    if (isMap(args)) {
      for (var key of args.keys()) {
        map.set(key, args.get(key));
      }
    } else if (isObject(args)) {
      for (var _key2 of Object.keys(args)) {
        map.set(_key2, args[_key2]);
      }
    }
  }

  return map;
}