#include <stdio.h>
#include <stdbool.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <string.h>
#include "DSLog.h"


#define TRUE   0
#define FALSE -1

#define COM_STR_SIZE            24

#define DS_COM_PREFIX           0x5FF5
#define DS_COM_TAIL             0xA0FA
#define DS_COM_CMD_THRESHOLD    0x01
#define DS_COM_CMD_MODE         0x02
#define DS_COM_CMD_REFRESH      0x03
#define DS_COM_CMD_SPEED        0x04
#define DS_COM_CMD_RTC          0x05
#define DS_COM_CMD_FRONTLIGHT   0x07
#define DS_COM_CMD_TEMPERATURE  0x08
#define DS_COM_CMD_LIGHT        0x09
#define DS_COM_CMD_ENHANCEMENT  0x12

#define DS_COM_CMD_GET_PARAMETERS 0x0A
#define DS_COM_GET_VER          0x10

#define DS_KEY_PAGEDOWN         0x01
#define DS_KEY_PAGEUP           0x02
#define DS_KEY_DOWN             0x03
#define DS_KEY_UP               0x04
#define DS_MOUSE_LEFT           0x07
#define DS_MOUSE_RIGHT          0x08
#define DS_MOUSE_DOWN           0x09
#define DS_MOUSE_UP             0x0A
#define DS_MOUSE_LEFT_CLICL     0x0B

#define DS_COSTOM_KEY_1         0x0C
#define DS_COSTOM_KEY_2         0x06
#define DS_COSTOM_KEY_3         0x05
#define DS_COSTOM_KEY_4         0x0D
#define DS_COSTOM_KEY_5         0x0E
#define DS_COSTOM_KEY_6         0x0F

#define DS_MOUSE_LONG_UP        0x10
#define DS_MOUSE_LONG_DOWN      0x11
#define DS_MOUSE_LONG_RIGHT     0x12
#define DS_MOUSE_LONG_LEFT      0x13

#define DS_STOP_MOUSE_LONG_UP   0x14
#define DS_STOP_MOUSE_LONG_DOWN 0x15
#define DS_STOP_MOUSE_LONG_RIGHT 0x16
#define DS_STOP_MOUSE_LONG_LEFT 0x17
bool isModeCheck = false;

int UART_Open(int fd,char* port);
void UART_Close(int fd);
int UART_Set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity);
int UART_Init(int fd, int speed, int flow_ctrlint , int databits, int stopbits, char parity);
int UART_Recv(int fd, char *rcv_buf, int data_len);
int UART_Send(int fd, char *send_buf, int data_len);

void set_253_parametes(int fd, unsigned int type, int value);
int set_rtc(int fd);
int get_253_parametes(int fd, unsigned int cmd);

int receiveing_remote_control(int fd);
void kill_python();
void dasung_detection();
int main(int argc, char *argv[])
{
    int fd;
    int ret;
    int result;

    if( argc < 3 || argc > 4 ){
        printf("Wrong number of arguments.\n");
        dasung_detection();

        exit(1);
    }

    if(argc == 3){
        DSLog(" %s %s %s \n", argv[0], argv[1], argv[2]);
    }else{
        DSLog(" %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]);
    }


    fd = UART_Open(fd, argv[1]);
    if(fd == FALSE){
        printf("Open  %s  error.\n", argv[1]);
        exit(1);
    }

    ret = UART_Init(fd, 115200, 0, 8, 1, 'N');
    if(ret == FALSE){
        printf("Init error.\n");
        UART_Close(fd);
        exit(1);
    }


    int mcu_version = get_253_parametes(fd, DS_COM_GET_VER);
    //printf("main get_253_parametes mcu_version =  %x.\n", mcu_version);
    bool isDisplayEnhancement = (0x11 == mcu_version || 0x31 == mcu_version);
    bool isFrontLight = (0x30 == mcu_version || 0x31 == mcu_version);
    while((result = getopt(argc, argv, "CRrs:m:t:e:f:T:l:gh")) != -1){
        switch(result){
            case 'C':
                popen("xhost +", "w");
                receiveing_remote_control(fd);
                break;
            case 'R':
                set_rtc(fd);
                break;
            case 'r':
                set_253_parametes(fd, DS_COM_CMD_REFRESH, 0);
                break;
            case 's':
                if( 0 < atoi(argv[3]) && atoi(argv[3]) < 6) {
                    set_253_parametes(fd, DS_COM_CMD_SPEED, atoi(argv[3]));
                }else{
                    printf(" 1-5 \n");
                }
                break;
            case 'm':
                {
                    isModeCheck = true;
                    int ds_ret = get_253_parametes(fd, DS_COM_CMD_ENHANCEMENT);
                    isModeCheck = false;
                    //printf("main mode check get_253_parametes ENHANCEMENT ds_ret = %d", ds_ret);
                    ds_ret = ds_ret ? 5 : 4;
                    //printf("main mode check get_253_parametes ENHANCEMENT         ds_ret = %d", ds_ret);
                    if( 1 < atoi(argv[3]) && atoi(argv[3]) <= ds_ret) {
                        set_253_parametes(fd, DS_COM_CMD_MODE, atoi(argv[3]));
                    } else{
                        printf(" 2-%d \n", ds_ret);
                    }
                break;
                }
            case 't':
                if( 0 < atoi(argv[3]) && atoi(argv[3]) < 10) {
                    set_253_parametes(fd, DS_COM_CMD_THRESHOLD, atoi(argv[3]));
                }else{
                    printf(" 1-9 \n");
                }
                break;
            case 'e':
                if( 0 <= atoi(argv[3]) && atoi(argv[3]) <= 1) {
                    set_253_parametes(fd, DS_COM_CMD_ENHANCEMENT, atoi(argv[3]));
                } else {
                    printf(" 0-1 \n");
                }
                break;
            case 'f':
                if( 0 <= atoi(argv[3]) && atoi(argv[3]) < 4) {
                    set_253_parametes(fd, DS_COM_CMD_FRONTLIGHT, atoi(argv[3]));
                } else {
                    printf(" 0-3 \n");
                }
                break;
            case 'T':
                if( 0 <= atoi(argv[3]) && atoi(argv[3]) <= 100) {
                    set_253_parametes(fd, DS_COM_CMD_TEMPERATURE, atoi(argv[3]));
                } else {
                    printf(" 0-100 \n");
                }
                break;
            case 'l':
                if( 0 <= atoi(argv[3]) && atoi(argv[3]) <= 100) {
                    set_253_parametes(fd, DS_COM_CMD_LIGHT, atoi(argv[3]));
                } else {
                    printf(" 0-100 \n");
                }
                break;
            case 'g':
                //get_253_parametes(fd, DS_COM_GET_VER);
                get_253_parametes(fd, DS_COM_CMD_THRESHOLD);
                get_253_parametes(fd, DS_COM_CMD_MODE);
                get_253_parametes(fd, DS_COM_CMD_SPEED);
                get_253_parametes(fd, DS_COM_CMD_FRONTLIGHT);
                get_253_parametes(fd, DS_COM_CMD_TEMPERATURE);
                get_253_parametes(fd, DS_COM_CMD_LIGHT);
                get_253_parametes(fd, DS_COM_CMD_ENHANCEMENT);
                UART_Close(fd);
                break;
            case 'h':
                printf("*********************************\n");
                printf("*********************************\n");
                printf(" sudo ./ds253 /dev/ttyUSB* -X X\n");
                printf(" 执行程序， 输入参数\n");
                printf(" /dev/ttyUSB*   这是设备串口号\n");
                printf(" -X  X   ：\n");
                printf("    -C      使用遥控器\n");
                //printf("    -R      发送RTC\n");
                printf("    -g      获取设备速度,模式,阈值%s%s信息\n",isDisplayEnhancement ? ",画质增强" : "", isFrontLight ? ",前光模式" : "");
                printf("    -r      刷新屏幕残影\n");
                printf("    -s  1   速度设为1，可选 1-5\n");
                printf("    -m  2   模式设为2，可选 2-4\n");
                printf("    -t  3   阈值设为3，可选 1-9\n");
                if (isDisplayEnhancement) {
                    printf("    -e  0   画质增强功能关闭, 可选0-1\n");
                    printf("            画质增强功能打开时,显示模式可选 2-5\n");
                }
                if (isFrontLight) {
                    printf("    -f  0   前光模式设为0, 可选0-3\n");
                    printf("    -T  0   色温设为0, 可选0-100\n");
                    printf("    -l  0   亮度设为0, 可选0-100\n");
                }
                printf("*********************************\n");
                printf("*********************************\n");
                
                UART_Close(fd);
                break;
            default:
                printf("Input parameter error, exit.\n");
                UART_Close(fd);
                break;
        }
    }
    return 0;
}


void dasung_detection(){
    int i = 48, fd = 0, ret = 0;
    char rcv_buf[256];
    char buf[20] = "/dev/ttyUSB";
    for(i = 48 ; i < 58 ; i++){
        buf[11] = (char)i;
        if(access(buf,F_OK) != -1){
            fd = UART_Open(fd, buf);
            if(fd == FALSE){
                printf("Open  %s  error.\n", buf);
                exit(1);
            }
    
            ret = UART_Init(fd, 115200, 0, 8, 1, 'N');
            if(ret == FALSE){
                printf("Init error.\n");
                UART_Close(fd);
                exit(1);
            }

           // UART_Send(fd, buf, COM_STR_SIZE );
            ret = get_253_parametes(fd, DS_COM_GET_VER);
            if(ret == FALSE){
                printf( "set 253 parameter error .\n");
            }else if( ret == 0x10){
                printf("you can use \'sudo ./ds253 %s xxx \'to control monitor\n" ,buf);
            }
            UART_Close(fd);
        }
    }

}


void set_253_parametes(int fd, unsigned int type, int value)
{
    int ret;
    char buf[256];

    memset(buf, 0, sizeof(buf));
    snprintf(buf, 256, "%.04X%.2X%.2X%.12X%.4X", DS_COM_PREFIX, type, value & 0xFF, 0, DS_COM_TAIL);
    
    ret = UART_Send(fd, buf, COM_STR_SIZE );
    if(ret == FALSE){
        printf( "set 253 parameter error .\n");
    }
    UART_Close(fd);

}


int set_rtc(int fd)
{
    int ret;
    char buf[256];

    time_t timep;
    struct tm *p;
    time(&timep);
    p = gmtime(&timep);

    memset(buf, 0, sizeof(buf));
    snprintf(buf, 256, "%.04X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.4X", DS_COM_PREFIX, DS_COM_CMD_RTC, 8+p->tm_hour, p->tm_min, p->tm_sec,
                                                                                                1900+p->tm_year, p->tm_mon, p->tm_mday, 0, DS_COM_TAIL);
    printf("set rtc : %s\n", buf);
    DSLog("set_rtc : %s\n", buf);
    
    ret = UART_Send(fd, buf, COM_STR_SIZE);
    if(ret == FALSE){
        printf("set RTC error.\n");
    }
    UART_Close(fd);
    return ret;
}



int get_253_parametes(int fd, unsigned int cmd)
{
    int ret;
    int i;
    char buf[256];
    char rcv_buf[256];
    char temp;
    FILE *fp;
    int ch;
    unsigned int data1,data2;

    memset(buf, 0, sizeof(buf));
    snprintf(buf, 256, "%.04X%.2X%.2X%.12X%.4X", DS_COM_PREFIX, DS_COM_CMD_GET_PARAMETERS, cmd & 0xFF, 0, DS_COM_TAIL);
    UART_Send(fd, buf, COM_STR_SIZE);
    memset(rcv_buf, 0, sizeof(rcv_buf));
    ret = UART_Recv(fd, rcv_buf, COM_STR_SIZE);
    if(ret > 0){
        rcv_buf[ret] = '\0';
    }else{
        printf("Can not receive.\n");
        return 0;
    }
    
    if(sscanf(rcv_buf, "5FF5%04X%02X%02X", &cmd, &data1, &data2) < 0){
        printf("not send ACK, jusk return.\n");
        return 0;
    }
    switch(data1){
        case DS_COM_CMD_THRESHOLD:
            printf("Get threshold   : %d.\n", data2);
            break;
        case DS_COM_CMD_MODE:
            printf("Get mode        : %d.\n", data2);
            break;
        case DS_COM_CMD_SPEED:
            printf("Get speed       : %d.\n", data2);
            break;
        case DS_COM_CMD_FRONTLIGHT:
            printf("Get front light : %d.\n", data2);
            break;
        case DS_COM_CMD_TEMPERATURE:
            printf("Get temperature : %d.\n", data2);
            break;
        case DS_COM_CMD_LIGHT:
            printf("Get light       : %d.\n", data2);
            break;
        case DS_COM_CMD_ENHANCEMENT:
            if (!isModeCheck) printf("Get enhancement : %d.\n", data2);
            break;
        case DS_COM_GET_VER:
            //printf("Get version   : %x.\n", data2);
            break;
        default:
            printf("Get Info error.\n");
    }
    return data2;
}

int receiveing_remote_control(int fd)
{
    int ret;
    int i;
    char buf[256];
    char rcv_buf[256];
    char temp;
    FILE *fp;
    int ch;
    unsigned int cmd,data1,data2;
    FILE *fstream = NULL;
    memset(rcv_buf, 0, sizeof(rcv_buf));

    for(;;){
        ret = UART_Recv(fd, rcv_buf, COM_STR_SIZE);
        if(ret > 0){
            rcv_buf[ret] = '\0';
//            printf("recv_buf :  %s\n", rcv_buf);

            if(sscanf(rcv_buf, "5FF5%02X%02X%02X", &cmd, &data1, &data2) < 0){
                printf("not send ACK, jusk return.\n");
                continue;
            }
            
/*
                printf("cmd = %d\n", cmd);
                printf("data1 = %d\n", data1);
                printf("data2 = %d\n", data2);
*/
            switch(data1){
                case DS_KEY_PAGEDOWN:
                    popen("python gg.py --KPagedown", "w");
                    break;
                case DS_KEY_PAGEUP:
                    popen("python gg.py --KPageup", "w");
                    break;
                case DS_KEY_DOWN:
                    popen("python gg.py --Kdown", "w");
                    break;
                case DS_KEY_UP:
                    popen("python gg.py --Kup", "w");
                    break;
                case DS_MOUSE_LEFT:
                    popen("python gg.py --Mleft", "w");
                    break;
                case DS_MOUSE_RIGHT:
                    popen("python gg.py --Mright", "w");
                    break;
                case DS_MOUSE_DOWN:
                    popen("python gg.py --Mdown", "w");
                    break;
                case DS_MOUSE_UP:
                    popen("python gg.py --Mup", "w");
                    break;
                case DS_MOUSE_LONG_UP:
                    popen("python gg.py --MLup", "w");
                    break;
                case DS_MOUSE_LONG_DOWN:
                    popen("python gg.py --MLdown", "w");
                    break;
                case DS_MOUSE_LONG_RIGHT:
                    popen("python gg.py --MLright", "w");
                    break;
                case DS_MOUSE_LONG_LEFT:
                    popen("python gg.py --MLleft", "w");
                    break;

                case DS_STOP_MOUSE_LONG_UP:
                    kill_python();
                    break;
                case DS_STOP_MOUSE_LONG_DOWN:
                    kill_python();
                    break;
                case DS_STOP_MOUSE_LONG_RIGHT:
                    kill_python();
                    break;
                case DS_STOP_MOUSE_LONG_LEFT:
                    kill_python();
                    break;

                case DS_MOUSE_LEFT_CLICL:
                    popen("python gg.py --Mleftclick", "w");
                    break;
        /********* costom key 1-6  ***************/
        /*                                       */
                case DS_COSTOM_KEY_1:
                    popen("python gg.py --Custom_Key_1", "w");
                    break;
                case DS_COSTOM_KEY_2:
                    popen("python gg.py --Custom_Key_2", "w");
                    break;
                case DS_COSTOM_KEY_3:
                    popen("python gg.py --Custom_Key_3", "w");
                    break;
                case DS_COSTOM_KEY_4:
                    popen("python gg.py --Custom_Key_4", "w");
                    break;
                case DS_COSTOM_KEY_5:
                    popen("python gg.py --Custom_Key_5", "w");
                    break;
                case DS_COSTOM_KEY_6:
                    popen("python gg.py --Custom_Key_6", "w");
                    break;

                default:
                    printf("Get Info error, continue.\n");
                    kill_python();
            }
            continue;
        }else{
            printf(" no receive, continue .\n");
            continue;
        }
    } 
}

void kill_python()
{
    FILE *fp = NULL;
    char buf[1024] = {0};
    char cmd[1024] = {0};

//    fp = popen("ps -aux | grep -w 'python gg.py' | tr -s ' ' | cut -d ' '  -f 2", "r");
    fp = popen("ps -aux | grep 'python gg.py' | grep -v 'grep' | tr -s ' ' | cut -d ' '  -f 2", "r");
    while(fgets(buf, 100, fp) != NULL){
        sprintf(cmd, "kill %s", buf);
        popen(cmd, "r");
    }
    pclose(fp);
}


int UART_Open(int fd,char* port)
{
	fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);
    if (FALSE == fd){
		perror("Can't Open Serial Port");
		DSLog("UART_Open : Can't Open Serial Port\n");
        return(FALSE);
    }
    
    if(fcntl(fd, F_SETFL, 0) < 0){
		printf("fcntl failed!\n");
		DSLog("fcntl failed!\n");
        return(FALSE);
    }
    
    if(0 == isatty(STDIN_FILENO)){
		printf("standard input is not a terminal device\n");
		DSLog("standard input is not a terminal device\n");
        return(FALSE);
    }
   
    return fd;
}

void UART_Close(int fd)
{
    close(fd);
}

int UART_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)
{
    int i;
    int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
                   B38400, B19200, B9600, B4800, B2400, B1200, B300, B115200};
    int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,
                   19200, 9600, 4800, 2400, 1200, 300, 115200};
    struct termios options;

    if(tcgetattr( fd,&options) != 0){
		perror("SetupSerial 1");
        return(FALSE);
    }
        
    for(i= 0;i < sizeof(speed_arr) / sizeof(int);i++) {     
		if (speed == name_arr[i]) {
			cfsetispeed(&options, speed_arr[i]);
            cfsetospeed(&options, speed_arr[i]);
        }
    }    
      
    options.c_cflag |= CLOCAL;
    options.c_cflag |= CREAD;

    switch(flow_ctrl){
		case 0 :
			options.c_cflag &= ~CRTSCTS;
            break;    
        case 1 :
            options.c_cflag |= CRTSCTS;
            break;
        case 2 :
            options.c_cflag |= IXON | IXOFF | IXANY;
            break;
    }

    options.c_cflag &= ~CSIZE;
    switch (databits){
        case 5 :
            options.c_cflag |= CS5;
            break;
        case 6    :
            options.c_cflag |= CS6;
            break;
        case 7    :
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            fprintf(stderr,"Unsupported data size\n");
            return (FALSE);
    }
 
    switch (parity) {
        case 'n':
        case 'N': 
            options.c_cflag &= ~PARENB;
            options.c_iflag &= ~INPCK;
            break;
        case 'o':
        case 'O':
            options.c_cflag |= (PARODD | PARENB);
            options.c_iflag |= INPCK;
            break;
        case 'e':
        case 'E':
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            options.c_iflag |= INPCK;
            break;
        case 's':
        case 'S':
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;
            break;
        default:
            fprintf(stderr,"Unsupported parity\n");
            return (FALSE);
    }

    switch (stopbits){
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            fprintf(stderr,"Unsupported stop bits\n");
            return (FALSE);
    }

    options.c_oflag &= ~OPOST;

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

    options.c_cc[VTIME] = 1;
    options.c_cc[VMIN] = 1;
   
    tcflush(fd,TCIFLUSH);
   
    if (tcsetattr(fd,TCSANOW,&options) != 0)
    {
       perror("com set error!/n");
       DSLog("UART_Set: com set error!/n");
       return (FALSE);
    }
    return (TRUE);
}


int UART_Init(int fd, int speed,int flow_ctrlint,int databits,int stopbits,char parity)
{
     
    if (FALSE == UART_Set(fd,speed,flow_ctrlint,databits,stopbits,parity)) {         
        return FALSE;
        } else {
			return TRUE;
       }
}

int UART_Recv(int fd, char *rcv_buf,int data_len)
{
    int len,fs_sel;
    fd_set fs_read;
    
    struct timeval time;
    
    FD_ZERO(&fs_read);
    FD_SET(fd,&fs_read);
    
    time.tv_sec = 10;
    time.tv_usec = 0;
    
    fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);
    if(fs_sel){
		len = read(fd,rcv_buf,data_len);
		return len;
    } else {
        return FALSE;
    }    
}

int UART_Send(int fd, char *send_buf,int data_len)
{
    int ret;
    
    ret = write(fd,send_buf,data_len);
    if (data_len == ret ){    
		return ret;
    } else {
		tcflush(fd,TCOFLUSH);
		return FALSE;
    } 
}


