import unittest2
import ../stew/[base64, byteutils]

{.used.}

const TVBasePadding = [
  ["f", "Zg=="],
  ["fo", "Zm8="],
  ["foo", "Zm9v"],
  ["foob", "Zm9vYg=="],
  ["fooba", "Zm9vYmE="],
  ["foobar", "Zm9vYmFy"]
]

const TVBaseNoPadding = [
  ["f", "Zg"],
  ["fo", "Zm8"],
  ["foo", "Zm9v"],
  ["foob", "Zm9vYg"],
  ["fooba", "Zm9vYmE"],
  ["foobar", "Zm9vYmFy"]
]

suite "BASE64 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: int
    var e1 = Base64.encode(empty1)
    var e2 = Base64Url.encode(empty1)
    var e3 = Base64Pad.encode(empty1)
    var e4 = Base64UrlPad.encode(empty1)
    check:
      Base64.encode(empty1, encoded, o1) == Base64Status.Success
      Base64Url.encode(empty1, encoded, o2) == Base64Status.Success
      Base64Pad.encode(empty1, encoded, o3) == Base64Status.Success
      Base64UrlPad.encode(empty1, encoded, o4) == Base64Status.Success
      len(e1) == 0
      len(e2) == 0
      len(e3) == 0
      len(e4) == 0
      o1 == 0
      o2 == 0
      o3 == 0
      o4 == 0
    var d1 = Base64.decode("")
    var d2 = Base64Url.decode("")
    var d3 = Base64Pad.decode("")
    var d4 = Base64UrlPad.decode("")
    check:
      Base64.decode(empty2, decoded, o1) == Base64Status.Success
      Base64Url.decode(empty2, decoded, o2) == Base64Status.Success
      Base64Pad.decode(empty2, decoded, o3) == Base64Status.Success
      Base64UrlPad.decode(empty2, decoded, o4) == Base64Status.Success
      len(d1) == 0
      len(d2) == 0
      len(d3) == 0
      len(d4) == 0
      o1 == 0
      o2 == 0
      o3 == 0
      o4 == 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 = Base64.encode(buffer.toOpenArray(0, i))
      var b = Base64.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 = Base64.encode(buffer)
      var b = Base64.decode(a)
      check:
        equalMem(addr buffer[0], addr b[0], 256) == true

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

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

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

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

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

      var d1 = Base64.decode(expect)
      var d2 = newSeq[byte](Base64.decodedLength(len(expect)))
      check:
        Base64.decode(expect, d2, dlen) == Base64Status.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:
      Base64.encode([0'u8], encres, encsize) == Base64Status.Overrun
      encsize == Base64.encodedLength(1)
      Base64.decode("AA", decres, decsize) == Base64Status.Overrun
      decsize == Base64.decodedLength(2)

  test "Incorrect test":
    var decres = newSeq[byte](10)
    var decsize = 0
    check:
      Base64.decode("A", decres, decsize) == Base64Status.Incorrect
      decsize == 0
      Base64.decode("AAAAA", decres, decsize) == Base64Status.Incorrect
      decsize == 0
      Base64.decode("!", decres, decsize) == Base64Status.Incorrect
      decsize == 0
      Base64.decode("!!", decres, decsize) == Base64Status.Incorrect
      decsize == 0
      Base64.decode("AA==", decres, decsize) == Base64Status.Incorrect
      decsize == 0
      Base64.decode("_-", decres, decsize) == Base64Status.Incorrect
      decsize == 0
      Base64Url.decode("/+", decres, decsize) == Base64Status.Incorrect
      decsize == 0

  test "Decode byte sequence":
    let data = "Hello World".toBytes()
    let base64 = Base64.encode(data).toBytes()
    var decres = newSeq[byte](20)
    var decsize = 0
    check:
      Base64.decode(base64, decres, decsize) == Base64Status.Success
