#include <stdio.h>
#include <ntcan.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>


int letsquit=0;

void* thread_snd(void* arg) {
    NTCAN_RESULT retvalue; /* return values of NTCAN API calls */
	CMSG cmsg;
    cmsg.id=1;
    cmsg.len=2; 
    cmsg.data[0] = 00;
    cmsg.data[1] = 00;
    CSCHED schedule;
    uint64_t timestampFreq; /*timestamp frequency*/
	uint32_t time_interval_ms=50;

    /* Request timestamp/tick frequency of interface */
	retvalue = canIoctl(*(NTCAN_HANDLE*)arg, NTCAN_IOCTL_GET_TIMESTAMP_FREQ, &timestampFreq);
	if (retvalue != NTCAN_SUCCESS) {
		printf("Gathering timestamp frequency failed with %d\n", retvalue);
        letsquit=1;
	}
    
    memset(&schedule, 0, sizeof(CSCHED));
	schedule.id = (uint32_t) 1;
	schedule.flags = NTCAN_SCHED_FLAG_EN|
					 NTCAN_SCHED_FLAG_INC16|
					 NTCAN_SCHED_FLAG_OFS0;
	schedule.time_start = 0;
	schedule.time_interval = ((timestampFreq * time_interval_ms) / 1000ULL);
	schedule.count_start = 0;
	schedule.count_stop = 0x1000;
    
    /* Configure the scheduling for the Tx object */
	retvalue = canIoctl(*(NTCAN_HANDLE*)arg, NTCAN_IOCTL_TX_OBJ_CREATE, &cmsg);
	if (retvalue != NTCAN_SUCCESS) {
		printf("Configuration of scheduling (NTCAN_IOCTL_TX_OBJ_CREATE) failed with error %x\n", retvalue);
        letsquit=1;
	}
    	/* Configure the scheduling for the Tx object */
	retvalue = canIoctl(*(NTCAN_HANDLE*)arg, NTCAN_IOCTL_TX_OBJ_SCHEDULE, &schedule);
	if (retvalue != NTCAN_SUCCESS) {
		printf("Configuration of scheduling (NTCAN_IOCTL_TX_OBJ_SCHEDULE) failed with error %d\n", retvalue);
        letsquit=1;
	}
	
	/* Start the scheduling set */
	retvalue = canIoctl(*(NTCAN_HANDLE*)arg, NTCAN_IOCTL_TX_OBJ_SCHEDULE_START, NULL);
	if (retvalue != NTCAN_SUCCESS) {
		printf("Start of scheduling failed with error %d\n", retvalue);
        letsquit=1;
	}

    sleep(60); /* OS specific delay for 5 seconds !!! */

	/* Stop the scheduling */
	retvalue = canIoctl(*(NTCAN_HANDLE*)arg, NTCAN_IOCTL_TX_OBJ_SCHEDULE_STOP, NULL);
	if (retvalue != NTCAN_SUCCESS) {
		printf("Stop of scheduling failed with error %d\n", retvalue);
	}
    letsquit=1;
}

void* thread_rcv(void* arg) {
    int i,j; /* Loop counter */
    int32_t count=8;
    CMSG rxcmsg[8];
    NTCAN_RESULT retvalue; /* return values of NTCAN API calls */
    int32_t idCount = 0x800;
    retvalue = canIdRegionAdd(*(NTCAN_HANDLE*)arg, 0, &idCount); /* Enable CAN-IDs 0-0x7ff */
    if (retvalue != NTCAN_SUCCESS)
    {
        printf("canIdRegionAdd() failed with error %d!\n", retvalue);
        return (void *) -1;
    }
    while (1) {
        if (letsquit==1) return (void *) 0;
        count=(int32_t)(sizeof(rxcmsg) / sizeof(rxcmsg[0]));
        retvalue = canRead(*(NTCAN_HANDLE*)arg, &rxcmsg[0], &count, NULL);
        if (retvalue == NTCAN_RX_TIMEOUT)
        {
            printf("canRead() returned timeout\n");
            continue;
        }
        else if (retvalue != NTCAN_SUCCESS)
        {
            printf("canRead() failed with error %d!\n", retvalue);
        }
        else
        {
            printf("canRead() received %d message(s) !\n", count);
            for (j = 0; j < (int)count; j++) {
                if(NTCAN_IS_INTERACTION(rxcmsg[j].len))
                    printf("message sent from the same port! \n");
                int len = NTCAN_LEN_TO_DATASIZE(rxcmsg[j].len);
                printf("CAN-ID of received message %d : %03x\n",j, NTCAN_ID(rxcmsg[j].id));
                if (NTCAN_IS_RTR(rxcmsg[j].len)) {
                    printf("Received a RTR message (%d)", len);
                } else {
                    printf("Received a data message with %d bytes : ", len);
                    for (i = 0; i < len; i++)
                        printf("%02x ", rxcmsg[j].data[i]);
                }
                printf("\n\n");
            }
        }
        continue;
    }
    return(0);
}

int main(){
    int net=0; /* logical net number (here: 0) */
    uint32_t mode = NTCAN_MODE_MARK_INTERACTION; /* mode used for canOpen() */
    NTCAN_HANDLE handle; /* can handle returned by canOpen() */
    uint32_t baud=0; /* configured CAN baudrate (here: 1000 kBit/s.) */
	int32_t txqueuesize=8; /* size of transmit queue */
    int32_t rxqueuesize=8; /* size of receive queue */
    int32_t txtimeout=100; /* timeout for transmit operations in ms */
    int32_t rxtimeout=1000; /* timeout for receive operations in ms */
    NTCAN_RESULT retvalue; /* return values of NTCAN API calls */
/* ############################################################### */
    retvalue = canOpen(net,
                        mode,
                        txqueuesize,
                        rxqueuesize,
                        txtimeout,
                        rxtimeout,
                        &handle);
    if (retvalue != NTCAN_SUCCESS)
    {
        printf("canOpen() failed with error %d!\n", retvalue);
        return(0);
    }
    printf("canOpen() returned OK !\n");
/* ############################################################### */
    retvalue = canSetBaudrate(handle, baud);
    if (retvalue != 0)
    {
        printf("canSetBaudrate() failed with error %d!\n", retvalue);
        canClose(handle);
        return(0);
    }
    printf("function canSetBaudrate() returned OK !\n");
/* ############################################################### */
    pthread_t tid_snd;
    pthread_t tid_rcv;
    // create thread cc
    if (pthread_create(&tid_snd, NULL, thread_snd, &handle) != 0) {
        perror("cc pthread_create error");
        return (-1);
    }
	// create thread fd
    if (pthread_create(&tid_rcv, NULL, thread_rcv, &handle) != 0) {
        perror("fd pthread_create error");
        return (-1);
    }    
    // block main() until thread_rcv return
    if (pthread_join(tid_rcv, NULL)!=0){
        perror("pthread_join fail");
    }
/* ############################################################### */
	retvalue = canClose (handle);
    if (retvalue != NTCAN_SUCCESS)
        printf("canClose failed with error %d!\n", retvalue);
    else
        printf("canClose() returned OK !\n");
/* ############################################################### */
    return(0);
}