import unittest2
import ../stew/base32

{.used.}

const TVBaseUpperPadding = [
  ["f", "MY======"],
  ["fo", "MZXQ===="],
  ["foo", "MZXW6==="],
  ["foob", "MZXW6YQ="],
  ["fooba", "MZXW6YTB"],
  ["foobar", "MZXW6YTBOI======"]
]

const TVBaseUpperNoPadding = [
  ["f", "MY"],
  ["fo", "MZXQ"],
  ["foo", "MZXW6"],
  ["foob", "MZXW6YQ"],
  ["fooba", "MZXW6YTB"],
  ["foobar", "MZXW6YTBOI"]
]

const TVBaseLowerPadding = [
  ["f", "my======"],
  ["fo", "mzxq===="],
  ["foo", "mzxw6==="],
  ["foob", "mzxw6yq="],
  ["fooba", "mzxw6ytb"],
  ["foobar", "mzxw6ytboi======"]
]

const TVBaseLowerNoPadding = [
  ["f", "my"],
  ["fo", "mzxq"],
  ["foo", "mzxw6"],
  ["foob", "mzxw6yq"],
  ["fooba", "mzxw6ytb"],
  ["foobar", "mzxw6ytboi"]
]

const TVHexUpperPadding = [
  ["f", "CO======"],
  ["fo", "CPNG===="],
  ["foo", "CPNMU==="],
  ["foob", "CPNMUOG="],
  ["fooba", "CPNMUOJ1"],
  ["foobar", "CPNMUOJ1E8======"]
]

const TVHexUpperNoPadding = [
  ["f", "CO"],
  ["fo", "CPNG"],
  ["foo", "CPNMU"],
  ["foob", "CPNMUOG"],
  ["fooba", "CPNMUOJ1"],
  ["foobar", "CPNMUOJ1E8"]
]

const TVHexLowerPadding = [
  ["f", "co======"],
  ["fo", "cpng===="],
  ["foo", "cpnmu==="],
  ["foob", "cpnmuog="],
  ["fooba", "cpnmuoj1"],
  ["foobar", "cpnmuoj1e8======"]
]

const TVHexLowerNoPadding = [
  ["f", "co"],
  ["fo", "cpng"],
  ["foo", "cpnmu"],
  ["foob", "cpnmuog"],
  ["fooba", "cpnmuoj1"],
  ["foobar", "cpnmuoj1e8"]
]

suite "BASE32 encoding test suite":
  test "Empty seq/string test":
    var empty1 = newSeq[byte]()
    var empty2 = ""
    var encoded = newString(16)
    var decoded = newSeq[byte](16)

    var o1, o2, o3, o4, o5, o6, o7, o8: int
    var e1 = Base32Upper.encode(empty1)
    var e2 = Base32Lower.encode(empty1)
    var e3 = Base32UpperPad.encode(empty1)
    var e4 = Base32LowerPad.encode(empty1)
    var e5 = HexBase32Upper.encode(empty1)
    var e6 = HexBase32Lower.encode(empty1)
    var e7 = HexBase32UpperPad.encode(empty1)
    var e8 = HexBase32LowerPad.encode(empty1)
    check:
      Base32Upper.encode(empty1, encoded, o1) == Base32Status.Success
      Base32Lower.encode(empty1, encoded, o2) == Base32Status.Success
      Base32UpperPad.encode(empty1, encoded, o3) == Base32Status.Success
      Base32LowerPad.encode(empty1, encoded, o4) == Base32Status.Success
      HexBase32Upper.encode(empty1, encoded, o5) == Base32Status.Success
      HexBase32Lower.encode(empty1, encoded, o6) == Base32Status.Success
      HexBase32UpperPad.encode(empty1, encoded, o7) == Base32Status.Success
      HexBase32LowerPad.encode(empty1, encoded, o8) == Base32Status.Success
      len(e1) == 0
      len(e2) == 0
      len(e3) == 0
      len(e4) == 0
      len(e5) == 0
      len(e6) == 0
      len(e7) == 0
      len(e8) == 0
      o1 == 0
      o2 == 0
      o3 == 0
      o4 == 0
      o5 == 0
      o6 == 0
      o7 == 0
      o8 == 0
    var d1 = Base32Upper.decode("")
    var d2 = Base32Lower.decode("")
    var d3 = Base32UpperPad.decode("")
    var d4 = Base32LowerPad.decode("")
    var d5 = HexBase32Upper.decode("")
    var d6 = HexBase32Lower.decode("")
    var d7 = HexBase32UpperPad.decode("")
    var d8 = HexBase32LowerPad.decode("")
    check:
      Base32Upper.decode(empty2, decoded, o1) == Base32Status.Success
      Base32Lower.decode(empty2, decoded, o2) == Base32Status.Success
      Base32UpperPad.decode(empty2, decoded, o3) == Base32Status.Success
      Base32LowerPad.decode(empty2, decoded, o4) == Base32Status.Success
      HexBase32Upper.decode(empty2, decoded, o5) == Base32Status.Success
      HexBase32Lower.decode(empty2, decoded, o6) == Base32Status.Success
      HexBase32UpperPad.decode(empty2, decoded, o7) == Base32Status.Success
      HexBase32LowerPad.decode(empty2, decoded, o8) == Base32Status.Success
      len(d1) == 0
      len(d2) == 0
      len(d3) == 0
      len(d4) == 0
      len(d5) == 0
      len(d6) == 0
      len(d7) == 0
      len(d8) == 0
      o1 == 0
      o2 == 0
      o3 == 0
      o4 == 0
      o5 == 0
      o6 == 0
      o7 == 0
      o8 == 0

  test "Zero test":
    var s = newString(256)
    for i in 0..255:
      s[i] = 'A'
    var buffer: array[256, byte]
    for i in 0..255:
      var a = Base32.encode(buffer.toOpenArray(0, i))
      var b = Base32.decode(a)
      check b == buffer[0..i]

  test "Leading zero test":
    var buffer: array[256, byte]
    for i in 0..255:
      buffer[255] = byte(i)
      var a = Base32.encode(buffer)
      var b = Base32.decode(a)
      check:
        equalMem(addr buffer[0], addr b[0], 256) == true

  test "BASE32 uppercase padding test vectors":
    for item in TVBaseUpperPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = Base32UpperPad.encode(plain)
      var e2 = newString(Base32UpperPad.encodedLength(len(plain)))
      check:
        Base32UpperPad.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = Base32UpperPad.decode(expect)
      var d2 = newSeq[byte](Base32UpperPad.decodedLength(len(expect)))
      check:
        Base32UpperPad.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "BASE32 lowercase padding test vectors":
    for item in TVBaseLowerPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = Base32LowerPad.encode(plain)
      var e2 = newString(Base32LowerPad.encodedLength(len(plain)))
      check:
        Base32LowerPad.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = Base32LowerPad.decode(expect)
      var d2 = newSeq[byte](Base32LowerPad.decodedLength(len(expect)))
      check:
        Base32LowerPad.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "BASE32 uppercase no-padding test vectors":
    for item in TVBaseUpperNoPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = Base32Upper.encode(plain)
      var e2 = newString(Base32Upper.encodedLength(len(plain)))
      check:
        Base32Upper.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = Base32Upper.decode(expect)
      var d2 = newSeq[byte](Base32Upper.decodedLength(len(expect)))
      check:
        Base32Upper.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "BASE32 lowercase no-padding test vectors":
    for item in TVBaseLowerNoPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = Base32Lower.encode(plain)
      var e2 = newString(Base32Lower.encodedLength(len(plain)))
      check:
        Base32Lower.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = Base32Lower.decode(expect)
      var d2 = newSeq[byte](Base32Lower.decodedLength(len(expect)))
      check:
        Base32Lower.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "HEX-BASE32 uppercase padding test vectors":
    for item in TVHexUpperPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = HexBase32UpperPad.encode(plain)
      var e2 = newString(HexBase32UpperPad.encodedLength(len(plain)))
      check:
        HexBase32UpperPad.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = HexBase32UpperPad.decode(expect)
      var d2 = newSeq[byte](HexBase32UpperPad.decodedLength(len(expect)))
      check:
        HexBase32UpperPad.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "HEX-BASE32 lowercase padding test vectors":
    for item in TVHexLowerPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = HexBase32LowerPad.encode(plain)
      var e2 = newString(HexBase32LowerPad.encodedLength(len(plain)))
      check:
        HexBase32LowerPad.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = HexBase32LowerPad.decode(expect)
      var d2 = newSeq[byte](HexBase32LowerPad.decodedLength(len(expect)))
      check:
        HexBase32LowerPad.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "HEX-BASE32 uppercase no-padding test vectors":
    for item in TVHexUpperNoPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = HexBase32Upper.encode(plain)
      var e2 = newString(HexBase32Upper.encodedLength(len(plain)))
      check:
        HexBase32Upper.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = HexBase32Upper.decode(expect)
      var d2 = newSeq[byte](HexBase32Upper.decodedLength(len(expect)))
      check:
        HexBase32Upper.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "HEX-BASE32 lowercase no-padding test vectors":
    for item in TVHexLowerNoPadding:
      let plain = cast[seq[byte]](item[0])
      let expect = item[1]
      var elen = 0
      var dlen = 0

      var e1 = HexBase32Lower.encode(plain)
      var e2 = newString(HexBase32Lower.encodedLength(len(plain)))
      check:
        HexBase32Lower.encode(plain, e2, elen) == Base32Status.Success
      e2.setLen(elen)
      check:
        e1 == expect
        e2 == expect

      var d1 = HexBase32Lower.decode(expect)
      var d2 = newSeq[byte](HexBase32Lower.decodedLength(len(expect)))
      check:
        HexBase32Lower.decode(expect, d2, dlen) == Base32Status.Success
      d2.setLen(dlen)
      check:
        d1 == plain
        d2 == plain

  test "Buffer Overrun test":
    var encres = ""
    var encsize = 0
    var decres: seq[byte] = @[]
    var decsize = 0
    check:
      Base32.encode([0'u8], encres, encsize) == Base32Status.Overrun
      encsize == Base32.encodedLength(1)
      Base32.decode("AA", decres, decsize) == Base32Status.Overrun
      decsize == Base32.decodedLength(2)

  test "Incorrect test":
    var decres = newSeq[byte](10)
    var decsize = 0
    check:
      Base32.decode("A", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      Base32.decode("AAA", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      Base32.decode("AAAAAA", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      Base32Upper.decode("aa", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      Base32Upper.decode("11", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      Base32Lower.decode("AA", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      Base32Lower.decode("11", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      HexBase32Upper.decode("aa", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      HexBase32Upper.decode("WW", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      HexBase32Lower.decode("AA", decres, decsize) == Base32Status.Incorrect
      decsize == 0
      HexBase32Lower.decode("ww", decres, decsize) == Base32Status.Incorrect
      decsize == 0
