/* eslint-disable class-methods-use-this */
const {BigInteger} = require('jsbn')

function bigintToValue(bigint) {
  let h = bigint.toString(16)
  if (h[0] !== '-') {
    // 正数
    if (h.length % 2 === 1) h = '0' + h // 补齐到整字节
    else if (!h.match(/^[0-7]/)) h = '00' + h // 非0开头，则补一个全0字节
  } else {
    // 负数
    h = h.substr(1)

    let len = h.length
    if (len % 2 === 1) len += 1 // 补齐到整字节
    else if (!h.match(/^[0-7]/)) len += 2 // 非0开头，则补一个全0字节

    let mask = ''
    for (let i = 0; i < len; i++) mask += 'f'
    mask = new BigInteger(mask, 16)

    // 对绝对值取反，加1
    h = mask.xor(bigint).add(BigInteger.ONE)
    h = h.toString(16).replace(/^-/, '')
  }
  return h
}

class ASN1Object {
  constructor() {
    this.tlv = null
    this.t = '00'
    this.l = '00'
    this.v = ''
  }

  /**
   * 获取 der 编码比特流16进制串
   */
  getEncodedHex() {
    if (!this.tlv) {
      this.v = this.getValue()
      this.l = this.getLength()
      this.tlv = this.t + this.l + this.v
    }
    return this.tlv
  }

  getLength() {
    const n = this.v.length / 2 // 字节数
    let nHex = n.toString(16)
    if (nHex.length % 2 === 1) nHex = '0' + nHex // 补齐到整字节

    if (n < 128) {
      // 短格式，以 0 开头
      return nHex
    } else {
      // 长格式，以 1 开头
      const head = 128 + nHex.length / 2 // 1(1位) + 真正的长度占用字节数(7位) + 真正的长度
      return head.toString(16) + nHex
    }
  }

  getValue() {
    return ''
  }
}

class DERInteger extends ASN1Object {
  constructor(bigint) {
    super()

    this.t = '02' // 整型标签说明
    if (bigint) this.v = bigintToValue(bigint)
  }

  getValue() {
    return this.v
  }
}

class DERSequence extends ASN1Object {
  constructor(asn1Array) {
    super()

    this.t = '30' // 序列标签说明
    this.asn1Array = asn1Array
  }

  getValue() {
    this.v = this.asn1Array.map(asn1Object => asn1Object.getEncodedHex()).join('')
    return this.v
  }
}

/**
 * 获取 l 占用字节数
 */
function getLenOfL(str, start) {
  if (+str[start + 2] < 8) return 1 // l 以0开头，则表示短格式，只占一个字节
  return +str.substr(start + 2, 2) & 0x7f + 1 // 长格式，取第一个字节后7位作为长度真正占用字节数，再加上本身
}

/**
 * 获取 l
 */
function getL(str, start) {
  // 获取 l
  const len = getLenOfL(str, start)
  const l = str.substr(start + 2, len * 2)

  if (!l) return -1
  const bigint = +l[0] < 8 ? new BigInteger(l, 16) : new BigInteger(l.substr(2), 16)

  return bigint.intValue()
}

/**
 * 获取 v 的位置
 */
function getStartOfV(str, start) {
  const len = getLenOfL(str, start)
  return start + (len + 1) * 2
}

module.exports = {
  /**
   * ASN.1 der 编码，针对 sm2 签名
   */
  encodeDer(r, s) {
    const derR = new DERInteger(r)
    const derS = new DERInteger(s)
    const derSeq = new DERSequence([derR, derS])

    return derSeq.getEncodedHex()
  },

  /**
   * 解析 ASN.1 der，针对 sm2 验签
   */
  decodeDer(input) {
    // 结构：
    // input = | tSeq | lSeq | vSeq |
    // vSeq = | tR | lR | vR | tS | lS | vS |
    const start = getStartOfV(input, 0)

    const vIndexR = getStartOfV(input, start)
    const lR = getL(input, start)
    const vR = input.substr(vIndexR, lR * 2)

    const nextStart = vIndexR + vR.length
    const vIndexS = getStartOfV(input, nextStart)
    const lS = getL(input, nextStart)
    const vS = input.substr(vIndexS, lS * 2)

    const r = new BigInteger(vR, 16)
    const s = new BigInteger(vS, 16)

    return {r, s}
  }
}
