#include <jni.h>
#include <string>
#include <thread>
#include <Factory.h>
#include <Client/Terminal.h>
#include "TerminalDevice.h"
#include "PhoneDevice.h"
#include "PhoneDevice2.h"
#include <LogThread.h>
#include <sys/system_properties.h>
#include <AndroidHelper.h>
#include <linux/spi/spidev.h>

#include <android/multinetwork.h>

#define NRSEC_PATH "/dev/spidev0.0"

#ifdef USING_BREAK_PAD
#include "client/linux/handler/exception_handler.h"
#include "client/linux/handler/minidump_descriptor.h"
#endif

#include <android/native_window.h>
#include <android/native_window_jni.h>

#include "Camera.h"
#include "Camera2Reader.h"
#include "GPIOControl.h"

#ifdef USING_BREAK_PAD
bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
                  void* context,
                  bool succeeded) {

    LOGE("Dump path: %s\n", descriptor.path());
    return succeeded;
}
#endif

#ifdef USING_NRSEC
#include <Client/NrsecPort.h>
#endif


static jmethodID mRegisterTimerMid = 0;
static jmethodID mRegisterHeartbeatMid = 0;
static jmethodID mUnregisterTimerMid = 0;
static jmethodID mUpdateTimeMid = 0;

static jmethodID mRequestWakelockMid = 0;
static jmethodID mReleaseWakelockMid = 0;

static jmethodID mGetSystemInfoMid = 0;

static jmethodID mRebootMid = 0;
static jmethodID mEnableGpsMid = 0;
static jmethodID mRequestPositionMid = 0;

static jmethodID mWriteLogMid = 0;


void posix_signal_handler(int sig, siginfo_t *siginfo, void *context)
{
    (void)context;
    switch(sig)
    {
        case SIGSEGV:
            fputs("Caught SIGSEGV: Segmentation Fault\n", stderr);
            break;
        case SIGINT:
            fputs("Caught SIGINT: Interactive attention signal, (usually ctrl+c)\n",
                  stderr);
            break;
        case SIGFPE:
            switch(siginfo->si_code)
            {
                case FPE_INTDIV:
                    fputs("Caught SIGFPE: (integer divide by zero)\n", stderr);
                    break;
                case FPE_INTOVF:
                    fputs("Caught SIGFPE: (integer overflow)\n", stderr);
                    break;
                case FPE_FLTDIV:
                    fputs("Caught SIGFPE: (floating-point divide by zero)\n", stderr);
                    break;
                case FPE_FLTOVF:
                    fputs("Caught SIGFPE: (floating-point overflow)\n", stderr);
                    break;
                case FPE_FLTUND:
                    fputs("Caught SIGFPE: (floating-point underflow)\n", stderr);
                    break;
                case FPE_FLTRES:
                    fputs("Caught SIGFPE: (floating-point inexact result)\n", stderr);
                    break;
                case FPE_FLTINV:
                    fputs("Caught SIGFPE: (floating-point invalid operation)\n", stderr);
                    break;
                case FPE_FLTSUB:
                    fputs("Caught SIGFPE: (subscript out of range)\n", stderr);
                    break;
                default:
                    fputs("Caught SIGFPE: Arithmetic Exception\n", stderr);
                    break;
            }
        case SIGILL:
            switch(siginfo->si_code)
            {
                case ILL_ILLOPC:
                    fputs("Caught SIGILL: (illegal opcode)\n", stderr);
                    break;
                case ILL_ILLOPN:
                    fputs("Caught SIGILL: (illegal operand)\n", stderr);
                    break;
                case ILL_ILLADR:
                    fputs("Caught SIGILL: (illegal addressing mode)\n", stderr);
                    break;
                case ILL_ILLTRP:
                    fputs("Caught SIGILL: (illegal trap)\n", stderr);
                    break;
                case ILL_PRVOPC:
                    fputs("Caught SIGILL: (privileged opcode)\n", stderr);
                    break;
                case ILL_PRVREG:
                    fputs("Caught SIGILL: (privileged register)\n", stderr);
                    break;
                case ILL_COPROC:
                    fputs("Caught SIGILL: (coprocessor error)\n", stderr);
                    break;
                case ILL_BADSTK:
                    fputs("Caught SIGILL: (internal stack error)\n", stderr);
                    break;
                default:
                    fputs("Caught SIGILL: Illegal Instruction\n", stderr);
                    break;
            }
            break;
        case SIGTERM:
            fputs("Caught SIGTERM: a termination request was sent to the program\n",
                  stderr);
            break;
        case SIGABRT:
            fputs("Caught SIGABRT: usually caused by an abort() or assert()\n", stderr);
            break;
        default:
            break;
    }

    _Exit(1);
}

class Runner
{
public:
    static void RequestCapture(CTerminal* pTerminal, unsigned int channel, unsigned int preset, unsigned int type, unsigned long scheduleTime);
};
void Runner::RequestCapture(CTerminal* pTerminal, unsigned int channel, unsigned int preset, unsigned int type, unsigned long scheduleTime)
{
    pTerminal->RequestCapture(channel, preset, type, scheduleTime);
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

#if defined(JNI_VERSION_1_6)
    if (result==-1 && vm->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_OK)
    {
        result = JNI_VERSION_1_6;
    }
#endif
#if defined(JNI_VERSION_1_4)
    if (result==-1 && vm->GetEnv((void**)&env, JNI_VERSION_1_4) == JNI_OK)
    {
        result = JNI_VERSION_1_4;
    }
#endif
#if defined(JNI_VERSION_1_2)
    if (result==-1 && vm->GetEnv((void**)&env, JNI_VERSION_1_2) == JNI_OK)
    {
        result = JNI_VERSION_1_2;
    }
#endif

    if(result == -1 || env == NULL)
    {
        return JNI_FALSE;
    }

#ifdef USING_BREAK_PAD
    google_breakpad::MinidumpDescriptor descriptor("/sdcard/Android/data/com.xypower.mpapp/files/logs/");
    google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback, NULL, true, -1);
#endif


    {
        struct sigaction sig_action = {};
        sig_action.sa_sigaction = posix_signal_handler;
        sigemptyset(&sig_action.sa_mask);

#ifdef __APPLE__
        /* for some reason we backtrace() doesn't work on osx
           when we use an alternate stack */
        sig_action.sa_flags = SA_SIGINFO;
#else
        sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
#endif

        if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
            // err(1, "sigaction");
            int aa = 0;
        }
    }

    const char* className = "com/xypower/mpapp/MicroPhotoService";

    jclass clazz = (env)->FindClass(className);
    // if((env)->RegisterNatives(clazz, gMethods, 1)< 0) {
        // return -1;
    // }

    env->DeleteLocalRef(clazz);

    return result;
}

bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread)
{
    didAttachThread = false;
    *env = nullptr;
    // Check if the current thread is attached to the VM
    auto get_env_result = vm->GetEnv((void**)env, JNI_VERSION_1_6);
    if (get_env_result == JNI_EDETACHED)
    {
        get_env_result = vm->AttachCurrentThread(env, NULL);
        if (get_env_result == JNI_OK) {
            didAttachThread = true;
        } else {
            // Failed to attach thread. Throw an exception if you want to.
        }
    } else if (get_env_result == JNI_EVERSION)
    {
        // Unsupported JNI version. Throw an exception if you want to.
    }

    return get_env_result == JNI_OK;
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MainActivity_takePhoto(
        JNIEnv* env,
        jobject pThis, jint channel, jint preset, jstring path, jstring fileName) {

    if (channel < 1 || channel > 0xFF)
    {
        return JNI_FALSE;
    }

    unsigned char id = (unsigned char)channel - 1;

    Camera2Reader *camera = new Camera2Reader(id);
    const char *pathStr = env->GetStringUTFChars(path, 0);
    const char *fileNameStr = env->GetStringUTFChars(fileName, 0);

    camera->Open(pathStr, fileNameStr);
    env->ReleaseStringUTFChars(fileName, fileNameStr);
    env->ReleaseStringUTFChars(path, pathStr);

    camera->start();

    return JNI_TRUE;
}

extern "C" JNIEXPORT jlong JNICALL
Java_com_xypower_mpapp_MicroPhotoService_init(
        JNIEnv* env,
        jobject pThis, jstring appPath,
        jstring ip, jint port, jstring cmdid, jint protocol,
        jint networkProtocol, jint encryptData, jlong netHandle, jint signalLevel,
        jint versionCode, jlong buildTime, jstring simcard, jstring tfCardPath) {

    /*
    google_breakpad::MinidumpDescriptor descriptor(".");
    google_breakpad::ExceptionHandler eh(descriptor, NULL, NULL,
                                         NULL, true, -1);

     */

    if (netHandle != NETID_UNSET) {
        net_handle_t nh = (net_handle_t)netHandle;

        android_setprocnetwork(nh);
    }

    char model[PROP_VALUE_MAX] = { 0 };
    __system_property_get("ro.product.model", model);

    jclass classService = env->GetObjectClass(pThis);
    jfieldID fieldId = env->GetFieldID(classService, "mModelName", "Ljava/lang/String;");
    env->DeleteLocalRef(classService);
    jstring modelName = env->NewStringUTF(model);
    env->SetObjectField(pThis, fieldId, modelName);

    bool udpOrTcp = (networkProtocol != 0); // 0: tcp
    const char *appPathStr = appPath == NULL ? NULL : env->GetStringUTFChars(appPath, 0);
    const char *ipStr = ip == NULL ? NULL : env->GetStringUTFChars(ip, 0);
    const char *cmdidStr = cmdid == NULL ? NULL : env->GetStringUTFChars(cmdid, 0);
    const char *simcardStr = simcard == NULL ? NULL : env->GetStringUTFChars(simcard, 0);
    const char *tfCardPathStr = tfCardPath == NULL ? NULL : env->GetStringUTFChars(tfCardPath, 0);

    JavaVM* vm = NULL;
    jint ret = env->GetJavaVM(&vm);
    // const string& appPath, const string& termId, const string& server, unsigned short port, const string& bindIp
    // CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    // CTerminalDevice* device = new CTerminalDevice(vm, pThis);
    // CPhoneDevice2* device = new CPhoneDevice2(vm, pThis);

    CTerminal* pTerminal = NewTerminal(protocol);

    CPhoneDevice* device = new CPhoneDevice(vm, pThis, MakeString(appPathStr), NETID_UNSET, versionCode);
    device->SetListener(pTerminal);
    device->UpdateSignalLevel(signalLevel);
    device->SetBuildTime(buildTime / 1000);
    device->UpdateSimcard(MakeString(simcardStr));
    device->UpdateTfCardPath(MakeString(tfCardPathStr));

    pTerminal->InitServerInfo(MakeString(appPathStr), MakeString(cmdidStr), MakeString(ipStr), port, udpOrTcp, encryptData);
    // pTerminal->SetPacketSize(1 * 1024);	// 1K
#ifdef USING_NRSEC
    pTerminal->InitEncryptionInfo(simcardStr, "/dev/spidev0.0", "");
#endif
    bool res = pTerminal->Startup(device);

    if (appPathStr != NULL) env->ReleaseStringUTFChars(appPath, appPathStr);
    if (ipStr != NULL) env->ReleaseStringUTFChars(ip, ipStr);
    if (cmdidStr != NULL) env->ReleaseStringUTFChars(cmdid, cmdidStr);
    if (simcardStr != NULL) env->ReleaseStringUTFChars(simcard, simcardStr);
    if (tfCardPathStr != NULL) env->ReleaseStringUTFChars(tfCardPath, tfCardPathStr);

    if (!res)
    {
        delete pTerminal;
        pTerminal = NULL;
    }

    return reinterpret_cast<jlong>(pTerminal);
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_notifyToTakePhoto(
        JNIEnv* env,
        jobject pThis, jlong handler, jint channel, jint preset, jlong scheduleTime, jboolean photoOrVideo) {

    if (channel < 1 || channel > 0xFF)
    {
        return JNI_FALSE;
    }
    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return JNI_FALSE;
    }

    unsigned char type = photoOrVideo ? 0 : 1;
    // std::thread th(&Runner::RequestCapture, pTerminal, (unsigned int)channel, (unsigned int)preset, type, (unsigned long)scheduleTime, 0, true);
    // th.detach();
    pTerminal->RequestCapture((unsigned int)channel, (unsigned int)preset, type, (unsigned long)scheduleTime, 0, true);

    return JNI_TRUE;
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_sendHeartbeat(
        JNIEnv* env,
        jobject pThis,
        jlong handler, jint signalLevel) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return JNI_FALSE;
    }

    CPhoneDevice* device = (CPhoneDevice*)pTerminal->GetDevice();
    if (device != NULL)
    {
        device->UpdateSignalLevel(signalLevel);
    }

    pTerminal->SendHeartbeat();

    return JNI_TRUE;
}

extern "C" JNIEXPORT void JNICALL
Java_com_xypower_mpapp_MicroPhotoService_updatePosition(
        JNIEnv* env,
        jobject pThis, jlong handler, jdouble lon, jdouble lat, jdouble radius, jlong ts) {

    if (handler == 0)
    {
        return;
    }

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    IDevice* dev = pTerminal->GetDevice();
    if (dev == NULL)
    {
        return;
    }

    CPhoneDevice* phoneDevice = (CPhoneDevice *)dev;
    phoneDevice->UpdatePosition(lon, lat, radius, ts);
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_uninit(
        JNIEnv* env,
        jobject pThis, jlong handler) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return JNI_FALSE;
    }

    IDevice* dev = pTerminal->GetDevice();
    if (dev != NULL)
    {
        ((CPhoneDevice *)dev)->CloseCamera();
    }
    pTerminal->SignalExit();
    pTerminal->Shutdown();

    delete pTerminal;

    return JNI_TRUE;
}

extern "C" JNIEXPORT jlong JNICALL
Java_com_xypower_mpapp_MicroPhotoService_getHeartbeatDuration(
        JNIEnv* env,
        jobject pThis, jlong handler) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return DEFAULT_HEARTBEAT_DURATION;
    }

    return pTerminal->GetHeartbeatDuration();
}

extern "C" JNIEXPORT jlongArray JNICALL
Java_com_xypower_mpapp_MicroPhotoService_getPhotoTimeData2(
        JNIEnv* env,
        jobject pThis, jlong handler) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return NULL;
    }

    map<unsigned char, vector<unsigned int>> photoTime;
    if (!pTerminal->GetPhotoTime(photoTime) || photoTime.empty())
    {
        return NULL;
    }

    size_t numberOfData = photoTime.size() * photoTime.begin()->second.size();
    if (numberOfData == 0)
    {
        return NULL;
    }

    vector<jlong> dataArray;
    dataArray.reserve(numberOfData);

    unsigned long val = 0;
    jint channel = 0;
    for (map<unsigned char, vector<unsigned int>>::const_iterator it = photoTime.cbegin(); it != photoTime.cend(); ++it)
    {
        if (it->second.empty())
        {
            continue;
        }

        channel = (jint)((unsigned short)it->first);
        // dataArray.push_back(channel);

        // val = (jint)it->second.size();
        // dataArray.push_back(val);
        for (vector<unsigned int>::const_iterator it2 = it->second.cbegin(); it2 != it->second.cend(); ++it2)
        {
            // time
            val = ((unsigned long)((*it2) & 0xFFFFFF00)) << 24;
            // channel
            val |= ((unsigned long)channel) << 16;
            // preset
            val |= ((unsigned long)((*it2) & 0xFF)) << 8;

            dataArray.push_back((jlong)val);
        }
    }

    std::sort(dataArray.begin(), dataArray.end());

    jlongArray data = env->NewLongArray(dataArray.size());
    if (data == NULL) {
        return NULL;
    }

    env->SetLongArrayRegion(data, 0, dataArray.size(), &dataArray[0]);

    return data;
}


extern "C" JNIEXPORT jlongArray JNICALL
Java_com_xypower_mpapp_MicroPhotoService_getPhotoTimeData(
        JNIEnv* env,
        jobject pThis, jlong handler, jlong startTime) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return NULL;
    }

    unsigned int scheduleTime = 0;
    time_t zeroPointTime = 0;
    std::vector<std::pair<unsigned char, unsigned char> > channelsAndPresets;
    if (!pTerminal->GetAndRefreshLatestScheduleTime(startTime, zeroPointTime, scheduleTime, channelsAndPresets))
    {
        return NULL;
    }

    if (channelsAndPresets.empty())
    {
        return NULL;
    }

    vector<jlong> dataArray;
    dataArray.reserve(channelsAndPresets.size() + 3);

    dataArray.push_back((jlong)zeroPointTime);
    dataArray.push_back((jlong)scheduleTime);
    dataArray.push_back((jlong)channelsAndPresets.size());

    unsigned long val = 0;
    for (std::vector<std::pair<unsigned char, unsigned char> >::const_iterator it = channelsAndPresets.cbegin(); it != channelsAndPresets.cend(); ++it)
    {
        val = (unsigned long)scheduleTime << 24;
        // channel
        val |= ((unsigned long)(it->first)) << 16;
        // preset
        val |= ((unsigned long)(it->second)) << 8;

        dataArray.push_back((jlong)val);
    }

    jlongArray data = env->NewLongArray(dataArray.size());
    if (data == NULL) {
        return NULL;
    }

    env->SetLongArrayRegion(data, 0, dataArray.size(), &dataArray[0]);

    return data;
}

/*
extern "C" JNIEXPORT jlongArray JNICALL
Java_com_xypower_mpapp_MicroPhotoService_getNextScheduleItem(
        JNIEnv* env,
        jobject pThis, jlong handler) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return NULL;
    }

    map<unsigned char, vector<unsigned int>> photoTime;
    if (!pTerminal->GetPhotoTime(photoTime) || photoTime.empty())
    {
        return NULL;
    }

    size_t numberOfData = photoTime.size() * photoTime.begin()->second.size();
    if (numberOfData == 0)
    {
        return NULL;
    }

    vector<jlong> dataArray;
    dataArray.reserve(numberOfData);

    unsigned long val = 0;
    jint channel = 0;
    for (map<unsigned char, vector<unsigned int>>::const_iterator it = photoTime.cbegin(); it != photoTime.cend(); ++it)
    {
        if (it->second.empty())
        {
            continue;
        }

        channel = (jint)((unsigned short)it->first);
        // dataArray.push_back(channel);

        // val = (jint)it->second.size();
        // dataArray.push_back(val);
        for (vector<unsigned int>::const_iterator it2 = it->second.cbegin(); it2 != it->second.cend(); ++it2)
        {
            // time
            val = ((unsigned long)((*it2) & 0xFFFFFF00)) << 24;
            // channel
            val |= ((unsigned long)channel) << 16;
            // preset
            val |= ((unsigned long)((*it2) & 0xFF)) << 8;

            dataArray.push_back((jlong)val);
        }
    }

    std::sort(dataArray.begin(), dataArray.end());

    jlongArray data = env->NewLongArray(dataArray.size());
    if (data == NULL) {
        return NULL;
    }

    env->SetLongArrayRegion(data, 0, dataArray.size(), &dataArray[0]);

    return data;
}
 */


extern "C" JNIEXPORT void JNICALL
Java_com_xypower_mpapp_MicroPhotoService_recordingFinished(
        JNIEnv* env,
        jobject pThis, jlong handler, jboolean result, jstring path, jlong videoId) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return;
    }

    IDevice* dev = pTerminal->GetDevice();
    if (dev != NULL)
    {
        const char *pathStr = NULL;
        if (path != NULL)
        {
            pathStr = env->GetStringUTFChars(path, 0);
        }

        // camera->Open(pathStr, fileNameStr);
        unsigned long photoId = videoId;
        ((CPhoneDevice *)dev)->OnVideoReady(result != JNI_FALSE, pathStr, photoId);
        if (path != NULL)
        {
            env->ReleaseStringUTFChars(path, pathStr);
        }
    }
}


extern "C" JNIEXPORT void JNICALL
Java_com_xypower_mpapp_MicroPhotoService_reloadConfigs(
        JNIEnv* env,
        jobject pThis, jlong handler) {

    CTerminal* pTerminal = reinterpret_cast<CTerminal *>(handler);
    if (pTerminal == NULL)
    {
        return;
    }

    pTerminal->LoadAppConfigs();
}


extern "C" JNIEXPORT void JNICALL
Java_com_xypower_mpapp_MicroPhotoService_infoLog(
        JNIEnv* env, jclass cls, jstring msg) {

    if (env->GetStringUTFLength(msg) <=0)
    {
        return;
    }

    const char *msgStr = env->GetStringUTFChars(msg, 0);
    XYLOG(XYLOG_SEVERITY_INFO, msgStr);
    env->ReleaseStringUTFChars(msg, msgStr);
}

extern "C" JNIEXPORT void JNICALL
Java_com_xypower_mpapp_MicroPhotoService_setOtgState(
        JNIEnv* env,
        jclass cls, jboolean enabled) {

    GpioControl::setOtgState(enabled != JNI_FALSE);

}

extern "C" JNIEXPORT void JNICALL
Java_com_xypower_mpapp_MicroPhotoService_setCam3V3Enable(
        JNIEnv* env,
        jclass cls, jboolean enabled) {

    GpioControl::setCam3V3Enable(enabled != JNI_FALSE);
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_xypower_mpapp_MicroPhotoService_getSerialNumber(
        JNIEnv* env,
        jclass cls) {

    char value[PROP_VALUE_MAX] = { 0 };
    __system_property_get("ro.serialno", value);
    return env->NewStringUTF(value);
}


extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_importPublicKeyFile(
        JNIEnv* env, jclass cls, jint index, jstring outputPath, jstring md5) {

#ifdef USING_NRSEC

    // NrsecSpiPort spi("/dev/mtkgpioctrl");
    // NrsecSpiPort spi("/dev/spidevSE");
    // const char *port = "/dev/mtkgpioctrl";

    if (env->GetStringUTFLength(outputPath) <=0)
    {
        return JNI_FALSE;
    }
    
    NrsecPort nrsec;
    const char *path = NRSEC_PATH;
    if (!nrsec.Open(path))
    {
        return JNI_FALSE;
    }

    const char *outputPathStr = env->GetStringUTFChars(outputPath, 0);
    const char *md5Str = env->GetStringUTFChars(md5, 0);

    bool res = false;
    std::vector<unsigned char> data;
    if (readFile(outputPathStr, data) && !data.empty())
    {
        res = nrsec.SM2ImportPublicKey(index, &data[0]) == 0;
    }

    nrsec.Close();

    env->ReleaseStringUTFChars(outputPath, outputPathStr);
    env->ReleaseStringUTFChars(md5, md5Str);

    return res ? JNI_TRUE : JNI_FALSE;
#endif
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_importPublicKey(
        JNIEnv* env, jclass cls, jint index, jbyteArray cert) {

#ifdef USING_NRSEC

    int byteCertLen = env->GetArrayLength(cert);
    if (byteCertLen <= 0)
    {
        return JNI_FALSE;
    }

    NrsecPort nrsec;
    const char *path = NRSEC_PATH;
    if (!nrsec.Open(path))
    {
        return JNI_FALSE;
    }

    jbyte* byteCert = env->GetByteArrayElements(cert, 0);

    bool res = nrsec.SM2ImportPublicKey(index, (const uint8_t*)byteCert) == 0;

    nrsec.Close();

    env->ReleaseByteArrayElements(cert, byteCert, JNI_ABORT);

    return res ? JNI_TRUE : JNI_FALSE;
#endif
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_genKeys(
        JNIEnv* env,
        jclass cls, jint index) {

#ifdef USING_NRSEC
    // GpioControl::setRS485Enable(true);

    //GpioControl::setSpiMode(SPI_MODE_3);
    //GpioControl::setSpiBitsPerWord(8);
    //GpioControl::setSpiMaxSpeedHz(33000000);

    const char *path = NRSEC_PATH;

    NrsecPort nrsec;
    if (!nrsec.Open(path))
    {
        return JNI_FALSE;
    }

    bool res = nrsec.SM2keypair(index) == 0;

    nrsec.Close();

    return res ? JNI_TRUE : JNI_FALSE;
#endif

}

extern "C" JNIEXPORT jstring JNICALL
Java_com_xypower_mpapp_MicroPhotoService_querySecVersion(
        JNIEnv* env,
        jclass cls) {
#ifdef USING_NRSEC

    const char *path = NRSEC_PATH;

    NrsecPort nrsec;
    if (!nrsec.Open(path))
    {
        return NULL;
    }

    std::string version = nrsec.Version();
    nrsec.Close();

    return env->NewStringUTF(version.c_str());
#endif
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_genCertRequest(
        JNIEnv* env, jclass cls, jint index, jint type, jstring subject, jstring outputPath) {

#ifdef USING_NRSEC
    if (env->GetStringUTFLength(subject) <=0 || env->GetStringUTFLength(outputPath) <=0)
    {
        return JNI_FALSE;
    }
    const char *path = NRSEC_PATH;

    NrsecPort nrsec;
    if (!nrsec.Open(path))
    {
        return JNI_FALSE;
    }

    uint8_t output[1024] = { 0 };
    uint16_t len = 0;
    const char* subjectStr = env->GetStringUTFChars(subject, 0);

    bool res = nrsec.SM2cert(type, index, MakeString(subjectStr), output, &len) == 0;
    nrsec.Close();
    env->ReleaseStringUTFChars(subject, subjectStr);
    if (!res)
    {
        return JNI_FALSE;
    }

    const char* outputPathStr = env->GetStringUTFChars(outputPath, 0);
    FILE* file = fopen(outputPathStr, "wb");
    env->ReleaseStringUTFChars(outputPath, outputPathStr);
    if (file == NULL)
    {
        return JNI_FALSE;
    }

    int bytes = fwrite(output, sizeof(unsigned char), len, file);
    fclose(file);

    return bytes == len ? JNI_TRUE : JNI_FALSE;
#endif
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_importPrivateKeyFile(
        JNIEnv* env, jclass cls, jint index, jstring outputPath, jstring md5) {

#ifdef USING_NRSEC

    if (env->GetStringUTFLength(outputPath)<=0)
    {
        return JNI_FALSE;
    }
    const char *path = NRSEC_PATH;

    NrsecPort nrsec;
    if (!nrsec.Open(path))
    {
        return JNI_FALSE;
    }

    const char *outputPathStr = env->GetStringUTFChars(outputPath, 0);
    const char *md5Str = env->GetStringUTFChars(md5, 0);

    bool res = false;
    std::vector<unsigned char> data;
    if (readFile(outputPathStr, data) && !data.empty())
    {
        res = nrsec.SM2ImportPrivateKey(index, &data[0]) == 0;
    }

    nrsec.Close();

    env->ReleaseStringUTFChars(outputPath, outputPathStr);
    env->ReleaseStringUTFChars(md5, md5Str);

    return res ? JNI_TRUE : JNI_FALSE;

#endif
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_exportPublicKeyFile(
        JNIEnv* env, jclass cls, jint index, jstring outputPath) {

#ifdef USING_NRSEC

    if (env->GetStringUTFLength(outputPath) <= 0)
    {
        return JNI_FALSE;
    }

    const char *path = NRSEC_PATH;
    NrsecPort nrsec;
    if (!nrsec.Open(path))
    {
        return JNI_FALSE;
    }

    bool res = false;
    std::vector<unsigned char> data(64, 0);
    uint8_t len = 0;
    res = nrsec.SM2ExportPublicKey(index, &data[0], &len) == 0;
    nrsec.Close();
    if (!res)
    {
        return JNI_FALSE;
    }

    const char* outputPathStr = env->GetStringUTFChars(outputPath, 0);
    FILE* file = fopen(outputPathStr, "wb");
    env->ReleaseStringUTFChars(outputPath, outputPathStr);
    if (file == NULL)
    {
        return JNI_FALSE;
    }

    int bytes = fwrite(&data[0], sizeof(unsigned  char), len, file);

    fclose(file);   
    return bytes == len ? JNI_TRUE : JNI_FALSE;
#endif
}

extern "C" JNIEXPORT jboolean JNICALL
Java_com_xypower_mpapp_MicroPhotoService_exportPrivateFile(
        JNIEnv* env, jclass cls, jint index, jstring outputPath) {

#ifdef USING_NRSEC

    if (env->GetStringUTFLength(outputPath)<=0)
    {
        return JNI_FALSE;
    }

    const char *path = NRSEC_PATH;
    NrsecPort nrsec;
    if (!nrsec.Open(path))
    {
        return JNI_FALSE;
    }

    bool res = false;
    std::vector<unsigned char> data(64,0);
    uint8_t len = 0;
    res = nrsec.SM2ExportPrivateKey(index, &data[0], &len) == 0;
    nrsec.Close();
    if (!res)
    {
        return JNI_FALSE;
    }

    const char* outputPathStr = env->GetStringUTFChars(outputPath, 0);
    FILE* file = fopen(outputPathStr, "wb");
    env->ReleaseStringUTFChars(outputPath, outputPathStr);
    if (file == NULL)
    {
        return JNI_FALSE;
    }

    int bytes = fwrite(&data[0], sizeof(unsigned  char), len, file);

    fclose(file);
    return bytes == len ? JNI_TRUE : JNI_FALSE;
#endif
}
