"use strict";

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

/* eslint-disable @typescript-eslint/no-use-before-define */
// 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.
const ALLOWED_BOXES = ['BTreeMap', 'Compact', 'DoubleMap', 'Linkage', 'Result', 'Option', 'Vec'];
const mappings = [// alias <T::InherentOfflineReport as InherentOfflineReport>::Inherent -> InherentOfflineReport
_alias('<T::InherentOfflineReport as InherentOfflineReport>::Inherent', 'InherentOfflineReport'), // alias TreasuryProposal from Proposal<T::AccountId, BalanceOf<T>>
_alias('Proposal<T::AccountId, BalanceOf<T>>', 'TreasuryProposal'), // <T::Balance as HasCompact>
_cleanupCompact(), // Remove all the trait prefixes
_removeTraits(), // remove PairOf<T> -> (T, T)
_removePairOf(), // remove boxing, `Box<Proposal>` -> `Proposal`
_removeWrap('Box'), // remove generics, `MisbehaviorReport<Hash, BlockNumber>` -> `MisbehaviorReport`
_removeGenerics(), // alias String -> Text (compat with jsonrpc methods)
_alias('String', 'Text'), // alias () -> Null
_alias('\\(\\)', 'Null'), // alias Vec<u8> -> Bytes
_alias('Vec<u8>', 'Bytes'), // alias &[u8] -> Bytes
_alias('&\\[u8\\]', 'Bytes'), // alias RawAddress -> Address
_alias('RawAddress', 'Address'), // alias Lookup::Source to Address (_could_ be AccountId on certain chains)
_alias('Lookup::Source', 'Address'), // alias Lookup::Target to AccountId (always the case)
_alias('Lookup::Target', 'AccountId'), // alias for grandpa, as used in polkadot
_alias('grandpa::AuthorityId', 'AuthorityId'), // specific for SessionIndex (could make this session::, but be conservative)
_alias('session::SessionIndex', 'SessionIndex'), // HACK duplication between contracts & primitives, however contracts prefixed with exec
_alias('exec::StorageKey', 'ContractStorageKey'), // Phantom
_alias('rstd::marker::PhantomData', 'PhantomData'), // flattens tuples with one value, `(AccountId)` -> `AccountId`
_flattenSingleTuple(), // converts ::Type to Type, <T as Trait<I>>::Proposal -> ::Proposal
_removeColonPrefix()]; // given a starting index, find the closing >

function _findClosing(value, start) {
  let depth = 0;

  for (let index = start; index < value.length; index++) {
    if (value[index] === '>') {
      if (!depth) {
        return index;
      }

      depth--;
    } else if (value[index] === '<') {
      depth++;
    }
  }

  throw new Error("Unable to find closing matching <> on '".concat(value, "' (start ").concat(start, ")"));
}

function _alias(src, dest) {
  return value => {
    return value.replace(new RegExp(src, 'g'), dest);
  };
}

function _cleanupCompact() {
  return value => {
    for (let index = 0; index < value.length; index++) {
      if (value[index] !== '<') {
        continue;
      }

      const end = _findClosing(value, index + 1) - 14;

      if (value.substr(end, 14) === ' as HasCompact') {
        value = "Compact<".concat(value.substr(index + 1, end - index - 1), ">");
      }
    }

    return value;
  };
}

function _flattenSingleTuple() {
  return value => {
    return value.replace(/\(([^,]*)\)/, '$1');
  };
}

function _removeColonPrefix() {
  return value => {
    return value.replace(/^::/, '');
  };
}

function _removeGenerics() {
  return value => {
    for (let index = 0; index < value.length; index++) {
      if (value[index] === '<') {
        // check against the allowed wrappers, be it Vec<..>, Option<...> ...
        const box = ALLOWED_BOXES.find(box => {
          const start = index - box.length;
          return start >= 0 && value.substr(start, box.length) === box;
        }); // we have not found anything, unwrap generic innards

        if (!box) {
          const end = _findClosing(value, index + 1);

          value = "".concat(value.substr(0, index)).concat(value.substr(end + 1));
        }
      }
    }

    return value;
  };
} // remove the PairOf wrappers


function _removePairOf() {
  return value => {
    for (let index = 0; index < value.length; index++) {
      if (value.substr(index, 7) === 'PairOf<') {
        const start = index + 7;

        const end = _findClosing(value, start);

        const type = value.substr(start, end - start);
        value = "".concat(value.substr(0, index), "(").concat(type, ",").concat(type, ")").concat(value.substr(end + 1));
      }
    }

    return value;
  };
} // remove the type traits


function _removeTraits() {
  return value => {
    return value // remove all whitespaces
    .replace(/\s/g, '') // anything `T::<type>` to end up as `<type>`
    .replace(/T::/g, '') // anything `Self::<type>` to end up as `<type>`
    .replace(/Self::/g, '') // `system::` with `` - basically we find `<T as system::Trait>`
    .replace(/system::/g, '') // replace `<T as Trait>::` (whitespaces were removed above)
    .replace(/<TasTrait>::/g, '') // replace `<T as something::Trait>::` (whitespaces were removed above)
    .replace(/<Tas[a-z]+::Trait>::/g, '') // replace `<Self as Trait>::` (whitespaces were removed above)
    .replace(/<SelfasTrait>::/g, '') // replace <Lookup as StaticLookup>
    .replace(/<LookupasStaticLookup>/g, 'Lookup') // replace `<...>::Type`
    .replace(/::Type/g, '') // replace `wasm::*` eg. `wasm::PrefabWasmModule`
    .replace(/wasm::/g, '') // `sr_std::marker::`
    .replace(/sr_std::marker::/g, '');
  };
} // remove wrapping values, i.e. Box<Proposal> -> Proposal


function _removeWrap(_check) {
  const check = "".concat(_check, "<");
  return value => {
    let index = 0;

    while (index !== -1) {
      index = value.indexOf(check);

      if (index !== -1) {
        const start = index + check.length;

        const end = _findClosing(value, start);

        value = "".concat(value.substr(0, index)).concat(value.substr(start, end - start)).concat(value.substr(end + 1));
      }
    }

    return value;
  };
}

function santize(value) {
  return mappings.reduce((result, fn) => {
    return fn(result);
  }, value).trim();
}