// 分流：是否是浮点数->TEE，纯软件
// 不使用ECALL_BATCH,不进行TaskQueue任务批处理
// 待处理：判断浮点数逻辑
#include <memory>
#include "Singleton.h"
#include "Timer.h"
#include "errmsgBridge.h"
#include "TimeCounter.h"
#include "LogBase.h"
#include <iostream>
// 新增
#include <fstream>
extern "C"
{
#include "Interface.h"
#include "udfDispatch.h"
}
using namespace util;
#ifdef USE_SGX
#include "Enclave_u.h"
#endif

#include "App.h"

#ifndef NDEBUG  // DEBUG

#include <fstream>
#include "en_config.h"

extern "C"
{
#include <unistd.h>
}
using namespace std;

class CalledCounter {
public:
    CalledCounter() {
        char buf[64];
        sprintf(buf, ENCRYPTSQL_CONFIG_DIR "/UDFcalled_by%d.count", (int) getpid());

        ofstream fout(buf);
        fout.close();
    }
};

CalledCounter c;
#endif

extern "C" int ore_compare(char *strctxt1, char *strctxt2);

constexpr char TERMCHAR = '^';
// 新增
char plaPath[] = "/etc/encryptsql/masterKey.plain";
char encPath[] = "/etc/encryptsql/masterKey.encrypted";
char dekPath[] = "/etc/encryptsql/dek.sealed";
constexpr int keySize = 32;

#ifdef USE_SGX
void encryptKey(sgx_enclave_id_t eid) {
    using namespace std;
    ifstream fin;
    fin.open(plaPath, ios::binary | ios::in);
    if(!fin.good()) {
        printf("can not open key file:%s\n", plaPath);
        exit(0);
    }
    char key[keySize];

    fin.get(key, keySize + 1);
    fin.close();
    ecall_encryptKeyWithEnclave(eid, key, keySize, encPath);
}
#endif

char *saheAdd(char *cipher1, char *cipher2, char *aescipher1, char *aescipher2, bool isLeftFloat, bool isRightFloat) {
    try {
        size_t csz1 = strlen(cipher1) + 1;
        size_t csz2 = strlen(cipher2) + 1;
        Log("saheAdd: cipher1=%s", aescipher1, log::info);
        Log("saheAdd: cipher2=%s", aescipher2, log::info);
        Log("saheAdd: isLeftFloat=%d", isLeftFloat, log::info); 
        Log("saheAdd: isRightFloat=%d", isRightFloat, log::info);
        if (!(cipher1[csz1 - 2] == TERMCHAR && cipher2[csz2 - 2] == TERMCHAR)) {
            Log("saheAdd: cannot find term char in cipher1(%s) or cipher2(%s)!", cipher1, cipher2, log::error);
            return nullptr;
        }

        char *ret = nullptr;
        Timer t = Timer();
        bool isFloat = isLeftFloat || isRightFloat;
#ifdef USE_SGX
        if (isFloat && ecall_sahe_add) {// 判断浮点数
            EnclaveManager *enclave = EnclaveManager::getInstance();
            sgx_enclave_id_t eid = enclave->getID();

            // Log("saheAdd: Using enclave ID: %llu", eid, log::info);

            size_t retsz = 128;
            ret = new char[retsz];
            if (strcmp(aescipher1, "null") == 0) {
                Log("saheAdd: client does not support SGX", log::error);
                delete[] ret;
                return nullptr;
            }
            // 新增
            encryptKey(eid);
            // Log("saheAdd: Calling ecall_sahe_add", log::info);
            int isLeftFloatInt = isLeftFloat ? 1 : 0;
            int isRightFloatInt = isRightFloat ? 1 : 0; 
            sgx_status_t status = ecall_sahe_add(eid, aescipher1, aescipher2, ret, retsz, isLeftFloatInt, isRightFloatInt);
            if (status != SGX_SUCCESS) {
                Log("saheAdd: ecall_sahe_add failed with status %d", status, log::error);
                delete[] ret;
                return nullptr;
            }
            Log("saheAdd: ecall_sahe_add succeeded", log::info);
        } else {
            Log("saheAdd: Using software implementation", log::info);
            ret = SAHE_add_udf(cipher1, cipher2, csz1, csz2);
        }
#else
        Log("saheAdd: Using software implementation", log::info);
        ret = SAHE_add_udf(cipher1, cipher2, csz1, csz2);
#endif
        if (ret) {
            Log("saheAdd: Operation successful", log::info);
        } else {
            Log("saheAdd: Operation failed - null result", log::error);
        }
        return ret;
    } catch (const std::exception& e) {
        Log("saheAdd: Exception caught: %s", e.what(), log::error);
        return nullptr;
    } catch (...) {
        Log("saheAdd: Unknown exception caught", log::error);
        return nullptr;
    }
}

char *saheSub(char *cipher1, char *cipher2, char *aescipher1, char *aescipher2, bool isLeftFloat, bool isRightFloat) {

    size_t csz1 = strlen(cipher1) + 1;
    size_t csz2 = strlen(cipher2) + 1;
    Log("saheSub: input lengths - csz1=%zu, csz2=%zu", csz1, csz2, log::info);
    Log("saheSub: cipher1=%s", cipher1, log::info);
    Log("saheSub: cipher2=%s", cipher2, log::info);
    Log("saheSub: isLeftFloat=%d", isLeftFloat, log::info);
    Log("saheSub: isRightFloat=%d", isRightFloat, log::info);

    if (!(cipher1[csz1 - 2] == TERMCHAR && cipher2[csz2 - 2] == TERMCHAR)) {
        Log("saheSub: cannot find term char in cipher1(%s) or cipher2(%s)!", cipher1, cipher2, log::error);
        abort();
    }
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    char *ret = nullptr;

    Timer t = Timer();
    bool isFloat = isLeftFloat || isRightFloat;
#ifdef USE_SGX
    if (isFloat && ecall_sahe_sub) {// 判断浮点数
        EnclaveManager *enclave = EnclaveManager::getInstance();
        sgx_enclave_id_t eid = enclave->getID();
        size_t retsz = 128;
        ret = new char[retsz];
        if (strcmp(aescipher1, "null") == 0) {
            Log("client does not support SGX while Server try to calculate AHE_SUB with SGX!\n", log::error);
            delete[] ret;
            return nullptr;
        }
        encryptKey(eid);    
        int isLeftFloatInt = isLeftFloat ? 1 : 0;
        int isRightFloatInt = isRightFloat ? 1 : 0; 
        sgx_status_t status = ecall_sahe_sub(eid, aescipher1, aescipher2, ret, retsz, isLeftFloatInt, isRightFloatInt);
        if (status != SGX_SUCCESS) {
            Log("saheSub: ecall_sahe_sub failed with status %d", status, log::error);
            delete[] ret;
            return nullptr;
        }
        Log("saheSub: ecall_sahe_sub succeeded", log::info);
    } else {
        Log("saheSub: Using software implementation", log::info);
        ret = SAHE_substract_udf(cipher1, cipher2, csz1, csz2);
    }
#else
    Log("saheSub: Using software implementation", log::info);
    ret = SAHE_substract_udf(cipher1, cipher2, csz1, csz2);
#endif
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    return ret;
}

char *saheMul(char *cipher1, int m) {
    size_t csz1 = strlen(cipher1) + 1;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    if (strcmp(cipher1, "null") == 0) {
#if !defined(USE_SGX) && !defined(SGX_ORE) // 前两个正常ORE为空，说明客户端有SGX_ORE，此时服务器没有SGX_ORE的话无法解密。
        Log("client use SGX to calculate MHE but server does not support SGX!\n", log::error);
#endif
    }

    char *ret = nullptr;
    Timer t = Timer();
    if (false && ecall_sahe_mul) { //判断浮点数
        EnclaveManager *enclave = EnclaveManager::getInstance();
        ret = new char[csz1];
        ecall_sahe_mul(enclave->getID(), cipher1, m, ret, csz1);
    } else 
        ret = SAHE_multiply_udf(cipher1, m, csz1);
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    return ret;
}

char *saheNeg(char *cipher1) {
    size_t csz1 = strlen(cipher1) + 1;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    char *ret = nullptr;
    Timer t = Timer();
    if (false && ecall_sahe_neg) {//判断浮点数
        EnclaveManager *enclave = EnclaveManager::getInstance();
        ret = new char[csz1];
        ecall_sahe_neg(enclave->getID(), cipher1, ret, csz1);
    } else
        ret = SAHE_neggate_udf(cipher1, csz1);
    return ret;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

char *smheMul(char *cipher1, char *cipher2, char *aescipher1, char *aescipher2, bool isLeftFloat, bool isRightFloat) {
    size_t csz1 = strlen(cipher1) + 1;
    size_t csz2 = strlen(cipher2) + 1;
    Log("smheMul: input lengths - csz1=%zu, csz2=%zu", csz1, csz2, log::info);
    Log("smheMul: cipher1=%s", cipher1, log::info);
    Log("smheMul: cipher2=%s", cipher2, log::info);
    if (!(cipher1[csz1 - 2] == TERMCHAR && cipher2[csz2 - 2] == TERMCHAR)) {
        Log("smheMul: cannot find term char in cipher1(%s) or cipher2(%s)!", cipher1, cipher2, log::error);
        abort();
    }
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    bool HardWare = false;
#ifdef SGX_MHE
    // 乘除用TEE
    HardWare = true;
#endif
    char *ret = nullptr;
    Timer t = Timer();
#ifdef USE_SGX
    if (HardWare && ecall_smhe_mul) {
        EnclaveManager *enclave = EnclaveManager::getInstance();
        sgx_enclave_id_t eid = enclave->getID();
        size_t retsz = 128;
        ret = new char[retsz];
        if (strcmp(aescipher1, "null") == 0) {
            Log("client does not support SGX while Server try to calculate MHE with SGX!\n", log::error);
            delete[] ret;
            return nullptr;
        }
        encryptKey(eid);
        int isLeftFloatInt = isLeftFloat ? 1 : 0;   
        int isRightFloatInt = isRightFloat ? 1 : 0; 
        sgx_status_t status = ecall_smhe_mul(eid, aescipher1, aescipher2, ret, retsz, isLeftFloatInt, isRightFloatInt);
        if (status != SGX_SUCCESS) {
            Log("smheMul: ecall_smhe_mul failed with status %d", status, log::error);
            delete[] ret;
            return nullptr;
        }
        Log("smheMul: ecall_smhe_mul succeeded", log::info);
    } else {
        // char *__ = new char[csz1];
        Log("smheMul: Using software implementation", log::info);
        ret = SMHE_MULTIPLY_udf(cipher1, cipher2, csz1, csz2);
    }
#else
    Log("smheMul: Using software implementation", log::info);
    ret = SMHE_MULTIPLY_udf(cipher1, cipher2, csz1, csz2);
#endif
    return ret;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

char *smheMulp(char *cipher1, int m) {

    size_t csz1 = strlen(cipher1) + 1;

    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    char *ret = nullptr;
    Timer t = Timer();
    if (false && ecall_smhe_mulp) {
        EnclaveManager *enclave = EnclaveManager::getInstance();
        ret = new char[csz1];

        ecall_smhe_mulp(enclave->getID(), cipher1, m, ret, csz1);
    } else
        ret = SMHE_MULTIPLYPLAIN_udf(cipher1, m, csz1);
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    return ret;
}

char *smheDiv(char *cipher1, char *cipher2, char *aescipher1, char *aescipher2, bool isLeftFloat, bool isRightFloat) {
    size_t csz1 = strlen(cipher1) + 1;
    size_t csz2 = strlen(cipher2) + 1;
    Log("smheDiv: input lengths - csz1=%zu, csz2=%zu", csz1, csz2, log::info);
    Log("smheDiv: cipher1=%s", cipher1, log::info);
    Log("smheDiv: cipher2=%s", cipher2, log::info);
    if (!(cipher1[csz1 - 2] == TERMCHAR && cipher2[csz2 - 2] == TERMCHAR)) {
        Log("smheDiv: cannot find term char in cipher1(%s) or cipher2(%s)!", cipher1, cipher2, log::error);
        // abort();
    }
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    bool HardWare = false;
#ifdef SGX_MHE
    HardWare = true;
#endif
    char *ret = nullptr;
    Timer t = Timer();
#ifdef USE_SGX
    if (HardWare && ecall_smhe_div) {
        EnclaveManager *enclave = EnclaveManager::getInstance();
        sgx_enclave_id_t eid = enclave->getID();
        size_t retsz = 128;
        ret = new char[retsz];
        if (strcmp(aescipher1, "null") == 0) {
            Log("client does not support SGX while Server try to calculate MHE with SGX!\n", log::error);
            delete[] ret;
            return nullptr;
        }
        encryptKey(eid);
        int isLeftFloatInt = isLeftFloat ? 1 : 0;   
        int isRightFloatInt = isRightFloat ? 1 : 0;     
        sgx_status_t status = ecall_smhe_div(eid, aescipher1, aescipher2, ret, retsz, isLeftFloatInt, isRightFloatInt);
        if (status != SGX_SUCCESS) {
            Log("smheDiv: ecall_smhe_div failed with status %d", status, log::error);
            delete[] ret;
            return nullptr;
        }
        Log("smheDiv: ecall_smhe_div succeeded", log::info);
    } else {
        Log("smheDiv: Using software implementation", log::info);
        ret = SMHE_DIVIDE_udf(cipher1, cipher2, csz1, csz2);
    }
#else
    Log("smheDiv: Using software implementation", log::info);
    ret = SMHE_DIVIDE_udf(cipher1, cipher2, csz1, csz2);
#endif
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    return ret;
}

char *smhePow(char *cipher1, char *aescipher1, int m) {
    size_t csz1 = strlen(cipher1) + 1;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    char *ret = nullptr;
    Timer t = Timer();
    if (false && ecall_smhe_pow) {
        EnclaveManager *enclave = EnclaveManager::getInstance();
        ret = new char[csz1 * 2];
        ecall_smhe_pow(enclave->getID(), aescipher1, m, ret, csz1);
    } else
        ret = SMHE_POW_udf(cipher1, m, csz1);
    return ret;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

char *smheInv(char *cipher1) {

    size_t csz1 = strlen(cipher1) + 1;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    char *ret = nullptr;
    Timer t = Timer();
    if (false && ecall_smhe_inv) {
        EnclaveManager *enclave = EnclaveManager::getInstance();
        char *ret = new char[csz1];
        ecall_smhe_inv(enclave->getID(), cipher1, ret, csz1);
    } else
        ret = SMHE_INVERSE_udf(cipher1, csz1);
    return ret;
    //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

char *saheAddp(char *cipher1, int m) {
    size_t sz1 = strlen(cipher1);
    return SAHE_addplaintext_udf(cipher1, m, sz1);
}

string getOREType(int kind) {
    switch (kind) {
        case 1:
            return "LT";
        case 2:
            return "LE";
        case 3:
            return "GT";
        case 4:
            return "GE";
        case 5:
            return "EE";
        case 6:
            return "MAX";
        case 7:
            return "MIN";
		case 8:
			return "NE";
            return "UN";
    }
}

int oreCompare(char *cipher1, char *cipher2, char *aescipher1, char *aescipher2, int kind) {
    size_t csz1 = strlen(cipher1) + 1;
    size_t csz2 = strlen(cipher2) + 1;
    
    if (strcmp(cipher1, "null") == 0) {
#if !defined(USE_SGX) && !defined(SGX_ORE) // 前两个正常ORE为空，说明客户端有SGX_ORE，此时服务器没有SGX_ORE的话无法解密。
        Log("client use SGX to calculate ORE but server does not support SGX!\n", log::error);
#endif
    }

    //!ORE
    int res = -2;
    bool hardWare = false;
#ifdef SGX_ORE
    // ORE用TEE
    hardWare = true;
#endif

    int ret = -3;
    Timer t = Timer();
    if (hardWare && ecall_ore_compare) {
        if (strcmp(aescipher1, "null") == 0)
            Log("client does not support SGX while Server try to calculate ORE with SGX!\n", log::error);

        EnclaveManager *enclave = EnclaveManager::getInstance();
        ecall_ore_compare(enclave->getID(), aescipher1, aescipher2, &ret);
    } else {
        ret = ore_compare(cipher1, cipher2);
    }

    return ret;
}


// int ore_com(char *cipher1, char *cipher2) {
//     int ret = -3;
//     ret = ore_compare(cipher1, cipher2);

//     return ret;

// }
