#include <string.h>
#include <stdlib.h>
#include "systemConfig.h"
#include "Log.h"
#include "B64Coder.h"


namespace vendor   {
namespace samsung  {
namespace hardware {
namespace security {
namespace drk      {

static const uint8_t b64BasicTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                       "abcdefghijklmnopqrstuvwxyz"
                                       "0123456789+/";

// Base64 Encoding with URL and Filename Safe Alphabet.
static const uint8_t b64SafeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                      "abcdefghijklmnopqrstuvwxyz"
                                      "0123456789-_";

uint8_t *B64Coder::B64Encoding(uint8_t *buf, uint32_t len, B64CONVERTTYPE type)
{
    char         *b64 = NULL,
                  *p = NULL,
                   *pB64Table = NULL,
                    *buf2 = (char *)buf;
    char        t;
    uint32_t       i = 0,
                   encodeLen = (len + 2) / 3 * 4 + 1;


    if (buf == NULL) {
        return NULL;
    }

    switch (type) {
        case B64_SAFE_ALPHABET :
            pB64Table = (char *)b64SafeTable;
            break;

        case B64_BASIC :
        default :
            pB64Table = (char *)b64BasicTable;
            break;
    }

    if (encodeLen >= MAX_PROV_BUF_SIZE) {
        LOGME("len is invalid %d", encodeLen);
        return NULL;
    }

    b64 = p = (char *)calloc((encodeLen * sizeof(*p)) + 1, 1);
    if (b64 == NULL) {
        LOGME("Failed to allocate memory len=%d", encodeLen);
        return NULL;
    }

    for (i = 0; i < len / 3; i++) {
        p[0] = pB64Table[buf2[0] >> 2];
        t = buf2[0] << 4 & 0x3f;
        p[1] = pB64Table[buf2[1] >> 4 | t];
        t = buf2[1] << 2 & 0x3f;
        p[2] = pB64Table[buf2[2] >> 6 | t];
        p[3] = pB64Table[buf2[2] & 0x3f];
        p += 4;
        buf2 += 3;
    }

    if (len % 3) {
        *p++ = pB64Table[*buf2 >> 2];
        t = *buf2++ << 4 & 0x3f;
        if (len % 3 == 2) {
            *p++ = pB64Table[*buf2 >> 4 | t];
            *p++ = pB64Table[*buf2 << 2 & 0x3f];
        } else {
            *p++ = pB64Table[(int)t];
        }

        for (i = 0; i < 3 - len % 3; i++) {
            *p++ = '=';
        }
    }

    *p = 0;

    return (uint8_t *)b64;
}

uint8_t *B64Coder::B64Decoding(uint8_t *buf, uint32_t *p_len, B64CONVERTTYPE type)
{
    char *b64       = NULL,
         *p         = NULL,
         *chr       = NULL,
         *pB64Table = NULL;
    char t;
    uint32_t i         = 0,
             decodeLen = 0,
             len       = 0;

    if (buf == NULL) {
        return NULL;
    }

    switch (type) {
        case B64_SAFE_ALPHABET :
            pB64Table = (char *)b64SafeTable;
            break;

        case B64_BASIC :
        default :
            pB64Table = (char *)b64BasicTable;
            break;
    }

    len = (uint32_t)strlen((char *)buf);
    if (len == 0 || len >= MAX_PROV_BUF_SIZE || (len % 4) != 0) {
        LOGME("len is invalid %d", len);
        return NULL;
    }

    decodeLen = len / 4 * 3;
    b64 = p = (char *)calloc((decodeLen * sizeof(*p)) + 1, 1);
    if (b64 == NULL) {
        LOGME("Failed to allocate memory len=%d", decodeLen);
        return NULL;
    }

    for (i = 0; i < len; i++) {
        if (!(chr = strchr(pB64Table, (int) * buf++))) {
            break;
        }

        t = (char)(chr - pB64Table);

        switch (i % 4) {
            case 0:
                *p = t << 2;
                break;

            case 1:
                *p++ |= t >> 4;
                *p = t << 4;
                break;

            case 2:
                *p++ |= t >> 2;
                *p = t << 6;
                break;

            case 3:
                *p++ |= t;
                break;
        }
    }

    *p_len = (uint32_t)(p - b64);
    return (uint8_t *)b64;
}

}  // namespace drk
}  // namespace security
}  // namespace hardware
}  // namespace samsung
}  // namespace vendor
