#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;
		//assert(NULL != input);  
		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;
		//assert(NULL != input);  
		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[20] = { '\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;  
		}  

		SHA1((const unsigned char *)text, strlen(text), sha1);  
		if (1 != RSA_sign(NID_sha1, sha1, 20, 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[20];  

		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;  
		}  


		SHA1((const unsigned char *)text, strlen(text), sha1);  
		if (1 != RSA_verify(NID_sha1, sha1, 20, (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 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()) > outpemkeymaxlen)
			return 0;

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

    static void GetJsonKeyArray(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 GetValueFromJson(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
