#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include "log.h"
#include "crypto.h"

const char* EvpBase64Encode(const char* pSrc)
{
    EVP_ENCODE_CTX  ctx;
    int enSize = 0;
    int sLen, size;
    char* pEncode = NULL;
    
    if(pSrc == NULL || strlen(pSrc) == 0)
    {
        return (NULL);
    }

    sLen = strlen(pSrc);
    size = ((sLen / 3) * 4) + 4 + (sLen / 64) + sLen % 64;

    pEncode = (char*)malloc(size);
    memset(pEncode, 0, size);

    EVP_EncodeInit(&ctx);
    EVP_EncodeUpdate(&ctx, pEncode, &enSize, pSrc, strlen(pSrc));
    EVP_EncodeFinal(&ctx, pEncode + enSize, &enSize);

//    fprintf(stdout, "Src: \n[%s]\n", pSrc);
//    fprintf(stdout, "Base64(%d --> %d | %d) Bytes: \n[%s]\n", sLen, size, strlen(pEncode), pEncode);

    return pEncode;
}

const char* EvpBase64Decode(const char* pBase64)
{
    EVP_ENCODE_CTX  ctx;
    int enSize = 0;
    int size = 0;
    char *pDecode = NULL;

    if(pBase64 == NULL || strlen(pBase64) == 0)
    {
        return (NULL);
    }
    
    size = strlen(pBase64);
    pDecode = (char*)malloc(size);
    memset(pDecode, 0, size);

    EVP_DecodeInit(&ctx);
    EVP_DecodeUpdate(&ctx, pDecode, &enSize, pBase64, strlen(pBase64));
    EVP_DecodeFinal(&ctx, pDecode + enSize, &enSize);

//    fprintf(stdout, "Decode(%d --> %d) Bytes: \n[%s]\n", size, strlen(pDecode), pDecode);

    return pDecode;
}

const char* EvpBase64EncodeNoAlign(const char* pSrc)
{
    int size, sLen;
    char* pEncode = NULL;
    
    if(pSrc == NULL || strlen(pSrc) == 0)
    {
        return (NULL);
    }

    sLen = strlen(pSrc);
    size = ((sLen / 3) * 4) + 4 + (sLen / 64) + sLen % 64;

    pEncode = (char*)malloc(size);
    memset(pEncode, 0, size);

    EVP_EncodeBlock(pEncode, (const unsigned char *)pSrc, sLen);
//    fprintf(stdout, "Src: \n[%s]\n", pSrc);
//    fprintf(stdout, "Base64(%d --> %d | %d) Bytes: \n[%s]\n", sLen, size, strlen(pEncode), pEncode);

    return pEncode;
}

const char* EvpBase64EncodeNoAlignV2(unsigned char* pSrc, int sLen)
{
    int size;
    char* pEncode = NULL;
    
    if(pSrc == NULL || sLen <= 0)
    {
        return (NULL);
    }

    size = ((sLen / 3) * 4) + 4 + (sLen / 64) + sLen % 64;

    pEncode = (char*)malloc(size);
    memset(pEncode, 0, size);

    EVP_EncodeBlock(pEncode, (const unsigned char *)pSrc, sLen);
//    fprintf(stdout, "Src: \n[%s]\n", pSrc);
//    fprintf(stdout, "Base64(%d --> %d | %d) Bytes: \n[%s]\n", sLen, size, strlen(pEncode), pEncode);

    return pEncode;
}

const char* EvpBase64DecodeNoAlign(const char *pBase64)
{
    int size = 0;
    char *pDecode = NULL;

    if(pBase64 == NULL || strlen(pBase64) == 0)
    {
        return (NULL);
    }

    size = strlen(pBase64);
    
    pDecode = (char*)malloc(size);
    memset(pDecode, 0, size);

    //CRYPTO_w_lock(CRYPTO_LOCK_DYNLOCK);
    EVP_DecodeBlock(pDecode, (const unsigned char *)pBase64, size);
    //CRYPTO_w_unlock(CRYPTO_LOCK_DYNLOCK);

//    fprintf(stdout, "Decode(%d --> %d) Bytes: \n[%s]\n", size, strlen(pDecode), pDecode);

    return pDecode;
}

unsigned char* EvpBase64DecodeNoAlignV2(const char *pBase64, int* pOutSize)
{
    int size = 0;
    unsigned char *pDecode = NULL;

    if(pBase64 == NULL || strlen(pBase64) == 0)
    {
        return (NULL);
    }

    size = strlen(pBase64);
    
    pDecode = (char*)malloc(size);
    memset(pDecode, 0, size);

    //CRYPTO_w_lock(CRYPTO_LOCK_DYNLOCK);
    size = EVP_DecodeBlock(pDecode, (const unsigned char *)pBase64, size);
    //CRYPTO_w_unlock(CRYPTO_LOCK_DYNLOCK);

//    fprintf(stdout, "Decode(%d --> %d) Bytes: \n[%s]\n", size, strlen(pDecode), pDecode);

    if(pOutSize)
    {
        *pOutSize = size;
    }

    return (pDecode);
}
