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

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

const char* EvpMD5HashFile(const char* pFileName)
{
    int             rdSize = 0;
    int             fd, size;
    uint8_t         md5[EVP_MAX_MD_SIZE];
    uint8_t         buf[1024];
    EVP_MD_CTX      ctx;
    char*           pString = NULL;

    memset(md5, 0, EVP_MAX_MD_SIZE);
    EVP_MD_CTX_init(&ctx);
    EVP_DigestInit_ex(&ctx, EVP_md5(), NULL);
    
    fd = open(pFileName, O_RDONLY);

    if(fd == -1)
    {
        LOG_EX(LOG_Error, "Open File \'%s\' error\n", pFileName);
        return NULL;
    }
    do
    {
        rdSize  = read(fd, buf, 1024);

        if(rdSize > 0)
        {
            EVP_DigestUpdate(&ctx, buf, rdSize);
        }
    } while(rdSize > 0);

    close(fd);
    
    EVP_DigestFinal_ex(&ctx, md5, &rdSize);
    EVP_MD_CTX_cleanup(&ctx);
    
    size = rdSize * 2 + 1;
    
    pString     = (char*)malloc(size);
    memset(pString, 0, size);

    IHW_bin2hex(pString, md5, rdSize);
    
//    print_hex_dump_bytes("MD5_", DUMP_PREFIX_ADDRESS, md5, rdSize);
//    fprintf(stdout, "MD5: [%s]\n", pString);
    
    return (const char*)pString;
}

int EvpMD5HashFileV2(const char* pFileName, unsigned char md5[16])
{
    int             rdSize = 0;
    int             fd;
    uint8_t         buf[1024];
    EVP_MD_CTX      ctx;
    //char*           pString = NULL;

    memset(md5, 0, 16);
    EVP_MD_CTX_init(&ctx);
    EVP_DigestInit_ex(&ctx, EVP_md5(), NULL);
    
    fd = open(pFileName, O_RDONLY);

    if(fd == -1)
    {
        LOG_EX(LOG_Error, "Open File %s error\n", pFileName);
        return (-ERR_OPEN_FILE);
    }
    do
    {
        rdSize  = read(fd, buf, 1024);

        EVP_DigestUpdate(&ctx, buf, rdSize);
    } while(rdSize > 0);

    close(fd);
    
    EVP_DigestFinal_ex(&ctx, md5, &rdSize);
    EVP_MD_CTX_cleanup(&ctx);
    
    
//    print_hex_dump_bytes("MD5_", DUMP_PREFIX_ADDRESS, md5, rdSize);
//    fprintf(stdout, "MD5: [%s]\n", pString);
    
    return 0;
}

int EvpMD5HashBuf(const unsigned char* pBuf, int iBufLen, unsigned char* pOutBuf, int* pOutSize)
{
    EVP_MD_CTX      ctx;

    if(pBuf == NULL || pOutBuf == NULL)
    {
        return (-ERR_INPUT_PARAMS);
    }
    
    memset(pOutBuf, 0, EVP_MAX_MD_SIZE);
    EVP_MD_CTX_init(&ctx);
    EVP_DigestInit_ex(&ctx, EVP_md5(), NULL);
    EVP_DigestUpdate(&ctx, pBuf, iBufLen);
    EVP_DigestFinal_ex(&ctx, pOutBuf, &iBufLen);
    EVP_MD_CTX_cleanup(&ctx);

    if(pOutSize)
    {
        *pOutSize   = iBufLen;
    }

    //print_hex_dump_bytes("MD5_", DUMP_PREFIX_ADDRESS, pOutBuf, iBufLen);
    
    return 0;
}

const char* EvpMD5HashBufV2(const unsigned char* pBuf, int iBufLen)
{
    int             size = 0;
    EVP_MD_CTX      ctx;
    uint8_t         md5[EVP_MAX_MD_SIZE];
    char*           pString = NULL;

    if(pBuf == NULL || iBufLen <= 0)
    {
        return NULL;
    }
    
    memset(md5, 0, EVP_MAX_MD_SIZE);
    EVP_MD_CTX_init(&ctx);
    EVP_DigestInit_ex(&ctx, EVP_md5(), NULL);
    EVP_DigestUpdate(&ctx, pBuf, iBufLen);
    EVP_DigestFinal_ex(&ctx, md5, &iBufLen);
    EVP_MD_CTX_cleanup(&ctx);

    //print_hex_dump_bytes("MD5_", DUMP_PREFIX_ADDRESS, pOutBuf, iBufLen);
    
    size = iBufLen * 2 + 1;
    
    pString     = (char*)malloc(size);
    memset(pString, 0, size);

    IHW_bin2hex(pString, md5, iBufLen);
    
//    print_hex_dump_bytes("MD5_", DUMP_PREFIX_ADDRESS, md5, rdSize);
//    fprintf(stdout, "MD5: [%s]\n", pString);
    
    return (const char*)pString;
}
