#ifndef _DECRYPT
#define _DECRYPT

#include "aes.hpp"

class decrypt : public aes {
private:
	    EVP_CIPHER_CTX* aesDecryptCtx;
public:
	decrypt(std::string filename, unsigned char* aesKey=0, unsigned char* aesIV=0);
	decrypt(const decrypt& a);
	
	void init_all(bool initSuper=true);
	int aesDecrypt(unsigned char *encMsg, size_t encMsgLen, unsigned char** decMsg);
	~decrypt();
};

decrypt::decrypt(std::string filename, unsigned char* aesKey, unsigned char* aesIV) : aes(filename) {
	aesDecryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
	if(aesDecryptCtx == NULL) {
        exit(FAILURE);
	}
	if(aesKey && aesIV) {
		setAesKey(aesKey);
		setAesIV(aesIV);
	    init_all(false);
	}
    else
	    init_all();
}

decrypt::decrypt(const decrypt& _a) : aes::aes() {
	this->clone(_a);
	aesDecryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX));
    if(aesDecryptCtx == NULL) {
        exit(FAILURE);
    }

	int res = EVP_CIPHER_CTX_copy(aesDecryptCtx, _a.aesDecryptCtx);
    if(!res)
    	exit(FAILURE);
}

void decrypt::init_all(bool initSuper) {
	setExtension(".dec");
	if(initSuper)
		aes::init_all();
	EVP_CIPHER_CTX_init(aesDecryptCtx);
}

int decrypt::aesDecrypt(unsigned char *encMsg, size_t encMsgLen, unsigned char **decMsg) {
    size_t decLen   = 0;
    size_t blockLen = 0;

    *decMsg = (unsigned char*)malloc(encMsgLen);
    if(*decMsg == NULL) return FAILURE;
    
    if(!EVP_DecryptInit_ex(aesDecryptCtx, EVP_aes_256_cbc(), NULL, aes::aesKey, aes::aesIV)) {
        return FAILURE;
    }

    if(!EVP_DecryptUpdate(aesDecryptCtx, (unsigned char*)*decMsg, (int*)&blockLen, encMsg, (int)encMsgLen)) {
        return FAILURE;
    }
    decLen += blockLen;

    if(!EVP_DecryptFinal_ex(aesDecryptCtx, (unsigned char*)*decMsg + decLen, (int*)&blockLen)) {
        return FAILURE;
    }
    decLen += blockLen;

    EVP_CIPHER_CTX_cleanup(aesDecryptCtx);

    return (int)decLen;
}

decrypt::~decrypt() {
	aes::clear_all();
	EVP_CIPHER_CTX_cleanup(aesDecryptCtx);
	free(aesDecryptCtx);
}

#endif