#ifndef FM_DATA_PROCESS_H
#define FM_DATA_PROCESS_H

#include <string>
#include <openssl/evp.h>
#include <openssl/bio.h>  
#include <openssl/buffer.h>  
#include <openssl/rsa.h>  
#include <openssl/pem.h>  
#include <openssl/err.h> 
#include <openssl/des.h>
#include <sstream>
#include "fmglobal.h"
#include <QJsonObject>
#include <QString>
#include <QStringList>

class DataProcess
{
public:

	static int base64(const char *input, size_t length, char *result, size_t size)  
	{  
		BIO * bmem = NULL;  
		BIO * b64 = NULL;  
		BUF_MEM * bptr = NULL; 
		int len = 0;

		if(input == NULL)
			return 0;

		b64 = BIO_new(BIO_f_base64());  
		bmem = BIO_new(BIO_s_mem());  
		if (NULL == b64 || NULL == bmem) {  
			perror("BIO_new");  
			return 0;  
		}  
		BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);  
		b64 = BIO_push(b64, bmem);  
		BIO_write(b64, input, length);  
		BIO_flush(b64);  
		BIO_get_mem_ptr(b64, &bptr);  

		if ((unsigned int)(bptr->length + 1) > size) {  
			BIO_free_all(b64);  
			return 0;  
		}  
		memcpy(result, bptr->data, bptr->length);  
		result[bptr->length] = 0;  
		len = bptr->length;

		BIO_free_all(b64);  
		return len;  
	}  


	static int debase64(const char *input, size_t length, char *result, size_t size)  
	{  
		BIO * b64 = NULL;  
		BIO * bmem = NULL;
		int len;

		if(input == NULL)
			return 0;

		if (length > size)  
			return 0;  

		memset(result, 0, size);  

		b64 = BIO_new(BIO_f_base64());  
		bmem = BIO_new_mem_buf((void *)input, length);  
		BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);  
		if (NULL == b64 || NULL == bmem) {  
			perror("BIO_new");  
			return 0;  
		}  
		bmem = BIO_push(b64, bmem);  
		len = BIO_read(bmem, result, length);  

		BIO_free_all(b64);  
		return len;  
	}


	static int RSASign(const char *text, char *signature, size_t size,const char *private_key)  
	{  
		RSA *rsa;  
		unsigned char *sig;  
		unsigned int len;
		unsigned int length = 0;
		BIO* in = NULL; 
        unsigned char sha1[32] = { '\0' };
		char tmpprivatekey[MAX_RSA_KEY_LEN] = {0};

		if(GetPEMKey(private_key, strlen(private_key), tmpprivatekey, MAX_RSA_KEY_LEN, 0) == 0)
			return 0;

		OpenSSL_add_all_algorithms(); 

		in = BIO_new_mem_buf((void*)tmpprivatekey, -1);
		if (in == NULL) {  
			perror("read private failed");  
			return 0;  
		}  

		rsa = PEM_read_bio_RSAPrivateKey(in, NULL, NULL, NULL);  

		if (in != NULL)  
			BIO_free(in);  

		if (rsa == NULL) {  
			perror("PEM_read_bio_RSAPrivateKey");  
			return 0;  
		}  

		if (NULL == (sig = (unsigned char*)malloc(RSA_size(rsa)))) {  
			RSA_free(rsa);  
			return 0;  
		}  

        SHA256((const unsigned char *)text, strlen(text), sha1);

        if (1 != RSA_sign(NID_sha256, sha1, 32, sig, &len, rsa)) {
			free(sig);  
			RSA_free(rsa);  
			printf("RSA_sign error.\n");  
			return 0;  
		}  

		if ((length= base64((char *)sig, 128, signature, size)) == 0) {  
			free(sig);  
			RSA_free(rsa);  
			printf("base64 error.\n");  
			return 0;  
		}  

		free(sig);  
		RSA_free(rsa);  
		return length;  
	}


	static int RSAVerify(const char *text,const char *signature, const char *public_key)  
	{  
		RSA *rsa;  
		BIO* in = NULL;  
		char * sig_debase = NULL;
        unsigned char sha1[32];
		char tmppublickey[MAX_RSA_KEY_LEN] = {0};

		if(GetPEMKey(public_key, strlen(public_key), tmppublickey, MAX_RSA_KEY_LEN, 1) == 0)
			return 0;
		
		in = BIO_new_mem_buf((void*)tmppublickey, -1);

		if (NULL == in) {  
			printf("BIO_read_filename error.\n");  
			return 0;  
		}  

		rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL);  

		if (in != NULL) BIO_free(in);  

		if (rsa == NULL) {  
			printf("PEM_read_bio_RSA_PUBKEY error.\n");  
			return 0;  
		}  

		sig_debase = (char *)malloc(250 * sizeof(char));  

		if (NULL == debase64(signature, strlen((char *)signature), sig_debase, 250)) {  
			RSA_free(rsa);  
			printf("debase64 error.\n");  
			return 0;  
		}  

        SHA256((const unsigned char *)text, strlen(text), sha1);

        if (1 != RSA_verify(NID_sha256, sha1, 32, (unsigned char *)sig_debase, 128, rsa)) {
			free(sig_debase);  
			RSA_free(rsa);  
			printf("RSA_verify error.\n");  
			return 0;  
		}  

		free(sig_debase);  
		RSA_free(rsa);  
		return 1;  
	}


    static int AESDecode(const unsigned char *key, unsigned char *iv,const unsigned char *in, int inlen, unsigned char *out, int outmaxlen)
    {
        int iOutLen = 0;
        int iTmpLen = 0;
        int length = 0;
        char tmpiv[16] = { 0 };
        memcpy(tmpiv, iv, 16);
        unsigned char *tmp = (unsigned char *)malloc(outmaxlen * sizeof(unsigned char));
        memset(tmp, 0, outmaxlen);
        char sha1[32] = { '\0' };

        SHA256((const unsigned char *)key, strlen((const char *)key), (unsigned char *)sha1);
        qDebug() << sha1;

        if((length = debase64((const char *)in, inlen, (char *)tmp, outmaxlen)) == 0)
        {
            perror("debase64 failed");
            return 0;
        }

        EVP_CIPHER_CTX ctx;
        EVP_CIPHER_CTX_init(&ctx);
        EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL,(const unsigned char *)sha1, (const unsigned char *)tmpiv);

        if(!EVP_DecryptUpdate(&ctx, (unsigned char*)out, &iOutLen, (const unsigned char *)tmp, length))
        {
            EVP_CIPHER_CTX_cleanup(&ctx);
            return 0;
        }

        if(!EVP_DecryptFinal_ex(&ctx, (unsigned char *)(out + iOutLen), &iTmpLen))
        {
            EVP_CIPHER_CTX_cleanup(&ctx);
            return 0;
        }

        iOutLen += iTmpLen;
        EVP_CIPHER_CTX_cleanup(&ctx);
        out[iOutLen] = 0;

        return iOutLen;
    }


    static int AESEncode(const unsigned char *key, unsigned char *iv,const unsigned char *in, int inlen, unsigned char *out, int outmaxlen)
    {
        int iOutLen = 0;
        int iTmpLen = 0;
        int len;
        char tmpiv[16] = { 0 };
        memcpy(tmpiv, iv, 16);
        unsigned char *tmp = (unsigned char *)malloc(outmaxlen * sizeof(unsigned char));
        memset(tmp, 0, outmaxlen);
        EVP_CIPHER_CTX ctx;
        char sha1[32] = { '\0' };

        SHA256((const unsigned char *)key, strlen((const char *)key), (unsigned char *)sha1);

        qDebug() << sha1;
        QByteArray array(sha1);
        qDebug() << array.toBase64();

        EVP_CIPHER_CTX_init(&ctx);
        EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, (const unsigned char *)sha1, (const unsigned char *)tmpiv);

        if(!EVP_EncryptUpdate(&ctx, (unsigned char*)tmp, &iOutLen, (const unsigned char *)in, inlen))
        {
            EVP_CIPHER_CTX_cleanup(&ctx);
            return 0;
        }

        if(!EVP_EncryptFinal_ex(&ctx, (unsigned char *)(tmp + iOutLen), &iTmpLen))
        {
            EVP_CIPHER_CTX_cleanup(&ctx);
            return 0;
        }

        iOutLen += iTmpLen;
        EVP_CIPHER_CTX_cleanup(&ctx);
        len = base64((char *)tmp, iOutLen, (char *)out, outmaxlen);
        qDebug() << (char *)out;

        free(tmp);
        return len;

    }


	static int DES3Encode(const unsigned char *key,const char *in, int inlen, unsigned char *out, int outmaxlen)
	{
		int iOutLen = 0;
		int iTmpLen = 0;
		int len;
		char iv[KEY_SIZE] = {0}; 
		unsigned char *tmp = (unsigned char *)malloc(outmaxlen * sizeof(unsigned char));
	    memset(tmp, 0, outmaxlen);
        EVP_CIPHER_CTX ctx;
		EVP_CIPHER_CTX_init(&ctx);    
        EVP_EncryptInit_ex(&ctx, EVP_des_ede3_ecb(), NULL, (const unsigned char *)key, (const unsigned char *)iv);

		if(!EVP_EncryptUpdate(&ctx, (unsigned char*)tmp, &iOutLen, (const unsigned char *)in, inlen))    
		{        
			EVP_CIPHER_CTX_cleanup(&ctx);        
			return 0;    
        }

		if(!EVP_EncryptFinal_ex(&ctx, (unsigned char *)(tmp + iOutLen), &iTmpLen))    
		{        
			EVP_CIPHER_CTX_cleanup(&ctx);        
			return 0;    
        }

		iOutLen += iTmpLen;      
		EVP_CIPHER_CTX_cleanup(&ctx);
		len = base64((char *)tmp, iOutLen, (char *)out, outmaxlen);
		
		free(tmp);
		return len;
	}


	static int DES3Decode(const unsigned char *key,const char * in, int inlen, unsigned char *out, int outmaxlen)
	{
		int iOutLen = 0;
		int iTmpLen = 0; 
		int length = 0;
		char iv[KEY_SIZE] = {0};
		unsigned char *tmp = (unsigned char *)malloc(outmaxlen * sizeof(unsigned char));
		memset(tmp, 0, outmaxlen);
		
		if((length = debase64(in, inlen, (char *)tmp, outmaxlen)) == 0)
		{
			perror("debase64 failed");
			return 0;
		}
		
        EVP_CIPHER_CTX ctx;
		EVP_CIPHER_CTX_init(&ctx);    
		EVP_DecryptInit_ex(&ctx, EVP_des_ede3_ecb(), NULL, (const unsigned char *)key, (const unsigned char *)iv);   

		if(!EVP_DecryptUpdate(&ctx, (unsigned char*)out, &iOutLen, (const unsigned char *)tmp, length))    
		{        
			EVP_CIPHER_CTX_cleanup(&ctx);        
			return 0;    
		}

		if(!EVP_DecryptFinal_ex(&ctx, (unsigned char *)(out + iOutLen), &iTmpLen))    
		{        
			EVP_CIPHER_CTX_cleanup(&ctx);        
			return 0;    
		}        

		iOutLen += iTmpLen;      
		EVP_CIPHER_CTX_cleanup(&ctx);
		out[iOutLen] = 0;

		return iOutLen;

	}


	static int GetPEMKey(const char *key, int keylen, char *outpemkey, int outpemkeymaxlen, int ispublickey)
	{
		std::stringstream sstm;
		std::string str(key, keylen);

        if (keylen > outpemkeymaxlen) {
			return 0;
		}

        if(ispublickey == 0) {
			int i = 0;
			sstm << "-----BEGIN RSA PRIVATE KEY-----\n";

            while(i + 64 < keylen) {
				sstm << str.substr(i, 64) << "\n";
				i += 64;
			}

			if(keylen - i > 0)
				sstm << str.substr(i, keylen - i) << "\n";
			sstm << "-----END RSA PRIVATE KEY-----\n";

        } else {
			int i = 0;
			sstm << "-----BEGIN PUBLIC KEY-----\n";

            while(i + 64 < keylen) {
				sstm << str.substr(i, 64) << "\n";
				i += 64;
			}

			if(keylen - i > 0)
				sstm << str.substr(i, keylen - i) << "\n";
			sstm << "-----END PUBLIC KEY-----\n";

		}

        if(strlen(sstm.str().c_str()) > (unsigned int)outpemkeymaxlen)
			return 0;

		strcpy(outpemkey, sstm.str().c_str());
		return 1;

	}


    static void GetKeyListFromJson(QStringList &keys, QJsonObject &json)
	{
        QJsonObject::Iterator it;
        keys.clear();

        for(it = json.begin(); it != json.end(); ++it) {
            if(it.value().isString() || it.value().isDouble())
                keys.append(it.key());
        }
	}


    static void SortString(QStringList &keys) {
        keys.removeOne(JSON_KEY_SIGN);
        keys.sort();
	}

    static void GetValueListFromJson(const QStringList &keys, const QJsonObject &json, QString &values) {
        values.clear();

        for(int i = 0; i < keys.length(); ++i) {
            if(json[keys[i]].isString()) {
                values.append(json[keys[i]].toString());
                values.append("|");

            } else if ( json[keys[i]].isDouble() ) {

                values.append(QString::number(json[keys[i]].toInt()));
                values.append("|");
            }
        }

        values = values.left(values.length() - 1);
        qDebug() << values;
	}
};

#endif
