var crypto = require('crypto');
var bignum = require('./Bignum');

var globalBuffer = new Buffer(1024);
var zerobuf = new Buffer(0);
var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
var ALPHABET_ZERO = ALPHABET[0];
var ALPHABET_BUF = new Buffer(ALPHABET, 'ascii');
var ALPHABET_INV = {};
for(var i=0; i < ALPHABET.length; i++) {
  ALPHABET_INV[ALPHABET[i]] = i;
};

// Vanilla Base58 Encoding
var base58 = {
  encode: function(buf) {
    var str;
    var x = bignum.fromBuffer(buf);
    var r;

    if(buf.length < 512) {
      str = globalBuffer;
    } else {
      str = new Buffer(buf.length << 1);
    }
    var i = str.length - 1;
    while(x.gt(0)) {
      r = x.mod(58);
      x = x.div(58);
      str[i] = ALPHABET_BUF[r.toNumber()];
      i--;
    }

    // deal with leading zeros
    var j=0;
    while(buf[j] == 0) {
      str[i] = ALPHABET_BUF[0];
      j++; i--;
    }

    return str.slice(i+1,str.length).toString('ascii');
  },

  decode: function(str) {
    if(str.length == 0) return zerobuf;
    var answer = bignum(0);
    for(var i=0; i<str.length; i++) {
    answer.mul(58)
      answer = answer.mul(58);
      answer = answer.add(ALPHABET_INV[str[i]]);
    };
    var i = 0;
    while(i < str.length && str[i] == ALPHABET_ZERO) {
      i++;
    }
    if(i > 0) {
      var zb = new Buffer(i);
      zb.fill(0);
      if(i == str.length) return zb;
      answer = answer.toBuffer();
      return Buffer.concat([zb, answer], i+answer.length);
    } else {
      return answer.toBuffer();
    }
  },
};

// Base58Check Encoding
function sha256(data) {
  return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary');
};

function doubleSHA256(data) {
  return sha256(sha256(data));
};

var base58Check = {
  encode: function(buf) {
    var checkedBuf = new Buffer(buf.length + 4);
    var hash = doubleSHA256(buf);
    buf.copy(checkedBuf);
    hash.copy(checkedBuf, buf.length);
    return base58.encode(checkedBuf);
  },

  decode: function(s) {
    var buf = base58.decode(s);
    if (buf.length < 4) {
      throw new Error("invalid input: too short");
    }

    var data = buf.slice(0, -4);
    var csum = buf.slice(-4);

    var hash = doubleSHA256(data);
    var hash4 = hash.slice(0, 4);

    if (csum.toString() != hash4.toString()) {
      throw new Error("checksum mismatch");
    }

    return data;
  },
};

// if you frequently do base58 encodings with data larger
// than 512 bytes, you can use this method to expand the
// size of the reusable buffer
exports.setBuffer = function(buf) {
  globalBuffer = buf;
};

exports.base58 = base58;
exports.base58Check = base58Check;
exports.encode = base58.encode;
exports.decode = base58.decode;