//
// Created by Administrator on 2018/07/04.
//

#include "FrameBuffer.h"


#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#include 	<time.h>
#include	<utime.h>
#include 	<wait.h>
#include 	<signal.h>
#include 	<fcntl.h>
#include 	<stdio.h>
#include 	<stdarg.h>
#include 	<stdlib.h>
#include 	<netdb.h>
#include	<string.h>
#include 	<pthread.h>
#include 	<semaphore.h>
#include 	<termios.h>
#include 	<unistd.h>
#include 	<limits.h>
#include 	<pwd.h>
#include 	<setjmp.h>
#include 	<dirent.h>
#include 	<ctype.h>
#include	<math.h>
#include 	<termio.h>
#include 	<dlfcn.h>
#include	<errno.h>

#include 	<sys/ioctl.h>
#include 	<sys/types.h>
#include 	<sys/mman.h>
#include 	<sys/socket.h>
#include 	<sys/time.h>

#include 	<sys/times.h>
#include 	<sys/stat.h>
#include 	<sys/vfs.h>    /* or <sys/statfs.h> */
#include 	<sys/statvfs.h>
#include	<sys/sysinfo.h>

#include 	<linux/fb.h>
#include 	<linux/vt.h>
#include 	<linux/kd.h>
#include 	<linux/unistd.h>
#include 	<linux/stat.h>
#include 	<linux/tcp.h>
#include 	<linux/rtc.h>
#include 	<linux/ioctl.h>
#include 	<linux/types.h>

#include 	<net/if.h>
#include 	<netinet/in.h>
#include 	<arpa/inet.h>
#include 	<asm/types.h>
#include 	<asm/ioctls.h>


#include <jni.h>


#include <android/log.h>
static const char *TAG="Framebuffer";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)


int iFileHandle=0;
jobject mFileDescriptor=NULL;
unsigned int * fbp=NULL;
int screensize=0;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;

JNIEXPORT jobject JNICALL Java_com_xixun_lededitor_FrameBuffer_Open(JNIEnv * env, jobject obj, jstring deviceName)
  {   int iWorkResault=-1;

	{
		jboolean isCopy;
		const char * pDeviceName= (*env)->GetStringUTFChars(env, deviceName, &isCopy);
		LOGD("Open %s.", pDeviceName);
		iFileHandle = open(pDeviceName, O_RDWR);

        // modified byfw 2019-4-19  ------------start
		(*env)->ReleaseStringUTFChars(env, deviceName, pDeviceName);
		// modified byfw 2019-4-19  ------------end


		if (iFileHandle<0)
		{
			/* Throw an exception */
			LOGE("Can't open device:%s",deviceName);
			/* TODO: throw an exception */
			return NULL;
		}
	//	(*env)->ReleaseStringUTFChars(env, deviceName, pDeviceName);
	}

	{



            jint location;
            if (ioctl(iFileHandle, FBIOGET_FSCREENINFO, &finfo)) {
                    LOGE("Error reading fixed information.\n");
                    return NULL;
                }
            // Get variable screen information
            if (ioctl(iFileHandle, FBIOGET_VSCREENINFO, &vinfo)) {
                    LOGE("Error reading variable information.\n");
                    return NULL;
            }
            screensize = finfo.smem_len;
            LOGD("screensize 0x%X.", screensize);
            // screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel >> 3  // >>3 表示算出字节数
            fbp = (unsigned int * )mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,iFileHandle, 0);
            if ((int)fbp == -1) {
                    LOGE("Error: failed to map framebuffer device to memory.\n");
                return NULL;
            }
	}

	{
		jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
		jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
		jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
		mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
		(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)iFileHandle);

		(*env)->DeleteLocalRef(env,cFileDescriptor);
	}

	return mFileDescriptor;
  }

int bFirstFrameBuffer=0;
JNIEXPORT jint JNICALL Java_com_xixun_lededitor_FrameBuffer_Write(JNIEnv * env, jobject obj, jintArray Databuffer, jint iOffset, jint iLength)
{
    if (iOffset<0) return -1;
    if (iOffset>screensize/sizeof(int)/2) return -1;

    if (iLength<=0) return -1;
    if (iLength>screensize/sizeof(int)/2) iLength=screensize/sizeof(int)/2;

    if (iFileHandle<=0) return -1;
    if (Databuffer==NULL) return -1;

    //LOGD("FrameBuffer_Write iLength=%d.",iLength);

    jint * array= (*env)->GetIntArrayElements(env,Databuffer,0);

    {   iOffset+=(bFirstFrameBuffer? 0 : (screensize/sizeof(int)/2));
    }

    {   int i=0;
        for (i=0;i<iLength;i++) fbp[iOffset+i]=array[i];
        vinfo.yoffset = bFirstFrameBuffer? 0: vinfo.yres;
        ioctl(iFileHandle,FBIOPAN_DISPLAY,&vinfo);
     }

    (*env)->ReleaseIntArrayElements(env,Databuffer,array,0);
    bFirstFrameBuffer= (bFirstFrameBuffer)?0:1;
    return 0;
}
/*
 * Class:     com_xixun_lededitor_FrameBuffer
 * Method:    Close
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_xixun_lededitor_FrameBuffer_Close(JNIEnv * env, jobject obj)
{
    munmap(fbp, screensize);
    close(iFileHandle);
    (*env)->DeleteLocalRef(env,mFileDescriptor);
    return 0;
}