text: 'use strict';
  
  // Important references for schnorr implementation
  // https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/2019-05-15-schnorr.md
  // https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/2019-11-15-schnorrmultisig.md#wallet-implementation-guidelines
  const secp256k1 = require('secp256k1-wasm');
  var BN = require('./bn');
  var Point = require('./point');
  var Signature = require('./signature');
  var PublicKey = require('../publickey');
  var Random = require('./random');
  var Hash = require('./hash');
  var BufferUtil = require('../util/buffer');
  var _ = require('lodash');
  var $ = require('../util/preconditions');
  
  var Schnorr = function Schnorr(obj) {
    if (!(this instanceof Schnorr)) {
      return new Schnorr(obj);
    }
    if (obj) {
      this.set(obj);
    }
  };
  
  /**
     * Function written to ensure r part of signature is at least 32 bytes, when converting 
     * from a BN to type Buffer.
     * The BN type naturally cuts off leading zeros, e.g.
     * <BN: 4f92d8094f710bc11b93935ac157730dda26c5c2a856650dbd8ebcd730d2d4> 31 bytes
     * Buffer <00 4f 92 d8 09 4f 71 0b c1 1b 93 93 5a c1 57 73 0d da 26 c5 c2 a8 56 65 0d bd 8e bc d7 30 d2 d4> 32 bytes
     * Both types are equal, however Schnorr signatures must be a minimum of 64 bytes.
     * In a previous implementation of this schnorr module, was resulting in 63 byte signatures. 
     * (Although it would have been verified, it's proper to ensure the min requirement)
     * @param {*} s BN
     * @return {Buffer}
     */
    function getrBuffer(r) {
  
      let rNaturalLength = r.toBuffer().length;
  
  
      if (rNaturalLength < 32) {
        return r.toBuffer({size: 32});
      }
      return r.toBuffer();
    }
  
    /**
     * Function written to ensure s part of signature is at least 32 bytes, when converting 
     * from a BN to type Buffer.
     * The BN type naturally cuts off leading zeros, e.g.
     * <BN: 4f92d8094f710bc11b93935ac157730dda26c5c2a856650dbd8ebcd730d2d4> 31 bytes
     * Buffer <00 4f 92 d8 09 4f 71 0b c1 1b 93 93 5a c1 57 73 0d da 26 c5 c2 a8 56 65 0d bd 8e bc d7 30 d2 d4> 32 bytes
     * Both types are equal, however Schnorr signatures must be a minimum of 64 bytes.
     * In a previous implementation of this schnorr module, was resulting in 63 byte signatures. 
     * (Although it would have been verified, it's proper to ensure the min requirement)
     * @param {*} s BN
     * @return {Buffer}
     */
    function getsBuffer(s) {
      let sNaturalLength = s.toBuffer().length;
  
  
      if (sNaturalLength < 32) {
        return s.toBuffer({size: 32});
      }
      return s.toBuffer();
    }
  
  /* jshint maxcomplexity: 9 */
  Schnorr.prototype.set = function(obj) {
      this.hashbuf = obj.hashbuf || this.hashbuf;
      this.endian = obj.endian || this.endian; // the endianness of hashbuf
      this.privkey = obj.privkey || this.privkey;
      this.pubkey = obj.pubkey || (this.privkey ? this.privkey.publicKey : this.pubkey);
      this.sig = obj.sig || this.sig;
      this.verified = obj.verified || this.verified;
      return this;
  };
  
  Schnorr.prototype.privkey2pubkey = function() {
      this.pubkey = this.privkey.toPublicKey();
  };
  
  Schnorr.prototype.toPublicKey = function() {
    return this.privkey.toPublicKey();
  }
  
  Schnorr.prototype.sign = function() {
      var hashbuf = this.hashbuf;
      var privkey = this.privkey;
      var d = privkey.bn;
    
      $.checkState(hashbuf && privkey && d, new Error('invalid parameters'));
      $.checkState(BufferUtil.isBuffer(hashbuf) && hashbuf.length === 32, new Error('hashbuf must be a 32 byte buffer'));
  
      var e = BN.fromBuffer(hashbuf, this.endian ? {
        endian: this.endian
      } : undefined);
      
      var obj = this._findSignature(d, e);
      obj.compressed = this.pubkey.compressed;
      obj.isSchnorr = true;
      
      this.sig = new Signature(obj);
      return this;
  };
  
  /**
   * Schnorr implementation used from bitcoinabc at https://reviews.bitcoinabc.org/D2501
   */
  Schnorr.prototype._findSignature = function(d, e) {
      // d is the private key;
      // e is the message to be signed
  
      let n = Point.getN();
      let G = Point.getG();
  
      $.checkState(!d.lte(new BN(0)), new Error('privkey out of field of curve'));
      $.checkState(!d.gte(n), new Error('privkey out of field of curve'));
    
      
      let k = this.nonceFunctionRFC6979(d.toBuffer({ size: 32 }), e.toBuffer({ size: 32 }));
  
      let P = G.mul(d);
      let R = G.mul(k);
  
      // Find deterministic k
      if(R.hasSquare()) {
        k = k;
      } else {
        k = n.sub(k);
      }
      
      let r = R.getX();
      let e0 = BN.fromBuffer(Hash.sha256(Buffer.concat([getrBuffer(r), Point.pointToCompressed(P), e.toBuffer({ size: 32 })])));
      
      let s = ((e0.mul(d)).add(k)).mod(n);
  
      return {
        r: r,
        s: s
      };
    };
    
  
    Schnorr.prototype.sigError = function() {
      if (!BufferUtil.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) {
        return 'hashbuf must be a 32 byte buffer';
      }
  
      let sigLength = getrBuffer(this.sig.r).length + getsBuffer(this.sig.s).length;
      
      if(!(sigLength === 64 || sigLength === 65)) {
        return 'signature must be a 64 byte or 65 byte array';
      } 
  
      let hashbuf = this.endian === 'little' ? BufferUtil.reverse(this.hashbuf) : this.hashbuf
      
      let P = this.pubkey.point;
      let G = Point.getG();
  
      if(P.isInfinity()) return true;
      
      let r = this.sig.r;
      let s = this.sig.s;
  
      let p = new BN('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 'hex');
      let n = Point.getN();
  
      if(r.gte(p) || s.gte(n)) {
        // ("Failed >= condition") 
        return true;
      }
      
      let Br = getrBuffer(this.sig.r);
      let Bp = Point.pointToCompressed(P);
      
      let hash = Hash.sha256(Buffer.concat([Br, Bp, hashbuf]));
      let e = BN.fromBuffer(hash, 'big').umod(n);
      
      let sG = G.mul(s);
      let eP = P.mul(n.sub(e));
      let R = sG.add(eP);
      
      if(R.isInfinity() || !R.hasSquare() || !R.getX().eq(r)) {
        return true;
      } 
      return false;
    };
  
    Schnorr.prototype.verify = function() {
  
      if (!this.sigError()) {
        this.verified = true;
      } else {
        this.verified = false;
      }
      return this;
    };
  
    /**
     * RFC6979 deterministic nonce generation used from https://reviews.bitcoinabc.org/D2501
     * @param {Buffer} privkeybuf 
     * @param {Buffer} msgbuf 
     * @return k {BN}
     */
    Schnorr.prototype.nonceFunctionRFC6979 = function(privkey, msgbuf) {
      let V = Buffer.from("0101010101010101010101010101010101010101010101010101010101010101","hex");
      let K = Buffer.from("0000000000000000000000000000000000000000000000000000000000000000","hex");
  
      let blob = Buffer.concat([privkey, msgbuf, Buffer.from("", "ascii"), Buffer.from("Schnorr+SHA256  ", "ascii")]);
  
      K = Hash.sha256hmac(Buffer.concat([V, Buffer.from('00', 'hex'), blob]), K);
      V = Hash.sha256hmac(V,K); 
  
      K = Hash.sha256hmac(Buffer.concat([V,Buffer.from('01','hex'), blob]), K);
      V = Hash.sha256hmac(V,K);
  
      let k = new BN(0);
      let T;
      while (true) {
        V = Hash.sha256hmac(V,K);
        T = BN.fromBuffer(V);
  
        k = T;
        $.checkState(V.length >= 32, "V length should be >= 32");
        if (k.gt(new BN(0)) && k.lt(Point.getN())) {
          break;
        }
        K = Hash.sha256hmac(Buffer.concat([V, Buffer.from("00", 'hex')]), K);
        V = Hash.hmac(Hash.sha256, V, K);
      }
      return k;
    }
  
    
    Schnorr.sign = function(hashbuf, privkey, endian) {
      /*
      return Schnorr().set({
        hashbuf: hashbuf,
        endian: endian,
        privkey: privkey
      }).sign().sig;
      */
      //console.log(":::sighash:", hashbuf.toString("hex"))
      hashbuf = hashbuf.toString("hex");
      let result = secp256k1.schnorrsig_sign(privkey.toString(), hashbuf);
      
      let sig = Signature.fromString(result.sig);
      let b = sig.toBuffer("schnorr").toString("hex");
      if(b.length < 128)
        throw new Error(`Invalid Signature\nsecp256k1 sig:${result.sig}\nSignature.fromString:${b}`)
      //console.log("schnorrsig_sign sig.length:", result.sig.length, b.length, b)
      sig.compressed = true;
      return sig;
    }
    
    Schnorr.verify = function(hashbuf, sig, pubkey, endian) {
      return true//TODO
      /*
      return Schnorr().set({
        hashbuf: hashbuf,
        endian: endian,
        sig: sig,
        pubkey: pubkey
      }).verify().verified;
      */
    };
  
    /*
    let str = "B341907592EE417F7433FDBE357B44B6837E2425FBE5CC1DE201585F07CB892B00DBE36182F03F0784FAA4D00F0384749E7002CE56DFF1FDED79A14C47399624";
    let buf = Buffer.from(str, "hex");
    let obj = Signature.parseSchnorrEncodedSig(buf);
    let _sig = new Signature();
    _sig.r = obj.r;
    _sig.s = obj.s;
    _sig.isSchnorr = true;
    let sig = Signature.fromString(str);
    let s = sig.toBuffer("schnorr").toString("hex");
    let s2 = _sig.toBuffer("schnorr").toString("hex");
    let cc = _sig.s.toBuffer({size1:32})
    console.log("s:", _sig.s, cc.toString("hex").toUpperCase(), cc.length)
    //console.log("sig:", s.length, buf.length, buf, obj, _sig, s2, s)
    */
  
    module.exports = Schnorr;