/**
 * Author: Dragino 
 * Date: 16/01/2018
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.dragino.com
 *
 * 
*/

#include <signal.h>		/* sigaction */
#include <errno.h>		/* error messages */
#include <ctype.h>

#include "radio.h"
#include "dy_utils/dy_common.h"

#define TX_MODE 0
#define RX_MODE 1
#define RADIOHEAD 1
#define HEX_STEAM 2

#define RADIO1    "/dev/spidev0.0"

#define MAXEPOLLSIZE 1

static char ver[8] = "0.2";

/* lora configuration variables */
static char sf[8] = "7";
static char bw[8] = "125000";
static char cr[8] = "5";
static char wd[8] = "52";
static char prlen[8] = "8";
static char power[8] = "14";
static char freq[16] = "868500000";            /* frequency of radio */
static char radio[16] = RADIO1;
static char filepath[32] = {'\0'};
static char invertiq[16] = "0";
static int mode = TX_MODE;
static int payload_format = 0; 
static int device = 49;
static bool getversion = false;

/* signal handling variables */
volatile bool exit_sig = false; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
volatile bool quit_sig = false; /* 1 -> application terminates without shutting down the hardware */

/* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */

static void sig_handler(int sigio) {
    if (sigio == SIGQUIT) {
	    quit_sig = true;;
    } else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
	    exit_sig = true;
    }
}

/**
 * bytes to string
 *
 * @param[dest] bytes output point
 * @param[src] string input
 * @param[len] bytes converlenght
 * @retval  void
 * @par 标识符
 *      保留
 * @par 其它
 *      保留
 * @par 修改日志
 *      张国宏于2019-09-30创建
 */
void str2hex(uint8_t* dest, char* src, int len) {
        int i;
        char ch1;
        char ch2;
        uint8_t ui1;
        uint8_t ui2;

        for(i = 0; i < len; i++) {
                ch1 = src[i*2];
                ch2 = src[i*2 + 1];
                ui1 = toupper(ch1) - 0x30;
                if (ui1 > 9)
                        ui1 -= 7;
                ui2 = toupper(ch2) - 0x30;
                if (ui2 > 9)
                        ui2 -= 7;
                dest[i] = ui1*16 + ui2;
        }
}

void print_help(void) {
    printf("Usage: lg02_single_rx_tx   [-d radio_dev] select radio 1 or 2 (default:1) \n");
    printf("                           [-t] set as tx\n");
    printf("                           [-r] set as rx\n");
    printf("                           [-f frequence] (default:868500000)\n");
    printf("                           [-s spreadingFactor] (default: 7)\n");
    printf("                           [-b bandwidth] default: 125k \n");
    printf("                           [-w syncword] default: 52(0x34)reserver for lorawan\n");
    printf("                           [-c coderate] default: 5(4/5), range 5~8(4/8)\n");
    printf("                           [-p PreambleLength] default: 8, range 6~65535\n");
    printf("                           [-m message ]  message to send\n");
    printf("                           [-P power ] Transmit Power (min:5; max:20) \n");
    printf("                           [-o filepath ] payload output to file\n");
    printf("                           [-R] Transmit in Radiohead format\n");
    printf("                           [-B] Transmit in HEX format\n");
    printf("                           [-v] show version \n");
    printf("                           [-h] show this help and exit \n");
    printf("                           [-i] interval radio IQ  \n");
}

int DEBUG_INFO = 0;       

/* -------------------------------------------------------------------------- */
/* --- MAIN FUNCTION -------------------------------------------------------- */

int main(int argc, char *argv[])
{

    struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
	
    int c, i;

    char input[128] = {'\0'};
    char input_hex[128] = {'\0'};
    FILE *fp = NULL;
    const char *board_name = NULL;

    board_name = get_board_name();
    MSG_LOG(DEBUG_INFO, "board name is %s!!!\n", board_name);
    // Make sure only one copy of the daemon is running.
    //if (already_running()) {
      //  MSG_DEBUG(DEBUG_INFO, "%s: already running!\n", argv[0]);
      //  exit(1);
    //}

    while ((c = getopt(argc, argv, "trd:m:p:c:f:s:b:w:P:o:i:RBvh")) != -1) {
        switch (c) {
            case 'v':
                getversion = true;
                break;
            case 'd':
	        if (optarg)
	            device = optarg[0];
	        else {
	            print_help();
		    exit(1);
	        }
                break;
            case 't':
                mode = TX_MODE;
                break;
            case 'r':
                mode = RX_MODE;
                break;
            case 'R':
                payload_format = RADIOHEAD;
                break;
            case 'B':
                payload_format = HEX_STEAM;
                break;
            case 'f':
                if (optarg)
                    strncpy(freq, optarg, sizeof(freq));
                else {
                    print_help();
                    exit(1);
                }
                break;
            case 's':
                if (optarg)
                    strncpy(sf, optarg, sizeof(sf));
                else {
                    print_help();
                    exit(1);
                }
                break;
            case 'b':
                if (optarg)
                    strncpy(bw, optarg, sizeof(bw));
                else {
                    print_help();
                    exit(1);
                }
                break;
            case 'w':
                if (optarg)
                    strncpy(wd, optarg, sizeof(wd));
                else {
                    print_help();
                    exit(1);
                }
                break;
            case 'p':
                if (optarg)
                    strncpy(prlen, optarg, sizeof(prlen));
                else {
                    print_help();
                    exit(1);
                }
            case 'c':
                if (optarg)
                    strncpy(cr, optarg, sizeof(cr));
                else {
                    print_help();
                    exit(1);
                }
            case 'm':
                if (optarg)
                    strncpy(input, optarg, sizeof(input));
                else {
                    print_help();
                    exit(1);
                }
                break;
            case 'P':
                if (optarg)
                    strncpy(power, optarg, sizeof(power));
                else {
                    print_help();
                    exit(1);
                }
                break;
            case 'o':
                if (optarg)
                    strncpy(filepath, optarg, sizeof(filepath));
                else {
                    print_help();
                    exit(1);
                }
                break;
            case 'i':
                if (optarg)
                    strncpy(invertiq, optarg, sizeof(invertiq));
                else {
                    print_help();
                    exit(1);
                }
            break;
            case 'h':
            case '?':
            default:
                print_help();
                exit(0);
        }
    }

	
    if (getversion) {
		printf("lg02_single_rx_tx ver: %s\n",ver);
        exit(0);
    }	

	
	/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    /* radio device SPI_DEV init */
    radiodev *loradev;
    loradev = (radiodev *) malloc(sizeof(radiodev));

    if (device == '1'){
    loradev->nss = 90;
    loradev->dio[0] = 85;
    loradev->dio[1] = 0;
	loradev->dio[2] = 0;	
    if (strcmp(board_name, BOARD_WOOLINK_MT7621) == 0)
    {
        loradev->dio[0] = 32;
            loradev->rst = 27;
    }
    else
    {
        loradev->dio[0] = 85;
            loradev->rst = 81;
    }
	strncpy(radio, RADIO1, sizeof(radio));
    }
    else if ( device == '2'){
    loradev->nss = 108;
    loradev->rst = 4;
    loradev->dio[1] = 0;
	loradev->dio[2] = 0;

    if (strcmp(board_name, BOARD_WOOLINK_MT7621) == 0)
    {
        loradev->dio[0] = 4;
            loradev->rst = 29;
        strncpy(radio, "/dev/spidev0.1", sizeof(radio));	
    }
    else
    {
        loradev->dio[0] = 1;
        strncpy(radio, "/dev/spidev2.0", sizeof(radio));	
    }
    }

    loradev->spiport = lgw_spi_open(radio);

    if (loradev->spiport < 0) { 
        printf("opening %s error!\n",radio);
        goto clean;
    }

    loradev->freq = atol(freq);
    loradev->sf = atoi(sf);
    loradev->bw = atol(bw);
    loradev->cr = atoi(cr);
    loradev->power = atoi(power);
    loradev->syncword = atoi(wd);
    loradev->nocrc = 1;  /* crc check */
    loradev->prlen = atoi(prlen);
    loradev->invertio = atoi(invertiq);
    strcpy(loradev->desc, "RFDEV");	

    MSG("Radio struct: spi_dev=%s, spiport=%d, freq=%u, sf=%d, bw=%u, cr=%d, pr=%d, wd=0x%2x, power=%d\n", radio, loradev->spiport, loradev->freq, loradev->sf, loradev->bw, loradev->cr, loradev->prlen, loradev->syncword, loradev->power);

    if(!get_radio_version(loradev))  
        goto clean;

    /* configure signal handling */
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigact.sa_handler = sig_handler;
    sigaction(SIGQUIT, &sigact, NULL); /* Ctrl-\ */
    sigaction(SIGINT, &sigact, NULL); /* Ctrl-C */
    sigaction(SIGTERM, &sigact, NULL); /* default "kill" command */

    setup_channel(loradev);

    struct epoll_event ev;
    int epollfd;
    int iofd;

    /* TX DIO 0 */
    epollfd = epoll_create(MAXEPOLLSIZE);
    if(epollfd <= 0)
    {
        MSG_LOG(DEBUG_INFO, "Epoll create error\n");
        exit(EXIT_FAILURE);
    }
    loradev->efd = epollfd;
    iofd = digitalInputInit(loradev->dio[0]);
    if(iofd <=0 )
    {
        MSG_LOG(DEBUG_INFO, "gpio[%d] create failed\n",loradev->dio[0]);
        exit(EXIT_FAILURE);
    }
    loradev->iofd = iofd;
    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = iofd;
    if(epoll_ctl(epollfd,EPOLL_CTL_ADD,iofd,&ev))
    {
        MSG_LOG(DEBUG_INFO, "epoll ctl failed\n");
        exit(EXIT_FAILURE);
    }

    if ( mode == TX_MODE ){

		uint8_t payload[256] = {'\0'};
		uint8_t payload_len;

		if (strlen(input) < 1)
			strcpy(input, "HELLO DRAGINO");	

		if ( payload_format == RADIOHEAD ) {
			input[strlen((char *)input)] = 0x00;
			payload_len = strlen((char *)input) + 5;			
			payload[0] = 0xff;
			payload[1] = 0xff;
			payload[2] = 0x00;
			payload[3] = 0x00;
			for (int i = 0; i < payload_len - 4; i++){
				payload[i+4] = input[i];
			}
        }else if ( payload_format == HEX_STEAM ) {
            payload_len = strlen((char *)input)/2;
            str2hex(payload,input,payload_len);
        }
		else {
			snprintf(payload, sizeof(payload), "%s", input);
			payload_len = strlen((char *)payload);
		}

		printf("Trasmit Data(ASCII): %s\n", payload);
		printf("Trasmit Data(HEX): ");
		for(int i = 0; i < payload_len; i++) {
            printf("%02x", payload[i]);
        }
		printf("\n");
        single_tx(loradev, payload, payload_len);
    } else if ( mode == RX_MODE){

        struct pkt_rx_s rxpkt = {0};

        FILE * chan_fp = NULL;
        char tmp[256] = {'\0'};
        char chan_path[32] = {'\0'};
        char *chan_id = NULL;
        char *chan_data = NULL;
        static int id_found = 0;
        static unsigned long next = 1, count_ok = 0, count_err = 0;
        int i, data_size;

        rxlora(loradev, RXMODE_SCAN);

        if (strlen(filepath) > 0) 
            fp = fopen(filepath, "w+");

        MSG("\nListening at SF%i on %.6lf Mhz. port%i\n", loradev->sf, (double)(loradev->freq)/1000000, loradev->spiport);
        fprintf(stdout, "REC_OK: %lu,    CRCERR: %lu\n", count_ok, count_err);

        struct epoll_event * events = malloc(MAXEPOLLSIZE * sizeof(struct epoll_event));
        while (!exit_sig && !quit_sig) {
            /* 等待有事件发生 */
            int nfds = epoll_wait(loradev->efd, events, loradev->iofd, 1000);
            if (nfds == -1)
            {
                continue;
            }
            else {
                /* 处理所有事件 */
                for (i = 0; i < nfds; ++i)
                {
                    if ((events[i].data.fd == loradev->iofd) && (digitalState(loradev->iofd) == 1))/* read IO if RXDONE */
                    {
                        /* get timestamp for statistics */
                        struct timeval now;
                        gettimeofday(&now, NULL);
                        uint32_t tmst = (uint32_t)(now.tv_sec*1000000 + now.tv_usec);

                memset(rxpkt.payload, 0, sizeof(rxpkt.payload));
                if (received(loradev->spiport, &rxpkt)) {

                    data_size = rxpkt.size;

                    memset(tmp, 0, sizeof(tmp));

                    for (i = 0; i < rxpkt.size; i++) {
                        tmp[i] = rxpkt.payload[i];
                    }

                    if (tmp[2] == 0x00 && tmp[3] == 0x00) /* Maybe has HEADER ffff0000 */
                         chan_data = &tmp[4];
                    else
                         chan_data = tmp;

                    chan_id = chan_data;   /* init chan_id point to payload */

//                    /* the format of payload maybe : <chanid>chandata like as <1234>hello  */
//                    for (i = 0; i < 16; i++) { /* if radiohead lib then have 4 byte of RH_RF95_HEADER_LEN */
//                        if (tmp[i] == '<' && id_found == 0) {  /* if id_found more than 1, '<' found  more than 1 */
//                            chan_id = &tmp[i + 1];
//                            ++id_found;
//                        }

//                        if (tmp[i] == '>') {
//                            tmp[i] = '\0';
//                            chan_data = tmp + i + 1;
//                            data_size = data_size - i;
//                            ++id_found;
//                        }

//                        if (id_found == 2) /* found channel id */
//                            break;
                            
//                    }
//                    id_found = 0;  /* reset id_found */
                    
                    chan_fp  = fopen(chan_path, "w+");
                    if ( NULL !=  chan_fp ) {
                        //fwrite(chan_data, sizeof(char), data_size, fp);  
                        fprintf(chan_fp, "tmst %u; SF%i on %.6lf Mhz %s\n", chan_data,tmst ,loradev->sf, (double)(loradev->freq)/1000000);
                        fflush(chan_fp);
                        fclose(chan_fp);
                    } else 
                        fprintf(stderr, "ERROR~ canot open file path: %s\n", chan_path); 

                    //fprintf(stdout, "Received: %s\n", chan_data);
                    fprintf(stdout, "tmst %u; count_OK: %lu, count_CRCERR: %lu,SF%i on %.6lf Mhz rssi:%.2f snr:%.2f\n", tmst, ++count_ok, count_err,loradev->sf, (double)(loradev->freq)/1000000,rxpkt.rssi,rxpkt.snr);
                } else
                    fprintf(stdout, "REC_OK: %lu, CRCERR: %lu\n",count_ok, ++count_err);
            }
            }
            }
        }

    }

clean:
    if(fp)
        fclose(fp);
    free(loradev);
	
    MSG("INFO: Exiting %s\n", argv[0]);
    exit(EXIT_SUCCESS);
}
