#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>


#include "ascii_tbl.h"
#include "exprecium.h"
#include "serial.h"

#ifdef _HAS_CURSES
#   include <ncurses.h>
#   define PUT_CHAR_SCR(x) ({ wprintw(traffic_win, "%c", x); wrefresh(traffic_win); })
#   define PUT_STRING_SCR(x) ({ wprintw(traffic_win, "%s", x); wrefresh(traffic_win); })
#   define CLEAR() werase(traffic_win); wrefresh(traffic_win);
#else
#   define PUT_CHAR_SCR(x) putchar(x)
#   define PUT_STRING_SCR(x) printf(x)
#   define CLEAR() ({ int i; for(i = 0; i < 30; i++) printf("\n"); })
#endif


#define ACK 0x06
#define DC4 0x14

enum {
    EXP_TYPE,
    SERIAL_TYPE,
};


void
show_usage(void)
{
    fprintf(stderr, "Usage compatible with DOS COMIRQ:\n");
    fprintf(stderr, "Ex: ./comirq x,y -> x  = Com Port (1 or 2 or 3 or 4)\n");
    fprintf(stderr, "                 -> y  = IRQ Nb (useless dummy number)\n\n");
    fprintf(stderr, "New usage interface: \n");
    fprintf(stderr, "./comirq [OPTION] [PORT]\n");
    fprintf(stderr, "Try comirq --help for more information.\n");
}


void 
show_help(void)
{
    printf("comirq [OPTION] [PORT]\n\n");
    printf("   OPTION:\n");
    printf("      -e   Open PCI Exprecium device\n");
    printf("      -s   Open serial port\n");
    printf("   PORT:\n");
    printf("       n   Card number for PCI Expreciums or serial port number (Starting at 0)\n");
}


#ifdef _HAS_CURSES
WINDOW *traffic_win;
int x_max, y_max;

#define TOP_BANNER "Hit \"Space Bar\" to send ACK "
#define LOW_BANNER " <ESC> To exit --- \"C\" Clear screen --- Press \"A\" to enable <ACK>"

int
init_curses(void)
{ 
    initscr();
    start_color();
    
    noecho();
    cbreak();
    clear();
    
    init_pair(1, COLOR_BLACK, COLOR_WHITE);
    init_pair(2, COLOR_WHITE, COLOR_BLUE);
    getmaxyx(stdscr, y_max, x_max);
    
    traffic_win = newwin(y_max - 3, x_max, 1, 0);
    
    bkgd(COLOR_PAIR(1));
    wbkgd(traffic_win, COLOR_PAIR(2) | WA_BOLD);
    
    scrollok(traffic_win, TRUE);
    
    curs_set(0);    
	
    mvprintw(0, x_max - strlen(TOP_BANNER), "%s", TOP_BANNER);
    mvprintw(y_max - 1, 0, "%s", LOW_BANNER);
    mvprintw(y_max - 2, 0, " [AUTO ACK]: DISABLED");
    
    refresh();
    wrefresh(traffic_win);
    
    return 0;
}


int
deinit_curses(void)
{
    curs_set(1);
    delwin(traffic_win);
    endwin();
    
    return 0;
}


#endif


int
main(int argc, char *argv[])
{
    int port = -1;
    int type = -1;
    int close_flag = 0;
    char *tmp;
    int (*init_dev)(int);
    int (*close_dev)(int);
    fd_set rfds;
    fd_set wfds;
    int fd;
    int fd_tty;
    int auto_ack = 0;

    if(argc == 1) {
	show_usage();
	printf("\b");
	exit(1);
    } 
    if(!strcmp(argv[1], "--help")) {
	show_help();
	printf("\b");
	exit(1);    
    }

    else {
	if(argc == 2) {             /* old command line */
	    tmp = argv[1];
	    if(*tmp == 'x' || *tmp == 'X') {
		type = EXP_TYPE;
		port = 0;
	    }
	    else {
		tmp[1] = '\0';
		type = SERIAL_TYPE;
		port = atoi(tmp) - 1;
	    }
	}
	else if (argc == 3) {
	    tmp = argv[1];
	    switch(tmp[1]) {
		case 'e':
		case 'E':
		    type = EXP_TYPE;
		    break;
		    
		case 's':
		case 'S':
		    type = SERIAL_TYPE;
		    break;
	    }
	    tmp = argv[2];
	    port = atoi(tmp);
	}
    }
    
    if(port < 0 || type < 0) {
	show_usage();
	exit(1);
    }

    switch(type) {
	case EXP_TYPE:
	    init_dev = init_exp;
	    close_dev = close_exp;
	    break;
	case SERIAL_TYPE:
	    init_dev = init_serial;
	    close_dev = close_serial;
	    break;
	default:
	    exit(1);
    }   
    
    if((fd = init_dev(port)) > -1) {
	
#ifdef _HAS_CURSES
	fd_tty = 0;
	init_curses();
	mvprintw(0, 0, " [Reading from %s.%d]", type == SERIAL_TYPE ? "SERIAL" : "EXPRECIUM", port);
	refresh();
#else
	fd_tty = open("/dev/tty", O_RDONLY | O_NOCTTY);

	if(tcgetattr(fd_tty, &settings) < 0) perror("");
	memcpy(&backup, &settings, sizeof(struct termios));
	settings.c_lflag &= ~(ICANON | ECHO);
	if(tcsetattr(fd_tty, TCSANOW, &settings) < 0) perror("");
#endif        
	FD_ZERO(&rfds);
	FD_ZERO(&wfds);

	do {
	    unsigned char buf;
	    unsigned char out;
	    int ret;

	    FD_SET(fd_tty, &rfds);
	    FD_SET(fd, &rfds);

	    ret = select(FD_SETSIZE, &rfds, &wfds, NULL, NULL);

	    if(ret > 0) {
	    
		if(FD_ISSET(fd, &wfds)) {
		    if(buf == DC4) {
			write(fd, &out, sizeof(out));
			FD_CLR(fd, &wfds);
		    } else FD_SET(fd, &wfds);
		}

		if(FD_ISSET(fd, &rfds)) {
		    if(read(fd, &buf, sizeof(buf)) > 0) { 
			if(buf >= ' ') {
			    if(buf <= 0x7f) PUT_CHAR_SCR(buf);
			    else PUT_STRING_SCR("<XXX>");
			} 
			else PUT_STRING_SCR(__ascii[buf]);
			
			if(buf == DC4 && auto_ack) { FD_SET(fd, &wfds); out = ACK; }
			fflush(stdout);
		    }
		}

		if(FD_ISSET(fd_tty, &rfds)) {
		    int ret;
		    out = '\0';
		    if((ret = read(fd_tty, &out, sizeof(out))) > 0) {
			switch(out) {
			    struct timeval tv;
			    int l_ret;
			    
			    case ' ':
				out = ACK;
				FD_SET(fd, &wfds);
				break;    
			    case 27: /* ESC */
				tv.tv_sec = 0;
				tv.tv_usec = 10;
				FD_SET(fd_tty, &rfds);
				l_ret = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
				if(l_ret > 0) {
				    if(FD_ISSET(fd_tty, &rfds)) {
					char dummy[3]; /* get rid of the trailling garbage on stdin */
					read(fd_tty, dummy, sizeof(dummy));
					break; /* ignore non ascii input */
				    }
				}
			    case 'q':
			    case 'Q':
				close_flag++;
				break;
			    case 'a' :
			    case 'A' :
				auto_ack = !auto_ack;
				mvprintw(y_max - 2, 13, auto_ack ? "ENABLED " : "DISABLED");
				refresh();
				break;
			    case 127: /* DEL */
			    case 'c':
			    case 'C':
				CLEAR();
				break;
			}
		    }            
		}            
	    }
	
	} while(!close_flag);
    
	close_dev(fd);
    
#ifdef _HAS_CURSES
	deinit_curses();
#else

	if(tcsetattr(fd_tty, TCSANOW, &backup) < 0) perror(NULL);
	close(fd_tty);
#endif
	return 0;
    }
    return 1;
    
}


