"use strict";

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

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

var _types = require("./types");

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

var _sanitize = _interopRequireDefault(require("./sanitize"));

var _typeSplit = require("./typeSplit");

// 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.
// decode an enum of either of the following forms
//  { _enum: ['A', 'B', 'C'] }
//  { _enum: { A: AccountId, B: Balance, C: u32 } }
function _decodeEnum(value, details) {
  value.info = _types.TypeDefInfo.Enum; // not as pretty, but remain compatible with oo7 for both struct and Array types

  value.sub = Array.isArray(details) ? details.map(name => ({
    info: _types.TypeDefInfo.Plain,
    name,
    type: 'Null'
  })) : Object.entries(details).map((_ref) => {
    let [name, type] = _ref;
    return (// eslint-disable-next-line @typescript-eslint/no-use-before-define
      getTypeDef(type || 'Null', {
        name
      })
    );
  });
  return value;
} // decode a set of the form
//   { _set: { A: 0b0001, B: 0b0010, C: 0b0100 } }


function _decodeSet(value, details) {
  value.info = _types.TypeDefInfo.Set;
  value.sub = Object.entries(details).map((_ref2) => {
    let [name, index] = _ref2;
    return {
      index,
      info: _types.TypeDefInfo.Plain,
      name,
      type: name
    };
  });
  return value;
} // decode a struct, set or enum
// eslint-disable-next-line @typescript-eslint/no-unused-vars


function _decodeStruct(value, type, _) {
  const parsed = JSON.parse(type);
  const keys = Object.keys(parsed);

  if (keys.length === 1 && keys[0] === '_enum') {
    return _decodeEnum(value, parsed[keys[0]]);
  } else if (keys.length === 1 && keys[0] === '_set') {
    return _decodeSet(value, parsed[keys[0]]);
  }

  value.sub = keys.map(name => // eslint-disable-next-line @typescript-eslint/no-use-before-define
  getTypeDef(parsed[name], {
    name
  }));
  return value;
} // decode a fixed vector, e.g. [u8;32]
// eslint-disable-next-line @typescript-eslint/no-unused-vars


function _decodeFixedVec(value, type, _) {
  const [vecType, _vecLen] = type.substr(1, type.length - 2).split(';');
  const vecLen = parseInt(_vecLen.trim(), 10); // as a first round, only u8 via u8aFixed, we can add more support

  (0, _util.assert)(vecLen <= 256, "".concat(type, ": Only support for [Type; <length>], where length <= 256"));
  value.ext = {
    length: vecLen,
    type: vecType
  };
  return value;
} // decode a tuple


function _decodeTuple(value, _, subType) {
  value.sub = (0, _typeSplit.typeSplit)(subType).map(inner => // eslint-disable-next-line @typescript-eslint/no-use-before-define
  getTypeDef(inner));
  return value;
}

function hasWrapper(type, _ref3) {
  let [start, end] = _ref3;

  if (type.substr(0, start.length) !== start) {
    return false;
  }

  (0, _util.assert)(type.endsWith(end), "Expected '".concat(start, "' closing with '").concat(end, "'"));
  return true;
}

const nestedExtraction = [['[', ']', _types.TypeDefInfo.VecFixed, _decodeFixedVec], ['{', '}', _types.TypeDefInfo.Struct, _decodeStruct], ['(', ')', _types.TypeDefInfo.Tuple, _decodeTuple], // the inner for these are the same as tuple, multiple values
['BTreeMap<', '>', _types.TypeDefInfo.BTreeMap, _decodeTuple], ['Result<', '>', _types.TypeDefInfo.Result, _decodeTuple]];
const wrappedExtraction = [['Compact<', '>', _types.TypeDefInfo.Compact], ['DoubleMap<', '>', _types.TypeDefInfo.DoubleMap], ['Linkage<', '>', _types.TypeDefInfo.Linkage], ['Option<', '>', _types.TypeDefInfo.Option], ['Vec<', '>', _types.TypeDefInfo.Vec]];

function extractSubType(type, _ref4) {
  let [start, end] = _ref4;
  return type.substr(start.length, type.length - start.length - end.length);
}

function getTypeDef(_type) {
  let {
    name,
    displayName
  } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  // create the type via Type, allowing types to be sanitized
  const type = (0, _sanitize.default)(_type);
  const value = {
    info: _types.TypeDefInfo.Plain,
    displayName,
    name,
    type
  };
  const nested = nestedExtraction.find(nested => hasWrapper(type, nested));

  if (nested) {
    value.info = nested[2];
    return nested[3](value, type, extractSubType(type, nested));
  }

  const wrapped = wrappedExtraction.find(wrapped => hasWrapper(type, wrapped));

  if (wrapped) {
    value.info = wrapped[2];
    value.sub = getTypeDef(extractSubType(type, wrapped));
  }

  return value;
}