// To compile JNI library use the script file.
// For Linux 32-bit use file:
// compile32.sh
// For Linux 64-bit use file:
// compile64.sh


#include <stdio.h>
#include <dlfcn.h>

#include <string.h>


#include <fenv.h> // feclearexcept, fetestexcept, FE_ALL_EXCEPT, FE_INVALID


#include "NIST_jniexport.h"





void* handle = NULL;
//handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
char* error;



//error = dlerror();
//if(error) { printf("%s\n", error); exit(1) };

//handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library


//void (*printer)(void);
//printer = dlsym(handle, "b_printer");
//if(error) { prinf("%s\n", error); exit(1) };

//printer(); //call funtion

//dlclose(handle);// close the shared library




typedef char*(*GenerateNISTLibrarySerialNumber)();
GenerateNISTLibrarySerialNumber pGenerateNISTLibrarySerialNumber = 0;

typedef int (*UnlockNISTLibrary)(const char* authorization_code);
UnlockNISTLibrary pUnlockNISTLibrary = 0;

typedef int (*RegisterNIST)();
RegisterNIST pRegisterNIST = 0;

typedef int (*CreateNewNISTFile)();
CreateNewNISTFile pCreateNewNISTFile = 0;

typedef int (*WriteNISTToFile)(const char *filename);
WriteNISTToFile pWriteNISTToFile = 0;

typedef unsigned char* (*ReadNISTByteArray)();
ReadNISTByteArray pReadNISTByteArray = 0;

typedef int (*ReadSizeOfNISTByteArray)();
ReadSizeOfNISTByteArray pReadSizeOfNISTByteArray = 0;

typedef int (*ReadNISTFromFile)(const char *filename);
ReadNISTFromFile pReadNISTFromFile = 0;

typedef int (*ReadNISTFromMemory)(unsigned char* byte_array, int size_of_byte_array);
ReadNISTFromMemory pReadNISTFromMemory = 0;

typedef int (*CheckIfRecordTypeExists)(int record_type_number);
CheckIfRecordTypeExists pCheckIfRecordTypeExists = 0;

typedef int (*ReadNumberOfRecords)(int record_type_number);
ReadNumberOfRecords pReadNumberOfRecords = 0;

typedef int (*ReadIDCofRecord)(int record_type_number, int record_number);
ReadIDCofRecord pReadIDCofRecord = 0;

typedef char* (*ReadRecordTxtField)(int record_type_number, int idc, int record_field_number, const char *record_field_name);
ReadRecordTxtField pReadRecordTxtField = 0;

typedef int (*ReadSizeOfRecordTxtField)(int record_type_number, int idc, int record_field_number, const char *record_field_name);
ReadSizeOfRecordTxtField pReadSizeOfRecordTxtField = 0;

typedef char* (*ReadRecordTxtFieldName)(int record_type_number, int idc, int record_field_number);
ReadRecordTxtFieldName pReadRecordTxtFieldName = 0;

typedef int (*ReadSizeOfRecordTxtFieldName)(int record_type_number, int idc, int record_field_number, const char *record_field_name);
ReadSizeOfRecordTxtFieldName pReadSizeOfRecordTxtFieldName = 0;

typedef char* (*ReadRecordTxtFieldDescription)(int record_type_number, int idc, int record_field_number, const char *record_field_name);
ReadRecordTxtFieldDescription pReadRecordTxtFieldDescription = 0;

typedef int (*ReadSizeOfRecordTxtFieldDescription)(int record_type_number, int idc, int record_field_number, const char *record_field_name);
ReadSizeOfRecordTxtFieldDescription pReadSizeOfRecordTxtFieldDescription = 0;

typedef char* (*ReadRecordExportedTxtFields)(int record_type_number, int idc);
ReadRecordExportedTxtFields pReadRecordExportedTxtFields = 0;

typedef int (*ReadSizeOfRecordExportedTxtFields)(int record_type_number, int idc);
ReadSizeOfRecordExportedTxtFields pReadSizeOfRecordExportedTxtFields = 0;

typedef unsigned char* (*ReadRecordBinaryField)(int record_type_number, int idc);
ReadRecordBinaryField pReadRecordBinaryField = 0;

typedef int (*ReadSizeOfRecordBinaryField)(int record_type_number, int idc);
ReadSizeOfRecordBinaryField pReadSizeOfRecordBinaryField = 0;

// typedef HBITMAP (*CreateHBITMAPfromRecordBinaryField)(int record_type_number, int idc);
// typedef int (*CreateHBITMAPfromRecordBinaryField)(int record_type_number, int idc);
// CreateHBITMAPfromRecordBinaryField pCreateHBITMAPfromRecordBinaryField = 0;

typedef int (*CreateImageFromRecordBinaryField)(int record_type_number, int idc, int* width, int* height, unsigned char** imageData);
CreateImageFromRecordBinaryField pCreateImageFromRecordBinaryField = 0;

typedef int (*InsertRecordBinaryFieldFromFile)(int record_type_number, int idc, const char *filename);
InsertRecordBinaryFieldFromFile pInsertRecordBinaryFieldFromFile = 0;

typedef int (*InsertRecordBinaryFieldFromMemory)(int record_type_number, int idc, unsigned char* input_byte_array, int size_of_input_byte_array, const char* CGA, int CGA_int, int HLL, int VLL);
InsertRecordBinaryFieldFromMemory pInsertRecordBinaryFieldFromMemory = 0;

typedef int (*InsertRecordTxtField)(int record_type_number, int idc, int record_field_number, const char *record_field_name, const char * input_data);
InsertRecordTxtField pInsertRecordTxtField = 0;

typedef int (*DeleteRecord)(int record_type_number, int idc);
DeleteRecord pDeleteRecord = 0;

typedef int (*SetNISTLibErrorReportMode)(int nist_error_report_mode);
SetNISTLibErrorReportMode pSetNISTLibErrorReportMode = 0;

typedef char* (*ReadNISTLibLastErrorMessage)();
ReadNISTLibLastErrorMessage pReadNISTLibLastErrorMessage = 0;





//----------------------------------------------------------------------------------------------------------
JNIEXPORT jstring JNICALL Java_nist_1sun_1java_NIST_1library_GenerateNISTLibrarySerialNumber(JNIEnv *env, jclass)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }


	if (!pGenerateNISTLibrarySerialNumber) 
        {
          pGenerateNISTLibrarySerialNumber = (GenerateNISTLibrarySerialNumber)dlsym(handle, "GenerateNISTLibrarySerialNumber");
          if(pGenerateNISTLibrarySerialNumber == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	const char* serial_number_char = pGenerateNISTLibrarySerialNumber();
	jstring serial_number = env->NewStringUTF(serial_number_char);

        feclearexcept(FE_ALL_EXCEPT);
	return serial_number;
}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_UnlockNISTLibrary(JNIEnv * env, jclass,  jstring authorization_code)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pUnlockNISTLibrary) 
        {
          pUnlockNISTLibrary = (UnlockNISTLibrary)dlsym(handle, "UnlockNISTLibrary");
          if(pUnlockNISTLibrary == NULL){fputs(dlerror(), stderr); return 0;}
	}

	const char* authorization_code_char = env->GetStringUTFChars(authorization_code, 0);


        feclearexcept(FE_ALL_EXCEPT);
	return (jint)pUnlockNISTLibrary(authorization_code_char);


}
//----------------------------------------------------------------------------------------------------------



JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_RegisterNIST(JNIEnv *env, jclass)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pRegisterNIST) 
        {
          pRegisterNIST = (RegisterNIST)dlsym(handle, "RegisterNIST");
          if(pRegisterNIST == NULL){fputs(dlerror(), stderr); return 0;}
	}

	int ret = pRegisterNIST();


        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}



//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_CreateNewNISTFile(JNIEnv *env, jclass)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pCreateNewNISTFile) 
        {
          pCreateNewNISTFile = (CreateNewNISTFile)dlsym(handle, "CreateNewNISTFile");
          if(pCreateNewNISTFile == NULL){fputs(dlerror(), stderr); return 0;}
	}

	int ret = pCreateNewNISTFile();


        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}




//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_WriteNISTToFile(JNIEnv *env, jclass, jstring filename)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pWriteNISTToFile) 
        {
          pWriteNISTToFile = (WriteNISTToFile)dlsym(handle, "WriteNISTToFile");
          if(pWriteNISTToFile == NULL){fputs(dlerror(), stderr); return 0;}
	}

	const char* jfilename = env->GetStringUTFChars(filename, 0);

	int ret = pWriteNISTToFile(jfilename);


        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}




//----------------------------------------------------------------------------------------------------------
JNIEXPORT jbyteArray JNICALL Java_nist_1sun_1java_NIST_1library_ReadNISTByteArray(JNIEnv *env, jclass)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pReadNISTByteArray) 
        {
          pReadNISTByteArray = (ReadNISTByteArray)dlsym(handle, "ReadNISTByteArray");
          if(pReadNISTByteArray == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	jint size_of_byte_array = pReadSizeOfNISTByteArray();
	unsigned char* byte_array = pReadNISTByteArray();
	jbyteArray output = env->NewByteArray(size_of_byte_array);
	jbyte *poutput = env->GetByteArrayElements(output, 0);	
	memcpy(poutput, byte_array, size_of_byte_array);
	env->ReleaseByteArrayElements(output, poutput, 0);


        feclearexcept(FE_ALL_EXCEPT);
	return output;


}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadSizeOfNISTByteArray(JNIEnv *env, jclass)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadSizeOfNISTByteArray) 
        {
          pReadSizeOfNISTByteArray = (ReadSizeOfNISTByteArray)dlsym(handle, "ReadSizeOfNISTByteArray");
          if(pReadSizeOfNISTByteArray == NULL){fputs(dlerror(), stderr); return 0;}
	}

	int ret = pReadSizeOfNISTByteArray();

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}



//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadNISTFromFile(JNIEnv * env, jclass, jstring fileName)
{

       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadNISTFromFile) 
        {
          pReadNISTFromFile = (ReadNISTFromFile)dlsym(handle, "ReadNISTFromFile");
          if(pReadNISTFromFile == NULL){fputs(dlerror(), stderr); return 0;}
	}

	const char* file = env->GetStringUTFChars(fileName, 0);
	int ret = pReadNISTFromFile(file);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;

}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadNISTFromMemory(JNIEnv *env, jclass, jbyteArray byte_array, jint size_of_byte_array)
{

       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadNISTFromMemory) 
        {
          pReadNISTFromMemory = (ReadNISTFromMemory)dlsym(handle, "ReadNISTFromMemory");
          if(pReadNISTFromMemory == NULL){fputs(dlerror(), stderr); return 0;}
	}


	jboolean isCopy = JNI_FALSE;
	unsigned char* input_data_stream = (unsigned char*)env->GetByteArrayElements(byte_array, &isCopy);
	int ret = pReadNISTFromMemory(input_data_stream, size_of_byte_array);

        //if (isCopy == JNI_TRUE) 
        //free(input_data_stream); input_data_stream = (unsigned char*) NULL; //new debug delete

        feclearexcept(FE_ALL_EXCEPT);
	return ret;

}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_CheckIfRecordTypeExists(JNIEnv *env, jclass, jint record_type_number)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pCheckIfRecordTypeExists) 
        {
          pCheckIfRecordTypeExists = (CheckIfRecordTypeExists)dlsym(handle, "CheckIfRecordTypeExists");
          if(pCheckIfRecordTypeExists == NULL){fputs(dlerror(), stderr); return 0;}
	}


	int ret = pCheckIfRecordTypeExists(record_type_number);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}





//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadNumberOfRecords(JNIEnv *env, jclass, jint record_type_number)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadNumberOfRecords) 
        {
          pReadNumberOfRecords = (ReadNumberOfRecords)dlsym(handle, "ReadNumberOfRecords");
          if(pReadNumberOfRecords == NULL){fputs(dlerror(), stderr); return 0;}
	}



	int ret = pReadNumberOfRecords(record_type_number);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}



//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadIDCofRecord(JNIEnv *env, jclass, jint record_type_number, jint record_number)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadIDCofRecord) 
        {
          pReadIDCofRecord = (ReadIDCofRecord)dlsym(handle, "ReadIDCofRecord");
          if(pReadIDCofRecord == NULL){fputs(dlerror(), stderr); return 0;}
	}



	int ret = pReadIDCofRecord(record_type_number, record_number);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}



//----------------------------------------------------------------------------------------------------------
JNIEXPORT jstring JNICALL Java_nist_1sun_1java_NIST_1library_ReadRecordTxtField(JNIEnv *env, jclass, jint record_type_number, jint idc, jint record_field_number, jstring record_field_name)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pReadRecordTxtField) 
        {
          pReadRecordTxtField = (ReadRecordTxtField)dlsym(handle, "ReadRecordTxtField");
          if(pReadRecordTxtField == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	const char* jrecord_field_name = env->GetStringUTFChars(record_field_name, 0);
	const char* txt_nist_field = pReadRecordTxtField(record_type_number, idc, record_field_number, jrecord_field_name);
	jstring jstring_txt_nist_field = env->NewStringUTF(txt_nist_field);

        feclearexcept(FE_ALL_EXCEPT);
	return jstring_txt_nist_field;

}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadSizeOfRecordTxtField(JNIEnv *env, jclass, jint record_type_number, jint idc, jint record_field_number, jstring record_field_name)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadSizeOfRecordTxtField) 
        {
          pReadSizeOfRecordTxtField = (ReadSizeOfRecordTxtField)dlsym(handle, "ReadSizeOfRecordTxtField");
          if(pReadSizeOfRecordTxtField == NULL){fputs(dlerror(), stderr); return 0;}
	}


	const char* jrecord_field_name = env->GetStringUTFChars(record_field_name, 0);

	int ret = pReadSizeOfRecordTxtField(record_type_number, idc, record_field_number, jrecord_field_name);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jstring JNICALL Java_nist_1sun_1java_NIST_1library_ReadRecordTxtFieldName(JNIEnv *env, jclass, jint record_type_number, jint idc, jint record_field_number)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pReadRecordTxtFieldName) 
        {
          pReadRecordTxtFieldName = (ReadRecordTxtFieldName)dlsym(handle, "ReadRecordTxtFieldName");
          if(pReadRecordTxtFieldName == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	const char* txt_nist_field = pReadRecordTxtFieldName(record_type_number, idc, record_field_number);
	jstring jstring_txt_nist_field = env->NewStringUTF(txt_nist_field);

        feclearexcept(FE_ALL_EXCEPT);
	return jstring_txt_nist_field;

}




//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadSizeOfRecordTxtFieldName(JNIEnv *env, jclass, jint record_type_number, jint idc, jint record_field_number, jstring record_field_name)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadSizeOfRecordTxtFieldName) 
        {
          pReadSizeOfRecordTxtFieldName = (ReadSizeOfRecordTxtFieldName)dlsym(handle, "ReadSizeOfRecordTxtFieldName");
          if(pReadSizeOfRecordTxtFieldName == NULL){fputs(dlerror(), stderr); return 0;}
	}


	const char* jrecord_field_name = env->GetStringUTFChars(record_field_name, 0);

	int ret = pReadSizeOfRecordTxtFieldName(record_type_number, idc, record_field_number, jrecord_field_name);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}





//----------------------------------------------------------------------------------------------------------
JNIEXPORT jstring JNICALL Java_nist_1sun_1java_NIST_1library_ReadRecordTxtFieldDescription(JNIEnv *env, jclass, jint record_type_number, jint idc, jint record_field_number, jstring record_field_name)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pReadRecordTxtFieldDescription) 
        {
          pReadRecordTxtFieldDescription = (ReadRecordTxtFieldDescription)dlsym(handle, "ReadRecordTxtFieldDescription");
          if(pReadRecordTxtFieldDescription == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	const char* jrecord_field_name = env->GetStringUTFChars(record_field_name, 0);
	const char* txt_nist_field = pReadRecordTxtFieldDescription(record_type_number, idc, record_field_number, jrecord_field_name);
	jstring jstring_txt_nist_field = env->NewStringUTF(txt_nist_field);

        feclearexcept(FE_ALL_EXCEPT);
	return jstring_txt_nist_field;

}



//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadSizeOfRecordTxtFieldDescription(JNIEnv *env, jclass, jint record_type_number, jint idc, jint record_field_number, jstring record_field_name)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadSizeOfRecordTxtFieldDescription) 
        {
          pReadSizeOfRecordTxtFieldDescription = (ReadSizeOfRecordTxtFieldDescription)dlsym(handle, "ReadSizeOfRecordTxtFieldDescription");
          if(pReadSizeOfRecordTxtFieldDescription == NULL){fputs(dlerror(), stderr); return 0;}
	}


	const char* jrecord_field_name = env->GetStringUTFChars(record_field_name, 0);

	int ret = pReadSizeOfRecordTxtFieldDescription(record_type_number, idc, record_field_number, jrecord_field_name);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jstring JNICALL Java_nist_1sun_1java_NIST_1library_ReadRecordExportedTxtFields(JNIEnv *env, jclass, jint record_type_number, jint idc)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pReadRecordExportedTxtFields) 
        {
          pReadRecordExportedTxtFields = (ReadRecordExportedTxtFields)dlsym(handle, "ReadRecordExportedTxtFields");
          if(pReadRecordExportedTxtFields == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	const char* txt_nist_field = pReadRecordExportedTxtFields(record_type_number, idc);
	jstring jstring_txt_nist_field = env->NewStringUTF(txt_nist_field);

        feclearexcept(FE_ALL_EXCEPT);
	return jstring_txt_nist_field;

}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadSizeOfRecordExportedTxtFields(JNIEnv *env, jclass, jint record_type_number, jint idc)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadSizeOfRecordExportedTxtFields) 
        {
          pReadSizeOfRecordExportedTxtFields = (ReadSizeOfRecordExportedTxtFields)dlsym(handle, "ReadSizeOfRecordExportedTxtFields");
          if(pReadSizeOfRecordExportedTxtFields == NULL){fputs(dlerror(), stderr); return 0;}
	}



	int ret = pReadSizeOfRecordExportedTxtFields(record_type_number, idc);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}
 



//----------------------------------------------------------------------------------------------------------
JNIEXPORT jbyteArray JNICALL Java_nist_1sun_1java_NIST_1library_ReadRecordBinaryField(JNIEnv *env, jclass, jint record_type_number, jint idc)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pReadRecordBinaryField) 
        {
          pReadRecordBinaryField = (ReadRecordBinaryField)dlsym(handle, "ReadRecordBinaryField");
          if(pReadRecordBinaryField == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	jint size_of_byte_array = pReadSizeOfRecordBinaryField(record_type_number, idc);
	unsigned char* byte_array = pReadRecordBinaryField(record_type_number, idc);	
	jbyteArray output = env->NewByteArray(size_of_byte_array);
	jbyte *poutput = env->GetByteArrayElements(output, 0);	
	memcpy(poutput, byte_array, size_of_byte_array);		
	env->ReleaseByteArrayElements(output, poutput, 0);	




        feclearexcept(FE_ALL_EXCEPT);
	return output;


}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_ReadSizeOfRecordBinaryField(JNIEnv *env, jclass, jint record_type_number, jint idc)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pReadSizeOfRecordBinaryField) 
        {
          pReadSizeOfRecordBinaryField = (ReadSizeOfRecordBinaryField)dlsym(handle, "ReadSizeOfRecordBinaryField");
          if(pReadSizeOfRecordBinaryField == NULL){fputs(dlerror(), stderr); return 0;}
	}



	int ret = pReadSizeOfRecordBinaryField(record_type_number, idc);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jintArray JNICALL Java_nist_1sun_1java_NIST_1library_CreateImageFromRecordBinaryField(JNIEnv *env, jclass, jintArray imageDimension, jint record_type_number, jint idc)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pCreateImageFromRecordBinaryField) 
        {
          pCreateImageFromRecordBinaryField = (CreateImageFromRecordBinaryField)dlsym(handle, "CreateImageFromRecordBinaryField");
          if(pCreateImageFromRecordBinaryField == NULL){fputs(dlerror(), stderr); return NULL;}
	}



	int width = 0;
	int height = 0;
	unsigned char* imageData = 0;


        pCreateImageFromRecordBinaryField(record_type_number, idc, &width, &height, &imageData);
	
	jboolean isDimensionCopy = JNI_FALSE;
	
	//jint* dimension = (jint *)env->GetPrimitiveArrayCritical(imageDimension, &isDimensionCopy);
        jint* dimension = env->GetIntArrayElements(imageDimension, &isDimensionCopy);
	dimension[0] = width;
	dimension[1] = height;
	//if (isDimensionCopy == JNI_TRUE) env->ReleasePrimitiveArrayCritical(imageDimension, dimension, JNI_ABORT);

        //if (isDimensionCopy == JNI_TRUE) 
        env->ReleaseIntArrayElements(imageDimension, dimension, 0);

	jboolean isCopy = JNI_FALSE;
	jintArray pixels = env->NewIntArray(width * height);


// https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html
//
// After calling GetPrimitiveArrayCritical, the native code should not run for an extended period of time before it calls ReleasePrimitiveArrayCritical.
// We must treat the code inside this pair of functions as running in a "critical region."
// Inside a critical region, native code must not call other JNI functions, or any system call that may cause the current thread to block 
// and wait for another Java thread. (For example, the current thread must not call read on a stream being written by another Java thread.)

//jint *pixel = (jint *)env->GetPrimitiveArrayCritical(pixels, &isCopy); //works, but crashes on big images



        //jboolean isCopy = JNI_FALSE;
        jint *pixel = env->GetIntArrayElements(pixels, &isCopy);

	int number_of_pixels = width*height;
	for (int index = 0; index < number_of_pixels; index++) {
//	pixel[index] = (imageData[index] << 16) | (imageData[index+number_of_pixels] << 8) | (imageData[index+2*number_of_pixels]);

		pixel[index] = ((int)imageData[index] << 16) | ((int)imageData[index+number_of_pixels] << 8) | ((int)imageData[index+2*number_of_pixels]);
	}

	//if (isCopy == JNI_TRUE) env->ReleasePrimitiveArrayCritical(pixels, pixel, JNI_ABORT); //works, but crashes on big images


// https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html
// Primitive Array Release Modes
//
//Release<PrimitiveType>ArrayElements Routines
//void Release<PrimitiveType>ArrayElements(JNIEnv *env, ArrayType array, NativeType *elems, jint mode);
//
// mode	actions
// 0	copy back the content and free the elems buffer
// JNI_COMMIT	copy back the content but do not free the elems buffer
// JNI_ABORT	free the buffer without copying back the possible changes
//
//In most cases, programmers pass “0” to the mode argument to ensure consistent behavior for both pinned and copied arrays.
// The other options give the programmer more control over memory management and should be used with extreme care.


        //if (isCopy == JNI_TRUE) 
        env->ReleaseIntArrayElements(pixels, pixel, 0);

        feclearexcept(FE_ALL_EXCEPT);
	return pixels;

}





//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_InsertRecordBinaryFieldFromFile(JNIEnv *env, jclass, jint record_type_number, jint idc, jstring filename)
{

       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pInsertRecordBinaryFieldFromFile) 
        {
          pInsertRecordBinaryFieldFromFile = (InsertRecordBinaryFieldFromFile)dlsym(handle, "InsertRecordBinaryFieldFromFile");
          if(pInsertRecordBinaryFieldFromFile == NULL){fputs(dlerror(), stderr); return 0;}
	}

	const char* jfilename = env->GetStringUTFChars(filename, 0);

	int ret = pInsertRecordBinaryFieldFromFile(record_type_number, idc, jfilename);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;

}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_InsertRecordBinaryFieldFromMemory(JNIEnv *env, jclass, jint record_type_number, jint idc, jbyteArray input_byte_array, jint size_of_input_byte_array, jstring CGA, jint CGA_int, jint HLL, jint VLL)
{

       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pInsertRecordBinaryFieldFromMemory) 
        {
          pInsertRecordBinaryFieldFromMemory = (InsertRecordBinaryFieldFromMemory)dlsym(handle, "InsertRecordBinaryFieldFromMemory");
          if(pInsertRecordBinaryFieldFromMemory == NULL){fputs(dlerror(), stderr); return 0;}
	}

	const char* jCGA = env->GetStringUTFChars(CGA, 0);

	jboolean isCopy = JNI_FALSE;
	unsigned char* jinput_byte_array = (unsigned char*)env->GetByteArrayElements(input_byte_array, &isCopy);

	int ret = pInsertRecordBinaryFieldFromMemory(record_type_number, idc, jinput_byte_array, size_of_input_byte_array, jCGA, CGA_int, HLL, VLL);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;

}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_InsertRecordTxtField(JNIEnv *env, jclass, jint record_type_number, jint idc, jint record_field_number, jstring record_field_name, jstring input_data)
{

       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pInsertRecordTxtField) 
        {
          pInsertRecordTxtField = (InsertRecordTxtField)dlsym(handle, "InsertRecordTxtField");
          if(pInsertRecordTxtField == NULL){fputs(dlerror(), stderr); return 0;}
	}

	const char* jrecord_field_name = env->GetStringUTFChars(record_field_name, 0);
	const char* jinput_data = env->GetStringUTFChars(input_data, 0);

	int ret = pInsertRecordTxtField(record_type_number, idc, record_field_number, jrecord_field_name, jinput_data);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;

}



//----------------------------------------------------------------------------------------------------------
JNIEXPORT jint JNICALL Java_nist_1sun_1java_NIST_1library_DeleteRecord(JNIEnv *env, jclass, jint record_type_number, jint idc)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pDeleteRecord) 
        {
          pDeleteRecord = (DeleteRecord)dlsym(handle, "DeleteRecord");
          if(pDeleteRecord == NULL){fputs(dlerror(), stderr); return 0;}
	}


	int ret = pDeleteRecord(record_type_number, idc);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT int JNICALL Java_nist_1sun_1java_NIST_1library_SetNISTLibErrorReportMode(JNIEnv *env, jclass, jint nist_error_report_mode)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return 0;}
       }

	if (!pSetNISTLibErrorReportMode) 
        {
          pSetNISTLibErrorReportMode = (SetNISTLibErrorReportMode)dlsym(handle, "SetNISTLibErrorReportMode");
          if(pSetNISTLibErrorReportMode == NULL){fputs(dlerror(), stderr); return 0;}
	}


	int ret = pSetNISTLibErrorReportMode(nist_error_report_mode);

        feclearexcept(FE_ALL_EXCEPT);
	return ret;
}


//----------------------------------------------------------------------------------------------------------
JNIEXPORT jstring JNICALL Java_nist_1sun_1java_NIST_1library_ReadNISTLibLastErrorMessage(JNIEnv* env, jclass)
{
       if(handle == NULL)
       {
         handle = dlopen(filename_with_full_path_of_NIST_library, RTLD_LAZY); // open shared library;
         if(handle == NULL){fputs(dlerror(), stderr); return NULL;}
       }

	if (!pReadNISTLibLastErrorMessage) 
        {
          pReadNISTLibLastErrorMessage = (ReadNISTLibLastErrorMessage)dlsym(handle, "ReadNISTLibLastErrorMessage");
          if(pReadNISTLibLastErrorMessage == NULL){fputs(dlerror(), stderr); return NULL;}
	}


	const char* last_error_message = pReadNISTLibLastErrorMessage();
	jstring com = env->NewStringUTF(last_error_message);

        feclearexcept(FE_ALL_EXCEPT);
	return com;
}
//----------------------------------------------------------------------------------------------------------

JNIEXPORT void JNICALL Java_wsq_1sun_1java_NIST_1library_Set_1Path_1of_1NIST_1library
  (JNIEnv *env, jclass, jstring path_of_NIST_library)
{


filename_with_full_path_of_NIST_library[0] = '\0';

strcat(filename_with_full_path_of_NIST_library, env->GetStringUTFChars(path_of_NIST_library, 0));


#ifdef __x86_64__
  strcat(filename_with_full_path_of_NIST_library, "/libNIST_library_macOS_x86_64.dylib");
#else
  strcat(filename_with_full_path_of_NIST_library, "/libNIST_library_macOS_i386.dylib");
#endif


NIST_library_path_is_set = 1;



}
//----------------------------------------------------------------------------------------------------------
