'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var multiformats = require('multiformats');
var base64 = require('multiformats/bases/base64');
var cborg = require('cborg');
var cborgJson = require('cborg/json');

function _interopNamespace(e) {
  if (e && e.__esModule) return e;
  var n = Object.create(null);
  if (e) {
    Object.keys(e).forEach(function (k) {
      if (k !== 'default') {
        var d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: function () { return e[k]; }
        });
      }
    });
  }
  n["default"] = e;
  return Object.freeze(n);
}

var cborgJson__namespace = /*#__PURE__*/_interopNamespace(cborgJson);

function cidEncoder(obj) {
  if (obj.asCID !== obj) {
    return null;
  }
  const cid = multiformats.CID.asCID(obj);
  if (!cid) {
    return null;
  }
  const cidString = cid.toString();
  return [
    new cborg.Token(cborg.Type.map, Infinity, 1),
    new cborg.Token(cborg.Type.string, '/', 1),
    new cborg.Token(cborg.Type.string, cidString, cidString.length),
    new cborg.Token(cborg.Type.break, undefined, 1)
  ];
}
function bytesEncoder(bytes) {
  const bytesString = base64.base64.encode(bytes).slice(1);
  return [
    new cborg.Token(cborg.Type.map, Infinity, 1),
    new cborg.Token(cborg.Type.string, '/', 1),
    new cborg.Token(cborg.Type.map, Infinity, 1),
    new cborg.Token(cborg.Type.string, 'bytes', 5),
    new cborg.Token(cborg.Type.string, bytesString, bytesString.length),
    new cborg.Token(cborg.Type.break, undefined, 1),
    new cborg.Token(cborg.Type.break, undefined, 1)
  ];
}
function undefinedEncoder() {
  throw new Error('`undefined` is not supported by the IPLD Data Model and cannot be encoded');
}
function numberEncoder(num) {
  if (Number.isNaN(num)) {
    throw new Error('`NaN` is not supported by the IPLD Data Model and cannot be encoded');
  }
  if (num === Infinity || num === -Infinity) {
    throw new Error('`Infinity` and `-Infinity` is not supported by the IPLD Data Model and cannot be encoded');
  }
  return null;
}
const encodeOptions = {
  typeEncoders: {
    Object: cidEncoder,
    Uint8Array: bytesEncoder,
    Buffer: bytesEncoder,
    undefined: undefinedEncoder,
    number: numberEncoder
  }
};
class DagJsonTokenizer extends cborgJson__namespace.Tokenizer {
  constructor(data, options) {
    super(data, options);
    this.tokenBuffer = [];
  }
  done() {
    return this.tokenBuffer.length === 0 && super.done();
  }
  _next() {
    if (this.tokenBuffer.length > 0) {
      return this.tokenBuffer.pop();
    }
    return super.next();
  }
  next() {
    const token = this._next();
    if (token.type === cborg.Type.map) {
      const keyToken = this._next();
      if (keyToken.type === cborg.Type.string && keyToken.value === '/') {
        const valueToken = this._next();
        if (valueToken.type === cborg.Type.string) {
          const breakToken = this._next();
          if (breakToken.type !== cborg.Type.break) {
            throw new Error('Invalid encoded CID form');
          }
          this.tokenBuffer.push(valueToken);
          return new cborg.Token(cborg.Type.tag, 42, 0);
        }
        if (valueToken.type === cborg.Type.map) {
          const innerKeyToken = this._next();
          if (innerKeyToken.type === cborg.Type.string && innerKeyToken.value === 'bytes') {
            const innerValueToken = this._next();
            if (innerValueToken.type === cborg.Type.string) {
              for (let i = 0; i < 2; i++) {
                const breakToken = this._next();
                if (breakToken.type !== cborg.Type.break) {
                  throw new Error('Invalid encoded Bytes form');
                }
              }
              const bytes = base64.base64.decode(`m${ innerValueToken.value }`);
              return new cborg.Token(cborg.Type.bytes, bytes, innerValueToken.value.length);
            }
            this.tokenBuffer.push(innerValueToken);
          }
          this.tokenBuffer.push(innerKeyToken);
        }
        this.tokenBuffer.push(valueToken);
      }
      this.tokenBuffer.push(keyToken);
    }
    return token;
  }
}
const decodeOptions = {
  allowIndefinite: false,
  allowUndefined: false,
  allowNaN: false,
  allowInfinity: false,
  allowBigInt: true,
  strict: true,
  useMaps: false,
  tags: []
};
decodeOptions.tags[42] = multiformats.CID.parse;
const name = 'dag-json';
const code = 297;
const encode = node => cborgJson__namespace.encode(node, encodeOptions);
const decode = data => {
  const options = Object.assign(decodeOptions, { tokenizer: new DagJsonTokenizer(data, decodeOptions) });
  return cborgJson__namespace.decode(data, options);
};

exports.code = code;
exports.decode = decode;
exports.encode = encode;
exports.name = name;
