#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <jni.h>
#include <stdlib.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <fcntl.h>

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <termios.h>

//
// Created by ritos on 2025-03-10.
//

//------------------------------------------------------------------------------
// 설명 : 시리얼포트를 연다.
// 주의 : RTS/CTS 를 제어하지 않는다.
// 시리얼포트를 열고 이전의 포트설정상태를 저장하지 않았다.
//------------------------------------------------------------------------------
int open_serial(const char *dev_name, int baud, int vtime, int vmin )
{
    int fd;
    struct termios newtio;
    // 시리얼포트를 연다.
    fd = open( dev_name, O_RDWR | O_NOCTTY | O_NONBLOCK );
    if ( fd < 0 )
    {
        // 화일 열기 실패
        printf( "Device OPEN FAIL %s\n", dev_name );
        return -1;
    }
    // 시리얼 포트 환경을 설정한다.
    memset(&newtio, 0, sizeof(newtio));
    newtio.c_iflag = IGNPAR; // non-parity
    newtio.c_oflag = 0;
    newtio.c_cflag = CS8 | CLOCAL | CREAD; // NO-rts/cts

    switch( baud )
    {
        case 115200 : newtio.c_cflag |= B115200; break;
        case 57600 : newtio.c_cflag |= B57600; break;
        case 38400 : newtio.c_cflag |= B38400; break;
        case 19200 : newtio.c_cflag |= B19200; break;
        case 9600 : newtio.c_cflag |= B9600; break;
        case 4800 : newtio.c_cflag |= B4800; break;
        case 2400 : newtio.c_cflag |= B2400; break;
        default : newtio.c_cflag |= B115200; break;
    }
    //set input mode (non-canonical, no echo,.....)
    newtio.c_lflag = 0;
    newtio.c_cc[VTIME] = vtime; // timeout 0.1초 단위
    newtio.c_cc[VMIN] = vmin; // 최소 n 문자 받을 때까진 대기
    tcflush ( fd, TCIFLUSH );
    tcsetattr( fd, TCSANOW, &newtio );
    return fd;
}
//------------------------------------------------------------------------------
// 설명 : 시리얼포트를 닫는다.
// 주의 :
//------------------------------------------------------------------------------
void close_serial( int fd )
{
    close( fd );
}

JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_openSerial(JNIEnv *env, jobject thiz, jstring strDev, jint baudrate)
{
    (void)env;
    (void)thiz;
    const char *pType;
    int result;

    pType = (*env)->GetStringUTFChars(env, strDev, NULL);
    result = open_serial(pType, baudrate, 100, 1);
    (*env)->ReleaseStringUTFChars(env, strDev, pType);

    return result;
}

JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_readSerial(JNIEnv *env, jobject thiz, jint devHandle, jbyteArray buffer, jint size)
{
    (void)env;
    (void)thiz;
    int result;

    jbyte *pBuffer = (*env)->GetByteArrayElements(env, buffer, NULL);
    result = read( devHandle, (char *)pBuffer, size );
    (*env)->ReleaseByteArrayElements(env, buffer, pBuffer, 0);

    return result;
}

JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_writeSerial(JNIEnv *env, jobject thiz, jint devHandle, jbyteArray buffer, jint size)
{
    (void)env;
    (void)thiz;
    int result;

    jbyte *pBuffer = (*env)->GetByteArrayElements(env, buffer, NULL);
    result = write( devHandle, (char *)pBuffer, size );
    (*env)->ReleaseByteArrayElements(env, buffer, pBuffer, 0);

    return result;
}

JNIEXPORT jint JNICALL Java_com_tutpro_baresip_plus_Utils_closeSerial(JNIEnv *env, jobject thiz, jint devHandle)
{
    (void)env;
    (void)thiz;
    close_serial(devHandle);

    return 1;
}
