"use strict";

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

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

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

var _rxjs = require("rxjs");

var _operators = require("rxjs/operators");

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

var _base = _interopRequireDefault(require("../base"));

var _Combinator = _interopRequireDefault(require("./Combinator"));

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

// extract the arguments and callback params from a value array possibly containing a callback
function extractArgs(args, needsCallback) {
  let callback;
  const actualArgs = args.slice(); // If the last arg is a function, we pop it, put it into callback.
  // actualArgs will then hold the actual arguments to be passed to `method`

  if (args.length && (0, _util.isFunction)(args[args.length - 1])) {
    callback = actualArgs.pop();
  } // When we need a subscription, ensure that a valid callback is actually passed


  (0, _util.assert)(!needsCallback || (0, _util.isFunction)(callback), 'Expected a callback to be passed with subscriptions');
  return [actualArgs, callback];
} // a Promise completion tracker, wrapping an isComplete variable that ensures the promise only resolves once


function promiseTracker(_resolve, _reject) {
  let isCompleted = false;

  const complete = (fn, value) => {
    if (!isCompleted) {
      isCompleted = true;
      fn(value);
    }
  };

  return {
    reject: error => {
      complete(_reject, error);
      return _rxjs.EMPTY;
    },
    resolve: value => {
      complete(_resolve, value);
    }
  };
}
/**
 * @description Decorate method for ApiPromise, where the results are converted to the Promise equivalent
 */


function decorateMethod(method, options) {
  const needsCallback = options && options.methodName && options.methodName.includes('subscribe');
  return function () {
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    const [actualArgs, callback] = extractArgs(args, !!needsCallback);

    if (!callback) {
      return method(...actualArgs).pipe((0, _operators.first)()).toPromise();
    }

    return new Promise((resolve, reject) => {
      const tracker = promiseTracker(resolve, reject);
      const subscription = method(...actualArgs).pipe( // if we find an error (invalid params, etc), reject the promise
      (0, _operators.catchError)(error => tracker.reject(error)), // upon the first result, resolve the with the unsub function
      (0, _operators.tap)(() => tracker.resolve(() => subscription.unsubscribe()))).subscribe(callback);
    }); // ???
  };
}
/**
 * # @polkadot/api/promise
 *
 * ## Overview
 *
 * @name ApiPromise
 * @description
 * ApiPromise is a standard JavaScript wrapper around the RPC and interfaces on the Polkadot network. As a full Promise-based, all interface calls return Promises, including the static `.create(...)`. Subscription calls utilise `(value) => {}` callbacks to pass through the latest values.
 *
 * The API is well suited to real-time applications where either the single-shot state is needed or use is to be made of the subscription-based features of Polkadot (and Substrate) clients.
 *
 * @see [[ApiRx]]
 *
 * ## Usage
 *
 * Making rpc calls -
 * <BR>
 *
 * ```javascript
 * import ApiPromise from '@polkadot/api/promise';
 *
 * // initialise via static create
 * const api = await ApiPromise.create();
 *
 * // make a subscription to the network head
 * api.rpc.chain.subscribeNewHeads((header) => {
 *   console.log(`Chain is at #${header.number}`);
 * });
 * ```
 * <BR>
 *
 * Subscribing to chain state -
 * <BR>
 *
 * ```javascript
 * import { ApiPromise, WsProvider } from '@polkadot/api';
 *
 * // initialise a provider with a specific endpoint
 * const provider = new WsProvider('wss://example.com:9944')
 *
 * // initialise via isReady & new with specific provider
 * const api = await new ApiPromise({ provider }).isReady;
 *
 * // retrieve the block target time
 * const blockPeriod = await api.query.timestamp.blockPeriod().toNumber();
 * let last = 0;
 *
 * // subscribe to the current block timestamp, updates automatically (callback provided)
 * api.query.timestamp.now((timestamp) => {
 *   const elapsed = last
 *     ? `, ${timestamp.toNumber() - last}s since last`
 *     : '';
 *
 *   last = timestamp.toNumber();
 *   console.log(`timestamp ${timestamp}${elapsed} (${blockPeriod}s target)`);
 * });
 * ```
 * <BR>
 *
 * Submitting a transaction -
 * <BR>
 *
 * ```javascript
 * import ApiPromise from '@polkadot/api/promise';
 *
 * ApiPromise.create().then((api) => {
 *   const nonce = await api.query.system.accountNonce(keyring.alice.address);
 *
 *   api.tx.balances
 *     // create transfer
 *     transfer(keyring.bob.address, 12345)
 *     // sign the transcation
 *     .sign(keyring.alice, { nonce })
 *     // send the transaction (optional status callback)
 *     .send((status) => {
 *       console.log(`current status ${status.type}`);
 *     })
 *     // retrieve the submitted extrinsic hash
 *     .then((hash) => {
 *       console.log(`submitted with hash ${hash}`);
 *     });
 * });
 * ```
 */


class ApiPromise extends _base.default {
  /**
   * @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance.
   * @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or a
   * provider (see the constructor arguments)
   * @example
   * <BR>
   *
   * ```javascript
   * import Api from '@polkadot/api/promise';
   *
   * Api.create().then(async (api) => {
   *   const timestamp = await api.query.timestamp.now();
   *
   *   console.log(`lastest block timestamp ${timestamp}`);
   * });
   * ```
   */
  static create(options) {
    return new ApiPromise(options).isReady;
  }
  /**
   * @description Creates an instance of the ApiPromise class
   * @param options Options to create an instance. This can be either [[ApiOptions]] or
   * an [[WsProvider]].
   * @example
   * <BR>
   *
   * ```javascript
   * import Api from '@polkadot/api/promise';
   *
   * new Api().isReady.then((api) => {
   *   api.rpc.subscribeNewHeads((header) => {
   *     console.log(`new block #${header.number.toNumber()}`);
   *   });
   * });
   * ```
   */


  constructor(options) {
    super(options, 'promise', decorateMethod);
    this._isReadyPromise = void 0;
    this._isReadyPromise = new Promise(resolve => {
      super.once('ready', () => {
        resolve(this);
      });
    });
  }
  /**
   * @description Promise that returns the first time we are connected and loaded
   */


  get isReady() {
    return this._isReadyPromise;
  }
  /**
   * @description Returns a clone of this ApiPromise instance (new underlying provider connection)
   */


  clone() {
    return new ApiPromise(_objectSpread({}, this._options, {
      source: this
    }));
  }
  /**
   * @description Creates a combinator that can be used to combine the latest results from multiple subscriptions
   * @param fns An array of function to combine, each in the form of `(cb: (value: void)) => void`
   * @param callback A callback that will return an Array of all the values this combinator has been applied to
   * @example
   * <BR>
   *
   * ```javascript
   * const address = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7';
   *
   * // combines values from balance & nonce as it updates
   * api.combineLatest([
   *   api.rpc.chain.subscribeNewHeads,
   *   [api.query.balances.freeBalance, address],
   *   (cb) => api.query.system.accountNonce(address, cb)
   * ], ([head, balance, nonce]) => {
   *   console.log(`#${head.number}: You have ${balance} units, with ${nonce} transactions sent`);
   * });
   * ```
   */
  // eslint-disable-next-line @typescript-eslint/require-await


  async combineLatest(fns, callback) {
    const combinator = new _Combinator.default(fns, callback);
    return () => {
      combinator.unsubscribe();
    };
  }

}

exports.default = ApiPromise;