//std C includes
#include <stdio.h>
#include <sys/time.h>

//JNI definition (and jni.h)
#include "sensicamJNI_Sensicam.h"

//Sensicam library includes
#include "sencaml.h"
#include "loglevel.h"
#include "errcodes.h"

JNIEXPORT jlongArray JNICALL Java_sensicamJNI_Sensicam_sen_1initboard
      (JNIEnv *env, jclass cls, jint board){

	printf("sen_initboard(%i)\n", board);
	
	HANDLE hDriver = 0;
	int ret = sen_initboard(board, &hDriver);
	
	jlongArray result;
	result = (*env)->NewLongArray(env, 2);
	if (result == NULL) { fprintf(stderr,"sen_initboard(): Alloc ret array failed"); return NULL; }

	long l[2];
	l[0] = ret;
	l[1] = hDriver;

	(*env)->SetLongArrayRegion(env, result, 0, 2, l);
	return result;
}


JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1closeboard
  (JNIEnv *env, jclass cls, jlong hDriver){
	HANDLE hand = hDriver;
	return sen_closeboard(&hand);
}


JNIEXPORT void JNICALL Java_sensicamJNI_Sensicam_sen_1enable_1message_1log
  (JNIEnv *env, jclass cls, jint msg_lev, jstring name){
 	char *nameCSTR = (char *)(*env)->GetStringUTFChars(env, name , NULL ) ;
	sen_enable_message_log(msg_lev, nameCSTR);
}

JNIEXPORT void JNICALL Java_sensicamJNI_Sensicam_sen_1set_1syslog_1facility
  (JNIEnv *env, jclass cls, jint msg_lev){
	sen_set_syslog_facility(msg_lev);
}


JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1setup_1camera
   (JNIEnv *env, jclass cls, jlong hdriver){
	return sen_setup_camera(hdriver);
}

void cam_param_in(JNIEnv *env, jobject cam_param_obj, struct cam_param *cam_param_struct){
	jclass cam_param_class = (*env)->GetObjectClass(env, cam_param_obj);

	cam_param_struct->hdriver = (*env)->GetLongField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "hdriver", "J"));
	cam_param_struct->boardnr = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "boardnr", "I"));
	cam_param_struct->boardtyp = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "boardtyp", "I"));
	cam_param_struct->boardrev = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "boardrev", "I"));
	cam_param_struct->cam_typ = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "cam_typ", "I"));
	cam_param_struct->cam_ccd = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "cam_ccd", "I"));
	cam_param_struct->cam_id = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "cam_id", "I"));
	cam_param_struct->ccdwidth = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "ccdwidth", "I"));
	cam_param_struct->ccdheight = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "ccdheight", "I"));
	cam_param_struct->bit_pix = (*env)->GetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "bit_pix", "I"));
}


void cam_param_out(JNIEnv *env, jobject cam_param_obj, struct cam_param *cam_param_struct){
	jclass cam_param_class = (*env)->GetObjectClass(env, cam_param_obj);

	(*env)->SetLongField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "hdriver", "J"), cam_param_struct->hdriver);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "boardnr", "I"), cam_param_struct->boardnr);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "boardtyp", "I"), cam_param_struct->boardtyp);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "boardrev", "I"), cam_param_struct->boardrev);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "cam_typ", "I"), cam_param_struct->cam_typ);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "cam_ccd", "I"), cam_param_struct->cam_ccd);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "cam_id", "I"), cam_param_struct->cam_id);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "ccdwidth", "I"), cam_param_struct->ccdwidth);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "ccdheight", "I"), cam_param_struct->ccdheight);
	(*env)->SetIntField(env, cam_param_obj, (*env)->GetFieldID(env, cam_param_class, "bit_pix", "I"), cam_param_struct->bit_pix);
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1get_1cam_1param
  (JNIEnv *env, jclass cls, jlong hDriver, jobject cam_param_obj){
	
	struct cam_param cam_param_struct;

	cam_param_in(env, cam_param_obj, &cam_param_struct);

	int ret = sen_get_cam_param(hDriver, &cam_param_struct);

	cam_param_out(env, cam_param_obj, &cam_param_struct);
	
	return ret;
}



JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1set_1coc
  (JNIEnv *env, jclass cls, jlong hdriver,
              jint mode,    jint trig,
              jint roixmin, jint roixmax,
              jint roiymin, jint roiymax,
              jint hbin,    jint vbin,
              jstring timevalues){

	char *timevaluesCSTR = (char *)(*env)->GetStringUTFChars(env, timevalues, NULL ) ;

	int ret = sen_set_coc(hdriver,
              mode,    trig,
              roixmin, roixmax,
              roiymin, roiymax,
              hbin,    vbin,
              timevaluesCSTR);

	return ret;
}

JNIEXPORT jintArray JNICALL Java_sensicamJNI_Sensicam_sen_1getsizes
  (JNIEnv *env, jclass cls, jlong hDriver){

	int ccdxsize, ccdysize, actualxsize, actualysize, bit_pix;

	int ret = sen_getsizes(hDriver, &ccdxsize, &ccdysize,
			                &actualxsize, &actualysize, &bit_pix);

	jintArray retArray;
	retArray = (*env)->NewIntArray(env, 6);
	if (retArray == NULL) { fprintf(stderr,"sen_getsizes(): Alloc ret array failed"); return NULL; }

	int iArr[6];
	iArr[0] = ret;
	iArr[1] = ccdxsize;
	iArr[2] = ccdysize;
	iArr[3] = actualxsize;
	iArr[4] = actualysize;
	iArr[5] = bit_pix;

	(*env)->SetIntArrayRegion(env, retArray, 0, 6, iArr);
	return retArray;

}


JNIEXPORT jintArray JNICALL Java_sensicamJNI_Sensicam_sen_1allocate_1buffer
  (JNIEnv *env, jclass cls, jlong hDriver, jint bufnr, jint size){

	int ret = sen_allocate_buffer(hDriver, &bufnr, &size);

	jintArray retArray;
	retArray = (*env)->NewIntArray(env, 3);
	if (retArray == NULL) { fprintf(stderr,"sen_allocate_buffer(): Alloc ret array failed"); return NULL; }

	int iArr[6];
	iArr[0] = ret;
	iArr[1] = bufnr;
	iArr[2] = size;

	(*env)->SetIntArrayRegion(env, retArray, 0, 3, iArr);
	return retArray;
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1free_1buffer
  (JNIEnv *env, jclass cls, jlong hDriver, jint bufnr){

	return sen_free_buffer(hDriver, bufnr);
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1run_1coc
  (JNIEnv *env, jclass cls, jlong hDriver, jint mode) {

	return sen_run_coc(hDriver, mode);
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1stop_1coc
  (JNIEnv *env, jclass cls, jlong hDriver, jint mode) {

	return sen_stop_coc(hDriver, mode);
}


void cam_values_in(JNIEnv *env, jobject cam_values_obj, struct cam_values *cam_values_struct){
	jclass cam_values_class = (*env)->GetObjectClass(env, cam_values_obj);


	cam_values_struct->runcoc = (*env)->GetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "runcoc", "I"));
	cam_values_struct->pic_in_buf = (*env)->GetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "pic_in_buf", "I"));
	cam_values_struct->ccdtemp = (*env)->GetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "ccdtemp", "I"));
	cam_values_struct->eletemp = (*env)->GetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "eletemp", "I"));
	cam_values_struct->readtime = (*env)->GetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "readtime", "I"));

	cam_values_struct->coctime = (*env)->GetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "coctime", "F"));
	cam_values_struct->beltime = (*env)->GetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "beltime", "F"));
	cam_values_struct->exptime = (*env)->GetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "exptime", "F"));
	cam_values_struct->deltime = (*env)->GetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "deltime", "F"));
}


void cam_values_out(JNIEnv *env, jobject cam_values_obj, struct cam_values *cam_values_struct){
	jclass cam_values_class = (*env)->GetObjectClass(env, cam_values_obj);

	(*env)->SetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "runcoc", "I"), cam_values_struct->runcoc);
	(*env)->SetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "pic_in_buf", "I"), cam_values_struct->pic_in_buf);
	(*env)->SetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "ccdtemp", "I"), cam_values_struct->ccdtemp);
	(*env)->SetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "eletemp", "I"), cam_values_struct->eletemp);
	(*env)->SetIntField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "readtime", "I"), cam_values_struct->readtime);

	(*env)->SetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "coctime", "F"), cam_values_struct->coctime);
	(*env)->SetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "beltime", "F"), cam_values_struct->beltime);
	(*env)->SetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "exptime", "F"), cam_values_struct->exptime);
	(*env)->SetFloatField(env, cam_values_obj, (*env)->GetFieldID(env, cam_values_class, "deltime", "F"), cam_values_struct->deltime);
	
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1get_1cam_1values
  (JNIEnv *env, jclass cls, jlong hDriver, jobject cam_values_obj) {

	struct cam_values cam_values_struct;
	cam_values_in(env, cam_values_obj, &cam_values_struct);	

	int ret = sen_get_cam_values(hDriver, &cam_values_struct);

	cam_values_out(env, cam_values_obj, &cam_values_struct);
	return ret;
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1get_1buffer_1status
  (JNIEnv *env, jclass cls, jlong hDriver, jint bufnr, jint mode, jobject devbuf_obj, jint len) {

	struct DEVBUF devbuf_struct;

	int ret = sen_get_buffer_status(hDriver, bufnr, mode, &devbuf_struct, len);

	//printf("SensicamJNI: Copied %i bytes into devbuf_struct. status is %x (size = %lu)\n", len, devbuf_struct.status,sizeof(devbuf_struct.status));

	jclass devbuf_class = (*env)->GetObjectClass(env, devbuf_obj);
	
	//What needs copying back depends on how much they asked to copy (len)
	if(len >= sizeof(devbuf_struct.status)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "status", "I"), devbuf_struct.status);
		len -= sizeof(devbuf_struct.status);
	}
	if(len >= sizeof(devbuf_struct.counter)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "counter", "I"), devbuf_struct.counter);
		len -= sizeof(devbuf_struct.counter);
	}
	if(len >= sizeof(devbuf_struct.isize)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "isize", "I"), devbuf_struct.isize);
		len -= sizeof(devbuf_struct.isize);
	}
	if(len >= sizeof(devbuf_struct.rsize)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "rsize", "I"), devbuf_struct.rsize);
		len -= sizeof(devbuf_struct.rsize);
	}
	if(len >= sizeof(devbuf_struct.map_com)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "map_com", "I"), devbuf_struct.map_com);
		len -= sizeof(devbuf_struct.map_com);
	}
	if(len >= sizeof(devbuf_struct.offset)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "offset", "I"), devbuf_struct.offset);
		len -= sizeof(devbuf_struct.offset);
	}
	if(len >= sizeof(devbuf_struct.allocsize)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "allocsize", "I"), devbuf_struct.allocsize);
		len -= sizeof(devbuf_struct.allocsize);
	}
	if(len >= sizeof(devbuf_struct.pagenum)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "pagenum", "I"), devbuf_struct.pagenum);
		len -= sizeof(devbuf_struct.pagenum);
	}
	if(len >= sizeof(devbuf_struct.pagesize)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "pagesize", "I"), devbuf_struct.pagesize);
		len -= sizeof(devbuf_struct.pagesize);
	}
	if(len >= sizeof(devbuf_struct.pagesize_last)){
		(*env)->SetIntField(env, devbuf_obj, (*env)->GetFieldID(env, devbuf_class, "pagesize_last", "I"), devbuf_struct.pagesize_last);
		len -= sizeof(devbuf_struct.pagesize_last);
	}
/* Remaining fields - not sure what todo with them and not much point in transferring
 EVENTHANDLE picevent;     //10 picture in event wait_queue
 DEVBUF_ALLOC *pagetab;    //table of vitual and physical addresses of device buffer  8
 DEVBUF_TRANSFER  *transfertab;   //table of [physical addresses,size] of actual transfer  11
 DEVBUF_TRANSFER  *ptrtransfer;   //actual pointer into transfertab 12
 struct pci_dev *pci_dev;   //actual pci device for this buffer
*/
	return ret;


}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1copy_1buffer
  (JNIEnv *env, jclass cls, jlong hDriver, jint bufnr, jobject imageByteBuf, jint size, jint offset){

	jbyte *imageData = (*env)->GetDirectBufferAddress(env, imageByteBuf);
	long imageDataLen = (*env)->GetDirectBufferCapacity(env, imageByteBuf);

	if(imageData == NULL || imageDataLen < size){
		fprintf(stderr, "SensicamJNI: sen_copy_buffer(): Byte buffer is at %p length = %li, but  "
			"%i bytes were requested to be copied. Object ptr = %p\n", imageData, imageDataLen, size, imageByteBuf);
		fflush(stderr);
		return WRONGVAL;
	}

	int ret = sen_copy_buffer(hDriver, bufnr, imageData, size, offset);
	
	return ret;
	
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1add_1buffer_1to_1list
  (JNIEnv *env, jclass cls, jlong hDriver, jint bufnr, jint size, jint offset, jint data) {

	return sen_add_buffer_to_list(hDriver, bufnr, size, offset, data);
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1remove_1buffer_1from_1list
  (JNIEnv *env, jclass cls, jlong hDriver, jint bufnr) {

	return sen_remove_buffer_from_list(hDriver, bufnr);
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_sen_1set_1buffer_1event
  (JNIEnv *env, jclass cls, jlong hDriver, jint bufnr, jint mode) {

	return sen_set_buffer_event(hDriver, bufnr, mode);
}

JNIEXPORT jint JNICALL Java_sensicamJNI_Sensicam_select_1with_1timeout
  (JNIEnv *env, jclass cls, jlong hDriver, jdouble timeoutSecs){

	struct timeval tv;
	tv.tv_sec = (time_t)timeoutSecs;
	tv.tv_usec= (suseconds_t)((timeoutSecs - tv.tv_sec) * 1e6);

	fd_set rfds,efds;
	FD_ZERO(&rfds);
	FD_ZERO(&efds);
	FD_SET(hDriver, &efds);

	//printf("SensicamJNI: Entering select with timeout %ds %dus\n", (int)tv.tv_sec, (int)tv.tv_usec);

	int ret = select(hDriver+1, NULL, NULL, &efds, &tv);

	if(FD_ISSET(0,&rfds)){
		printf("SensicamJNI: Break on ESC-key");
		return 0;
	}

	return ret;
}

int main(void){

	sen_enable_message_log(ERROR_M|INIT_M,"sencam.log");
	sen_set_syslog_facility(0);

	HANDLE hDriver = 0;
	int ret = sen_initboard(0, &hDriver);
	printf("open: ret=%i, h=%i\n", ret, hDriver);

	int retc = sen_closeboard(&hDriver);
	printf("close: %i\n", retc);
	return 0;
}
