#include <stdlib.h>
#include <string.h>
#include "Bytes.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-_";

Bytes::Bytes()
{
    data = NULL;
    data_pos = 0;
    data_last_pos = 0;
}

Bytes::~Bytes()
{
    empty();
}

int32_t Bytes::set(uint8_t *src, uint32_t length)
{
    uint32_t temp_pos;

    if ((length > max_data_len) || (data_pos > max_data_len)) {
        return -1;
    }

    if ((data_pos + length) > max_data_len) {
        return -1;
    }

    if (data == NULL) {
        data_pos = 0;
        data = (uint8_t *)calloc(length + 1, sizeof(uint8_t));
        if (data == NULL) {
            return -2;
        }
        data_last_pos = length;
    } else {
        temp_pos = data_pos + length;
        if (temp_pos > data_last_pos) {
            data = (uint8_t *)realloc(data, temp_pos + 1);
            if (data == NULL) {
                return -2;
            }
            data_last_pos = temp_pos;
        }
    }

    if (src != NULL) {
        memcpy(data + data_pos, src, length);
        data_pos += length;
    }
    return 0;
}

int32_t Bytes::set(uint32_t length)
{
    return set(NULL, length);
}

int32_t  Bytes::b64Decode(B64_CONVERT_TYPE type, Bytes& out)
{
    char *b64 = NULL,
          *p = NULL,
           *chr = NULL,
            *pB64Table = NULL,
             *buf = (char *)data;
    char  t;
    uint32_t i = 0,
             decodeLen = ((data_pos / 4) * 3),
             len = data_pos;

    if (buf == NULL || len == 0 || (len % 4) != 0) {
        return -1;
    }

    switch (type) {
        case BASE64_SAFE_ALPHABET :
            pB64Table = (char *)b64SafeTable;
            break;

        case BASE64_BASIC :
        default :
            pB64Table = (char *)b64BasicTable;
            break;
    }

    b64 = p = (char *)calloc(decodeLen + 1, 1);
    if (b64 == NULL) {
        return -2;
    }

    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;
        }
    }


    out.set((uint8_t *)b64, (uint32_t)(p - b64));

    if (b64) {
        memset(b64, 0x0, decodeLen);
        free(b64);
    }
    return 0;
}

int32_t Bytes::b64Encode(B64_CONVERT_TYPE type, Bytes& out)
{
    char        *b64 = NULL,
                 *p = NULL,
                  *pB64Table = NULL,
                   *buf2 = (char *)data;
    char        t;
    uint32_t    i = 0,
                encodeLen = ((data_pos + 2) / 3) * 4;


    if (buf2 == NULL) {
        return -1;
    }

    switch (type) {
        case BASE64_SAFE_ALPHABET :
            pB64Table = (char *)b64SafeTable;
            break;

        case BASE64_BASIC :
        default :
            pB64Table = (char *)b64BasicTable;
            break;
    }

    b64 = p = (char *)calloc(encodeLen + 1, 1);
    if (b64 == NULL) {
        return -2;
    }

    for (i = 0; i < (data_pos / 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 (data_pos % 3) {
        *p++ = pB64Table[*buf2 >> 2];
        t = *buf2++ << 4 & 0x3f;
        if (data_pos % 3 == 2) {
            *p++ = pB64Table[*buf2 >> 4 | t];
            *p++ = pB64Table[*buf2 << 2 & 0x3f];
        } else {
            *p++ = pB64Table[(int)t];
        }

        for (i = 0; i < 3 - data_pos % 3; i++) {
            *p++ = '=';
        }
    }

    out.set((uint8_t *)b64, encodeLen);
    if (b64) {
        memset(b64, 0x0, encodeLen);
        free(b64);
    }
    return 0;
}

int32_t Bytes::setAt(uint32_t index, uint8_t value)
{
    if (index >= data_pos) {
        return -1;
    }
    data[index] = value;
    return 0;
}

uint8_t Bytes::getAt(uint32_t index)
{
    if (index >= data_pos) {
        return 0;
    }
    return data[index];
}

void Bytes::lowerCase()
{
    uint32_t i = 0;

    for (i = 0; i < data_pos; i++) {
        if (data[ i ] == '\0') {
            break;
        }

        if (data[ i ] >= 'A' && data[ i ] <= 'Z') {
            data[ i ] = data[ i ] + 32;
        }
    }
}

void Bytes::delAtEnd()
{
    data[data_pos - 1] = 0x0;
    data_pos--;
}

void Bytes::empty()
{
    if (data) {
        memset(data, 0x0, data_last_pos);
        free(data);
        data = NULL;
    }
    data_pos = 0;
    data_last_pos = 0;
}

Bytes& Bytes::operator=(Bytes& in)
{
    if (this != &in) {
        set((uint8_t *)in, in.length());
    }
    return *this;
}

}  // namespace drk
}  // namespace security
}  // namespace hardware
}  // namespace samsung
}  // namespace vendor

