"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _util = require("@polkadot/util");

var _utilCrypto = require("@polkadot/util-crypto");

var _U8a = _interopRequireDefault(require("./U8a"));

var _utils = require("./utils");

// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.

/**
 * @name Set
 * @description
 * An Set is an array of string values, represented an an encoded type by
 * a bitwise representation of the values.
 */
// FIXME This is a prime candidate to extend the JavaScript built-in Set
class CodecSet extends Set {
  constructor(setValues, value) {
    super(CodecSet.decodeSet(setValues, value));
    this._setValues = void 0;
    this._setValues = setValues;
  }

  static decodeSet(setValues) {
    let value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    if ((0, _util.isU8a)(value)) {
      return value.length === 0 ? [] : CodecSet.decodeSetNumber(setValues, value[0]);
    } else if (value instanceof Set) {
      return CodecSet.decodeSetArray(setValues, [...value.values()]);
    } else if (Array.isArray(value)) {
      return CodecSet.decodeSetArray(setValues, value);
    }

    return CodecSet.decodeSetNumber(setValues, value);
  }

  static decodeSetArray(setValues, value) {
    return value.reduce((result, key) => {
      (0, _util.assert)(!(0, _util.isUndefined)(setValues[key]), "Set: Invalid key '".concat(key, "' passed to Set, allowed ").concat(Object.keys(setValues).join(', ')));
      result.push(key);
      return result;
    }, []);
  }

  static decodeSetNumber(setValues, value) {
    const result = Object.keys(setValues).reduce((result, key) => {
      if ((value & setValues[key]) === setValues[key]) {
        result.push(key);
      }

      return result;
    }, []);
    const computed = CodecSet.encodeSet(setValues, result);
    (0, _util.assert)(value === computed, "Set: Mismatch decoding '".concat(value, "', computed as '").concat(computed, "' with ").concat(result));
    return result;
  }

  static encodeSet(setValues, value) {
    return value.reduce((result, value) => {
      return result | (setValues[value] || 0);
    }, 0);
  }

  static with(values) {
    return class extends CodecSet {
      constructor(value) {
        super(values, value);
        Object.keys(values).forEach(_key => {
          const name = (0, _util.stringUpperFirst)((0, _util.stringCamelCase)(_key));
          const iskey = "is".concat(name); // do not clobber existing properties on the object

          if ((0, _util.isUndefined)(this[iskey])) {
            Object.defineProperty(this, iskey, {
              enumerable: true,
              get: () => this.strings.includes(_key)
            });
          }
        });
      }

    };
  }
  /**
   * @description The length of the value when encoded as a Uint8Array
   */


  get encodedLength() {
    return 1;
  }
  /**
   * @description returns a hash of the contents
   */


  get hash() {
    return new _U8a.default((0, _utilCrypto.blake2AsU8a)(this.toU8a(), 256));
  }
  /**
   * @description true is the Set contains no values
   */


  get isEmpty() {
    return this.size === 0;
  }
  /**
   * @description The actual set values as a string[]
   */


  get strings() {
    return [...super.values()];
  }
  /**
   * @description The encoded value for the set members
   */


  get valueEncoded() {
    return CodecSet.encodeSet(this._setValues, this.strings);
  }
  /**
   * @description adds a value to the Set (extended to allow for validity checking)
   */


  add(key) {
    // we have the isUndefined(this._setValues) in here as well, add is used internally
    // in the Set constructor (so it is undefined at this point, and should allow)
    (0, _util.assert)((0, _util.isUndefined)(this._setValues) || !(0, _util.isUndefined)(this._setValues[key]), "Set: Invalid key '".concat(key, "' on add"));
    super.add(key);
    return this;
  }
  /**
   * @description Compares the value of the input to see if there is a match
   */


  eq(other) {
    if (Array.isArray(other)) {
      // we don't actually care about the order, sort the values
      return (0, _utils.compareArray)(this.strings.sort(), other.sort());
    } else if (other instanceof Set) {
      return this.eq([...other.values()]);
    } else if ((0, _util.isNumber)(other)) {
      return this.valueEncoded === other;
    }

    return false;
  }
  /**
   * @description Returns a hex string representation of the value
   */


  toHex() {
    return (0, _util.u8aToHex)(this.toU8a());
  }
  /**
   * @description Converts the Object to JSON, typically used for RPC transfers
   */


  toJSON() {
    return this.strings;
  }
  /**
   * @description The encoded value for the set members
   */


  toNumber() {
    return this.valueEncoded;
  }
  /**
   * @description Returns the base runtime type name for this instance
   */


  toRawType() {
    // FIXME We don't cater for this in createType as of yet
    return JSON.stringify({
      _set: this._setValues
    });
  }
  /**
   * @description Returns the string representation of the value
   */


  toString() {
    return "[".concat(this.strings.join(', '), "]");
  }
  /**
   * @description Encodes the value as a Uint8Array as per the SCALE specifications
   * @param isBare true when the value has none of the type-specific prefixes (internal)
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars


  toU8a(isBare) {
    return new Uint8Array([this.valueEncoded]);
  }

}

exports.default = CodecSet;