var assert = require('assert')
var base58check = require('./base58check')
var ecdsa = require('./ecdsa')
var network = require('./network')
var secureRandom = require('secure-random')

var Address = require('./address')
var crypto = require('./crypto')

var sec = require('./jsbn/sec')
var ecparams = sec("secp256k1")

var BigInteger = require('./jsbn/jsbn')
var ECPointFp = require('./jsbn/ec').ECPointFp

function ECKey(D, compressed) {
  assert(D instanceof BigInteger)
  assert(D.compareTo(BigInteger.ZERO) > 0)
  assert(D.compareTo(ecparams.getN()) < 0)

  var Q = ecparams.getG().multiply(D)

  this.D = D
  this.pub = new ECPubKey(Q, compressed)
}

// Static constructors
ECKey.fromBuffer = function(buffer, compressed) {
  assert(Buffer.isBuffer(buffer))
  var D = BigInteger.fromByteArrayUnsigned(buffer)

  return new ECKey(D, compressed)
}
ECKey.fromHex = function(hex, compressed) {
  return ECKey.fromBuffer(new Buffer(hex, 'hex'), compressed)
}

ECKey.fromWIF = function(string) {
  var decode = base58check.decode(string)

  var payload = decode.payload
  if (payload.length === 33) {
    assert(payload[32] === 0x01)

    return ECKey.fromBuffer(payload.slice(0, 32), true)
  }

  assert(payload.length === 32)
  return ECKey.fromBuffer(payload, false)
}

ECKey.makeRandom = function(compressed, rng) {
  rng = rng || secureRandom

  var buffer = new Buffer(rng(32))
  var D = BigInteger.fromByteArrayUnsigned(buffer)
  D = D.mod(ecparams.getN())

  return new ECKey(D, compressed)
}

// Operations
ECKey.prototype.sign = function(hash) {
  return ecdsa.sign(hash, this.D)
}

// Export functions
ECKey.prototype.toBuffer = function() {
  var buffer = new Buffer(this.D.toByteArrayUnsigned())

  // pad out the zero bytes
  if (buffer.length < 32) {
    var padded = new Buffer(32)

    padded.fill(0)
    buffer.copy(padded, 32 - buffer.length)

    return padded
  }

  assert(buffer.length === 32)

  return buffer
}
ECKey.prototype.toHex = function() {
  return this.toBuffer().toString('hex')
}

ECKey.prototype.toWIF = function(version) {
  version = version || network.bitcoin.wif

  var buffer
  if (this.pub.compressed) {
    buffer = Buffer.concat([this.toBuffer(), new Buffer([0x01])])
  } else {
    buffer = this.toBuffer()
  }

  return base58check.encode(buffer, version)
}

//////////////////////////////////////////////////////

function ECPubKey(Q, compressed) {
  if (compressed == undefined) compressed = true
  assert(typeof compressed === 'boolean')
  assert(Q instanceof ECPointFp)

  this.compressed = compressed
  this.Q = Q
}

// Static constructors
ECPubKey.fromBuffer = function(buffer) {
  assert(Buffer.isBuffer(buffer))

  var Q = ECPointFp.decodeFrom(ecparams.getCurve(), buffer)

  var type = buffer.readUInt8(0)
  assert(type >= 0x02 || type <= 0x04)

  var compressed = (type !== 0x04)
  return new ECPubKey(Q, compressed)
}
ECPubKey.fromHex = function(hex) {
  return ECPubKey.fromBuffer(new Buffer(hex, 'hex'))
}

// Operations
ECPubKey.prototype.verify = function(hash, sig) {
  return ecdsa.verify(hash, sig, this.Q)
}

ECPubKey.prototype.getAddress = function(version) {
  return new Address(crypto.hash160(this.toBuffer()), version)
}

// Export functions
ECPubKey.prototype.toBuffer = function() {
  var buffer = new Buffer(this.Q.getEncoded(this.compressed))
  assert(buffer.length === (this.compressed ? 33 : 65))

  return buffer
}
ECPubKey.prototype.toHex = function() {
  return this.toBuffer().toString('hex')
}

module.exports = {
  ECKey: ECKey,
  ECPubKey: ECPubKey
}