'use strict';

var constants = require('./constants');
var bs58 = require('bs58');

exports = module.exports = multibase;
exports.encode = encode;
exports.decode = decode;
exports.isEncoded = isEncoded;

var errNotSupported = new Error('Unsupported encoding');

// returns a new buffer with the multibase varint+code`
function multibase(nameOrCode, buf) {
  if (!buf) {
    throw new Error('requires an encoded buffer');
  }
  var code = getCode(nameOrCode);
  var codeBuf = new Buffer(code);

  var name = getName(nameOrCode);
  validEncode(name, buf);
  return Buffer.concat([codeBuf, buf]);
}

function encode(nameOrCode, buf) {
  var name = getName(nameOrCode);

  var encode = void 0;

  switch (name) {
    case 'base58btc':
      {
        encode = function encode(buf) {
          return new Buffer(bs58.encode(buf));
        };
      }break;
    default:
      throw errNotSupported;
  }

  return multibase(name, encode(buf));
}

// receives a buffer or string encoded with multibase header
// decodes it and returns an object with the decoded buffer
// and the encoded type { base: <name>, data: <buffer> }
function decode(bufOrString) {
  if (Buffer.isBuffer(bufOrString)) {
    bufOrString = bufOrString.toString();
  }

  var code = bufOrString.substring(0, 1);
  bufOrString = bufOrString.substring(1, bufOrString.length);

  if (typeof bufOrString === 'string') {
    bufOrString = new Buffer(bufOrString);
  }

  var decode = void 0;

  switch (code) {
    case 'z':
      {
        decode = function decode(buf) {
          return new Buffer(bs58.decode(buf.toString()));
        };
      }break;
    default:
      throw errNotSupported;
  }

  return decode(bufOrString);
}

function isEncoded(bufOrString) {
  if (Buffer.isBuffer(bufOrString)) {
    bufOrString = bufOrString.toString();
  }

  var code = bufOrString.substring(0, 1);
  try {
    var name = getName(code);
    return name;
  } catch (err) {
    return false;
  }
}

function validNameOrCode(nameOrCode) {
  var err = new Error('Unsupported encoding');

  if (!constants.names[nameOrCode] && !constants.codes[nameOrCode]) {
    throw err;
  }
}

function validEncode(name, buf) {
  var decode = void 0;

  switch (name) {
    case 'base58btc':
      {
        decode = bs58.decode;
        buf = buf.toString(); // bs58 only operates in strings bs58 strings
      }break;
    default:
      throw errNotSupported;
  }

  decode(buf);
}

function getCode(nameOrCode) {
  validNameOrCode(nameOrCode);

  var code = nameOrCode;

  if (constants.names[nameOrCode]) {
    code = constants.names[nameOrCode];
  }

  return code;
}

function getName(nameOrCode) {
  validNameOrCode(nameOrCode);

  var name = nameOrCode;

  if (constants.codes[nameOrCode]) {
    name = constants.codes[nameOrCode];
  }

  return name;
}